feat(agent): redesign project_cli with module/method/input protocol

- Replace command/subcommand/args with module/method/input envelope
- Calendar handler uses discriminated union (mode) for read operations
- Strict Pydantic models with extra='forbid' for all calendar methods
- Worker max_iters=7, router prompt simplified (removed project_cli_defaults)
- Skill index cards + per-action files for progressive disclosure
- Frontend/AG-UI aligned to module/method dispatch
- Protocol docs updated to module/method/input contract

WIP: action cards need envelope fix, 2 tests need update, memory
handler needs Pydantic models.
This commit is contained in:
qzl
2026-04-24 13:24:13 +08:00
parent ab526af2c4
commit d060962a5f
62 changed files with 4802 additions and 805 deletions
+2
View File
@@ -176,6 +176,8 @@ run 过滤语义:
`/history` 会返回 tool 消息用于 UI 重建。tool 消息的 `ui_schema` 来自 `metadata.tool_agent_output.ui_hints` 的编译结果。
补充说明:tool 消息若来自 `project_cli`,其 `metadata.tool_agent_output.tool_call_args` 内部形状以当前 CLI 协议为准。当前 canonical 结构为 `module/method/input`,调用方不得再假设存在 `skill/action/input` 或旧 `command/subcommand/args`
`messages[].content` 在当前协议中始终是字符串:
- assistant: answer 文本
+30 -8
View File
@@ -197,7 +197,13 @@ data: <json>
说明:`TOOL_CALL_RESULT``result` 字段提供紧凑、结构化、可执行的信息(优先包含 id/status/count 等关键事实),用于 agent 后续推理与工具编排。若对应工具输出存在 `ui_hints`,后端会在 codec 层编译得到 `ui_schema` 并随事件下发。
当前 `ui_hints` 策略:仅对当前 canonical CLI 的 CRUD 子命令生成(`calendar.create/read/update/delete``contacts.read``memory.update`);`calendar.share` 不生成 `ui_hints`
当前 `ui_hints` 策略:仅对当前有稳定展示语义的 canonical method 生成,例如 `calendar.read``calendar.create``calendar.update``calendar.delete``calendar.share``calendar.accept_invite``calendar.reject_invite``contacts.read``memory.update`
协议迁移说明:
- `tool_call_args` 的模型侧 canonical 结构已统一为 `module/method/input`
- SSE 事件字段名 `tool_call_args` 保持不变,但其内部对象形状以当期 `project_cli` 协议为准。
- 前端和调试工具不得再假设 `tool_call_args.command` / `tool_call_args.subcommand` 一定存在。
补充约束:
@@ -206,6 +212,18 @@ data: <json>
- `result` 仅表示执行输出事实,不重复 `tool_call_args` 已包含的输入参数。
- `ui_schema` 为可渲染 UI 线缆格式;其源数据来自 `metadata.tool_agent_output.ui_hints`
推荐的 `tool_call_args` 形状:
```json
{
"skill": "calendar",
"action": "get_event",
"input": {
"event_id": "evt_123"
}
}
```
#### 3.3.1 tool 名称展示规范(前端本地化)
SSE 协议中的工具名字段保持后端原样,不做服务端翻译:
@@ -215,16 +233,20 @@ SSE 协议中的工具名字段保持后端原样,不做服务端翻译:
前端展示层统一通过工具名本地化映射进行中文渲染,要求兼容两类命名风格:
- dot 风格:`memory.update``calendar.read`
- snake 风格:`memory_update``calendar_read`
- dot 风格:`memory.update``calendar.get_event`
- snake 风格:`memory_update``calendar_get_event`
当前规范映射(canonical -> 中文)如下:
- `calendar.read` -> `读取日程`
- `calendar.create` -> `创建日程`
- `calendar.update` -> `更新日程`
- `calendar.delete` -> `删除日程`
- `calendar.share` -> `共享日程`
- `calendar.list_day` -> `读取当日日程`
- `calendar.list_range` -> `读取区间日程`
- `calendar.get_event` -> `读取日程详情`
- `calendar.create_event` -> `创建日程`
- `calendar.update_event` -> `更新日程`
- `calendar.delete_event` -> `删除日程`
- `calendar.invite_subscriber` -> `邀请参与者`
- `calendar.accept_invite` -> `接受邀请`
- `calendar.reject_invite` -> `拒绝邀请`
- `contacts.read` -> `读取联系人`
- `memory.update` -> `更新记忆`
+62 -20
View File
@@ -24,11 +24,11 @@
1. 项目 CLI 是受限工具执行边界,不是通用 shell。
2. agent 只暴露一个 AgentScope tool`project_cli`
3. skills 只负责向 agent 披露如何使用 `project_cli`,不承担执行 transport 或权限决策。
4. Router 是 CLI 的唯一命令分发核心,只允许白名单 `command + subcommand`
5. 每个 CLI 子命令绑定 Python handler。
3. skills 只负责向 agent 披露如何使用 `project_cli`,不承担执行 transport、分发或权限决策。
4. Router 是 CLI 的唯一动作分发核心,只允许白名单 `module + method`
5. 每个 CLI method 绑定 Python handler 或 action dispatcher
6. handler 只能调用允许的内部能力,不开放任意系统命令执行。
6.1 `project_cli` 命令权限由 runtime `allowed_commands` 与 CLI router 白名单共同约束,不能由 skills 启用状态隐式放开。
6.1 `project_cli` 权限由 runtime 的 module 白名单与 CLI router 白名单共同约束,不能由 skills 可见性隐式放开。
7. `ToolAgentOutput.result` 是 canonical machine-oriented tool result。
8. `ToolResponse` 不承载完整 `ToolAgentOutput`,只承载给 agent 使用的文本投影。
9. tool UI 只来自 `ToolAgentOutput.ui_hints`,不再经过 worker `ui_hints -> ui_schema` 链路。
@@ -38,9 +38,9 @@
一次 tool 调用按如下顺序执行:
1. AgentScope tool `project_cli` 接收到模型生成的 tool call。
2. wrapper 将 `command + subcommand + args` 映射为项目 CLI 输入。
2. wrapper 将 `module + method + input` 映射为项目 CLI 输入。
3. runtime 将受控认证凭证通过环境变量注入 CLI 子进程。
4. CLI router 将 `(command, subcommand)` 分发给对应 Python handler。
4. CLI router 将 `(module, method)` 分发给对应 Python handler。
5. handler 执行业务逻辑并返回结构化 `result`
6. wrapper 将 `result` 投影为文本,写入 `ToolResponse.content`
7. runtime tool post-processor 基于 `result` 和 runtime 上下文生成完整 `ToolAgentOutput`
@@ -56,20 +56,28 @@ Agent -> `project_cli` 的结构化入参:
```json
{
"command": "calendar",
"subcommand": "read",
"args": {
"module": "calendar",
"method": "read",
"input": {
"mode": "range",
"start_at": "2026-04-21T00:00:00+08:00",
"end_at": "2026-04-22T00:00:00+08:00"
}
}
```
补充说明:
- `project_cli` 对模型暴露的 schema 保持薄封装,只保证存在 `module``method``input` 三层。
- method 级别的严格字段校验由服务端 dispatch 到对应 Pydantic model 后执行。
- 对日期/时间字段使用强类型约束:`date` 使用 Pydantic `date`,时间区间使用带时区的 `datetime`
- 不再把完整 action matrix 直接注入工具 schema,以保留渐进式披露和较低 token 成本。
CLI 运行时输入通道采用“两者结合”:
- `argv` 为主:
- command
- subcommand
- module
- method
- mode / formatting flags
- `stdin` 为辅:
- 较大的 JSON payload
@@ -88,8 +96,8 @@ CLI 运行时输入通道采用“两者结合”:
权限边界:
- `enabled_skills` 仅控制 skill 文档可见性与注册。
- `allowed_commands` 控制 `project_cli` 可执行命令集合。
- 两者职责解耦,避免“技能可见即命令授权”的隐式耦合。
- runtime 白名单控制 `project_cli` 可执行的 module/method 集合。
- 两者职责解耦,避免“技能可见即动作授权”的隐式耦合。
## 5. CLI Output Contract
@@ -100,8 +108,8 @@ CLI handler 的原始成功输出必须是统一结构化结果。
```json
{
"ok": true,
"command": "calendar",
"subcommand": "read",
"module": "calendar",
"method": "read",
"data": {
"items": [
{
@@ -150,21 +158,24 @@ post-processor 负责生成完整 `ToolAgentOutput`,至少包括:
规则:
- `result` 是真源。
- `result` 应保留 `command``subcommand``data`
- `result` 应保留 `module``method``data`
- `ui_hints` 由 post-processor 生成,不由 worker 生成。
- tool 失败时 `error` 必须为结构化对象。
- `status` 必须是 `success | failure | partial`
`ui_hints` 输出范围(当前协议):
- 输出:当前 CLI canonical 子命令中的 CRUD 调用
- `calendar.create`
- 输出:当前业务 action 中适合稳定结构化展示的调用
- `calendar.read`
- `calendar.create`
- `calendar.update`
- `calendar.delete`
- `calendar.share`
- `calendar.accept_invite`
- `calendar.reject_invite`
- `contacts.read`
- `memory.update`
- 不输出:非 CRUD 子命令(例如 `calendar.share`
- 若某 action 暂无稳定 UI 模板,可不输出 `ui_hints`,但不能回退为 worker 生成 UI。
## 8. ToolAgentOutput Contract
@@ -184,6 +195,36 @@ post-processor 负责生成完整 `ToolAgentOutput`,至少包括:
- 必须包含后续链式调用所需的 ID/outcome/status/count 等事实。
- `ui_hints` 是 tool UI 的唯一真源。
推荐的 `result` 形状:
```json
{
"module": "calendar",
"method": "read",
"data": {
"id": "evt_123",
"title": "Project sync"
}
}
```
失败时应返回结构化、可纠正的错误对象,而不是只返回泛化文案:
```json
{
"status": "failure",
"error": {
"code": "INVALID_ACTION_INPUT",
"message": "calendar.read input does not match method schema",
"module": "calendar",
"method": "read",
"input_schema": {
"mode": "string enum(day|range|event)"
}
}
}
```
## 9. History Replay Contract
`/history` 必须支持 tool UI 回放。
@@ -234,7 +275,8 @@ tool runtime 的认证边界使用 controlled credential。
## 13. Compatibility Strategy
- 策略:`backward-compatible`
- 策略:`migration`
- `ui_schema` 作为 wire format 保留,由后端 codec 从 `ui_hints` 编译而来。
- 前端 renderer 继续消费 `ui_schema`
- `ui_hints` 作为内部字段,不直接传输给前端。
- 模型侧 `command/subcommand/args` 输入协议废弃并移除,不保留广义别名兼容。