第 1 课:什么是 Harness:模型之外的那层控制代码
学习目标:
- 说清楚 harness 和模型之间的分界线,指出「提议动作」和「执行 + 控制」分别归谁
- 用 Agent 与工作流的区别,解释一个系统凭什么被叫做 Agent
- 判断一个「Agent 不够可靠」的问题,该在模型层解决还是在 harness 层解决
前置要求:完成本系列前 6 门课,理解一次工具调用的往返(stop_reason: "tool_use" / tool_result) | 下一课 第 2 课 >>
一次往返,你其实已经见过 harness 了
你在上一门课里已经拆过一次工具调用的完整往返:模型看到你的问题和一份工具清单,回一个 stop_reason: "tool_use",附带一个 tool_use 块,说「我要调用这个工具、参数是这些」。模型自己从不执行任何操作,它只发出一个结构化请求;真正打开文件、跑命令、发网络请求的,是运行这个 Agent 的宿主代码。宿主执行完,把结果包成 tool_result 拼回对话,再发起一次请求。1
那段「执行工具 + 决定下一步怎么办」的宿主代码,就是这一课的主角。给它起个名字:harness,你可以理解成「套在模型外面的那层控制代码」。
不过上一门课停在「一次往返」就收手了。真实的 Agent 很少只来回一次:它调完一个工具,看到结果,往往还要接着调下一个、再下一个,一圈圈转下去。这个「一圈圈转」的过程谁在管?谁决定转到什么时候该停?跑偏了谁来拉回来?——全是 harness 的活。这一课先把 harness 和模型的分界线划清楚,后面几课再把 harness 这层代码一块块拆开。
Agent 是什么:在循环里用工具的 LLM
Anthropic 工程团队给 Agent 下过一个很朴素的定义:它们通常就是一些在循环里、根据环境反馈使用工具的 LLM。2 这句话拆开看有三个词——工具、环境反馈、循环。模型调用一个工具(工具),宿主执行后把结果喂回去(环境反馈),模型看着结果决定下一步、可能再调一个工具,如此往复(循环)。你上一门课学的那次往返,其实就是这个循环转的第一圈。
同一篇文章还把两类容易混淆的系统分得很清楚。一类叫工作流:LLM 和工具沿着预先写死的代码路径被编排起来——先干这个、再干那个、遇到分支往哪走,都是人提前定好的。2 另一类才叫 Agent:由 LLM 动态地自己主导流程、自己决定怎么用工具。2
把这两句摆在一起,你会发现它俩的区别不在模型多强,而在谁在做控制流决策。工作流里,下一步走哪条路是代码写死的;Agent 里,下一步做什么是模型在循环中当场定的——一个系统之所以算 Agent,正是因为它在一个循环里自己做控制流决策。3 而承载这个循环、把模型的决策真正执行出来的,就是 harness。
这里要小心一个措辞:说「模型自己决定下一步」,指的是模型在每一轮里提议做什么动作;但循环本身转不转、要不要再问模型一次,仍然是 harness 这层代码说了算。模型提议,harness 裁决——这条分界线,是理解后面所有内容的地基。
同一个模型,换个 harness,结果可能天差地别
现在到了这一课最想让你记住的一句话。有一份社区路线图把它讲得很直白:同样的模型,不同的 harness,结果完全不同。3
这话初听有点反直觉。我们太习惯把「Agent 靠不靠谱」记在模型头上——这个模型强,那个模型弱。但想想上一门课那次往返里,模型到底干了什么:它只负责「看着当前对话,提议下一个动作」。至于这个动作要不要真的执行、执行完循环要不要继续、转了二十圈还没结果要不要喊停、要调用的是个删数据库的危险操作时要不要先问一下人——这些没有一个是模型在管的,全是 harness 在管。
举个具体的对比。同一个模型,接同一套工具,去做同一个「清理项目里的无用依赖」的任务:
- Harness A:模型每提议一个动作就无条件执行,没有轮次上限,也不看动作是不是在原地打转。模型某一轮误判、提议了个错误的删除,harness 照删不误;接下来模型基于这个已经被搞坏的状态继续往下推,一步错步步错。这类系统的麻烦,正来自 Agent 的自主性——自主意味着更高的成本,也意味着错误会累积放大。2
- Harness B:同样的模型、同样的提议,但这层代码给循环设了轮次上限,会检测「连续几轮没有实质进展」,并且在执行「删除」这类高影响动作前先停下来等人确认。同一个误判提议,在这里会被审批阀挡住,而不会直接落到磁盘上。
模型一模一样,两次提议可能都一样,但一个把项目搞坏了、一个稳稳当当。差别整个来自外面那层控制代码。所以当你的 Agent「不靠谱」时,先别急着换更强的模型——很多时候,问题出在 harness 这层,而不是模型那层。
Harness 不是一个东西,是一组零件
到这里你可能会把 harness 想象成「那个循环」——不完全对。循环是它最核心的部件,但 harness 是一组配合工作的组件,那份路线图就把它拆成了循环控制、工具分发、上下文管理等几块。3 提前认个脸,后面几课会各拆一块:
- 循环控制:驱动「模型 → 执行工具 → 把结果喂回模型」这个 while 循环转起来的那段代码。3 它读模型返回的
stop_reason,判断这一圈之后是接着转还是停下——这是 harness 的心脏,第 2 课专门讲。
- 工具分发:模型提议「调用某工具、参数是这些」之后,把这个请求路由到对应的实际函数、执行、再把输出包成
tool_result 的那段代码。你上一门课见过它的单步版本,harness 要做的是把它接进循环里反复用。
- 上下文管理:循环每转一圈都会往对话里堆更多数据。一个在循环里运转的 Agent 会不断生成对下一轮推理可能有用的数据,越堆越多;4 而模型的「注意力预算」是有限的,每引入一个新 token 都会消耗掉一点。4 所以 harness 得管着这些历史往哪放、留多少,不能任由它无限膨胀。
还有两块这门课后面会重点讲,先在这儿点个名,好让你知道它们也归 harness 管:
- 停止条件:除了跟着
stop_reason 走,通常还会加上显式的停止条件(比如一个最大迭代次数)来保住控制权。2 什么时候该收手,是第 3 课的主题。
- 失控兜底与人工干预:循环可能连转很多轮,你必须对模型的决策有一定程度的信任;2 但信任不等于放任——Agent 可以在检查点、或者遇到障碍时,暂停下来等人反馈。2 死循环、空转、预算耗尽怎么兜底(第 4 课),人怎么在中途打断和转向(第 5 课),都在这一层。
你不需要现在就记住每一块的细节,只要建立一个整体印象:harness = 模型外面那层控制代码的总称,它由若干零件拼成,共同决定这个 Agent 转得稳不稳。 这也解释了为什么换个 harness 结果会天差地别——你换掉的不是一行代码,是整套「怎么控制这个循环」的策略。
什么时候才需要这层控制
看到 harness 能管这么多事,容易滑向另一个极端:是不是每个用到模型的场景都得搭一套带循环控制、审批阀、空转检测的复杂 harness?不是。
回到 Agent 和工作流那条线。如果你的任务路径是固定的——比如「收到工单 → 分类 → 按类别转发」,每一步走哪条分支都能提前写死,那这是个工作流,用预定义的代码路径编排就行,2 根本不需要让模型在循环里自己做控制流决策。硬给它套一个自主循环,只是徒增让它跑偏的机会。
真正需要完整 harness 的,是那些步骤数和路径事先说不清的任务:要改几个文件、先读哪个后读哪个、某一步的结果会不会让它回头重来——这些只有模型看到当前状态才能定。这时你才需要一个能让它在循环里自主决策、同时又被牢牢控制住的 harness。有一条通用的分寸值得记住:应当只在复杂度确实能改善结果时才增加复杂度。2 harness 的每一块控制逻辑,都该是为了解决一个你真实遇到的失控问题而加的,而不是因为「别人都这么搭」。
这门课接下来聚焦的,正是后一类场景里最核心、也最容易出问题的那个零件——循环本身。既然 harness 决定了 Agent 靠不靠谱,而循环控制又是 harness 的心脏,那我们下一课就从「这个循环到底怎么从一次往返,转成持续运转」讲起。
小结
- harness 是套在模型外面那层控制代码的总称——执行工具、驱动循环、决定何时停何时等人,全归它管。模型只提议下一个动作,harness 才裁决这个动作要不要执行、循环要不要继续。1
- Agent = 在循环里靠环境反馈用工具的 LLM。2 它和工作流的区别不在模型强弱,而在控制流决策由谁做:工作流是代码写死的路径,Agent 是模型在循环里自己定的,2 而承载这个循环的就是 harness——Agent 之所以是 Agent,正因为它在循环里自主做控制流决策。3
- 同一个模型,换个 harness,结果可能天差地别。3 所以 Agent 不可靠时,先查 harness 这层:轮次上限、空转检测、高影响动作的审批,往往比换更强的模型更能治本。
- harness 是一组零件而非单一物件:循环控制、工具分发、上下文管理等各司其职。3 其中上下文得专门管,因为循环每转一圈都在往对话里堆数据、消耗有限的注意力预算。4
- 不是每个场景都要搭完整 harness。 路径固定的任务用工作流就够;2 只在步骤和路径事先说不清、确实需要模型自主决策时才上,且应当只在复杂度确实改善结果时才加复杂度。2
>> 第 2 课:核心循环:从一次往返到持续运转