diff --git a/build-manifest.json b/build-manifest.json index 7beaee8..002ca74 100644 --- a/build-manifest.json +++ b/build-manifest.json @@ -2,7 +2,7 @@ "meta": { "source": "docs/prd/newfunction", "prdRev": "v0.2", - "extractedAt": "2026-05-13T05:26:32.145Z" + "extractedAt": "2026-05-13T08:13:35.698Z" }, "model": [ { diff --git a/docs/prd/newfunction/arch-acp-bridge.html b/docs/prd/newfunction/arch-acp-bridge.html index 1a04831..c915b7c 100644 --- a/docs/prd/newfunction/arch-acp-bridge.html +++ b/docs/prd/newfunction/arch-acp-bridge.html @@ -240,7 +240,145 @@ LoopController 决定 retry / block / 升 L3
-

⑦ 性能考量

+

⑦ Mock Claude CLI 模式(开发期省 token)

+

P0 阶段开发 79 工具时,不可能每次都真调 Claude(烧 token + 等响应慢)。需要一个 MOCK_CLAUDE=1 环境变量让 ClaudeCliProtocol 返回 stub 回复,工具流照常跑通。

+ +

设计

+
// packages/acp-core/protocol/ClaudeCliProtocol.js · 增量改动 ~40 行
+const MOCK = process.env.MOCK_CLAUDE === '1';
+const mockResponsesDir = path.resolve(__dirname, '../../../system/mock-responses');
+
+class ClaudeCliProtocol {
+  async sendPrompt(input) {
+    if (MOCK) return this._sendPromptMock(input);
+    return this._sendPromptReal(input);
+  }
+
+  async _sendPromptMock(input) {
+    // 1. 解析 agent name 从 system prompt 或 input.agent
+    const agent = this.agentName;             // 启动时存的
+    const mode  = input.mode || 'default';
+
+    // 2. 读 mock 响应文件
+    const p = path.join(mockResponsesDir, agent, `${mode}.json`);
+    if (!fs.existsSync(p)) {
+      return this._fallbackMock(agent, input);
+    }
+
+    const mock = JSON.parse(fs.readFileSync(p, 'utf8'));
+
+    // 3. 按 mock 描述的 tool_use 序列,逐个回调工具
+    //    模拟 Claude 自己调工具的行为
+    for (const step of mock.steps) {
+      if (step.tool_use) {
+        const result = await this._dispatchTool(step.tool_use.name, step.tool_use.input);
+        // 工具回执理论上影响下一步,简化版直接忽略 result
+        // (高级版可让 mock 描述含 tool_result 处理逻辑)
+      }
+      if (step.delay_ms) await new Promise(r => setTimeout(r, step.delay_ms));
+      if (step.text) this._emitChunk(step.text);
+    }
+
+    return mock.final_result;
+  }
+
+  _fallbackMock(agent, input) {
+    // 没有专门 mock 文件时,返回最小合规结果
+    return {
+      ok: true,
+      duration_ms: 100,
+      tokens_used: 0,
+      content: '[mock fallback response]',
+    };
+  }
+}
+ +

Mock 响应文件结构

+
+system/mock-responses/                  # 由 P0.A2 创建
+├── pm/
+│   ├── init.json                       # PM init mode 的 stub
+│   ├── incremental.json
+│   └── resume.json
+├── architect/
+│   ├── generate.json
+│   ├── patch.json
+│   └── resume.json
+├── coder/
+│   ├── implement.json
+│   └── retry.json
+├── tester/
+│   ├── test.json
+│   └── retry.json
+├── integrator/
+│   ├── run.json
+│   └── retry.json
+└── bug-triager/
+    └── triage.json
+
+ +

Mock 响应文件示例 · pm/init.json

+
{
+  "description": "PM init mock: 假装解析需求并写出 prd/ 11 章节",
+  "steps": [
+    { "text": "读取上下文...\n" },
+    { "tool_use": {
+        "name": "write_todolist",
+        "input": { "items": [
+          { "id": "step-001", "description": "解析需求" },
+          { "id": "step-002", "description": "bootstrap_prd_site" },
+          { "id": "step-003", "description": "写 index.html" }
+        ] }
+    } },
+    { "tool_use": { "name": "bootstrap_prd_site", "input": { "pid": "$pid" } } },
+    { "tool_use": { "name": "write_chapter",
+        "input": { "pid": "$pid", "section": "index", "page": "index",
+                   "html": "<!doctype html>...mock content...</html>" }
+    } },
+    { "tool_use": { "name": "echo_diff",
+        "input": { "summary": "创建 mock PRD 站点" } } }
+  ],
+  "final_result": {
+    "ok": true,
+    "mode": "init",
+    "prd_rev": 1,
+    "files_written": ["prd/index.html"],
+    "change_summary": "[mock] PM init",
+    "duration_ms": 100,
+    "tokens_used": 0
+  }
+}
+ +

什么时候用 mock vs 真

+ + + + + + + + + + +
场景用什么原因
开发新工具,验证落盘 / SSE 正确mock不烧 token;快速迭代
调试 LoopController 主循环mock同上
跑 phase0-verify CImockCI 不能依赖真 Claude
验证 Agent system prompt 质量真mock 不能反映 prompt 修改
P1 验收(30s 内 prd/ 落盘)真真 token 消耗 + 真 prompt 检验
P3 BugTriager 归因准确率测试真核心检验项
+ +

$pid / $unit_id 占位符

+

mock JSON 里写 "$pid" 时,dispatch 前 protocol 替换为实际运行时值。简单但够用:

+
function interpolate(input, ctx) {
+  const json = JSON.stringify(input);
+  const resolved = json.replace(/"\$(\w+)"/g, (_, key) =>
+    JSON.stringify(ctx[key] || '__missing_' + key)
+  );
+  return JSON.parse(resolved);
+}
+ +
+ 关键好处:有了 mock 模式,工具实现(P0.A)+ 集成调试(P0.H)+ 验证脚本(P0.I/J)全程零 token 消耗。等所有工具就位 + system prompt 都提取了,再切到 MOCK_CLAUDE=0 跑一遍真 PM 验证。 +
+
+ +
+

⑧ 性能考量

@@ -255,7 +393,7 @@ LoopController 决定 retry / block / 升 L3
-

⑧ 安全边界

+

⑨ 安全边界

沙箱通过三层强制:

  1. ACP 进程层:Claude CLI 起在 cwd 锁定的子进程,不能 cd 出去
  2. @@ -278,14 +416,14 @@ module.exports.Write = {
-

⑨ 一行总结

+

⑩ 一行总结

桥的全貌:LoopController.callAgent (P-8 包装) → agent-manager.callAgent (透传 toolRegistry/toolCtx) → SessionPool (key 含 pid) → ClaudeCliProtocol (spawn claude + --tools) → Claude CLI (tool_use API) → ToolRegistry.execute (业务工具) → 写文件 / 发 SSE → 回到 Claude。没有新协议,只有现有接口的扩展。
-

⑩ 关联

+

⑪ 关联

项关注点数字 / 缓解