AGAgent 学习路线
第 20 / 20
CHAPTER 20 · GPT 生成学习页

完整 Harness

把前十九章接到同一个组合根,用跨功能场景证明边界一致。

01 / 路线

先看它怎样跑起来

从输入到验收

这一章不是几个孤立知识点,而是一条会产生结果的因果链。

关键判断

完整 Harness

亮起的是当前动作,留下的是已经满足的前置条件。点击任意一步,可以从那里继续。

  • 完整 Agent 的核心不是功能数量,而是边界一致。
  • 不要复制第二套 AgentRunner。
  • 模型提议动作,Harness 拥有真正控制权。
1ToolRegistry 控制可见性
2PermissionPolicy 控制副作用
3Memory + Compaction 管上下文
4MCP / Team / Worktree 协同
点击播放,观察动作怎样传递0 / 4
02 / 正文

顺着原文把边界看清

20 个小节13 组代码38 行表格

按原文顺序阅读。摘要只负责定位,真正的边界、例外和代码都在展开内容里。

01导读:问题背景与本章目标前十九章依次加入了 Agent Loop、工具、权限、Hook、TODO、Subagent、Skill、上下文压缩、记忆、动态 Prompt、模型恢复、Task DAG、后台任务、Cron、Teammate、协作协议、工作窃取、Worktree 和 MCP。

前十九章依次加入了 Agent Loop、工具、权限、Hook、TODO、Subagent、Skill、上下文压缩、记忆、动态 Prompt、模型恢复、Task DAG、后台任务、Cron、Teammate、协作协议、工作窃取、Worktree 和 MCP。

第 20 章不再发明一个“终极框架”,也不复制第二套 Loop。它只做一件事:把已经实现的能力接到同一个组合根,并用跨功能场景证明边界同时成立。

图片
图片
021. 完整 Agent 的核心不是功能数量,而是边界一致模型只负责提出下一步动作。Harness 必须拥有真正的控制权:

模型只负责提出下一步动作。Harness 必须拥有真正的控制权:

  • ToolRegistry 决定当前能看到和调用哪些工具;
  • PermissionPolicy 在 handler 前执行硬权限判断;
  • HookRegistry 提供生命周期扩展,但不能放宽系统拒绝;
  • MemorySessionCompactionManagerDynamicPromptProvider 控制上下文;
  • RecoveryManager 处理 429、529、输出截断和输入过长;
  • SQLite Task、Protocol、Mailbox 和 Worktree 保存可恢复状态;
  • JobSupervisorCronRuntimeTeammateRuntimeMcpRuntime 持有长期资源;
  • AgentRunner.close() 按逆序尝试关闭全部资源。

这意味着 Prompt 可以提示模型“不要写出工作区”,但真正阻止越界写入的必须是运行时权限和路径解析。MCP server 可以在 description 中声称自己只读,但权限只能来自本地可信策略。

032. 第 20 章仍然使用同一个 AgentRunner固定 Profile 位于 code/chapters/ch20/src/core/profiles.ts:

固定 Profile 位于 code/chapters/ch20/src/core/profiles.ts

// PROFILE_DELTAS 末尾追加 full_harness,P20 由 profileAt(20) 返回累计档案。
const PROFILE_DELTAS = [
  // P1-P19 的增量能力,此处省略
  ["full_harness"],
] as const satisfies readonly (readonly Capability[])[];

export const P20 = profileAt(20);

full_harness 是整合验收标记,不对应新的业务模块。它表示:P1-P19 的全部 Capability 已经在同一组合根中连接并通过交叉验证。

因此,第 20 章没有新增业务模块,但需要在四个代码位置把完整 Harness 标记、运行态状态、Prompt 尾部段落和固定入口连接起来。

第一处,在 profiles.tsPROFILE_DELTAS 末尾追加 full_harness 增量,并让 profileForChapter(20) 返回累计出的 P20 固定对象。

第二处,在 prompting.ts 的渲染器末尾增加可选的 runtime_status 段落,并允许 Provider 用 statusProvider 每轮重新读取状态。

第三处,在 bootstrap.ts 汇总 MCP 连接与后台 pending work,并只给 P20 安装该状态 Provider。

第四处,提供固定章节入口 code/chapters/ch20/src/chapters/ch20.ts

import { runProfile } from "../cli.js";
import { P20 } from "../core/profiles.js";

// 固定入口运行 P20 Full Harness,并由 CLI 负责结束后的资源释放与错误码返回。
process.exitCode = await runProfile(P20, process.argv.slice(2));

入口不复制工具注册、恢复、调度或关闭逻辑。后续修复公共运行时,所有已引入该能力的章节会一起受益。

043. 组合根如何连接全部能力BuildDependencies 是第 20 章唯一的外部边界。离线测试可注入模型、时钟、命令执行器和各类持久化运行时;真实 CLI 则创建正式适配器。

BuildDependencies 是第 20 章唯一的外部边界。离线测试可注入模型、时钟、命令执行器和各类持久化运行时;真实 CLI 则创建正式适配器。

关键依赖关系如下:

资源必须共享的对象原因
CronJobSupervisorEventInbox调度和后台完成事件必须进入同一 typed inbox
TeammateSupervisor、Cron、Inbox、Mailbox队友生命周期、消息和事件由同一 owner 管理
ProtocolTeammate、Mailbox请求状态与实际投递参与方必须一致
Work stealingSQLite Task store、Worktree claim service自动认领与手动认领必须使用同一租约边界
WorktreeSQLite Task store、Git runnerTask、claim、binding 是独立状态线,但必须引用同一 Task
MCPLead 的 live ToolRegistry动态工具只进入 Lead,并在下一次模型请求可见

buildAgent() 会检查这些对象身份。依赖虽然类型正确,只要共享关系错误,也会在启动阶段明确失败。

054. 单一循环的真实执行顺序-> 渲染当前 system prompt,取得不可变 ToolRegistry Snapshot

每个用户 turn 都遵守同一顺序:

UserPromptSubmit Hook
-> drain typed events
-> 选择相关 Memory,整理历史并压缩完整消息组
-> 渲染当前 system prompt,取得不可变 ToolRegistry Snapshot
-> RecoveryManager 调用 OpenAI Chat Completions
-> 追加规范化 assistant 消息
-> 对每个 tool call:
     JSON + Zod schema 校验
     -> 解析可信 ToolContext / Worktree cwd
     -> PreToolUse Hook
     -> PermissionPolicy / 人工审批
     -> handler 或后台提交
     -> PostToolUse Hook
     -> 追加同 ID tool result
-> 无工具调用时运行 Stop Hook,最多强制续跑一次
-> 返回最终文本

这里有三个不能破坏的不变量。

064.1 一次回复只使用一个 Registry Snapshot模型请求中的 tools 与该回复中全部 tool calls 使用同一个 sealed snapshot。即使回复里的第一个调用连接了 MCP,第二个调用也不能提前使用新工具;新工具只能出现在下一次模型请求。

模型请求中的 tools 与该回复中全部 tool calls 使用同一个 sealed snapshot。即使回复里的第一个调用连接了 MCP,第二个调用也不能提前使用新工具;新工具只能出现在下一次模型请求。

074.2 一个 tool call 必须恰好配一个 tool result未知工具、坏 JSON、schema 错误、权限拒绝、超时、远端失败都必须形成相同 toolcallid 的错误结果。不能因为其中一个调用失败,就丢弃同一 assistant 消息里的其他调用。

未知工具、坏 JSON、schema 错误、权限拒绝、超时、远端失败都必须形成相同 tool_call_id 的错误结果。不能因为其中一个调用失败,就丢弃同一 assistant 消息里的其他调用。

084.3 Prompt 和 Hook 都不是授权边界工作区外写入由 PermissionPolicy 拒绝。Teammate 的 effectful 工具由计划门控规则拒绝。MCP 权限由本地 McpToolPolicy 决定。Hook 只能在硬边界允许的范围内修改输入或输出。

工作区外写入由 PermissionPolicy 拒绝。Teammate 的 effectful 工具由计划门控规则拒绝。MCP 权限由本地 McpToolPolicy 决定。Hook 只能在硬边界允许的范围内修改输入或输出。

095. 动态上下文:Skill、Memory、工具和工作区第 20 章的 system prompt 不是固定字符串。DynamicPromptProvider 每次请求按固定顺序渲染:

第 20 章的 system prompt 不是固定字符串。DynamicPromptProvider 每次请求按固定顺序渲染:

  1. identity 与静态 context;
  2. 当前工具目录;
  3. 当前 workspace;
  4. Skill catalog;
  5. 本轮选中的 Memory;
  6. 可选的 runtime_status

Skill 初始只公开名称和描述,正文通过 load_skill 按需读取。Memory 只注入与当前查询相关的记录。大工具结果和旧历史保存到 workspace artefact,再把有界引用放回对话。

动态 MCP 工具也使用同一个 live registry。因此,连接前 system prompt 不含远程工具;连接成功后的下一轮才同时更新 prompt 和 OpenAI tools schema。

P20 新加入的 runtime_status 不是普通对话文本,而是组合根在每轮渲染前读取的可同步状态:

function fullHarnessRuntimeStatus(dependencies: BuildDependencies): JsonObject {
  const pendingWork =
    dependencies.backgroundSupervisor?.hasPendingWork === true ||
    dependencies.cronRuntime?.hasPendingWork === true ||
    dependencies.teammateRuntime?.hasPendingWork === true;
  const mcpConnections =
    dependencies.mcpRuntime === undefined ? [] : dependencies.mcpRuntime.connectedAliases;
  return Object.freeze({
    mcp_connections: Object.freeze([...mcpConnections]),
    pending_work: pendingWork,
  });
}

这段状态放在全部固定段落之后,例如:

10runtimestatus{"mcpconnections":["fake"],"pendingwork":false}

{"mcp_connections":["fake"],"pending_work":false}


它不替代 `EventInbox`:typed event 仍在请求前消费,状态只回答“当前是否仍有异步工作、MCP 已连接哪些 alias”这两个模型决策问题。这里只读取 synchronous getter,不等待后台任务、不查询远端,也不把运行态 JSON 当作授权依据。这段固定在提示词最后并参与缓存键:只改 status、其余输入不变时 cacheHits 加一,之前的正文保持不变。
116. Task、Teammate 与 Worktree 是三条状态线完整 Harness 不把所有状态塞进一个 Task object。

完整 Harness 不把所有状态塞进一个 Task object。

  • Task 保存 DAG、状态、owner、claim token 和 lease;
  • Protocol 保存计划或关机请求的 typed 状态机;
  • Worktree binding 保存 Task 与隔离 Git 工作区的绑定和审计事件。

典型流程是:

Lead 创建 A -> B DAG
-> B 因依赖保持 blocked
-> A completed 后 B 变为 pending
-> Teammate 提交计划
-> Lead 批准计划
-> Teammate 原子 claim B
-> claim token 绑定 Worktree execution scope
-> 每次工具调用重新解析可信 cwd
-> 文件只写入对应 Worktree

计划未批准时,effectful handler 和后台提交次数必须为零。批准计划不会绕过其他权限:工作区边界和人工审批仍然生效。

127. Background、Cron 和 typed event耗时工具可以由 JobSupervisor 接管,当前 tool call 立即得到 job 占位结果。任务完成或失败后,Supervisor 向 EventInbox 发布稳定 ID 的 typed event。

耗时工具可以由 JobSupervisor 接管,当前 tool call 立即得到 job 占位结果。任务完成或失败后,Supervisor 向 EventInbox 发布稳定 ID 的 typed event。

CronRuntime 使用相同 Supervisor 和 Inbox。调度到期时,它提交后台工作;事件在下一轮由 Runner drain。事件采用至少一次投递,但相同 event ID 只注入一次,确认后再次 poll 必须为空。

测试使用 FakeClock 和显式释放的 worker,不依赖真实 sleep。关闭时 Supervisor 会 await 或取消所有受管任务,不能遗留无主后台任务或无人持有的 Promise

138. 模型恢复与上下文压缩RecoveryManager 把供应商错误转换为明确恢复路径:

RecoveryManager 把供应商错误转换为明确恢复路径:

情况行为
429解析 Retry-After,在总时限内退避
连续 529达到阈值后切换显式配置的 fallback model
finish_reason="length"先提升输出预算;仍截断时使用 continuation prompt
ModelPromptTooLongError对完整消息组做一次响应式压缩后重试
第二次输入过长明确失败,不无限压缩

未完成的 length 片段不会先写进正式会话历史。压缩不能拆开 assistant tool calls 与对应 tool results。

149. MCP 是动态能力边界真实 MCP 客户端使用官方 TypeScript SDK 的 stdio transport:

真实 MCP 客户端使用官方 TypeScript SDK 的 stdio transport:

StdioClientTransport
-> Client.connect
-> listTools / callTool

模型只能提交本地 allowlist 中的 alias,不能提供 command、args 或 cwd。每个 connection 由独立 owner task 持有;远端 schema 先做安全检查,再由 Ajv 在 handler 前验证参数。

远端业务错误保留健康连接。timeout、进程退出或 transport failure 会原子撤销该 alias 的全部工具。MCP management 工具和远程工具只安装到 Lead,Subagent 与 Teammate 不继承。

1510. 关闭和重建也是业务行为JobSupervisor -> CronRuntime -> TeammateRuntime -> McpRuntime

完整 Harness 的资源注册顺序为:

JobSupervisor -> CronRuntime -> TeammateRuntime -> McpRuntime

AgentRunner.close() 逆序关闭:

McpRuntime -> TeammateRuntime -> CronRuntime -> JobSupervisor

某个资源关闭失败时,Runner 仍会尝试关闭剩余资源;一个异常原样抛出,多个异常组成 AggregateError。失败状态允许再次调用 close 完成收尾。

重建 Harness 后,SQLite Task、Memory、Cron pending event、Mailbox/Protocol 和 Worktree binding 从各自权威存储恢复。已经确认消费的事件不能重放,未知外部副作用也不能因进程重启被盲目重试。事件去重共两层:进程内 #seenEventIds 防同一轮重投,store 里的 ack 防跨重启重放——至少一次投递加 eventId 去重,对模型表现成恰好一次;恢复矩阵的中间过程一条都不进 canonical history。

1611. 离线整合验收第 20 章测试从 buildAgent(P20, BuildDependencies(...)) 进入,不导入真实凭据,也不访问公网。当前组合测试直接验证:

第 20 章测试从 buildAgent(P20, BuildDependencies(...)) 进入,不导入真实凭据,也不访问公网。当前组合测试直接验证:

  • UserPrompt Hook 执行;
  • 初始 prompt 同时包含 Skill、相关 Memory 与完整内置工具;
  • 初始 runtime_status{"mcp_connections":[],"pending_work":false}
  • 未连接的 MCP 工具不泄漏到 prompt 或 schema;
  • 同一回复连接 MCP 并请求工作区外写入时,连接成功,越界写入被硬拒绝;
  • 两个 tool call 都得到相同 ID 的配对结果;
  • 下一轮远程 MCP 工具同时进入 prompt 和 tools,且 runtime_status.mcp_connections 更新为 ["fake"]
  • 最终关闭 MCP、Teammate、Cron 和 Supervisor,无活跃 worker。

新增的 ch20-full-harness.test.ts 覆盖上述跨功能场景,ch20-entry.test.ts 覆盖 P20 固定入口、有效配置、缺失配置和非 Git 根目录的启动边界;其余测试文件继承前十九章聚焦套件。

P20 聚焦套件现在直接覆盖以下场景:固定入口在有效配置下拒绝非 Git 根目录且不创建状态;大 MCP 结果与 TODO/A→B DAG;计划审批后的 Worktree claim 路由;Background/Cron typed event 各一次;429/529/length/prompt-too-long 恢复与单次摘要;Stop 单次续跑;以及 Task/Memory/Cron pending event/Worktree binding 重建和已确认事件不重放。 第 1-19 章聚焦测试仍负责各状态机的细粒度失败分支;P20 只从同一个 buildAgent(P20, BuildDependencies(...)) 组合根证明跨能力接缝,不复制第二套 Loop。

运行 P20 聚焦测试:

Set-Location F:\笔记\Agent实操\code
npm run test:ch20

运行全量离线门禁:

Set-Location F:\笔记\Agent实操\code
npm run typecheck
npm test
npm run lint
npm run format:check
npm run build
1712. 第 20 章源码讲解地图code/chapters/ch20/src 是 P1-P20 的累计快照,不是只包含 P20 新文件。下面的地图覆盖该目录中的全部源码位置;P1-P19 已经引入的模块沿用其原始章节讲解,第 20 章正文负责新增标记、完整组合、运行态状态、MCP 生命周期和入口收束。

code/chapters/ch20/src 是 P1-P20 的累计快照,不是只包含 P20 新文件。下面的地图覆盖该目录中的全部源码位置;P1-P19 已经引入的模块沿用其原始章节讲解,第 20 章正文负责新增标记、完整组合、运行态状态、MCP 生命周期和入口收束。

源码位置责任本章讲解位置
core/commands.tscore/filesystem.tscore/events.tsCommand、文件系统与 typed event 的稳定契约第 3 节组合根、第 7 节事件
core/hooks.tscore/permissions.tsHook 生命周期与硬权限策略,两个边界不合并第 4 节,尤其 4.3
core/loop.tscore/messages.tscore/tools.ts唯一 Loop、消息配对和 Registry Snapshot第 4 节全部
core/model.ts供应商无关 Model 请求、回复、错误与恢复契约第 8 节
core/profiles.tsP1-P20 Capability 增量与 full_harness第 2 节
features/builtin-tools.tsfeatures/todos.tsfeatures/skills.ts标准工具、TODO 快照、Skill 渐进披露第 3、4、5 节
features/subagents.tsfeatures/tasks.tsSubagent 最小工具面与 Task DAG 状态机第 6 节及第 6/12 章
features/worktrees.tsfeatures/work-stealing.ts可信 Worktree cwd、claim token 与自动认领第 6 节及第 17/18 章
features/memory.tsfeatures/compaction.tsfeatures/recovery.ts选中记忆、上下文压缩、供应商故障恢复第 5、8 节
features/prompting.ts固定顺序 Prompt 渲染、缓存键、尾部 runtime_status第 5 节
features/background.tsfeatures/cron.ts受管后台任务与定时调度第 7 节及第 13/14 章
features/teammates.tsfeatures/mailbox.tsfeatures/protocol.ts队友生命周期、持久 Mailbox、审批/关机协议第 6、7 节及第 15/16 章
features/mcp-tools.tsMCP alias、本地策略、动态发布、原子撤销与关闭第 9 节及第 19 章
adapters/mcp-client.tsadapters/mcp-schema.ts官方 SDK stdio transport 与远端 JSON Schema 校验第 9 节及第 19 章
adapters/background-json.tsadapters/cron-json.tsadapters/mailbox-json.tsadapters/protocol-json.ts后台、Cron、Mailbox、Protocol 的 JSON 持久化第 6、7 节
adapters/task-json.tsadapters/task-sqlite.tsTask JSON/SQLite 持久化,后者持有 claim token 与 lease第 6 节及第 12/17 章
adapters/git.tsadapters/filesystem.tsadapters/powershell.tsGit、Node 文件系统和 PowerShell 边界实现第 3、6 节
adapters/openai-chat.tsOpenAI wire format 与响应规范化,供应商细节隔离第 8 节
mcp-servers/demo.ts可运行的双 alias 演示 MCP server第 9 节
bootstrap.tscli.tsconfig.ts组合根、命令行入口、环境配置装配第 2、3、12/13 节
chapters/ch01.tschapters/ch02.tschapters/ch03.tschapters/ch04.tschapters/ch05.tschapters/ch06.tschapters/ch07.tschapters/ch08.tschapters/ch09.tschapters/ch10.tschapters/ch11.tschapters/ch12.tschapters/ch13.tschapters/ch14.tschapters/ch15.tschapters/ch16.tschapters/ch17.tschapters/ch18.tschapters/ch19.tschapters/ch20.ts每章固定入口,其中 ch20.ts 只转发 P20runProfile第 2 节和第 13 节

这个地图也是验收清单:不能只确认文件存在,必须确认每个位置都有对应职责、所属章节说明和直接测试。若后续修改某一行,必须先回到该行的原始章节契约,再检查 P20 是否因此产生新的跨能力假设。

1813. 运行第 20 章config.ts 只负责把四项显式 OpenAI 配置读取成冻结的普通配置对象,不会在缺失配置时创建 .agenttutorial 或 MCP 状态。cli.ts 的 runProfile() 再校验当前目录是目标 Git 仓库根,根据 P20 与配置构造真实 BuildDependencies,最后统一交给 buildAgent()。固定入口 ch20.ts 与通用 CLI 没有第二套装配逻辑。

config.ts 只负责把四项显式 OpenAI 配置读取成冻结的普通配置对象,不会在缺失配置时创建 .agent_tutorial 或 MCP 状态。cli.tsrunProfile() 再校验当前目录是目标 Git 仓库根,根据 P20 与配置构造真实 BuildDependencies,最后统一交给 buildAgent()。固定入口 ch20.ts 与通用 CLI 没有第二套装配逻辑。

第 18 章起必须从目标 Git 仓库根目录运行。假设教程代码在 F:\笔记\Agent实操\code,目标仓库在 F:\workspace\demo-repo

Set-Location F:\workspace\demo-repo
npm exec --prefix F:\笔记\Agent实操\code -- tsx F:\笔记\Agent实操\code\chapters\ch20\src\chapters\ch20.ts --prompt "分析仓库并先给出计划"

通用入口等价:

Set-Location F:\workspace\demo-repo
npm exec --prefix F:\笔记\Agent实操\code -- tsx F:\笔记\Agent实操\code\chapters\ch20\src\cli.ts run --chapter 20 --prompt "分析仓库并先给出计划"

没有四项 OpenAI 配置时,入口会在模型或 MCP 状态创建前列出缺失字段。cwd 不是 Git 仓库根目录时,也会在创建 .agent_tutorial 状态前失败。


19与 Claude Code 的差异参照 learn-claude-code/s20comprehensive/README.md 中深入 CC 源码的分析,P20 的完整 Harness 在教学简化上与 Claude Code 的真实机制存在以下差异:

参照 learn-claude-code/s20_comprehensive/README.md 中深入 CC 源码的分析,P20 的完整 Harness 在教学简化上与 Claude Code 的真实机制存在以下差异:

工具面。 CC 的 s20 教学代码列出 27 个内置工具,包含 compactlist_cronscancel_croncheck_inboxrequest_plan 等。P20 的固定 Lead 工具面是 23 个教学工具。compact 不暴露为模型工具,由 CompactionManagerRecoveryManager 在请求前或恢复期自动执行。Cron 只保留 schedule_cron。队友消息通过 typed EventInbox 自动注入,不提供 check_inbox。协议把 request_plan 归入 shutdown/plan_approval 状态机,并由 review_plansubmit_plan 表达。额外提供 disconnect_mcp 用于显式断开连接。

循环信号。 CC 教学代码以 has_tool_use(response.content) 作为是否继续工具轮的信号,而不是直接信任 stop_reason。P20 使用 OpenAI tool_calls 字段,并在组合根中强制一个 assistant tool call 恰好配对一个同 ID 的 tool result。没有工具调用时,由 Stop Hook 做至多一次续跑检查。

组装位置。 CC 用 assemble_tool_pool()assemble_system_prompt(context) 分别组装每轮工具和 Prompt。P20 在 buildAgent(P20, BuildDependencies(...)) 统一组合,并对 CronRuntimeTeammateRuntimeProtocolRuntimeWorkStealingRuntimeWorktreeRuntime 做对象身份检查。类型相同但共享关系错误会在启动阶段失败,而不是到运行时才暴露出错。

权限架构。 CC 把权限、日志和审计都挂在 PreToolUse hook 上:blocked = trigger_hooks("PreToolUse", block) 直接决定是否执行工具。P20 将 HookRegistryPermissionPolicy 分离。Hook 只记录、转换和拦截输入输出,不能放宽 PermissionPolicy、计划门控或 McpToolPolicy 的硬拒绝;这是第 4.3 节不变量在组合根上的具体体现。

执行者边界。 CC 的 MCP 工具对主 Agent 和子 Agent 都可用。P20 明确 MCP 管理工具和远程工具只安装到 Lead 的 live registry,Subagent 与 Teammate 不继承;Teammate 默认只获得受限文件、消息、协议和任务工具面。

教学收束。 CC 用“机制很多,循环一个”总结:所有组件仍挂在同一个 while True 上。P20 用“模型提议,Harness 决策”收束:循环结构相同,但每个状态和权限边界都必须在组合根中可验证、可重建、可关闭。

这些简化让本章能聚焦于“分章能力如何收束成同一个 Harness”。真实场景中还应关注 CC 的成熟 harness 细节:工具池排序与缓存断点、MCP 重连/OAuth/反向通知、后台任务通知优先级,以及更细的恢复与压缩策略。详情见前十九章的差异说明与参考快照。

2014. 最后的架构结论一个可运行的 Agent 不是“模型加几十个工具”。它是一组可以验证的所有权和状态边界:

一个可运行的 Agent 不是“模型加几十个工具”。它是一组可以验证的所有权和状态边界:

  • 模型提议,Harness 决策;
  • Prompt 指导,Policy 授权;
  • Hook 扩展,硬边界优先;
  • 消息成组,调用与结果严格配对;
  • 状态各有权威存储,重建不猜测;
  • 长期任务都有 owner,关闭能回收;
  • 动态能力下一轮生效,不污染当前快照;
  • Subagent 和 Teammate 默认只获得裁剪后的最小工具面。

第 20 章因此没有第二个 Agent。它只有一个被前十九章逐步加固、现在可以整体证明的 Agent Harness。


至此,20 章教程形成完整闭环:每章有固定入口、累进能力、离线行为测试和真实 OpenAI 兼容运行路径;最终章复用同一实现完成整合验收。

03 / 自测

换个场景,你还会判断吗?

答完再看理由

每题只测一个边界。先做决定,再看解释。

SCENARIO CHECK01 / 030 分

准备开始