这篇文档解决什么问题
很多人讨论 AI Agent 时,容易把问题简化成:
模型越强,Agent 越强。
Prompt 写得越好,Agent 越可靠。
这两个判断都只说对了一部分。真正做过 Agent 工程的人会发现:模型只是能力来源,Agent 能不能稳定完成任务,取决于模型外面的 Runtime。这层系统通常要处理:
- 如何循环执行任务
- 如何选择和调用 Tools
- 如何读取和压缩 Context
- 如何限制危险操作
- 如何保存中间状态
- 如何验证结果
- 如何记录日志和追踪问题
这套模型外层的工程系统,就是本文讨论的 Harness Engineering。也可以把它理解为:让 Agent 真正可运行、可控制、可恢复的 Runtime 设计。
如果要设计一个可用的 AI Agent Runtime,应该从哪些模块下手?
一、先把 Agent 拆成五层
不要把 Agent 理解成“一个会调用工具的模型”。工程上更准确的拆法是:
1. Model 层
负责理解、推理、计划、生成。
2. Runtime 层
负责循环、Tools 调度、Context 管理、Permissions、State、审计。
3. Tools 层
负责读文件、改代码、跑命令、搜索、浏览器、协议接入、数据库等动作。
4. Environment 层
负责提供真实执行环境,例如本地机器、容器、云端虚拟机、文件系统、网络。
5. Evaluation 层
负责测试、检查、回放、日志、人工审批和质量评估。
这五层里,模型只是 Model 层。真正决定 Agent 是否可控、可恢复、可审计的,是 Runtime、Tools、Environment 和 Evaluation。
二、Agent Loop:先设计执行流程
一个最小可用的 Agent Loop 通常是:
接收目标
↓
收集 Context
↓
制定下一步动作
↓
调用 Tool
↓
读取 Tool 结果
↓
判断是否继续
↓
验证结果
↓
输出总结
Claude Code 的公开文档把工作方式概括为收集 Context、采取行动、验证结果。Codex 的公开技术文章也强调,Runtime 会把模型推理、Tool 调用、Tool 结果继续送回下一轮 Prompt,形成可迭代的 Agent Loop。实现时不要一开始就追求复杂。先把循环边界定义清楚:
每轮输入是什么?
每轮允许模型做哪些动作?
Tool 结果如何回传?
什么时候停止?
失败后是否重试?
最多允许循环多少轮?
最终结果如何验证?
一个可落地的循环可以这样理解:
当任务未完成时:
读取任务、State 和 Context
让模型决定下一步动作
Runtime 检查这个动作是否允许
调用对应 Tool
记录 Tool 结果
如果验证通过:
结束任务
如果超过预算或次数:
带原因停止
否则:
继续下一轮
三、Context:不要把所有东西都塞给模型
Agent 的 Context 不是越多越好。实用做法是把 Context 分成四类:
1. 长期规则 Context
项目结构、编码规范、测试命令、提交要求。
2. 任务 Context
当前需求、相关文件、错误日志、用户约束。
3. Tools Context
可用工具、工具参数、权限说明、调用结果。
4. State Context
已完成步骤、失败记录、待办项、下一步计划。
不要把安全要求只写在项目说明文件里。错误写法:
请不要读取环境变量文件。
请不要执行部署命令。
请不要删除生产配置。
更可靠的写法:
项目说明文件:
说明项目规范和操作习惯。
Permissions 和审批:
限制敏感命令和敏感路径。
Hooks:
在工具执行前拦截危险动作。
Sandbox:
从文件系统和网络层面限制可访问范围。
Context 的职责是“引导模型”,不是“强制模型”。
四、Tools:工具要小、清楚、可审计
Tools 是 Agent 从“会说”变成“会做”的接口。一个 Tool 定义至少要讲清楚:
- 这个 Tool 解决什么问题
- 输入参数是什么
- 输出结构是什么
- 失败时如何表达错误
- 是否会修改外部状态
- 是否需要用户确认
- 是否受 Sandbox 或 Permissions 控制
Tools 设计常见错误是:给模型一个过大的万能工具。
例如:
运行任意命令:接收一段命令文本,然后直接执行。
这类工具虽然方便,但风险也最大。更好的方式是把高风险动作拆开:
读取文件
搜索文本
运行测试
应用补丁
创建分支
创建合并请求
Tool 越具体,Runtime 越容易判断风险,也越容易记录审计日志。对于会改变外部状态的 Tool,要额外标注:
只读:
搜索、读取文件、查看日志。
低风险写入:
改本地草稿、生成临时文件。
中风险写入:
修改代码、改配置、提交代码。
高风险动作:
删除文件、部署、调用生产接口、发送消息、发布内容。
高风险动作不要只靠模型自觉,应该交给 Permissions、审批、Hooks 和 Sandbox。
五、Permissions:安全边界要放在执行层
Claude Code 的 Permissions 规则由工具本身执行,而不是由模型执行。Codex 也有 Sandbox、审批、权限配置等机制,用来控制文件系统、网络和命令执行。实用原则:
软约束写进文档。
硬约束写进 Runtime。
高风险动作必须有审批。
可以按风险分层:
低风险:
读取仓库文件、搜索代码、运行只读命令。
中风险:
修改源码、更新测试、生成文件。
高风险:
删除文件、修改密钥、访问生产、部署、发布外部内容。
对应控制策略:
直接允许:
明确安全的只读操作。
请求确认:
修改文件、安装依赖、访问网络、运行不确定命令。
直接拒绝:
删除敏感路径、读取密钥、生产部署、危险命令模式。
不要把下面这种内容只写成 Prompt:
请不要执行删除命令。
请不要读取密钥目录。
请不要调用生产接口。
应该落到执行层:
直接拒绝:
删除类命令
读取密钥目录
读取环境变量文件
请求确认:
安装依赖
访问网络
调用外部接口
模型可以犯错,Runtime 不能把错误直接放行。
六、Hooks:把“希望它做”改成“必须执行”
Hooks 的价值是把流程控制从 Prompt 里拿出来,变成确定性的控制。Claude Code 的 Hooks 可以在会话开始、会话结束、用户输入、工具调用前后等节点触发。Codex 也提供 Hooks、Rules 和自定义能力。
适合做 Hooks 的事情:
Tool 执行前:
拦截危险命令
检查敏感文件路径
检查是否访问生产环境
检查是否越权访问网络
Tool 执行后:
文件修改后自动运行格式化
修改代码后提醒运行测试
Tool 失败后记录失败原因
会话结束时:
输出任务摘要
记录修改文件
保存失败点和下一步计划
典型场景:
不要在 Prompt 里写:
你改完代码后记得运行测试。
更可靠的做法:
在 Tool 执行后检测到代码文件变化,再提示或自动运行对应测试。
七、State:长任务不能只靠 Context 记忆
Agent 做短任务时,可以依赖当前 Context。做长任务时,必须有外部 State。因为长任务会遇到:
- Context 变长
- 中间失败
- 会话中断
- Tool 输出太多
- 需要多人或多个 Agent 接力
实用的状态文件可以很简单:
任务状态文档
当前目标:
修复登录测试失败。
已完成:
已运行测试,确认登录超时用例失败。
已定位到会话管理模块。
当前判断:
失败原因可能是令牌过期时间单位错误。
下一步:
检查会话超时计算逻辑。
修改后运行认证相关测试。
风险:
不要改动生产配置。
对于复杂任务,还可以拆成:
计划文档:任务计划
进度文档:当前进度
发现文档:排查发现
决策文档:关键决策
交接文档:中断恢复说明
这类状态文件的作用不是替代模型,而是让模型每次恢复时能读懂当前局面。
八、Subagents:用来隔离 Context,不是堆模型数量
Claude Code 的 Subagents 有独立 Context、独立 System Prompt 和 Tool 权限。Codex 也提供 Subagents,用于把任务拆给专门的 Agent。Subagents 最适合解决的问题是:
- 主 Context 太拥挤
- 某个子任务需要独立探索
- 不同子任务需要不同权限
- 需要并行做只读分析
- 需要 Review Agent 单独检查修改差异
常见拆法:
主 Agent:
负责目标、约束、最终决策。
探索 Agent:
只读代码,输出架构摘要。
实现 Agent:
修改限定范围内的文件。
测试 Agent:
运行测试,整理失败原因。
Review Agent:
审查修改差异、风险和遗漏测试。
不要把 Subagents 当成“多叫几个模型就更强”。如果没有清晰边界,多个 Agent 只会制造更多噪音。一个好的 Subagent 任务应该写清楚:
你负责哪个文件或模块?
你能不能修改文件?
你要输出什么?
你不能做什么?
结果如何交回主 Agent?
九、Evaluation:没有验证,就不是可靠 Agent
Agent 的结果不能只看最终回答,还要看它是否真的完成了任务。常见验证方式:
代码任务:
单元测试、类型检查、静态检查、构建、回归测试。
文档任务:
链接检查、格式检查、事实来源检查、结构检查。
数据任务:
查询结果校验、样本复核、指标口径检查。
发布任务:
预览检查、权限确认、内容差异、发布前确认。
可用的完成标准应该是具体的:
差:
修好这个问题。
好:
登录超时用例通过。
不修改公开接口。
新增一个覆盖令牌过期时间的测试。
输出修改文件和验证命令。
如果任务不能自动验证,也要设计人工检查点:
请用户确认发布。
请用户确认是否访问生产。
请用户确认是否删除文件。
请用户确认最终文案风格。
十、Observability:生产级 Agent 必须能追踪
Claude Code 支持通过 OpenTelemetry 导出指标、事件日志和可选追踪信息,用来观察用量、成本、工具活动、权限变化、MCP 连接、Hooks、Context 压缩等事件。生产级 Agent 至少要能回答:
谁触发了任务?
模型收到了什么目标?
调用了哪些 Tools?
哪些 Tools 成功或失败?
哪些命令被批准或拒绝?
修改了哪些文件?
花了多少 token 和时间?
在哪一步停止?
失败后能否恢复?
如果没有这些信息,Agent 一旦出错,就很难复盘。可以先从最简单的日志开始:
时间
任务编号
用户目标
Tool 名称
Tool 输入摘要
Tool 结果状态
修改文件
是否需要审批
错误信息
下一步
日志不是为了好看,而是为了让失败可以定位、任务可以恢复、风险可以追责。
十一、一套最小可用的 Agent Runtime 清单
如果要从零做一个 Agent Runtime,可以先实现这些模块:
1. 任务接收
接收用户目标,记录任务编号、约束、完成标准。
2. Context Loader
加载项目说明、相关文件、历史 State、Tools 说明。
3. Tools Registry
注册 Tools,标注输入、输出、风险等级和是否需要审批。
4. Permissions 层
对 Tool 调用做直接允许、请求确认、直接拒绝判断。
5. Sandbox
限制文件系统、网络和命令执行范围。
6. Agent Loop
负责模型调用、Tool 调用、结果回传和停止条件。
7. State Store
保存计划、进度、失败点、修改记录。
8. Hooks
在 Tool 调用前后执行固定检查。
9. Evaluator
运行测试、检查结果、判断是否完成。
10. Observability
记录日志、成本、Tool 调用、审批和错误。
最小版本不需要一开始做得很复杂,但这些边界要先存在。
十二、常见误区
误区 1:把安全规则写进 Prompt 就够了
Prompt 是软约束,Runtime 才能提供硬约束。敏感文件、生产环境、删除操作、外部发布,都应该由 Permissions 和 Sandbox 控制。
误区 2:Tools 越多,Agent 越强
Tools 越多,选择空间越大,误用风险也越高。Tool 应该按任务类型逐步开放,并标注风险等级。
误区 3:Context 越长,效果越好
长 Context 会增加噪音,也会增加成本。更好的做法是按任务加载相关 Context,并用状态文件保存长期信息。
误区 4:Subagents 等于并行加速
只有边界清楚、输出明确、权限隔离时,Subagents 才有价值。否则只是把一个混乱任务拆成多个混乱任务。
误区 5:最终回答看起来对,就算完成
Agent 的完成标准应该来自验证结果,而不是回答文本。代码要跑测试,文档要查来源,发布要看预览。
夜雨聆风