Harness Engineering 实战:让 Coding Agent 持续、可靠地完成工程任务

《Harness Engineering 实战:让 Coding Agent 持续、可靠地完成工程任务》

一、Harness Engineering 到底是什么?

Harness Engineering 工程关注的,就是这些让模型持续、可控地完成任务的工作条件。

先说清 Harness 在系统中的位置。 在 AI Agent 的语境里,可以把 Harness 理解为承载模型工作的运行机制:它准备上下文,协调模型与工具的调用,收集执行反馈,管理任务状态,并决定何时继续、停止或交接。不同系统的具体边界会有差异,本文讨论的是 Coding Agent 场景。

模型负责根据输入生成内容、判断和工具请求;运行机制负责把请求交给工具,并把结果带回下一轮;实际的文件读写、命令执行和网络访问,还受到执行环境与权限系统的控制。我们使用的 Coding Agent,通常已经把其中多项能力组合起来。

因此,在已有 Coding Agent 上实践 Harness 工程,可以从项目周围的机制入手:整理上下文入口、提供稳定的验证命令、记录任务状态、设置工具边界、保存交接证据。只有现有能力不足时,才需要自己编写更多运行时逻辑。

二、概念理解:

几个经常一起出现的概念,可以这样区分:

概念 主要回答的问题 退款任务中的例子
Spec 什么行为符合要求? 重复请求不能产生第二笔退款
Prompt 当前这一轮要做什么? 根据失败报告定位原因并提出修复
Skill 某类任务通常怎样操作? 按项目约定排查、修复并执行回归
Harness 工作如何持续推进并受到检查? 调用工具、回传结果、控制状态、检查完成条件、保存交接

它们可以配合使用。一个更详细的 Prompt 能改善当前指令,一套可执行的机制则能让后续每轮行动都有明确依据。

闭环的关键,在于执行结果能改变下一步。 下面用一个顺序执行、内存存储的模拟退款接口说明。这是教学设计,不涉及真实支付、并发请求或数据库事务。

先把验收条件写成能够观察的结果:本人可以退款;他人请求被拒绝且没有退款副作用;同一订单再次请求时,退款记录总数仍然为一;已有订单功能通过回归。重复请求具体返回什么状态码,由接口契约确定,幂等性本身并不要求所有系统采用同一种响应。

随后,把这些要求接入工作流程:
#mermaid-svg-umuolw8QBQXAn6g3{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-umuolw8QBQXAn6g3 .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-umuolw8QBQXAn6g3 .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-umuolw8QBQXAn6g3 .error-icon{fill:#552222;}#mermaid-svg-umuolw8QBQXAn6g3 .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-umuolw8QBQXAn6g3 .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-umuolw8QBQXAn6g3 .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-umuolw8QBQXAn6g3 .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-umuolw8QBQXAn6g3 .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-umuolw8QBQXAn6g3 .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-umuolw8QBQXAn6g3 .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-umuolw8QBQXAn6g3 .marker{fill:#333333;stroke:#333333;}#mermaid-svg-umuolw8QBQXAn6g3 .marker.cross{stroke:#333333;}#mermaid-svg-umuolw8QBQXAn6g3 svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-umuolw8QBQXAn6g3 p{margin:0;}#mermaid-svg-umuolw8QBQXAn6g3 .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-umuolw8QBQXAn6g3 .cluster-label text{fill:#333;}#mermaid-svg-umuolw8QBQXAn6g3 .cluster-label span{color:#333;}#mermaid-svg-umuolw8QBQXAn6g3 .cluster-label span p{background-color:transparent;}#mermaid-svg-umuolw8QBQXAn6g3 .label text,#mermaid-svg-umuolw8QBQXAn6g3 span{fill:#333;color:#333;}#mermaid-svg-umuolw8QBQXAn6g3 .node rect,#mermaid-svg-umuolw8QBQXAn6g3 .node circle,#mermaid-svg-umuolw8QBQXAn6g3 .node ellipse,#mermaid-svg-umuolw8QBQXAn6g3 .node polygon,#mermaid-svg-umuolw8QBQXAn6g3 .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-umuolw8QBQXAn6g3 .rough-node .label text,#mermaid-svg-umuolw8QBQXAn6g3 .node .label text,#mermaid-svg-umuolw8QBQXAn6g3 .image-shape .label,#mermaid-svg-umuolw8QBQXAn6g3 .icon-shape .label{text-anchor:middle;}#mermaid-svg-umuolw8QBQXAn6g3 .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-umuolw8QBQXAn6g3 .rough-node .label,#mermaid-svg-umuolw8QBQXAn6g3 .node .label,#mermaid-svg-umuolw8QBQXAn6g3 .image-shape .label,#mermaid-svg-umuolw8QBQXAn6g3 .icon-shape .label{text-align:center;}#mermaid-svg-umuolw8QBQXAn6g3 .node.clickable{cursor:pointer;}#mermaid-svg-umuolw8QBQXAn6g3 .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-umuolw8QBQXAn6g3 .arrowheadPath{fill:#333333;}#mermaid-svg-umuolw8QBQXAn6g3 .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-umuolw8QBQXAn6g3 .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-umuolw8QBQXAn6g3 .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-umuolw8QBQXAn6g3 .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-umuolw8QBQXAn6g3 .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-umuolw8QBQXAn6g3 .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-umuolw8QBQXAn6g3 .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-umuolw8QBQXAn6g3 .cluster text{fill:#333;}#mermaid-svg-umuolw8QBQXAn6g3 .cluster span{color:#333;}#mermaid-svg-umuolw8QBQXAn6g3 div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-umuolw8QBQXAn6g3 .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-umuolw8QBQXAn6g3 rect.text{fill:none;stroke-width:0;}#mermaid-svg-umuolw8QBQXAn6g3 .icon-shape,#mermaid-svg-umuolw8QBQXAn6g3 .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-umuolw8QBQXAn6g3 .icon-shape p,#mermaid-svg-umuolw8QBQXAn6g3 .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-umuolw8QBQXAn6g3 .icon-shape .label rect,#mermaid-svg-umuolw8QBQXAn6g3 .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-umuolw8QBQXAn6g3 .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-umuolw8QBQXAn6g3 .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-umuolw8QBQXAn6g3 :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 验收未通过
有新依据且允许继续
无法继续或达到停止条件
检查未能执行
恢复后重新检查
无法恢复
检查通过
仍有缺项
条件满足
确认目标、规格和当前版本
执行下一步操作
运行检查并收集证据
结果属于哪一类
分析失败并选择下一步
保存未完成状态并交接
排查环境或依赖
核对全部完成条件与证据版本
记录完成并交付

三、实际业务案例举例来理解:

你给 Coding Agent 一项需求:为订单增加退款功能,要求只有订单本人能操作,同一笔订单不能重复退款。Agent 修改了代码,也运行了测试。第二次退款仍然产生了新的退款记录,它继续修改,再次运行,最后告诉你:"退款功能已经完成。"

在这个设想的场景里,需求写了,代码改了,工具也调用了。缺少的是一套机制:让失败真正影响下一步行动,让未满足验收条件的任务保持未完成,让接手的人能够核对到底发生了什么。

"重复退款仍然失败"提供的信息太少。一份有用的反馈,应让 Agent 知道失败发生在哪里、预期是什么、实际是什么。例如:

yaml 复制代码
# 示意报告,字段与数值用于解释机制
check: repeated_refund_has_no_extra_effect
execution: completed
result: failed
expected_refund_records: 1
actual_refund_records: 2
evidence: reports/refund-check-014.log
task_status: incomplete

这份反馈证明,重复请求产生了额外退款记录。但它还不能证明原因一定是"订单状态没有更新"。也可能是检查顺序有误,或者重复请求走了另一条分支。

Agent 下一步应检查这些可能性,形成可以验证的修复假设,再修改并重跑。失败信息越具体,下一轮越容易围绕证据推进。

如果检查因为依赖缺失根本没有启动,应该记录"尚未验证",先处理环境问题。"业务断言失败"和"检查无法运行"需要不同的处理路径,把它们都压缩成一个"失败",会引导 Agent 修改错误的地方。

完成是一项需要证据支持的状态。 "代码已经修改""测试命令退出""当前检查通过"和"任务完成",分别说明不同的事情。

在这个例子中,允许进入完成状态的规则可以写成:必需检查全部实际执行且通过,结果对应当前待交付代码,没有未处理的阻塞项,并且规格要求的人工验收已经完成。是否需要人工验收,应在任务开始时约定。

这个规则应进入任务状态管理或交付检查。模型输出一句"完成了",可以视为完成申请;最终是否标记完成,由约定的条件判定。即使全部检查通过,结论也只覆盖已验证的要求,测试本身仍可能遗漏场景。

同样,停止工作有多种原因。连续几轮出现相同失败、没有获得新证据,或已经用完预算,都可能触发停止。此时任务应保留未完成状态,附带已尝试的方案、失败证据和下一步建议。"三次失败"可以是某个项目的策略参数,不是 Harness 的通用法则。

Anthropic 对长任务 Agent 的实验记录了提前宣布完成、一次尝试过多功能和验证不足等问题。他们采用初始化与后续增量开发的安排,通过功能清单、进度记录和 Git 历史帮助后续会话接续工作。这些实践说明,跨轮次状态需要被明确保存和核对。

把约定变成机制,需要知道约束由谁执行。 在 AGENTS.md 中写"不要修改验收测试",表达了协作要求。工具层的写入范围、沙箱权限或受保护的检查流程,才能在执行时阻止或检出越界操作。

如果验收测试和"通过报告"都能被同一个 Agent 任意改写,检查结果就需要额外审视。团队可以将验收基线交给独立的 CI 流程执行,对测试变更设置复核,或限制 Agent 对相关文件的写入权限。需求变化时,测试也可以更新,但应有明确的契约依据。

这里还有一个容易误解的地方:给报告附上哈希,能够帮助发现内容变化。假如 Agent 同时能改写报告和预期哈希,它就不构成独立的防篡改证明。机制提供多大保证,取决于执行它的环境和信任边界。

上下文也需要同样的工程处理。规格、代码和说明散落各处,即使都写得很详细,Agent 也可能读取过时版本。可以保留一个简短的入口,指向当前规格、架构说明、修改范围、运行命令和任务状态,再按需要加载细节。

OpenAI 的工程实践采用了这种资料导航方式,并通过自定义 linter 和结构测试检查架构约束。可读的说明帮助 Agent 找到依据,可执行的检查则让违反约定的行为产生明确反馈。

交接时,要交付能够重新核验的状态。 假设今天的 Agent 停在退款缺陷上,明天换了一个会话。如果只把昨天的分析原样复制过去,新会话可能继续修复一个已经被别人改掉的问题。

一份简洁的交接记录,至少需要包含:

  • 目标与依据:当前需求、验收条件、相关规格位置。
  • 当前产物:仓库路径、分支、提交,以及尚未提交的修改。
  • 已确认事实:运行过的检查、实际结果、对应日志和代码状态。
  • 尚未证实的判断:可能原因、排查思路、仍缺少的信息。
  • 待办与下一步:未完成事项、复查命令、允许继续的范围。

例如,"第二次请求后出现两笔退款记录"是观察事实;"可能漏掉状态更新"是假设。把两者分开,能避免猜测在传递中变成既定结论。

接手后先比较当前代码、规格和测试是否发生变化,再决定哪些证据仍然适用。代码已变化时,旧报告仍可用于理解历史,但不能直接证明当前版本正确。

只保存 Git 提交号有时也不够,因为工作区可能存在未提交修改。更稳妥的做法,是让验证对应明确的输入快照,并记录相关依赖、环境和测试配置。证据究竟需要覆盖哪些输入,取决于哪些变化会影响结果。

从一个最小任务开始,比一次配置所有机制更容易验证效果。 在现有项目中,可以先选一个范围小、验收明确的缺陷,完成四件事:

  1. 写清行为契约、修改范围和完成条件,让 Agent 有可查的依据。
  2. 提供一条稳定的检查命令,保留实际输出,区分断言失败与执行失败。
  3. 把检查结果接入任务状态,明确修复、停止和交接的条件。
  4. 保存一次可核验的交接记录,让下一会话能够从当前状态继续。

完成这一轮后,再观察真正的瓶颈:是经常读错资料,还是反馈过于笼统?是上下文丢失,还是验收没有约束力?针对问题增加机制,才能知道它解决了什么。

机制的效果也需要评估。可以选择同一批任务,尽量固定模型、初始代码、工具权限和预算,每次主要调整一种机制,并重复运行。除了完成率,还要观察误报完成次数、人工接管时间、总耗时和成本。成功多了一次,不能单独证明 Harness 得到了普遍改善。

Anthropic 在另一项应用开发实验中加入独立评价角色,同时记录了评价过于宽松、需要校准的问题;随着模型能力变化,他们还移除了先前使用的部分上下文重置机制。这提醒我们,评价者也需要验证,已有流程也需要随模型和任务重新评估。

相关推荐
liulilittle2 小时前
多智能体编排的三个点
ai·llm·agent·tools·opencode
七夜zippoe3 小时前
第一季·阶段总结:Agent 核心技能栈检查清单与实战自测
网络·ai·agent·核心技能·实战自测
一 铭12 小时前
Pi实战 05:本地模型 · MCP · 安全沙箱篇
人工智能·ai·agent·harness
墨心@15 小时前
Coding Agent 与通用 Agent
自然语言处理·agent·harness
网络毒刘17 小时前
Token 账单的「隐形税」:系统提示、工具定义与历史滚动为何比生成贵
agent·token·cursor·成本·mcp
吃饱了得干活18 小时前
Agent 的记忆与工具:从上下文窗口到 MCP
python·agent·mcp
燐妤18 小时前
LangGraph-复习总览
python·ai·面试·agent·学习方法·langgraph
与海boy20 小时前
Agent tool
agent
小盆女神节奶粉21 小时前
对于LangGraph的时间旅行底层机制的理解
agent