新增工具调用卡片技术方案(契约冻结)
- docs/technical:【技术方案】GameAgent对话工具调用卡片-2026-09-14.md:工具调用条目形状、独立历史文件 tool-calls.jsonl、事件新增 toolCalls 字段、read_direct_tool_calls 回读命令、卡片 DOM 与无障碍要求、验收判据与不做项 - docs/README.md:把该方案登记进「AI 游戏创作与 Agent Runtime」索引 - 方案明确不改 project.jsonl 既有格式与 Codex 上下文注入路径,避免污染模型上下文
This commit is contained in:
@@ -32,6 +32,7 @@
|
||||
- [AI 游戏创作智能体 App 实施计划](./technical/【技术方案】AI游戏创作智能体App实施计划-2026-06-24.md):当前 DirectProject、受控语义工具、UI workflow、资源和运行时合同。
|
||||
- [策划会话 Runtime V2 接入与旧链路退役方案](./technical/【技术方案】策划会话RuntimeV2接入与旧链路退役-2026-09-03.md):新单 Agent 策划会话、GDD 策略、未来 MCP/Skill 兼容插槽、阶段任务与退役验收合同。
|
||||
- [DirectProject Codex 原始历史与异常恢复](<./technical/【技术方案】DirectProject Codex原始历史与异常恢复-2026-09-04.md>):原始 Responses item 持久化、线程注入与异常回合收尾。
|
||||
- [GameAgent 对话工具调用卡片](./technical/【技术方案】GameAgent对话工具调用卡片-2026-09-14.md):把右侧对话里的执行命令 / 写文件投影成 Codex 风格可折叠卡片,含采集、独立历史文件、事件字段与回读契约。
|
||||
- [DirectProject 客户端 Skill 与 MCP 扩展导入方案](./technical/【技术方案】DirectProject客户端Skill与MCP扩展导入方案-2026-08-31.md):客户端扩展导入、按独立 Skill/MCP 拆分、命名、启用和启动时注入边界。
|
||||
- [AGC 通用插件宿主与编辑器适配](./technical/【技术方案】AGC通用插件宿主与编辑器适配-2026-09-09.md):通用插件宿主、SDK、权限审计、UI 挂载和 Cocos 编辑器适配边界。
|
||||
- [AGC Cocos Creator 编辑器桥接模块](<./technical/【技术方案】AGC Cocos Creator 编辑器桥接模块-2026-09-09.md>):独立 crate、feature 开关、目标校验与 Windows 注入边界。
|
||||
|
||||
@@ -0,0 +1,108 @@
|
||||
# 【技术方案】GameAgent 对话工具调用卡片(Codex 风格)-2026-09-14
|
||||
|
||||
## 一句话交付
|
||||
|
||||
把 GameAgent 右侧对话面板里的「执行命令 / 写文件 / 调工具」从一行中文进度文本,改成 Codex 桌面客户端那样的**可折叠卡片**(折叠态一行摘要,展开态看命令与文件明细),并且在**刷新页面、重开项目后仍然存在**。
|
||||
|
||||
## 背景与现状(已核实)
|
||||
|
||||
- 数据来源:Codex app-server 会推 `item/started` / `item/completed`,item 里带完整信息(`commandExecution.command`、`fileChange.changes[].path` 等)。
|
||||
- 现状投影:`apps/ai-game-creator-shell/src-tauri/src/agent/codex_app_server.rs` 的 `direct_codex_item_intermediate_text()`(约 791 行)把 item **压成一行中文文本**(`正在执行命令:xxx` / `正在写入文件:xxx`),经 `DirectCodexTurnObservation::IntermediateText` 下发。
|
||||
- 前端事件:`GameCreatorDirectTurnUpdateEvent`(`src/app/types.ts:1090`)只有 `projectPath / turnId / sequence / status / activity / accumulatedText / updatedAt`,**没有结构化工具调用**。
|
||||
- 历史持久化:`.agent/conversations/project.jsonl` 现在只写 message 条目(实测 61 条全是 message),工具调用不留痕。
|
||||
- 历史回读:`read_direct_project_chat_history_at()`(`direct_project_history.rs:575`)只把 `role ∈ {user, assistant}` 且有文本的条目投影成 `LocalConversationMessageRecord`,**形状上装不下工具调用**。
|
||||
- 结论:要做成卡片必须同时改「采集 → 传输 → 持久化 → 回读 → 渲染」五段,纯前端做不出来。
|
||||
|
||||
## 契约(实现必须照此,不得自行改形状)
|
||||
|
||||
### 1. 工具调用条目(采集与持久化形状)
|
||||
|
||||
新增独立历史文件:`<projectRoot>/.agent/conversations/tool-calls.jsonl`,一行一条,行信封与既有历史一致:
|
||||
|
||||
```json
|
||||
{ "type": "tool_call_item", "payload": { "schemaVersion": "agc-tool-call.v1", "id": "...", "turnId": "...", "kind": "command|file_change|mcp_tool|web_search|context_compaction|other", "title": "执行命令", "summary": "npm run build", "status": "running|completed|failed", "detail": { "command": "...", "output": "...", "changes": [{ "path": "game/src/x.ts", "kind": "add|update|delete" }] }, "startedAt": 0, "updatedAt": 0 } }
|
||||
```
|
||||
|
||||
- `id`:Codex item 的 id;同一 item 的 `started` 与 `completed` 必须落成**同一条**(按 id 幂等 upsert,不允许写两行)。
|
||||
- `title` 是折叠态的一行标题,按 kind 固定:`command` → `执行命令`、`file_change` → `编辑 N 个文件`(N = changes 去重后数量)、其余见 kind 枚举。
|
||||
- `summary` 是折叠态标题后面的短摘要:命令取命令首行(截断 120 字符),`file_change` 取首个变更路径。
|
||||
- `detail.command` / `detail.output` 各截断到 4000 字符;`detail.changes[].path` 用项目相对路径。
|
||||
- **必须脱敏**:沿用既有 `codex_app_server.rs` 里对 command/tool 参数的安全处理,不得把 API Key、Token、Cookie、绝对用户目录写进 `detail`。
|
||||
|
||||
### 2. 实时事件(新增字段,不改既有字段语义)
|
||||
|
||||
`GameCreatorDirectTurnUpdateEvent` 增加**可选**字段:
|
||||
|
||||
```ts
|
||||
toolCalls?: DirectTurnToolCall[] | null;
|
||||
```
|
||||
|
||||
- 只有在本回合工具调用集合发生变化时才带(不要每个 heartbeat 都重发全量)。
|
||||
- 字段**可选**:老版本事件解析路径必须保持兼容(前端拿到 `undefined` 时行为与现在一致)。
|
||||
- `DirectTurnToolCall` 与上面 payload 同形(去掉 `turnId`)。
|
||||
|
||||
### 3. 回读命令
|
||||
|
||||
新增 Tauri 命令 `read_direct_tool_calls(projectPath)`,返回按时间正序的 `DirectTurnToolCall[]`,最多最近 200 条(超出截断,保留最新)。
|
||||
|
||||
- 历史文件缺失 → 返回空数组,不报错。
|
||||
- 单行损坏 → 跳过该行继续,不整体失败(与 Codex item 流一样是"尽力而为"的展示数据,不是业务真相)。
|
||||
|
||||
### 4. 前端合并与渲染
|
||||
|
||||
- 加载对话时把回读结果按 `turnId` 归并进消息流:工具调用卡插在**同一回合最后一条 assistant 消息之后**,同一回合内按 `startedAt` 升序。
|
||||
- 实时回合(`directCodexProductRuntime` 且 `activeDirectCodexTurnRef` 命中)时,卡片跟着事件增量更新;回合结束后由持久化数据接管(不出现重复卡片,同一 `id` 只渲染一次)。
|
||||
- 卡片 DOM 与交互(对齐 Codex):
|
||||
|
||||
```html
|
||||
<section class="agent-tool-call" data-kind="command" data-status="running">
|
||||
<button type="button" class="agent-tool-call-head" aria-expanded="false">
|
||||
<span class="agent-tool-call-icon" aria-hidden="true"></span>
|
||||
<span class="agent-tool-call-title">执行命令</span>
|
||||
<span class="agent-tool-call-summary">npm run build</span>
|
||||
<span class="agent-tool-call-status">执行中</span>
|
||||
<svg class="agent-tool-call-chevron" aria-hidden="true"></svg>
|
||||
</button>
|
||||
<div class="agent-tool-call-body" hidden>
|
||||
<pre class="agent-tool-call-command">...</pre>
|
||||
<ul class="agent-tool-call-changes"><li>game/src/x.ts <small>新增</small></li></ul>
|
||||
<pre class="agent-tool-call-output">...</pre>
|
||||
</div>
|
||||
</section>
|
||||
```
|
||||
|
||||
- 必须用 `<button aria-expanded>` + `hidden` 控制展开(键盘可达、可读屏),`aria-label` 说明「执行命令:npm run build」。
|
||||
- 默认折叠;`running` 时标题右侧显示进行中状态点;`failed` 时标题与状态文案用错误色(用现有 `--platform-*` 变量,不新增色值)。
|
||||
- 输入框、消息气泡、消息列表滚动模型**不变**;卡片只是消息流里的一个块。
|
||||
|
||||
## 验收判据(每条都要有可复现证据)
|
||||
|
||||
1. 一轮真实回合里,`tool-calls.jsonl` 里同 id 只有一行,`completed` 后 `status` 变 `completed`。
|
||||
2. 刷新页面 / 重开项目后,工具调用卡仍在原位(不重复、不丢)。
|
||||
3. 实时回合中卡片状态从 `running` 走到 `completed`。
|
||||
4. 折叠/展开键盘可达(Tab 到头部按钮、Enter/Space 切换、`aria-expanded` 同步)。
|
||||
5. `Read` 到的既有对话(无工具调用)行为不变;老事件(无 `toolCalls` 字段)行为不变。
|
||||
6. 脱敏检查:`tool-calls.jsonl` 里不出现 API Key / Token / 绝对用户目录。
|
||||
|
||||
## 不做项(本次明确不做)
|
||||
|
||||
- 不做工具调用的重试 / 取消 / 编辑按钮。
|
||||
- 不做 diff 级展开(只在展开态列文件路径与变更类型)。
|
||||
- 不做工具输出的完整回放、不做卡片内搜索。
|
||||
- 不改 `.agent/conversations/project.jsonl` 的既有格式与注入 Codex 上下文的路径(新数据走独立文件,避免污染模型上下文)。
|
||||
- 不改工具调用之外的聊天渲染(markdown、气泡、滚动)。
|
||||
- 不做历史工具调用的迁移回填(存量项目没有卡片,属预期)。
|
||||
|
||||
## 涉及文件(实现边界)
|
||||
|
||||
- Rust(`apps/ai-game-creator-shell/src-tauri/src/`):`agent/codex_app_server.rs`(item → 结构化采集)、`agent/direct_runtime.rs`(随事件下发 + 回合结束持久化)、`agent/direct_project_history.rs` 或新增 `agent/direct_tool_calls.rs`(upsert 与回读)、`commands.rs`(新增命令注册)。
|
||||
- 前端(`apps/ai-game-creator-shell/src/`):`app/types.ts`(新增字段与类型)、`App.tsx`(订阅、累加、加载时合并)、`features/agent-runtime/` 或新增 `features/project-workspace/ToolCallCard.tsx`(卡片组件)、`styles.css`(卡片样式,新样式集中在文件末尾中文注释区块)。
|
||||
- 测试:Rust 侧单元测试(upsert 幂等、截断、脱敏、损坏行跳过)、前端 `tests/`(卡片渲染、折叠交互、重开项目后合并、老事件兼容)。
|
||||
|
||||
## 实施顺序(每步都要能独立验证)
|
||||
|
||||
1. Rust 采集 + 独立文件 upsert:先只落盘,单元测试证明幂等与截断。
|
||||
2. 事件字段下发 + 前端类型:老路径不受影响(回归现有 appSurface 用例)。
|
||||
3. Tauri 回读命令 + 前端加载合并:刷新后卡片存在。
|
||||
4. 卡片组件 + 样式 + 无障碍:折叠/展开 + 键盘。
|
||||
5. 真实回合联调(本地 Tauri 起一轮),截图留证。
|
||||
Reference in New Issue
Block a user