我们已经知道了LLM 如何说话、Agent如何长出手脚、ReAct 如何让它连续干活。但把这三样直接丢进 Agent Loop 放任不管,跑不了几轮就会出事:web_search 返回几万字撑爆窗口、脚本写错却没人告诉它、上下文满了开始出错、一条 rm -rf 删掉整个工作区。
让这台机器能稳定、安全、持续地跑起来的那一整套人工规则,就是 Harness。
7.1 什么是 Harness
Harness = 人工对 Agent Loop 制定的所有约束规则的集合,是 Agent 的工程脚手架。
如果说 LLM + ReAct 是引擎,那 Harness 就是让引擎能真正上路的底盘、刹车和仪表盘。
上一章节开头我们提到的截断超长返回、把脚本报错回传、上下文过半就压缩——手段各异,却都是 Harness,也都在做同一件事:管理喂给 LLM 的 Context。所以说,Harness 要解决的问题,大多本质上是上下文问题。
LLM 像刚上路的学员司机,Harness 就是那套教练车装置——在关键处兜底,让新手也敢真正上路。
7.2 Hooks
Agent 真正改变世界靠的是工具,所以 Harness 最常见的着力点,就是在工具执行的前后各挂一个钩子(hook):
- before_tool(执行前):校验 / 改写参数、拦截危险调用、按需要求人工确认。
- after_tool(执行后):加工返回结果——截断、附加提示、注入校验反馈。
after_tool
把它裁到预算内并附上“(内容过长,已截断)”,避免一次调用就超出上下文窗口(呼应第 5 章场景 1)。在 mybot 里,工具返回统一经过一层处理,见 mybot/web/agent_loop.py。7.3 工具边界与安全
工具让 Agent 能改造环境,也就意味着它能破坏环境。Harness 必须给工具划边界:
- 危险命令拦截:
rm -rf、sudo、越权路径等。 - 把文件读写与 shell 限制在工作区之内。
- 高风险动作要求人工确认。
rm -rf /,before_tool 命中黑名单直接拒绝,并把拒绝原因回传给它,让它换一个安全的做法。对应到 mybot:BashTool 的 restrict_to_workspace,以及
BASH_GUARD_PATH_TRAVERSAL / BASH_GUARD_OUTSIDE_WORKDIR
两个护栏开关;文件工具也都被限制在工作区内。(这里只做指向,实现细节见仓库代码。)
7.4 后处理
工具结果不只是「原样返回」,after hook 常在里面注入反馈,帮 Agent 自我修正:
截断
超长返回按 token 预算裁剪。
校验注入
写完脚本自动跑语法 / LSP 检查,有错就把错误粘进工具结果,下一轮自己改(第 5 章场景 2)。
错误引导
失败时附“分析错误再换种方式”,或“write_file 太大就改用 apply_patch”。
说到底,after hook 就是把“环境的真实反馈”翻译成 Agent 看得懂、能据此改进的下一轮输入。mybot 里就有这类提示,见 agent_loop.py 与 tool_base.py。
7.5 循环控制
while True 得有人管:什么时候停、停不下来怎么办、以及“它说做完了但其实没做完”。
- 用户中断:随时能打断正在干活的 Agent(呼应第 4 章思考题)。
- 步数上限:
max_iterations防止无限循环造成无谓的算力与费用开销(mybot 的 agent loop 设了上限)。 - 完成判定:ReAct 的收尾特征是“回复用户”;但要能识别“假完成”(没做完却回复了),用校验或追问作最后保障。
这些都是给自主循环装的“安全阀”。
7.6 可观测与回放
Agent 是一个黑箱式的自主过程,出了问题得能复盘。Harness 会记录完整的 trajectory(每一轮的 thought / action / observation),从而支持:
- 观测:实时看它在想什么、调了什么工具、拿到什么返回。
- 回放(Trajectory Replay):把一次运行完整重放,用于调试、评测与回归(呼应第 4 章思考题)。
有了可观测,Harness 才能从凭经验设规则,转向依据数据设规则。
至此,Agent = LLM + ReAct + Harness 的三块拼图补齐:LLM 会说话,工具让它长出手脚,ReAct 让它连续地干活,而 Harness 是让这一切在真实世界里稳定、安全、可控地跑起来的工程底盘。
7.7 Prompt / Context / Harness / Loop Engineering
走到这里,我们已一路穿过围绕模型的四层工程。它们常被混为一谈,其实是同一个栈的不同层级,各有各的工作单位与失效方式——理清它们,就能判断"问题该在哪一层解决"。
关键的直觉是:每一层都包裹住前一层。Loop 里跑 Harness,Harness 的每一步组装 Context,Context 里装着 Prompt,而最中心是那次模型调用。模型本身是可复用的"商品",围着它搭的那一圈圈,才是真正的工程。
把每一层拆开看内部,它们各自的流水线是这样的(每往外一层,视野就"拉远"一圈):
工作单位:一次输入
工作单位:窗口里留下什么
工作单位:整台机器(含 Gather 内的 prompt + context)
工作单位:一整段运行 the run
| 层 | 它在解决什么 | 工作单位 | 典型失效 |
|---|---|---|---|
| ① Prompt 措辞 |
单次调用里,把任务对模型讲清楚(角色、指令、示例、输出格式、CoT) | 一次模型调用 | 措辞不当 → 推理跑偏、格式不对 |
| ② Context 窗口 |
这一轮该把哪些信息放进有限窗口(检索、重排、记忆、历史压缩) | 一轮交互 | 信息丢失 / 关键内容被埋在中间 → 准确率下降 |
| ③ Harness 机器 |
把模型接到可执行环境:工具、解析、重试、子代理、校验 | 一次完整执行 | 工具报错、解析失败、边界情况没兜住 |
| ④ Loop 系统 |
无人介入下自主多轮:定义何时继续、何时停、何时算完成 | 一整段自主运行 | 死循环、过早收工、识别不出"已完成" |
回看整份讲义:第 2 章练的是 Prompt,第 5 章讲的是 Context,本章 Harness 正是第三层,而 7.5 的循环控制已经踩到了最外面的 Loop。排查问题时先定位到层,往往比盲目改 prompt 更快。