# AgentScope Skill + CLI Tool Protocol Refactor PRD ## 1. Goal 本次重构的目标不是发明一套新的 agent 工具体系,而是把当前项目的工具执行、skill 使用、UI 生成这三条链路收敛到一条一致的协议路径上: 1. 以 AgentScope 原生 `tools` 和 `skills` 能力为基础。 2. 将当前项目工具包装为符合 AgentScope 工具协议的 CLI 工具。 3. skill 不负责执行工具,而是负责向 agent 渐进式披露"有哪些能力、什么时候用、怎么用"。 4. CLI 工具的 runtime 认证边界以用户登录 token 为主,默认通过受控环境变量注入,而不是直接注入数据库 session / owner_id。 5. worker agent output 不再输出 `ui_hints`。 6. tool output 继续保留独立结构化协议对象,并可携带 `ui_hints` 给前端渲染使用。 7. worker 在 ReAct loop 中消费的工具信息应收敛为 tool message `content`,而不是完整 `ToolAgentOutput` 元数据对象。 8. `message.content` 继续保持文本投影,不升级为通用 schema 字段;结构化结果进入 `ToolAgentOutput.result`。 9. `ToolResponse` 只返回 `result` 文本投影;再由 tool 后处理器把 `result` 解析/补全为完整 `ToolAgentOutput`,包括 `ui_hints` 等字段。 本次改造的核心收益: - 减少 worker 输出 token 成本 - 消除 UI 在 worker / backend compiler / frontend renderer 三处漂移 - 让工具边界对 AgentScope、skill、前端渲染都更稳定 ## 2. Corrected Design Principles 以下原则是本 PRD 的前提,必须严格遵守: 1. AgentScope 原生支持 `skills`。 - 官方 README 明确列出 built-in support for `tools, skills` - 官方示例提供 `toolkit.register_agent_skill(...)` 2. skill 是给 agent 的能力说明与工作流知识,不是业务执行 transport。 3. 如果工具改成 CLI,CLI 仍然必须服从 AgentScope 的工具协议,而不是项目自己随意定义。 4. preset/runtime 参数应该以认证凭证为边界,当前项目应使用 Bearer token,并默认通过受控环境变量注入 CLI 执行环境,而不是把 DB session 直接暴露给工具调用接口。 5. worker output 和 tool output 的协议职责必须分离,不能混成同一套字段语义。 6. 协议文档先行,`docs/protocols/**` 仍是项目单一协议真源。 7. 本项目的"CLI"是受限命令行工具,不是通用 bash/shell 执行能力。 ## 3. Confirmed External Facts ### 3.1 AgentScope 对 Skills 的官方支持 已确认的外部事实: - AgentScope 官方 README 明确写有 built-in support for `skills` - 官方示例 `examples/functionality/agent_skill/main.py` 使用: - `toolkit.register_agent_skill(...)` - 官方示例同时说明: - 使用 agent skill 时,agent 需要具备查看文本文件等基础工具能力 - skill 目录通过 `SKILL.md` 进行渐进式披露 已确认的 skill 示例结构: - `SKILL.md` 包含 frontmatter:`name`、`description` - skill 内容描述: - 适用场景 - 推荐工作流 - 需要执行的脚本/命令 - 何时读取补充材料 结论:本项目不应绕开 AgentScope skill 能力自行设计一套"伪 skill"机制。 ### 3.2 AgentScope 工具协议的确认边界 已确认的 AgentScope 工具使用方式: - `Toolkit.register_tool_function(...)` 注册 callable function - tool 返回 `ToolResponse` - `ToolResponse.content` 可承载 `TextBlock` - toolkit 支持 `preset_kwargs` 这意味着如果本项目改成 CLI 路径,落点应是: - 对 agent 来说,固定暴露为单一 AgentScope tool:`project_cli` - 对 tool 的内部实现来说,它可以调用 CLI - CLI 的输入输出要能被稳定转换为 AgentScope `ToolResponse` ## 4. Confirmed Repository Facts ### 4.1 当前工具执行方式 当前项目仍然是 Python 函数直接注册多个工具: - `backend/src/core/agentscope/tools/toolkit.py` - `TOOL_FUNCTIONS` 直接绑定: - `calendar_read` - `calendar_write` - `calendar_share` - `user_lookup` - `memory_write` - `memory_forget` - `build_toolkit()` 通过 `toolkit.register_tool_function(...)` 注册这些函数 - `backend/src/core/agentscope/tools/tool_config.py` - 现统一使用 `enabled_skills`(`calendar|contacts|memory`)做能力白名单 - `backend/src/core/agentscope/runtime/runner.py` - `_build_toolkit()` 根据 `enabled_skills` 选择技能白名单 结论:当前实现尚未接入 AgentScope skill,也尚未接入 CLI 工具边界。 ### 4.2 当前项目中的认证边界 当前项目已有明确的 Bearer token 用户解析路径: - `backend/src/v1/users/dependencies.py` - `get_current_user()` 从 `Authorization: Bearer ` 解析 token - 先走 JWT verifier,再 fallback 到 Supabase user lookup - 最终产出 `CurrentUser` - `backend/src/core/auth/models.py` - `CurrentUser` 当前字段:`id`、`phone`、`role` 结论:如果工具改为 CLI,runtime 的合理边界应是 token 或 auth credential,而不是 DB session / owner_id。token 默认通过受控环境变量注入 CLI 运行环境,CLI 内部再根据 token 推导用户身份和数据访问上下文。 补充约束: - 不能仅凭 `owner_id` 直接构造用户 Bearer token;`owner_id` 不是签名凭证。 - automation 场景由当前 Bearer token 的签发方负责基于 `automation_jobs.owner_id` 签发短期受控凭证;若现有签发链路不支持,则补一个服务端签发函数。 - 当前假设该签发方与用户登录 Bearer token 的签发体系一致;现有若仅支持"邮箱 + 验证码"登录签发,则在同一信任边界下新增 automation 凭证签发能力。 - automation 受控凭证生命周期目标为 5-10 分钟,只覆盖 agent 调用工具所需窗口。 - 该凭证的签发方式、有效期、权限边界和审计规则必须在协议文档中写清。 ### 4.3 当前 worker output 与 UI 路径 当前 worker output 仍然包含 UI 描述: - `backend/src/schemas/agent/runtime_models.py` - `WorkerAgentOutputRich.ui_hints` 当前 runtime 会把 `ui_hints` 带入最终文本事件: - `backend/src/core/agentscope/runtime/stage_emitter.py` - `emit_final_text_end()` 将 `ui_hints` 写入 payload 当前对外 AG-UI payload 会把 `ui_hints` 编译为 `ui_schema`: - `backend/src/core/agentscope/events/agui_codec.py` - `TEXT_MESSAGE_END` 上执行 `compile_ui_hints(...)` 当前 history 回放同样依赖 `ui_hints -> ui_schema`: - `backend/src/v1/agent/utils.py` - `backend/src/v1/agent/schemas.py` 结论:当前 `ui_schema` 的真源是 worker 自身输出的 `ui_hints`。本次改造将把 `ui_hints` 的来源从 worker output 改为 tool output,`ui_hints → ui_schema` 的编译链路保持不变。 ### 4.4 当前前端消费方式 前端当前消费的是后端下发的编译后 `ui_schema`: - `apps/lib/core/chat/ag_ui_event.dart` - `apps/lib/core/chat/chat_history_repository.dart` - `apps/lib/shared/widgets/ui_schema/ui_schema_renderer.dart` - `apps/lib/features/home/presentation/widgets/home_chat_item_renderer.dart` 结论:前端目前理解的是后端已编译好的 `ui_schema`。本次改造目标不是改变前端消费方式,而是改变 `ui_hints` 的来源(从 worker 改为 tool),`ui_hints → ui_schema` 编译链路保持不变。 ### 4.5 当前 tool result 结构 当前 tool result 已经走 machine-oriented 路线,但结构还不完整: - `backend/src/core/agentscope/tools/utils/tool_response_builder.py` - tool 最终返回 `ToolResponse` - 当前 `ToolResponse` 中仍混入完整 tool 结构,不符合目标边界 - `backend/src/core/agentscope/utils/parsing.py` - 从 ToolResponse text block 中恢复 `ToolAgentOutput` - `backend/src/core/agentscope/runtime/stage_emitter.py` - `TOOL_CALL_RESULT` payload 包含 `tool_name`、`tool_call_args`、`status`、`result` 当前仓库已经存在"给 agent 的文本内容"和"给事件/持久化的完整结构化对象"分离的模式: - `backend/src/core/agentscope/events/store.py` - tool 消息持久化时:`content = tool_output.result` - 完整结构化对象进入 `metadata.tool_agent_output` - `backend/tests/unit/core/agentscope/events/test_store.py` - 明确校验 tool message `content` 等于文本结果 - 同时完整 `tool_agent_output` 保存在 metadata 中 结论:当前仓库已经证明一个正确的职责分层: - worker/上下文消费的应是 tool message `content` - AG-UI 事件、持久化和前端渲染可以保留完整 `ToolAgentOutput` 但当前仍有三个缺口: - `ToolAgentOutput` 还没有承载前端所需 `ui_hints` - `ToolAgentOutput.result` 仍是字符串,还没有升级为结构化对象字段 - `ToolResponse` 还没有收敛为只承载 `result` 文本投影,再交给 tool 后处理器生成完整 `ToolAgentOutput` 此外,history/replay 的正确恢复方向也已经比较明确: - 可以像 worker output 一样,从 `message.metadata` 中恢复结构化对象 - 对 tool 来说,恢复源应是 `metadata.tool_agent_output.ui_hints` ## 5. Problem Statement 当前实现存在 5 个核心问题: 1. 工具实现直接耦合在 Python runtime 函数注册层,缺少独立协议边界。 2. skill 尚未成为 agent 的原生能力入口,工具知识仍主要依赖 prompt 和函数 docstring。 3. worker 仍承担 UI 描述职责,导致 `ui_hints` 占 token,且与前后端实现容易漂移。 4. tool result 已经拥有独立结构化对象,但没有成为 UI 渲染主路径。 5. 当前 tool output 协议没有明确区分"给 worker 推理的 content"和"给前端渲染的完整结构化输出"。 6. 当前 CLI 工具边界尚未定义为受限命令行工具,容易和通用 shell 执行概念混淆。 ## 6. Target Architecture ### 6.1 总体方向 目标架构应遵循下面的职责分层: 1. AgentScope skill - 负责告诉 agent: - 有哪些工具能力 - 什么时候调用 - 调用前后如何决策 - 不直接承担业务执行 2. AgentScope tool - agent 固定只看到一个工具入口:`project_cli` - 内部执行方式为结构化 `command + subcommand + args` -> 调用项目 CLI -> 适配回 ToolResponse 3. CLI tool - 是业务执行边界 - 输入输出必须稳定、结构化、可验证 - 采用经典命令树:`command + subcommand + args` - 不按 `skill + action` 建模 CLI - 必须符合 AgentScope 工具协议适配需要 - 必须是受限项目命令行工具,不提供通用 bash/shell 能力 4. Tool output protocol - 保留独立的 `ToolAgentOutput` 类协议对象 - 对 AG-UI 事件、持久化元数据、前端渲染负责 - 可包含 `ui_hints` 这类前端渲染字段 5. UI contract - 由 tool output 中的 `ui_hints` 编译生成 `ui_schema` - `ui_schema` 成为前端渲染的输入 - worker output 不再输出 `ui_hints` ### 6.2 Token / 受控凭证作为 runtime 注入参数 本项目要求将 Bearer token 或受控服务端凭证作为 tool runtime 注入参数,而不是 DB 级参数。 目标边界: - AgentScope tool 被调用时,runtime 将认证 token 或受控服务端凭证注入 CLI 执行环境 - CLI 接收到 token 后: - 验证 token - 解析用户身份 - 推导可访问数据范围 - 再进入 service / repository / DB 层 禁止继续扩大的错误边界: - 不应把 `AsyncSession` 直接暴露到 CLI 工具调用边界 - 不应把 `owner_id` 当作唯一可信的输入 - 不应让 agent 直接控制数据库上下文对象 当前优先方案: - token 不作为模型可见的业务参数字段进入 tool args schema - token 由 runtime 以默认环境变量方式注入到 CLI 子进程 - CLI 只读取受控环境变量,不从自然语言或 tool args 中接受任意 token 字符串 - automation 不直接伪造用户 Bearer token;由当前 token 签发方基于 `automation_jobs.owner_id` 获取或签发 5-10 分钟短期受控凭证后注入 ### 6.3 ToolResponse 与 ToolAgentOutput 的边界 本次协议边界明确如下: - CLI/Tool 原始执行结果先产出结构化 `result` - AgentScope `ToolResponse` 只承载给 agent 使用的 `result` 文本投影 - 不再把完整 `ToolAgentOutput` 直接塞进 `ToolResponse.content` - runtime 内增加 tool 后处理器:基于 `result` 解析/补全完整 `ToolAgentOutput` - 该后处理器负责补齐: - `tool_name` - `tool_call_id` - `tool_call_args` - `status` - `result` - `error` - `ui_hints` 这样职责分层固定为: - agent 只看到 `message.content` / `ToolResponse` 中的 `result` 文本投影 - runtime / SSE / history / persistence 使用完整 `ToolAgentOutput` ### 6.4 Worker 与 Tool 输出职责分离 根据当前仓库已存在的运行时与持久化逻辑,本次重构必须明确保持以下分层: 1. worker output - 面向 worker 最终回答 - 不再包含 `ui_hints` 2. tool output - 使用 `ToolAgentOutput` 作为结构化协议对象 - 面向 AG-UI 事件、历史持久化、前端渲染 - 可以包含 `ui_hints` 3. tool message content - 面向 agent ReAct loop 上下文 - 应是工具结构化结果的稳定文本投影 - 不应把完整 `ToolAgentOutput` 字段直接作为下一轮 worker 输入上下文 当前仓库中的支持证据: - `backend/src/core/agentscope/events/store.py` - tool message `content = tool_output.result` - metadata 保留完整 `tool_agent_output` 因此正确方向不是删除 `ToolAgentOutput`,而是: - 保留 `ToolAgentOutput` 作为独立 tool 协议对象 - 只让 worker 主要消费 tool message `content` ### 6.4 `message.content` 字段策略 本次不建议把 `message.content` 从文本改成通用 schema 字段。 原因: 1. AgentScope/Msg 本身是通用消息抽象,不同 role 的 content 类型已经存在差异,但当前项目的 worker 推理、历史持久化、事件处理都默认把文本内容当作主信息载体。 2. 当前仓库已经有一条可工作的分层: - `message.content` 给 worker 推理 - `metadata.tool_agent_output` 给结构化消费 3. 若把 `message.content` 直接升级为 schema,会把: - ReAct loop - memory/context assembly - event store - history replay - frontend parsing 一起拉进大范围协议改动,收益不成比例。 因此推荐策略是: - `message.content` 继续保持文本 - 文本内容由 `ToolAgentOutput.result` 结构化对象稳定投影生成 - 结构化真源放在 `ToolAgentOutput.result` 也就是说,后续语义应变成: - `message.content`: 给 worker 的完整 JSON 文本投影 - `ToolAgentOutput.result`: 给系统使用的结构化对象真源 ### 6.5 UI 真源迁移方向 本次改造的目标不是"前端直接裸渲染任意 CLI 参数 schema",而是: 1. 先把 tool output 协议收敛为稳定的结构化 schema 2. 在 tool output 协议中保留前端所需 `ui_hints` 3. 再从该协议导出 AG-UI / history / frontend 统一 contract 4. AG-UI 传输时:后端 codec 将 `ui_hints` 编译为 `ui_schema` 后传输 5. 前端渲染 `ui_schema`(复用现有 `UiSchemaRenderer`) **关键设计决策**: - `ui_hints` 是 tool 输出的描述性 UI 表示(真源) - `ui_schema` 是编译后的渲染性 UI 表示(传输格式) - 编译链路 `ui_hints → ui_schema` 保持不变,仅改变 `ui_hints` 的来源 - 前端继续使用现有 `UiSchemaRenderer` 渲染 `ui_schema` 因此本次必须避免两个错误方向: 1. 继续保留 `worker -> ui_hints -> ui_schema`(应改为 `tool -> ui_hints -> ui_schema`) 2. 让前端直接绑定 `ui_hints` 而不是 `ui_schema` 3. 把完整 `ToolAgentOutput` 直接塞回 worker 推理输入,而不是让 worker 主要消费 tool message `content` 4. 把"项目 CLI"实现成可执行任意 shell 的通用命令入口 ## 7. Required Refactor Requirements ### 7.1 Skill 接入要求 必须: - 基于 AgentScope 原生 `register_agent_skill(...)` 接入 skill - 提供项目内 skill 目录与 `SKILL.md` - skill 内容必须包含: - 工具能力说明 - 何时调用哪个工具 - 参数构造规则 - 组合调用规则 - 失败后的恢复策略 不允许: - 用单纯 prompt 文本替代 skill - 自定义一套与 AgentScope skill 不兼容的 skill 机制 ### 7.2 CLI Tool Protocol 要求 CLI 工具必须满足: - 能被 AgentScope tool wrapper 稳定调用 - 输入参数可从当前 AgentScope tool schema 映射得到 - 输出结果可稳定映射回 `ToolResponse` - 输出至少包含: - 状态 - 结构化结果 - 错误信息 - 前端渲染所需的稳定字段 CLI 输出在进入 AgentScope tool wrapper 后,最终需要映射成两层信息: 1. `message.content` - 给 worker ReAct loop 使用 - 应为结构化 `result` 的稳定文本投影 - 由结构化 `result` 稳定投影生成 - 默认保留完整 JSON 文本投影,不做阉割式摘要裁剪 2. `ToolAgentOutput` - 给 AG-UI 事件、持久化、前端渲染使用 - 至少包含 `tool_name`、`tool_call_id`、`tool_call_args`、`status`、`result`、`error` - 本次应扩展支持 `ui_hints` 这里的实现约束已经确定: - `ToolResponse` 只返回 `result` 文本投影 - 完整 `ToolAgentOutput` 由 tool 后处理器在 runtime 内生成 - `ui_hints` 由 tool 后处理器生成并写入 `ToolAgentOutput` - `ui_hints` 经后端 codec 编译为 `ui_schema` 后传输给前端 - worker 不参与 tool UI 生成 需要显式设计: - stdin / argv / env 何者承载输入 - token 放在哪个输入通道 - stdout 返回 JSON 的格式 - 非 0 exit code 的错误语义 当前已确认的输入通道策略: - CLI 输入通道采用"两者结合" - 默认以 `argv` 为主、`stdin` 为辅 - token 继续通过受控环境变量注入,不进入模型可控参数 ### 7.3 Tool Configuration 保留要求 以下配置能力必须保留: - `enabled_skills` from system agents config - `enabled_skills` from automation jobs config 允许变化的部分: - `enabled_skills` 不再映射到 Python 函数名 - 改为映射到 CLI-backed tool capability ### 7.4 Worker Output 精简要求 worker output 必须移除: - `ui_hints` worker output 保留: - `status` - `answer` - `suggested_actions` - `error` 是否增加新的结构化字段,必须以协议文档先定义为前提;禁止再次引入新的"隐式 UI 描述字段"。 ### 7.5 Tool Output 协议重构要求 `ToolAgentOutput` 不应被删除,而应被重构为更完整的 tool 协议对象。 至少应满足: - 保留当前已有字段: - `tool_name` - `tool_call_id` - `tool_call_args` - `status` - `result` - `error` - 扩展支持: - `ui_hints` 其中 `result` 应升级为结构化对象字段,不再以字符串为真源。 同时需要新增一条投影规则: - 从结构化 `result` 生成稳定文本投影,写入 tool message `content` - 默认使用完整 JSON 投影,保持与 `result` 信息量一致,避免因摘要裁剪削弱模型对结构化数据的读取能力 同时需要固定 tool 后处理规则: - 后处理器以 tool 原始 `result` 为真源 - 根据 result 解析/补齐 `ToolAgentOutput` - `ui_hints` 由后处理器生成并写入 `ToolAgentOutput` - worker 不参与 tool UI 生成 这样可以满足两条不同消费链路: - worker 继续消费由 `result` 投影生成的 `message.content` - AG-UI / history / frontend 消费完整 `ToolAgentOutput` ### 7.6 UI Contract 重构要求 必须完成: - `TEXT_MESSAGE_END` 不再依赖 worker `ui_hints` 编译 - history assistant message 不再依赖 `metadata.agent_output.ui_hints` 编译 - tool result 成为 UI 合约的主要输入之一 - `TOOL_CALL_RESULT` / tool metadata / history replay 能保留 tool `ui_hints` AG-UI 传递策略调整为: - tool 事件内部携带 `ui_hints` - 后端 codec 将 `ui_hints` 编译为 `ui_schema` - AG-UI 对前端传输 `ui_schema` - 前端继续复用现有 UI 渲染器渲染 `ui_schema` - history 恢复时优先从 `message.metadata.tool_agent_output.ui_hints` 读取并编译为 `ui_schema` 后渲染 这里的设计结论已经确定: - `ui_hints` 是 tool 输出的 UI 描述(真源) - `ui_schema` 是编译后的渲染格式(传输格式) - 编译链路 `ui_hints → ui_schema` 保持不变 - 前端继续使用现有 `UiSchemaRenderer` 渲染 `ui_schema` ## 8. Scope Of Changes ### 8.1 Protocol Docs 必须更新: - `docs/protocols/ui/ui-schema.md` - `docs/protocols/ui/data-flow.md` - `docs/protocols/agent/sse-events.md` - `docs/protocols/agent/run-agent-input.md` - `docs/protocols/agent/api-endpoints.md` 建议新增: - 一个工具协议文档,明确: - CLI 输入输出约定 - token preset 约定 - tool result 到 UI contract 的映射边界 ### 8.2 Backend 主要改动点: - `backend/src/core/agentscope/tools/toolkit.py` - `backend/src/core/agentscope/tools/tool_config.py` - `backend/src/core/agentscope/runtime/runner.py` - `backend/src/core/agentscope/runtime/stage_emitter.py` - `backend/src/core/agentscope/events/agui_codec.py` - `backend/src/core/agentscope/events/store.py` - `backend/src/core/agentscope/runtime/tasks.py` - `backend/src/core/agentscope/utils/parsing.py` - `backend/src/schemas/agent/runtime_models.py` - `backend/src/v1/agent/utils.py` - `backend/src/v1/agent/schemas.py` - 新增 skill 资产与 CLI adapter - 新增 Python console entrypoint 与内嵌 command router skill 资产目录精确落点: - `backend/src/core/agentscope/tools/custom` skill 部署方式: - 参考 AgentScope skill 提供的原生装载/注册方法进入运行时与部署产物 - 现有 `custom/*.py` 不再保留为最终工具实现,后续由 CLI handler / router 重写替换 ### 8.3 Cleanup / 收尾清理 本次改造完成后,必须明确收尾清理以下代码与路径,避免旧协议残留: 1. 删除或下线 worker `ui_hints` 主路径 - `backend/src/schemas/agent/runtime_models.py` 中 worker rich output 的旧定义 - `backend/src/core/agentscope/runtime/stage_emitter.py` 中向 `TEXT_MESSAGE_END` 写入 worker `ui_hints` 的逻辑 - `backend/src/core/agentscope/events/agui_codec.py` 中 `TEXT_MESSAGE_END` 的 `compile_ui_hints(...)` 主路径 - `backend/src/v1/agent/utils.py` 中 assistant history 读取 worker `ui_hints` 并编译的逻辑 2. 清理旧文档语义 - `docs/protocols/ui/data-flow.md` 中"worker ui_hints 是 UI 真源"的描述 - `docs/protocols/ui/ui-schema.md` 中把 UI compiler 绑定到 worker `ui_hints` 的描述 - `docs/protocols/agent/sse-events.md` / `api-endpoints.md` / `run-agent-input.md` 中与旧 worker UI 路径冲突的描述 3. 清理测试中的旧假设 - 依赖 worker `ui_hints` 的 runtime/event/history 单测 - 依赖 `ToolAgentOutput.result` 为字符串真源的测试 4. 清理旧前端假设 - 只接受 assistant `ui_schema` 而不理解 tool `ui_schema` 的渲染逻辑 - 历史/事件解析中把 assistant UI 当唯一 UI 来源的路径 5. 清理旧工具调用边界 - 继续把 Python runtime 函数当作唯一稳定工具边界的 registry 代码 - 任何会把 DB session / owner_id 直接注入到 CLI 边界的适配代码 - 旧 `custom/*.py` 直接工具实现 6. 清理第二条 runtime 冷路径 - `backend/src/core/agentscope/runtime/tasks.py` 中与新 tool output/context rebuild 不一致的旧序列化路径 - 所有会导致热路径、冷路径看到不同 tool 上下文的兼容逻辑 ### 8.4 Frontend 主要改动点: - `apps/lib/core/chat/ag_ui_event.dart` - `apps/lib/core/chat/chat_history_repository.dart` - `apps/lib/shared/widgets/ui_schema/ui_schema_renderer.dart`(保持不变,继续渲染 `ui_schema`) - `apps/lib/features/home/presentation/widgets/home_chat_item_renderer.dart` ## 9. Phased Plan ### Phase 1. Protocol Correction 先修正文档中的错误前提: - 明确 AgentScope skill 是原生能力 - 明确 CLI 是工具实现载体,不是替代 AgentScope tool/skill - 明确 token 环境变量注入边界 - 明确仅 worker `ui_hints` 将被移除,tool `ui_hints` 保留在独立协议中 - 明确 `ui_hints → ui_schema` 编译链路保持不变 ### Phase 2. Skill And Tool Boundary Refactor - 接入项目内 skill - 定义单一 `project_cli` AgentScope tool wrapper - 使用 Pydantic 化的 `enabled_skills` 作为唯一配置字段 - CLI 输入通道采用"两者结合",默认 `argv` 为主、`stdin` 为辅 - 定义 Python console entrypoint 作为外部 CLI 入口 - 定义内嵌 command router 作为唯一分发核心 - router 只注册白名单 `command + subcommand` - 每个命令绑定 Python handler - handler 只调用允许的内部能力 - 默认不开放 shell 执行能力 - 移除旧多工具暴露实现,改为 skills 手册 + 单一 CLI 入口 ### Phase 3. Auth-Preset Refactor - 将 tool preset 从 runtime DB context 改为 auth token / 受控凭证 - CLI 内部完成身份解析与数据作用域推导 - token 默认通过受控环境变量进入 CLI,而不是模型可控参数 - automation 基于 `automation_jobs.owner_id` 获取或签发受控凭证,不直接伪造用户 Bearer token ### Phase 4. UI Source Refactor - 删除 worker `ui_hints` 输出 - 删除 worker `ui_hints -> ui_schema` 编译主路径 - 将 tool `ui_hints` 纳入 `ToolAgentOutput` 协议 - 将 `ToolAgentOutput.result` 升级为结构化对象 - 建立 `result -> message.content` 的稳定完整 JSON 文本投影规则 - 建立 tool output `ui_hints → ui_schema` 的编译路径 ### Phase 4.5 Runtime Path Unification - 改造 `backend/src/core/agentscope/runtime/tasks.py` - 确保热路径、冷路径、context rebuild 都使用同一套: - `ToolAgentOutput.result` 结构化对象真源 - `message.content` 完整 JSON 文本投影 - `metadata.tool_agent_output` 作为历史恢复真源 - 移除第二条旧路径,避免同一线程在不同路径下看到不同 tool 上下文 ### Phase 5. Frontend Alignment - 调整事件解析和 history 解析,从 `TOOL_CALL_RESULT.ui_schema` 读取 - 渲染逻辑保持不变,继续使用 `UiSchemaRenderer` 渲染 `ui_schema` ## 10. Acceptance Criteria - [ ] PRD 与协议文档不再错误描述 AgentScope skill 能力 - [ ] skill 通过 AgentScope 原生能力接入,而不是自定义伪 skill 机制 - [ ] 单一 `project_cli` 能通过 AgentScope tool protocol 被稳定调用 - [ ] 工具 runtime 以 Bearer token / 受控服务端凭证为边界 - [ ] token/凭证 默认以受控环境变量注入 CLI 执行环境 - [ ] CLI 能基于 token/凭证 解析用户身份与数据访问范围 - [ ] automation 能基于 `owner_id` 由当前 token 签发方签发 5-10 分钟受控凭证,但不直接伪造用户 Bearer token - [ ] `enabled_skills` 能驱动技能手册和允许命令域配置 - [ ] worker output 不再包含 `ui_hints` - [ ] `ToolAgentOutput` 保留为独立 tool 协议对象,并支持 `ui_hints` - [ ] worker 在上下文中主要消费 tool message `content/result`,而不是完整 `ToolAgentOutput` - [ ] `message.content` 保持文本,不升级为通用 schema 字段 - [ ] `ToolAgentOutput.result` 升级为结构化对象真源 - [ ] `message.content` 采用完整 JSON 文本投影,而不是裁剪摘要 - [ ] `ToolResponse` 只承载 `result` 文本投影 - [ ] tool 后处理器能基于 `result` 生成完整 `ToolAgentOutput`(含 `ui_hints`) - [ ] `TEXT_MESSAGE_END` 和 assistant history 不再依赖 worker `ui_hints` 编译 UI - [ ] history 能直接从 `metadata.tool_agent_output.ui_hints` 恢复 tool UI - [ ] `runtime/tasks.py` 等冷/热路径已统一,不再存在第二条旧路径 - [ ] 前端继续通过现有 UI 渲染器消费 `ui_schema` 渲染内容 - [ ] `ui_hints → ui_schema` 编译链路保持不变,仅改变 `ui_hints` 来源 - [ ] skill 资产目录已落在 `backend/src/core/agentscope/tools/skills` - [ ] skill 部署/装载方式与 AgentScope 原生方法保持一致 - [ ] CLI 输入通道已按 `argv` 主、`stdin` 辅的组合模式落地 - [ ] 旧 `custom/*.py` 直接工具实现已被 CLI handler / router 替换 ## 11. Open Questions 当前 open questions 已收口,不再保留架构级未决项。 已确认结论: 1. `ui_hints` 是 tool 输出的 UI 描述(真源),`ui_schema` 是编译后的渲染格式(传输格式) 2. skill 资产目录最终精确落点为 `backend/src/core/agentscope/tools/custom` 3. skill 部署方式参考 AgentScope skill 提供的原生方法 4. CLI 输入通道采用"两者结合",默认 `argv` 为主、`stdin` 为辅 5. history route 的 tool UI 回放从 `metadata.tool_agent_output.ui_hints` 读取并编译为 `ui_schema` 6. 旧 `custom/*.py` 不再保留,工具实现改为 CLI handler / router 7. `ToolResponse` 只返回 `result` 文本投影,完整 `ToolAgentOutput` 由 tool 后处理器生成 ## 12. Risks 1. 如果只把工具换成 CLI,但不先定义稳定工具协议,最终仍会把旧耦合问题原样搬过去。 2. 如果前端直接消费 `ui_hints` 而不是 `ui_schema`,会破坏现有渲染链路。 3. 如果把"项目 CLI"误实现为通用 shell/命令执行器,会引入提示词注入和越权风险。 4. 如果 token/凭证 注入边界设计不严谨,容易把认证逻辑散落到多个工具实现中。 5. 当前代码里仍有多处 broad catch,迁移时错误可能被掩盖: - `backend/src/core/agentscope/events/agui_codec.py` - `backend/src/v1/agent/utils.py` - `apps/lib/core/chat/ag_ui_service.dart` ## 13. Definition Of Done 当以下条件全部满足时,本任务完成: 1. 协议文档修正完毕并成为实现依据。 2. AgentScope skill 已在项目内落地并承担工具使用知识披露。 3. 当前工具已切换为单一 `project_cli` + skills 手册架构。 4. token/受控凭证 已通过受控环境变量成为 CLI runtime 的认证边界。 5. worker output 已移除 `ui_hints`。 6. `ToolAgentOutput` 已成为独立完整的 tool 协议对象,并承载前端所需 UI hints。 7. `ToolAgentOutput.result` 已升级为结构化对象真源。 8. worker 仅通过完整 JSON 文本投影的 tool message `content` 获取工具结果主信息。 9. history 能通过 `metadata.tool_agent_output.ui_hints` 编译为 `ui_schema` 恢复 tool UI。 10. `runtime/tasks.py` 等冷/热路径已统一,不再存在第二条旧路径。 11. AG-UI 对 tool UI 编译 `ui_hints → ui_schema` 后传输,前端继续复用现有 UI 渲染器。 12. 旧 worker UI 编译路径和相关清理项已完成收尾。 13. 前后端事件/history/渲染链路验证通过。 14. `ui_hints → ui_schema` 编译链路保持不变,仅改变 `ui_hints` 来源。 15. skill 资产已按约定落在 `backend/src/core/agentscope/tools/skills`,并按 AgentScope 原生方式装载。 16. CLI 输入通道已按 `argv` 主、`stdin` 辅的组合模式落地。 17. history route 已从 `metadata.tool_agent_output.ui_hints` 编译为 `ui_schema` 回放 tool UI。 18. 旧 `custom/*.py` 直接工具实现已被 CLI handler / router 替换。 19. `ToolResponse` 已收敛为只返回 `result` 文本投影,完整 `ToolAgentOutput` 由 tool 后处理器生成。