第 二十 章 Agent 架构实操 深入学习 · 交互式 含 QA 测试

Agent 架构设计:工具调用、权限控制、记忆机制、上下文压缩与 MCP 集成

💡 完整 Harness:统一组合根、单一 AgentRunner、动态上下文、MCP 边界与资源关闭。
不发明终极框架,不复制第二套 Loop。把前 19 章能力接到同一个组合根,用跨功能场景证明边界同时成立:模型提议,Harness 决策;Prompt 指导,Policy 授权;消息成组、状态各有权威存储、资源有 owner。
本章进度
0%
1 本章要掌握的目标
  • 同一 AgentRunner 与同一组合根 buildAgent(P20, deps);full_harness 是第 26 项能力,不带新代码(46 行新代码,profiles 删 174 行样板,净成果负数)。
  • 启动时 7 条依赖门禁 validateBuildDependencies():检查对象身份(===)而非值相等,错误共享活不到运行时。
  • 每轮一个不可变 Registry Snapshot;一个 tool call 恰好配一个 tool result(validateToolPairing 调三次)。
  • 权限管道 strongestDecision 按 deny > ask > allow 取第一个;Prompt 与 Hook 都不是授权边界。
  • 资源关闭逆序 Mcp → Teammate → Cron → JobSupervisor;1 失败原样抛,多失败 AggregateError,#closed 只在全部成功时置位。
2 核心知识点
第 20 章仍然使用同一个 AgentRunner

P1-P19 的增量能力用 PROFILE_DELTAS 累计出 P20;full_harness 只是整合验收标记,不对应新业务模块。

const PROFILE_DELTAS = [
  // P1-P19 增量能力,此处省略
  ["full_harness"],
] as const;
export const P20 = profileAt(20);
单一循环的真实执行顺序

UserPromptSubmit → drain typed events → 选择 Memory + 压缩 → 渲染 dynamic prompt + Registry Snapshot → RecoveryManager → 追加 assistant → 每个 tool call(校验→Context→Pre→权限→handler/后台→Post→回填)→ 无工具时 Stop 至多一次续跑。

每轮:
  UserPromptSubmit Hook
  → drain typed events
  → Memory + 压缩完整消息组
  → 渲染 dynamic prompt + Registry Snapshot
  → RecoveryManager 调模型
  → 对每个 tool call ...(Pre→权限→handler→Post→配对结果)
  → 无工具调用时 Stop Hook(至多续跑一次)
三个不能破坏的不变量

① 一次回复只用一个 Registry Snapshot;② 一个 tool call 必须恰好配一个 tool result;③ Prompt 和 Hook 都不是授权边界。

4.1 一次回复 = 一个 Registry Snapshot
4.2 一个 tool call = 一个相同 tool_call_id 的 tool result
4.3 Prompt 指导,Policy 授权;Hook 只在硬边界内扩展
runtime_status 动态段落

组合根在每轮渲染前读取可同步状态:mcp_connections 与 pending_work。它固定在提示词最后一段并参与缓存键——只改 status、其余不变时 cacheHits 加一,之前的正文不变。它只是状态同步 getter,不替代 EventInbox,更不是授权依据:模型看到 mcp_connections:["fake"] 不等于有权调用那个工具,权限事实源始终是本地 policy。

## runtime_status
{"mcp_connections":["fake"],"pending_work":false}
关闭和重建也是业务行为

注册顺序 JobSupervisor → Cron → Teammate → MCP;close 逆序 Mcp → Teammate → Cron → Supervisor。每个资源独立 try/catch,1 个失败原样抛,>1 个组成 AggregateError;#closed 只在全部成功时置位,失败后可重试 close、不能再 run。重建后 SQLite Task、Memory、Cron pending、Mailbox/Protocol、Worktree binding 各自恢复;事件去重两层:#seenEventIds 防进程内重投,store ack 防跨重启重放。

注册:  JobSupervisor → CronRuntime → TeammateRuntime → McpRuntime
关闭:  McpRuntime → TeammateRuntime → CronRuntime → JobSupervisor
单资源失败仍继续尝试剩余;多异常组成 AggregateError
3 机制流程
1
统一组合根

验证 Cron/Teammate/Protocol/WorkStealing/Worktree/MCP 共享正确对象身份。

2
单循环执行

历史、压缩、动态 prompt、snapshot、Recovery、工具配对、Stop。

3
跨功能验证

MCP+权限、计划+Worktree、事件各一次、恢复、重建、关闭。

4
逆序关闭

Mcp → Teammate → Cron → Supervisor;失败可重试。

4 术语表
full_harness第 26 项能力,唯一不带代码的能力:只断言前十九章能力同时生效。
runtime_status动态 prompt 固定最后一段,参与缓存键;是状态同步不是授权来源。
BuildDependencies第 20 章唯一的外部边界;离线注入替身,真实 CLI 建正式适配器。
对象身份检查7 条依赖门禁;类型正确但共享关系错误也在启动时失败(cronRuntime 与 backgroundSupervisor 的 supervisor 字段值一样也必须 ===)。
恢复矩阵429 按 Retry-After 等待、529×3 切 fallback、length 抬预算、过长反应式压缩;全部作用在冻结的 requestMessages,恢复过程一条不进 canonical history。
真实回退ch19 的 mcp-external-approval 规则在 ch20 消失,connect_mcp 不再被审批——用实验验证回退,教训是能力组合变化会静默改行为。
5 QA 测试环节(自测题)
已完成 0 / 6 · 答对 0
Q1. 第 20 章是不是又造了一套新 Loop?
Q2. 同一回复里连接 MCP 并请求工作区外写入会?
Q3. 一个 tool call 必须?
Q4. runtime_status 的作用是?
Q5. 资源关闭的顺序是?
Q6. 重建 Harness 后?
6 验证与实验
  • npm run test:ch20:65 个测试文件的全套累积(本章 2 文件 11 用例,全程离线);26 个能力位;Lead 工具面 25 个,队友被裁剪(无 connect_mcp,Lead 无 submit_plan)。
  • 全量门禁:npm run typecheck / npm test / npm run lint / npm run format:check / npm run build。
  • 在目标 Git 仓库根运行:npm run ch20 -- --prompt "验证完整 Harness 的动态上下文、MCP 边界和资源关闭"