Files
qzl d060962a5f 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.
2026-04-24 13:24:13 +08:00

283 lines
8.8 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Agent Tool Protocol
本文件定义当前项目中 AgentScope tool、项目 CLI、tool post-processor、SSE/history/persistence 之间的协议边界。
## 1. Scope
本协议覆盖:
- AgentScope tool wrapper
- 项目内受限 CLI tool
- runtime tool post-processor
- `ToolResponse`
- `ToolAgentOutput`
- tool message `content`
- tool 结果的 SSE/history/persistence 表达
本协议不覆盖:
- 前端视觉实现细节
- worker answer 文案格式
- 非 tool 的普通 assistant 文本输出
## 2. Core Principles
1. 项目 CLI 是受限工具执行边界,不是通用 shell。
2. agent 只暴露一个 AgentScope tool`project_cli`
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 的 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` 链路。
## 3. Execution Flow
一次 tool 调用按如下顺序执行:
1. AgentScope tool `project_cli` 接收到模型生成的 tool call。
2. wrapper 将 `module + method + input` 映射为项目 CLI 输入。
3. runtime 将受控认证凭证通过环境变量注入 CLI 子进程。
4. CLI router 将 `(module, method)` 分发给对应 Python handler。
5. handler 执行业务逻辑并返回结构化 `result`
6. wrapper 将 `result` 投影为文本,写入 `ToolResponse.content`
7. runtime tool post-processor 基于 `result` 和 runtime 上下文生成完整 `ToolAgentOutput`
8. `ToolAgentOutput` 进入:
- `TOOL_CALL_RESULT`
- `metadata.tool_agent_output`
- history replay
- context rebuild
## 4. Input Channels
Agent -> `project_cli` 的结构化入参:
```json
{
"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` 为主:
- module
- method
- mode / formatting flags
- `stdin` 为辅:
- 较大的 JSON payload
- 复杂对象参数
- 多步批量操作负载
- environment variables
- controlled credential
- runtime-only internal context
约束:
- 模型不可见的认证信息不得进入 tool args。
- CLI 不接受来自自然语言/模型参数的任意 token 字符串。
- backend runtime 只能通过受控环境变量注入认证凭证。
权限边界:
- `enabled_skills` 仅控制 skill 文档可见性与注册。
- runtime 白名单控制 `project_cli` 可执行的 module/method 集合。
- 两者职责解耦,避免“技能可见即动作授权”的隐式耦合。
## 5. CLI Output Contract
CLI handler 的原始成功输出必须是统一结构化结果。
示例:
```json
{
"ok": true,
"module": "calendar",
"method": "read",
"data": {
"items": [
{
"id": "evt_123",
"title": "Project sync",
"startAt": "2026-04-21T10:00:00+08:00"
}
],
"count": 1
}
}
```
失败时,CLI handler 必须返回结构化错误结果。
## 6. ToolResponse Contract
`ToolResponse` 只用于给 agent 提供 tool 结果文本投影。
规则:
- `ToolResponse.content` 只包含 `result` 的完整 JSON 文本投影。
- 不再把完整 `ToolAgentOutput` 序列化后塞进 `ToolResponse.content`
- 文本投影必须与 `result` 保持等价信息量,不做摘要裁剪。
示例:
```json
{"status":"success","items":[{"id":"evt_123","title":"Project sync","startAt":"2026-04-21T10:00:00+08:00"}],"count":1}
```
## 7. Tool Post-Processor Contract
runtime 必须在 tool 调用完成后运行 tool post-processor。
post-processor 负责生成完整 `ToolAgentOutput`,至少包括:
- `tool_name`(固定为 `project_cli`
- `tool_call_id`
- `tool_call_args`
- `status`
- `result`
- `error`
- `ui_hints`
规则:
- `result` 是真源。
- `result` 应保留 `module``method``data`
- `ui_hints` 由 post-processor 生成,不由 worker 生成。
- tool 失败时 `error` 必须为结构化对象。
- `status` 必须是 `success | failure | partial`
`ui_hints` 输出范围(当前协议):
- 输出:当前业务 action 中适合稳定结构化展示的调用
- `calendar.read`
- `calendar.create`
- `calendar.update`
- `calendar.delete`
- `calendar.share`
- `calendar.accept_invite`
- `calendar.reject_invite`
- `contacts.read`
- `memory.update`
- 若某 action 暂无稳定 UI 模板,可不输出 `ui_hints`,但不能回退为 worker 生成 UI。
## 8. ToolAgentOutput Contract
`ToolAgentOutput` 用于系统内部和前端消费,不直接作为模型上下文主输入。
消费位置:
- `TOOL_CALL_RESULT`
- 数据库存储 `metadata.tool_agent_output`
- `/history` tool UI replay
- cold-path context rebuild
规则:
- `tool_name` 固定为 `project_cli`
- `result` 必须是 JSON-native、machine-oriented 结构。
- 必须包含后续链式调用所需的 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 回放。
规则:
- history 对外返回 tool message。
- tool message 的 UI 恢复从 `metadata.tool_agent_output.ui_hints` 读取,编译为 `ui_schema` 后返回。
- tool message `content` 仍是 `result` 的 JSON 文本投影。
### 9.1 `messages.content` 存储类型决策
- 当前决策:`messages.content` 继续保持 `text`,不迁移到 `jsonb`
- 原因:
- `messages` 表承担多角色消息(user/assistant/tool),`content` 统一作为文本载荷更稳定;
- tool 的结构化数据已经由 `metadata.tool_agent_output.result``metadata.tool_agent_output.ui_hints` 承载;
- `/history`、SSE、context rebuild 当前都按“`content` 文本 + metadata 结构化字段”工作,避免双轨 schema 演进;
- 实测出现过 dict 直接写入 `messages.content` 导致驱动类型错误(`expected str, got dict`),保持 `text` 可减少写入歧义。
- 约束:凡写入 `messages.content` 的数据必须是字符串;结构化对象必须进入 `metadata`
## 10. SSE Contract
规则:
- `TEXT_MESSAGE_END` 不再包含 worker `ui_hints``ui_schema`
- `TOOL_CALL_RESULT` 携带 `ui_schema`(由后端 codec 从 `ui_hints` 编译而来)。
- tool UI 前端消费基于 `ui_schema`(由 `ui_hints` 编译)。
## 11. Controlled Credential Contract
tool runtime 的认证边界使用 controlled credential。
规则:
- chat 与 automation 都不得把 `owner_id` 当作凭证。
- controlled credential 由当前 bearer token 签发方在同一信任边界内签发。
- TTL 目标为 5-10 分钟。
- 该凭证只覆盖 tool runtime 所需权限窗口。
- 凭证仅通过 backend-controlled env 注入 CLI。
- 日志、错误响应、history、SSE 中不得暴露原始凭证。
## 12. Security Constraints
- 不开放 shell 执行能力。
- 不允许通过 tool args 传任意 token。
- 不允许通过 `owner_id` 伪造用户 Bearer token。
- 不允许把 DB session 直接注入 CLI 边界。
## 13. Compatibility Strategy
- 策略:`migration`
- `ui_schema` 作为 wire format 保留,由后端 codec 从 `ui_hints` 编译而来。
- 前端 renderer 继续消费 `ui_schema`
- `ui_hints` 作为内部字段,不直接传输给前端。
- 模型侧 `command/subcommand/args` 输入协议废弃并移除,不保留广义别名兼容。