fix(agent): 修复 skill action 卡片调用约定、memory 强类型校验和死代码清理

- 所有 calendar action .md: skill/action 替换为 module/method + mode 字段
- handler_memory: 新增 Pydantic extra=forbid 模型替代手工 dict 校验
- memory/SKILL.md: 补充 UserMemoryContent/WorkProfileContent 全字段文档
- 移除 handler_calendar 死代码 _batch_status 和 runner 旧别名 AgentScopeReActRunner
- PRD §5.2-5.6 和 sse-events 协议对齐实际 module/method 实现
This commit is contained in:
qzl
2026-04-24 14:10:57 +08:00
parent d060962a5f
commit d2d292a99e
16 changed files with 277 additions and 244 deletions
@@ -324,13 +324,14 @@ The worker still sees only:
## 5.2 New `project_cli` model-facing input contract
The new canonical model-facing payload is:
The canonical model-facing payload is:
```json
{
"skill": "calendar",
"action": "get_event",
"module": "calendar",
"method": "read",
"input": {
"mode": "event",
"event_id": "<uuid>"
}
}
@@ -338,91 +339,79 @@ The new canonical model-facing payload is:
Field meanings:
- `skill`: enabled business skill namespace
- `action`: concrete business operation inside the skill
- `input`: strict action-specific payload
- `module`: enabled business module namespace (calendar, contacts, memory)
- `method`: concrete business operation inside the module
- `input`: strict method-specific payload
This is still one tool call. The worker is not choosing among many tools.
## 5.3 Calendar action protocol
## 5.3 Calendar method protocol
The calendar skill should be redesigned around real business actions derived from `schedule_items` and `schedule_subscriptions`.
The calendar module exposes the following methods registered in the CLI router:
### Event actions
| Module | Method | Handler | Input Shape |
|----------|----------------|----------------------------------|-------------|
| calendar | read | `handle_calendar_list_day` | discriminated by `mode` |
| calendar | create | `handle_calendar_create_event` | title, start_at, timezone, ... |
| calendar | update | `handle_calendar_update_event` | event_id + patch |
| calendar | delete | `handle_calendar_delete_event` | event_id |
| calendar | share | `handle_calendar_invite_subscriber` | event_id, invitee, permissions |
| calendar | accept_invite | `handle_calendar_accept_invite` | event_id |
| calendar | reject_invite | `handle_calendar_reject_invite` | event_id |
1. `list_day`
2. `list_range`
3. `get_event`
4. `create_event`
5. `update_event`
6. `delete_event`
### Subscription actions
1. `invite_subscriber`
2. `accept_invite`
3. `reject_invite`
### Why this action set
This set directly maps to current product behavior:
- user asks what is scheduled today -> `list_day`
- user asks what is scheduled this week -> `list_range`
- user asks for a known event's details -> `get_event`
- user creates or edits a schedule item -> `create_event` / `update_event`
- user removes a schedule item -> `delete_event`
- user invites another person -> `invite_subscriber`
- invite recipient responds -> `accept_invite` / `reject_invite`
The `read` method uses a discriminated union with `mode` field to dispatch to list_day, list_range, or get_event internally.
This avoids overloading one label like `read` for two distinct business tasks.
## 5.4 Canonical calendar action shapes
## 5.4 Canonical calendar method shapes
### `list_day`
### `read` with mode=day (list one day)
```json
{
"skill": "calendar",
"action": "list_day",
"module": "calendar",
"method": "read",
"input": {
"mode": "day",
"date": "2026-04-23",
"timezone": "Asia/Shanghai"
}
}
```
### `list_range`
### `read` with mode=range (list time range)
```json
{
"skill": "calendar",
"action": "list_range",
"module": "calendar",
"method": "read",
"input": {
"mode": "range",
"start_at": "2026-04-23T00:00:00+08:00",
"end_at": "2026-04-24T00:00:00+08:00"
}
}
```
### `get_event`
### `read` with mode=event (get by ID)
```json
{
"skill": "calendar",
"action": "get_event",
"module": "calendar",
"method": "read",
"input": {
"mode": "event",
"event_id": "<uuid>"
}
}
```
### `create_event`
### `create`
```json
{
"skill": "calendar",
"action": "create_event",
"module": "calendar",
"method": "create",
"input": {
"title": "Project sync",
"start_at": "2026-04-23T16:00:00+08:00",
@@ -439,12 +428,12 @@ This avoids overloading one label like `read` for two distinct business tasks.
}
```
### `update_event`
### `update`
```json
{
"skill": "calendar",
"action": "update_event",
"module": "calendar",
"method": "update",
"input": {
"event_id": "<uuid>",
"patch": {
@@ -457,24 +446,24 @@ This avoids overloading one label like `read` for two distinct business tasks.
}
```
### `delete_event`
### `delete`
```json
{
"skill": "calendar",
"action": "delete_event",
"module": "calendar",
"method": "delete",
"input": {
"event_id": "<uuid>"
}
}
```
### `invite_subscriber`
### `share`
```json
{
"skill": "calendar",
"action": "invite_subscriber",
"module": "calendar",
"method": "share",
"input": {
"event_id": "<uuid>",
"invitee": {
@@ -493,8 +482,8 @@ This avoids overloading one label like `read` for two distinct business tasks.
```json
{
"skill": "calendar",
"action": "accept_invite",
"module": "calendar",
"method": "accept_invite",
"input": {
"event_id": "<uuid>"
}
@@ -505,8 +494,8 @@ This avoids overloading one label like `read` for two distinct business tasks.
```json
{
"skill": "calendar",
"action": "reject_invite",
"module": "calendar",
"method": "reject_invite",
"input": {
"event_id": "<uuid>"
}
@@ -556,21 +545,26 @@ This makes `view_skill_file` a real progressive-disclosure mechanism instead of
## 5.6 Error contract for self-correction
The redesigned CLI should return structured action-level validation feedback.
The redesigned CLI returns structured validation feedback with field-level detail.
Canonical error example:
```json
{
"status": "failure",
"ok": false,
"module": "calendar",
"method": "read",
"error": {
"code": "INVALID_ACTION_INPUT",
"message": "action list_range requires start_at and end_at",
"skill": "calendar",
"action": "list_range",
"missing_fields": ["start_at", "end_at"],
"unexpected_fields": ["event_id"],
"suggested_alternative_actions": ["get_event"]
"message": "input does not match method schema",
"retryable": false,
"details": {
"missing_fields": ["start_at", "end_at"],
"invalid_fields": [],
"alias_corrections": {
"start_time": "start_at"
}
}
}
}
```