如果你还在为生产级 AI Agent 的稳定性反复打磨 prompt,这篇文章给出的结论可能会颠覆你的直觉:到了 2026 年,真正拉开差距的已经不再是提示词技巧,而是 AI Agent Harness Engineering。核心原因很简单也很残酷——模型负责“想下一步”,但让系统在真实环境中连续、可靠、可审计地“把事做完”,靠的是围绕模型构建的控制线束。换句话说,Agent = Model + Harness,而后半部分才是决定能否上线、能否规模化运行的工程壁垒。Harness Engineering 通过上下文管理、工具调用、约束机制和验证循环,把一个容易幻觉、容易跑偏的裸模型,变成一个具备权限边界、错误恢复、状态持久化和可观测性的执行系统。这正是为什么无数团队在 demo 阶段惊艳、上线后崩溃:问题并不出在模型不够聪明,而是 Agent Infrastructure 缺乏控制回路,导致状态丢失、工具误用、错误无法拦截。对读者而言,这意味着一个关键转向:如果你关心的是 Production AI Agents,而不是一次性的展示效果,那么投入方向应该从“写得更好的 prompt”,转向 Agent Harness、Model Harness、Agent Validation Loops 等系统能力建设。模型能力决定上限,但 Harness Engineering 决定下限和可持续性;前者让 Agent 看起来聪明,后者决定它能否在复杂业务中长期跑下去、跑不失控。这也是当前顶级 Agent 架构正在形成的真正护城河。
什么是 AI Agent Harness Engineering(核心定义与公式)
AI Agent Harness Engineering,就是围绕 AI 模型构建一层运行时基础设施:它负责提供上下文、调用工具、施加约束,并通过验证循环纠正输出,从而让模型能在真实生产环境中稳定、可控、可恢复地完成任务。
最重要的结论可以压缩成一个公式:
Agent = Model + Harness
这里的意思不是“模型不重要”,而是:模型只负责生成下一步判断或动作,真正把它变成可用 Agent 的,是外面的控制线束(Harness)。正如 Martin Fowler 对 harness 的描述 所强调的,harness 本质上指“除模型本身之外的一切”;而 Addy Osmani 的总结 更直接:裸模型不是 agent,只有被状态、工具、反馈循环和约束包起来之后,它才开始像一个可执行系统。
从工程视角看,Harness 通常承担四类职责:
- Context(上下文)
决定模型在这一轮“看见什么”:用户目标、历史状态、业务规则、任务阶段、外部检索结果。 - Tools(工具)
决定模型“能做什么”:查数据库、调用 API、搜索知识库、写文件、发消息、执行代码。 - Constraints(约束)
决定模型“不能乱做什么”:权限边界、参数白名单、沙箱、人工审批、输出格式要求。 - Validation Loops(验证循环)
决定系统如何“发现并纠正错误”:结构校验、规则校验、测试、重试、回滚、人工复核。
如果把模型比作“大脑”,那么 Harness 更像神经系统 + 工具接口 + 刹车系统 + 仪表盘。生产级 Agent 的核心价值,往往不在“会不会说”,而在“能不能在复杂环境里连续做对”。
维度 | 裸模型调用 | 带 Harness 的 Agent |
|---|---|---|
输入 | 一次性 prompt | 动态上下文 + 历史状态 + 外部数据 |
能力 | 只能输出文本建议 | 可调用工具并执行多步任务 |
控制 | 主要靠 prompt 约束 | 有权限、规则、沙箱、审批机制 |
出错处理 | 出错后靠人补救 | 可校验、重试、回滚、终止 |
生产可用性 | demo 容易惊艳 | 更适合真实业务流程 |
一个简单例子就能看清差别。
假设你要做一个“自动生成周报”的 Agent。
裸模型调用的做法通常是:把“请根据本周项目进展生成周报”塞进 prompt。它也许能写出一份看起来不错的文本,但很快会暴露问题:
- 不知道要从哪个系统取真实数据;
- 可能编造进度或遗漏阻塞项;
- 无法校验数字是否和 Jira、Git、CRM 一致;
- 输出格式一变,后续系统就接不住。
而带 Harness 的 Agent会按更稳定的流程运行:
- 从项目管理工具拉取本周 ticket 状态;
- 从代码仓库汇总代码提交和发布记录;
- 根据模板生成结构化草稿;
- 校验关键数字、负责人、日期是否齐全;
- 对异常项打标,必要时发给人工确认;
- 最后再输出周报或写入企业系统。
你会发现,真正让流程可靠的不是“prompt 写得更华丽”,而是数据接入、工具调用、权限控制和校验闭环。这正是 Harness 的工作。
所以,Harness Engineering 是解决“demo 可行,但生产不可用”问题的关键工程层。模型决定上限,Harness 决定系统能不能落地、能不能持续跑、出了错能不能收得住。后面讨论的所有 Agent 架构问题,本质上几乎都可以回到这条主线:不要只优化模型输出,更要设计模型所处的运行环境。
为什么 Prompt Engineering 无法支撑生产级 Agent
Prompt 很重要,但它解决的是“这一轮怎么说”,不是“整个系统怎么跑”。
生产级 Agent 失败,通常不是因为提示词不够华丽,而是因为你把本应由系统层负责的能力,硬塞给了模型在一次次对话里“自己记住、自己约束、自己纠错”。
更直接地说:
Prompt Engineering 优化的是单次推理质量;Harness Engineering 管理的是多步执行可靠性。
当任务进入真实环境——要调用工具、跨多轮保留状态、处理中断、限制权限、校验结果、记录执行轨迹——仅靠 prompt 就会迅速碰到天花板。连针对真实 Agent 工作流的研究也开始把“模型能力”和“harness 配置”分开看,因为最终表现明显不只由模型决定,执行层的上下文、工具、状态、约束、追踪与恢复机制本身就是性能变量。
Prompt Engineering 的边界,恰好就是生产系统的起点
一个 prompt 再精致,也很难稳定承担下面这些职责:
- 状态管理
模型不会天然拥有可靠的长期状态机。多步任务一长,就会忘记目标、跳步、重复做事,或者覆盖先前结论。 - 工具调用控制
prompt 可以“建议”模型如何用工具,但很难强制它只在特定条件下调用、按正确参数调用、按最小权限调用。 - 错误恢复
真实系统一定会遇到 API 超时、工具返回脏数据、权限拒绝、格式错误。prompt 可以要求“出错时重试”,但没有运行时控制,就很难实现可验证的回退、重试和分支处理。 - 长期任务执行
长任务不是把 prompt 写长一点就能解决。上下文窗口会溢出,任务会跨会话,环境会漂移,外部资源状态会变化。Anthropic 在长时运行 Agent 的实践里就明确指出:即使有上下文压缩,单靠高层 prompt 仍不足以把任务稳定推进到生产可用结果。
常见失败场景:不是“模型笨”,而是系统没兜住
下面这些问题,在 demo 阶段常被忽略,一到生产就集中爆发:
失败场景 | 仅靠 Prompt 的典型表现 | 真正缺失的系统能力 |
|---|---|---|
幻觉式行动 | 编造不存在的 API、数据库字段或工具参数 | 上下文注入、工具 schema、执行前校验 |
无限循环/假性努力 | 反复搜索、反复总结、反复调用同一工具 | 步数上限、停止条件、状态机 |
上下文溢出 | 忘记用户目标、遗漏约束、覆盖已完成步骤 | 记忆管理、摘要压缩、任务拆分 |
长任务中断 | 中途失败后从头来过,或停在半成品状态 | checkpoint、恢复点、进度记录 |
输出看似合理但其实错误 | 生成“像对的答案”,没有任何机制拦截 | 测试、验证器、二次审查 |
这类故障模式和工程实践里的观察高度一致。例如 deepset 对生产 Agent 的分析把失败大致分成三层:context failure、constraint failure、verification failure。这很关键,因为它说明很多问题不是继续改 prompt 就能修好,而是应该分别在上下文工程、约束机制、验证闭环上修。
一个典型误区:把“会说”误当成“会执行”
看一个简单但常见的任务:自动生成周报,并发送给团队负责人。
仅依赖 prompt 的 Agent:
1. 读取聊天输入:“帮我整理本周项目周报并发邮件”
2. 模型自行决定需要哪些信息
3. 自行选择是否调用日历、工单、邮件工具
4. 生成一份看起来合理的周报
5. 直接发送,或宣称“已完成”这个流程在 demo 里可能很惊艳,但生产里风险很大:
- 可能漏掉某个数据源
- 可能把未完成事项写成已完成
- 可能发错收件人
- 可能在邮件发送失败后直接结束
- 可能把“草稿生成成功”误当成“任务完成”
带 Harness 的 Agent:
1. 创建任务 ID,记录目标、发件范围、截止时间
2. 按白名单拉取 Jira / Git / Calendar 数据
3. 若缺关键字段,进入澄清分支,不允许猜测
4. 生成周报草稿
5. 运行格式校验与事实检查
6. 高风险动作(发邮件)进入确认或策略审批
7. 发送失败则重试/回退,并写入执行日志
8. 输出最终状态:成功 / 部分成功 / 待人工处理两者的差异不在“谁写的 prompt 更高级”,而在于后者把任务变成了可控制、可恢复、可审计的执行流程。
Prompt-first 路径为什么总会卡在“demo 可行,生产不可用”
很多团队一开始都走同一条路:
更好的 system prompt
→ 更长的 few-shot
→ 更严格的输出格式要求
→ 更复杂的“如果失败请重试”指令
→ 更多补丁式规则短期看效果会提升;长期看系统会变脆。原因有三点:
- 规则只存在于自然语言里,不是强约束。
你写了“不要修改作用域外文件”,不等于系统真的禁止了它。 - 异常处理停留在语义层,不是执行层。
你要求“失败时重试”,但谁来判断失败?重试几次?重试前是否清理状态? - 行为不可观测,故障不可定位。
一旦线上出错,你很难知道是上下文缺失、工具误用、还是验证环节缺席。
这也是为什么生产环境需要的不只是“更会写提示词的人”,而是能设计运行时控制面的工程能力。
生产级 Agent 需要的,不是更长 Prompt,而是更完整的控制回路
一个最小可用的生产控制回路,至少应包含:
- 可观测性:记录每一步输入、决策、工具调用、耗时、失败原因
- 重试策略:区分可重试错误与不可重试错误,避免盲目循环
- 约束机制:权限边界、工具白名单、预算上限、步数上限
- 验证机制:格式校验、测试执行、事实核对、结果审查
- 状态持久化:让任务可以暂停、恢复、续跑,而不是每次重开一局
这不是理论洁癖,而是生产必需品。因为原型环境通常输入干净、路径单一、错误率低;而真实环境里,非确定性、外部系统波动、恶意输入和静默质量下降都会出现。一类关于生产 Agent 指标的工程讨论就强调:原型成功不代表系统具备上线可靠性,很多问题只有在真实使用中才会暴露。
判断标准很简单:你的 Agent 是“会回答”,还是“会完成”
如果一个 Agent 主要靠 prompt 维持正确性,你得到的通常是一个会生成解释的模型接口;
如果一个 Agent 具备状态、约束、恢复、验证和观测能力,你才更接近一个能稳定完成任务的执行系统。
所以,Prompt Engineering 并没有过时,它只是退回到了正确的位置:
它是 Agent 的一个输入层优化项,不再是生产可靠性的主支柱。
真正决定系统能否从 demo 走到生产的,是 Harness。
Agent Harness 的核心组件

如果把模型看成“推理引擎”,那么 Harness 就是它外面的运行时控制系统。按照 Addy Osmani 对 Agent Harness Engineering 的拆解,真正决定 Agent 在生产环境中是否可用的,往往不是模型本身,而是围绕模型构建的上下文、工具、约束、反馈和可观测性这套外层系统。
可以把一个典型的 Agent Harness 看成下面这条执行链:
用户目标
↓
Context Management(给模型正确、可控的任务上下文)
↓
Tooling Layer(把“会说”变成“会做”)
↓
Constraints & Sandboxing(限制权限、隔离风险)
↓
Validation Loops(检查结果、发现错误、触发重试)
↓
Observability(记录发生了什么、为什么失败、成本多少)
↓
稳定的 Agent 行为这几层不是彼此独立的插件,而是共同构成了 Agent 的“控制线束”:负责把模型输出变成可执行动作,再把执行结果转回下一轮决策输入。少了其中任何一层,系统都可能在 demo 中看似聪明、在生产里迅速失控。
组件 | 核心职责 | 常见实现方式 |
|---|---|---|
Context Management | 决定模型“此刻看到什么、记住什么、忽略什么” | system prompt、任务状态、摘要压缩、检索式记忆、会话状态持久化 |
Tooling Layer | 给模型提供可调用能力,而不是只生成文本 | function calling、API 封装、数据库/浏览器/文件系统工具、MCP server |
Constraints & Sandboxing | 限制 agent 能做什么,避免越权或破坏性操作 | 权限清单、allowlist、审批闸门、虚拟文件系统、容器/沙箱执行 |
Validation Loops | 在每一步后验证结果,失败时纠偏而不是一路错下去 | schema 校验、测试回放、lint/typecheck、自检、重试与回滚 |
Observability | 让系统行为可诊断、可审计、可优化 | 日志、trace、tool-call 记录、成本/延迟统计、loop detection |
State / Orchestration | 串联以上组件,管理任务流程、暂停恢复与多步执行 | planner、子任务调度、handoff、checkpoint、resume 机制 |
从工程视角看,这 6 层可以进一步压缩成 3 个问题:
- 模型知道什么?——由 Context Management 决定。
- 模型能做什么?——由 Tooling Layer 和权限边界决定。
- 模型做错了怎么办?——由 Validation Loops 与 Observability 负责闭环。
这也是为什么“裸 tool calling”还不等于完整 harness。一个只有工具调用能力的 wrapper,通常只解决了“能不能执行动作”,却没有解决“动作是否安全、状态是否连续、结果是否可信、失败后如何恢复”。关于这一点,很多工程实践也把成熟度从“裸调用”一路区分到“会话感知、可回滚、可恢复的 harness”阶段,相关架构分析基本都强调了状态持久化、沙箱隔离和上下文管理是生产级 Agent 的分水岭。
一个实用的判断标准是:如果你的 Agent 只是“拿到 prompt → 调模型 → 输出答案”,那它还不是完整的生产系统;如果它已经能携带状态、调用工具、受约束执行、验证结果、留下可审计轨迹,你才真正开始进入 Harness Engineering 的范围。
后面拆解每个模块时,可以把它们理解成一套分工明确的控制线束:
- Context Management 负责“喂对信息”
- Tooling Layer 负责“给到手脚”
- Constraints & Sandboxing 负责“装上保险丝”
- Validation Loops 负责“做完要复检”
- Observability 负责“出事能追溯”
顶级 Agent 的差异,通常就来自这套线束设计得是否细、是否稳、是否贴合具体任务。
Context Management:记忆、状态与上下文窗口控制
生产级 Agent 的上下文管理,核心不是“让模型记住更多”,而是决定什么该进窗口、什么该持久化、什么该丢弃。很多 Demo 失败,不是因为 prompt 写得不够花,而是因为任务一旦超过 10~30 轮,历史对话开始挤占上下文窗口,旧信息与新状态互相冲突,模型还会把已经失效的计划当成当前事实。像 Anthropic 对长时运行 Agent 的总结就明确指出:仅靠上下文压缩(compaction)并不足以支撑长任务,真正可用的系统还需要结构化进度文件、状态交接和恢复机制。
一个实用的做法是把上下文拆成 3 层,而不是把所有东西都塞回 prompt:
层级 | 存什么 | 生命周期 | 典型实现 |
|---|---|---|---|
短期工作上下文 | 最近几轮对话、当前子任务、最新工具结果 | 当前 session 内 | rolling context |
长期可检索记忆 | 历史决策、用户偏好、过往案例、知识片段 | 跨 session | vector memory / 检索库 |
任务状态对象 | 当前任务的结构化事实:目标、步骤、完成度、失败原因、产物路径 | 任务级 | JSON / DB row / state store |
这三层里,最容易被忽视但最关键的是任务状态对象。因为向量记忆适合“找相关内容”,不适合做“当前真相源(source of truth)”。例如,Agent 在第 12 步已经把 status=waitingforapproval 写入状态,但向量检索又捞回第 4 步的“继续执行部署”,如果你没有一个明确的 state object,模型就可能继续往下跑,直接越过审批节点。
常见的上下文策略有三种,通常需要组合使用:
- Rolling context(滚动上下文)
只保留最近几轮高价值消息,把更早历史压缩成摘要。
适合:多轮执行、窗口有限、需要保持当前连贯性。
实践上不要做“全文总结”,而要做面向执行的摘要,至少保留:
- 当前目标
- 已完成步骤
- 未完成步骤
- 最近一次失败及错误信息
- 明确约束(如不能写生产库、必须先跑测试)
Milvus 对 harness engineering 的讨论提到两种常见手段:compaction(压缩历史继续运行)和 context reset(清空窗口,靠结构化交接文档重启)。前者便宜,后者更干净,但要求你的交接产物足够完整。
- Vector memory(向量记忆)
把“可能以后有用”的信息写入可检索存储,在需要时按语义召回。
适合:跨会话偏好、知识片段、过往问题模式。
但要控制写入标准,别把每一句模型自言自语都存进去。更稳的做法是只写入:
- 用户明确声明的偏好
- 已验证的事实
- 可复用的解决方案
- 经过人工或程序确认的结论
- Task state object(任务状态对象)
用结构化对象记录任务执行状态,供每一步读写。
适合:长任务、可恢复执行、多工具编排。
它应该是可序列化、可审计、可恢复的,而不是藏在 prompt 里的“隐式记忆”。
一个简单的工程模式如下:
taskstate = {
"taskid": "deploy4821",
"goal": "修复结算服务超时并完成灰度发布",
"currentstep": "analyzelogs",
"completedsteps": [],
"blockedon": None,
"artifacts": {
"logreport": None,
"patchpr": None
},
"constraints": [
"禁止直接改生产配置",
"发布前必须通过集成测试"
],
"lasterror": None
}
recentmsgs = loadrecentmessages(limit=8)
summary = loadrunningsummary(taskid="deploy4821")
memoryhits = vectorsearch(query=taskstate["goal"], topk=3)
promptcontext = {
"taskstate": taskstate,
"summary": summary,
"recentmsgs": recentmsgs,
"memoryhits": memoryhits
}
response = agent.run(promptcontext)
if response.toolresult:
updatetaskstate(taskstate, response.toolresult)
if tokencount(recentmsgs, summary) > COMPACTTHRESHOLD:
summary = compact(recentmsgs, taskstate)
archiveold_messages()这个模式里有两个要点:
- 模型看到的是“裁剪后的上下文”,不是无限历史;
- 真正的任务进度存在
task_state里,不是靠模型自己回忆。
举个更具体的例子:一个修 Bug 的 coding agent 需要跨 20 个步骤工作。第 1 步它读需求,第 2 步跑测试,第 3 步定位文件,第 4 步修改代码,第 5 步再次跑测试。如果你只靠聊天历史,到了第 15 步模型可能已经忘了“最初失败的是哪个用例”或者“刚才修改过哪几个文件”。更稳的做法是每一步都更新状态:
{
"goal": "修复 checkout timeout",
"currentstep": "runregressiontests",
"completedsteps": [
"readbugreport",
"reproduceissue",
"patchretrylogic"
],
"artifacts": {
"failingtest": "tests/testcheckouttimeout.py::testretry",
"modifiedfiles": [
"checkout/retry.py",
"checkout/client.py"
]
},
"last_error": "integration test failed: payment gateway mock mismatch"
}这样即使发生上下文重置,新的 agent 实例也能从这份状态恢复,而不是从一大串自然语言里猜。
实际落地时,我更推荐用下面这个判断框架:
- 会影响当前执行决策的 → 放进短期上下文
- 以后可能相关,但不是当前真相的 → 放进向量记忆
- 必须准确、可恢复、可审计的 → 写入任务状态对象
- 已经过时、重复、只会增加噪声的 → 删除或归档
最常见的错误有四个:
- 把所有历史直接塞进 prompt:短期看“信息更全”,长期一定变贵、变慢、变乱。
- 把向量记忆当数据库:检索到的是“相似内容”,不是“当前真实状态”。
- 只做摘要,不做状态持久化:摘要适合压缩,不适合承载精确流程控制。
- 不区分事实、计划和草稿:模型上一轮提出的计划,并不等于已经执行成功。
如果你只记住一句话,那就是:
上下文管理不是“扩窗技巧”,而是 harness 层的信息分层设计。
顶级 Agent 的差距,往往不在 prompt 写法,而在它是否有能力在几十轮、几百次工具调用后,仍然知道:我现在在哪一步、哪些事实可信、哪些记忆该拿出来、哪些历史该忘掉。
Tooling Layer:Agent 如何安全调用外部工具

没有工具,Agent 本质上还是“会生成文本的模型”;有了工具,它才真正具备读系统、查数据、执行动作的能力。订单查询、数据库检索、网页搜索、代码执行、发工单、调用内部 API——这些都不是 prompt 本身能完成的,而是 Harness 在模型外面搭起的工具执行层。很多 demo 失败,不是因为模型不会“思考”,而是因为它被允许直接输出一段看似合理、实际上不可执行或高风险的操作建议。
工程上,安全的工具调用通常是一个固定闭环,而不是“模型想到什么就直接执行什么”:
- 声明工具契约:每个工具有明确的 name、description、参数 schema、返回格式。
- 模型只负责选择工具和填参数:它输出结构化
tool_call,而不是任意 shell 命令。 - Harness 负责校验与授权:检查参数类型、范围、权限、预算、上下文状态。
- 执行器运行工具:在沙盒、只读连接或受限网络中完成实际调用。
- 结果回传给模型:返回原始结果或规范化结果,让模型继续下一步。
一个最小可用的工具定义,通常长这样:
{
"name": "searchorders",
"description": "按邮箱查询最近订单",
"inputschema": {
"type": "object",
"properties": {
"email": { "type": "string" },
"limit": { "type": "integer", "minimum": 1, "maximum": 20 }
},
"required": ["email"]
}
}这里的 schema 不只是给模型看的说明书,更是 Harness 的执行合同:
- 它限制参数形状,避免
"limit": "all"这种脏输入; - 它让调用可观测,方便统计每个工具的错误率、延迟和重试次数;
- 它让权限控制能落到具体字段,而不是停留在 prompt 里说“请谨慎”。
一个典型的调用循环,可以写成下面这样:
tools = [searchorders, getshipment, websearch]
state = {
"toolbudget": 8,
"approvedwriteops": False,
"userrole": "supportreadonly"
}
while state["toolbudget"] > 0:
response = llm.chat(messages, tools=toolschemas)
if response.type != "toolcall":
return response.text
call = response.toolcall
validateschema(call.name, call.args) # 参数校验
authorize(state["userrole"], call.name, call.args) # 权限检查
checkbudget(state, call.name) # 调用预算/循环检测
result = executetool(
call.name,
call.args,
timeout=5,
sandbox=True,
maxrows=50
)
state["toolbudget"] -= 1
messages.append({"role": "tool", "name": call.name, "content": result})这段流程背后的关键点是:模型从不直接碰真实系统。它只能提出“调用哪个工具、带什么参数”的请求;真正能不能执行,由 Harness 决定。这也是很多生产系统采用的基础模式,AIQuinta 对这个 loop 的概括很到位:任务进入 → 模型请求工具 → Harness 执行 → 返回结果 → 模型更新计划。
实际系统里,最好把工具分成三层,而不是全都扔给模型:
工具类型 | 示例 | 默认策略 |
|---|---|---|
只读工具 |
| 默认开放,限制行数/页数/超时 |
受控写工具 |
| 参数白名单,必要时人工确认 |
高风险执行工具 |
| 沙盒运行、强审批、最小权限 |
例如,数据库访问不要只提供一个万能 run_sql。更稳妥的做法是拆成:
querycustomerorders(email, limit)getorderdetail(order_id)listrefundstatus(order_id)
这种“窄工具”比“全能工具”更容易校验,也更容易做权限边界。浏览器工具也类似:search_web、open_page、extract_text 通常比“允许自由点击和提交表单”的浏览器代理安全得多。
下面是一个现实一点的场景:客服 Agent 处理“帮我查一下 alice@example.com 最近订单,并判断是否已经发货”。
- 模型先调用
search_orders(email="alice@example.com", limit=3) - Harness 用只读 API 查询订单系统,返回订单 ID 列表
- 模型再调用
getshipment(orderid="O12345") - Harness 查询物流接口,返回状态
in_transit - 模型根据工具结果生成自然语言答复
这里有两个常见误区:
- 误区 1:让模型直接写 SQL 或 shell
- 看起来灵活,实际上最难控。
- 生产上更常见的是“模型选受限工具 + Harness 执行固定逻辑”。
- 误区 2:把权限控制写进 prompt 就算完了
- prompt 只能“提醒”,不能真正拦住错误执行。
- 真正有效的是 Harness 层的 allowlist、超时、审批和沙盒。
风险也基本都出在这层,而且大多是工程问题,不是语言问题:
- 无限调用或循环调用
Agent 反复search -> summarize -> search,把 API 配额烧光。
最低限度要有:
maxtoolcalls- 同参数重复调用检测
- 总执行时间预算
- 单工具冷却或去重缓存
- 参数合法但语义错误
例如把customer_id当成order_id传进去,schema 不会报错,但业务会出错。
解决办法是:
- 缩小工具职责
- 使用更严格的字段命名和 enum
- 在执行前加业务校验,例如“该 order_id 是否属于当前用户”
- 破坏性操作被误触发
像删库、强推分支、批量发信,都不应该由模型一跳直达。
Addy Osmani 关于 Harness hooks 的建议很实用:在工具调用前后挂钩子,直接阻断rm -rf、git push --force、DROP TABLE一类命令,并要求推送或开 PR 前必须审批。 - 代码执行环境失控
run_code看似只是“帮我跑一下脚本”,实际上最危险,因为它会把模型输出变成真实执行。
对单租户内部场景,容器已经比裸机好很多;对多租户或高风险代码执行,Blaxel 关于 microVM 隔离的讨论更接近生产要求:限制 CPU、内存、文件系统、网络出口,把执行环境当成默认不可信。
一个简单但有效的工具层规则集,通常至少包括:
- 只暴露必要工具,不要把所有内部 API 都给 Agent
- 读写分离,默认只读
- 每个工具单独超时
- 每轮/每任务调用预算
- 工具返回结构化结果,不要只回一大段文本
- 高风险动作必须审批
- 记录调用日志:工具名、参数摘要、耗时、结果状态、重试次数
- 对外部内容零信任:搜索结果、网页内容、代码仓库内容都可能含提示注入
如果你只能做一件事,那就先做这条:不要让模型直接执行任意命令;让它只能调用你定义过、验证过、可审计的工具。
这一步,往往就是 Agent 从“会演示”走向“能上线”的分界线。
Constraints 与 Sandboxing:防止 Agent 失控

真正把 Agent 跑进生产后,你会很快发现:“不要做危险操作”这种 prompt 约束,几乎不算约束。
模型只能“倾向于遵守”,但不能被信任为唯一执行边界。边界必须落在 harness:也就是工具调用前的校验、运行时预算、隔离执行环境、审批闸门、回滚机制。否则,一次错误规划、一次 prompt injection,或者一个工具参数拼错,就可能从“回答错了”升级成“把系统改坏了”。
一个实用的判断标准是:
Prompt 决定意图,Harness 决定权限。
如果权限控制只写在系统提示词里,而不是写进执行器,那它不是控制,只是建议。
生产里最常见的约束,通常至少有四层:
- Token / turn 限制
控制单次任务最多跑多少轮、消耗多少 tokens,避免 agent 在“想不明白—继续试—继续失败”的循环里烧钱。 - 工具权限控制
不是“给模型一个 shell”,而是给它一组明确 schema + allowlist 的工具。比如可以read_file、run_tests,但不能直接rm -rf;可以查只读数据库,但不能执行写操作。 - 执行预算
包括最大工具调用次数、最大 wall-clock 时间、最大 API 成本、最大浏览器步骤数。超预算就中断,而不是让 agent 无限自我修复。 - Sandbox 隔离环境
所有模型生成代码、脚本、抓取行为,都默认视为不可信执行。至少放到临时容器里;多租户或高风险场景,更稳妥的是零信任隔离,例如 microVM 级别沙箱。
可以把它理解成一个“工具调用防火墙”:
POLICY = {
"maxturns": 20,
"maxtoolcalls": 50,
"maxruntimesec": 300,
"maxcostusd": 2.0,
"tools": {
"dbquery": {"mode": "readonly"},
"websearch": {"domainsallowlist": ["docs.python.org", "api.example.com"]},
"shell": {"allow": ["pytest", "python", "ls", "cat"], "deny": ["rm", "sudo", "curl | sh"]},
},
"requireshumanapproval": ["deployprod", "sendemail", "dbwrite", "gitpushmain"]
}
def executetoolcall(call, state):
enforcebudget(state, POLICY) # turns / cost / tool calls / runtime
validatetoolname(call, POLICY["tools"]) # 是否在 allowlist
validateargs(call) # 参数 schema 校验
checkpermission(call, POLICY) # 只读、域名白名单、命令白名单
mayberequireapproval(call, POLICY) # 高风险动作人工确认
result = runinsandbox(
call,
cpulimit="2 cores",
memlimit="2GiB",
fsscope="/workspace/session123",
networkallowlist=["api.example.com"]
)
logevent(call, result, state)
detectloop(state, call, result)
return result这个流程里,模型从来拿不到“裸权限”。它只能提出工具调用请求,真正执行前由 harness 做五件事:
- 校验是否超预算;
- 校验工具和参数是否合法;
- 校验是否越权;
- 放进沙箱执行;
- 记录结果并做异常检测。
这和“在 prompt 里提醒模型小心一点”是两个层级的事情。
没有这些约束时,失控通常怎么发生?
最常见的不是戏剧化的“黑客攻击”,而是非常普通的工程事故:
- 无限循环:agent 为了修一个测试失败,反复
runtests -> editfile -> run_tests,20 分钟内打了上百次工具调用; - 错误写操作:本来要查订单,结果把 SQL 跑成了更新语句;
- 环境污染:一个任务装了依赖、改了配置,下一轮任务继承了脏状态,问题越来越难复现;
- 外部系统滥用:浏览器 agent 在弹窗、重定向、验证码页面里卡死,不断重试;
- 被 prompt injection 带偏:读了恶意仓库 README 或网页后,开始尝试访问不该访问的域名或执行危险命令。
Session-aware harness 与 sandboxing 的区别说得很直接:没有持久状态、回滚和沙箱时,agent 的错误往往就是永久性的副作用,最后只能靠人手工收拾残局。
实际落地时,建议把规则写成“可执行策略”,而不是团队默契
下面这组规则,比一句“请谨慎操作”有效得多:
agentpolicy:
limits:
maxturns: 20
maxtoolcalls: 50
maxruntimesec: 300
maxcostusd: 2.0
tools:
dbquery:
access: readonly
browseropen:
domainsallowlist:
- app.internal.example
- docs.example.com
shell:
commandallowlist:
- python
- pytest
- pip show
- ls
- cat
commanddenylist:
- rm
- sudo
- chmod 777
- curl
- wget
sandbox:
ephemeralworkspace: true
networkegressdefault: deny
cpulimit: 2
memorymb: 2048
filesystemroot: /tmp/agent-run
approvals:
- gitpushmain
- deployprod
- dbwrite
- sendexternalemail这里有几个细节很关键:
- 默认拒绝(default deny) 比默认允许安全得多;
- 只读工具优先,把“能写”变成少数特权工具;
- 工作区临时化,每次 run 都是新目录,任务结束即销毁;
- 网络白名单,不要让 agent 自由出网;
- 破坏性动作必须人工确认,例如生产部署、写库、发邮件、推主分支。
很多团队会在这里加一个 gatekeeper:当 agent 触发高风险动作时,harness 暂停执行,转而发 Slack/CLI 确认。这类“审批闸门”是实际可用的折中方案,一些生产实践也会把它作为标准组件,而不是事后补丁。
Sandboxing 不只是“用个 Docker”这么简单
对很多内部单机工具来说,容器已经比裸机强很多;但如果场景包含下面任一项,就该认真考虑更强隔离:
- 多租户;
- 执行模型生成代码;
- 允许联网;
- 接触真实仓库、密钥或客户数据;
- 需要把 agent 当成长期后台任务运行。
原因很简单:LLM 生成的代码应该默认视为不可信。
对于高风险场景,零信任沙箱和 microVM 隔离比共享宿主内核的普通容器更稳,代价是基础设施复杂度更高。
一个比较务实的分层方案是:
场景 | 最低建议 | 更稳妥方案 |
|---|---|---|
本地个人实验 | Docker 容器 + 临时目录 | 容器 + 网络白名单 |
团队内部工具 | 容器 + 资源限制 + 审批闸门 | 单任务单容器 + 快照回滚 |
多租户 / 高风险代码执行 | 强隔离沙箱 | microVM + 零信任网络策略 |
约束不仅是“拦住”,还要“观测到”
只有限制,没有观测,后面还是会失控。至少应该记录这些指标:
- 每任务工具调用次数;
- 每工具错误率;
- 平均任务时长;
- 循环检测触发次数;
- 被策略拒绝的调用数;
- 人工审批触发频率。
如果一个 agent 平均每个任务要调 80 次工具,或者某个工具错误率长期高于 30%,问题通常不在模型“笨”,而在 harness 设计不对:工具粒度太粗、参数 schema 太模糊、允许的动作太多,或者缺少显式验证。一些生产 hardening 清单就会把 loop detection、工具错误率、延迟日志直接列为必配项。
最后给一个很实用的结论:不要把“防失控”理解成安全团队的额外要求,它本质上是可靠性工程。
Agent 不是因为更“聪明”才更难控制,而是因为它开始接触真实系统。一旦能写文件、跑命令、查数据库、点浏览器,约束和沙箱就不再是可选项,而是 harness 的基础设施。
Validation Loops:让 Agent 自我检查与纠错

Validation loop 本质上是把 Agent 的一次输出,变成一个可验证、可回退、可修复的工程循环,而不是“一次生成,直接交付”。在生产环境里,最常见的结构就是:
生成(Generate)
↓
检查(Validate)
↓
修复(Repair)
↓
重新检查(Re-validate)
↓
通过 / 终止 / 升级人工这套机制的关键,不是“让模型更会反思”,而是让输出接受外部约束。很多稳定性问题并不是因为模型不会写,而是因为没有人或没有程序在它写完后立即检查:语法是否正确、接口是否存在、测试是否通过、页面是否还能跑、是否违反安全规则。
一个实用的 validation loop 通常包含 4 个阶段:
- 生成候选结果
Agent 先完成一个最小可检查单元,而不是一次性产出所有内容。比如修改一个函数、补一组 SQL、写一个 API handler。 - 执行验证器
Harness 触发外部检查,而不是只让模型“自评”。验证器可以是:
- 规则检查:JSON schema、输出格式、字段完整性、权限规则、禁止命令列表
- 静态检查:lint、typecheck、AST 规则、SQL 语法检查
- 程序化测试:unit test、integration test、API contract test、UI smoke test
- 模型评审:让另一个 reviewer prompt 按明确 rubric 审查输出
- 环境验证:启动服务、跑一个真实请求、检查页面是否报错
- 把失败信号结构化反馈给 Agent
不要只返回“失败了”。要返回可操作的错误上下文,例如:
- 失败的测试名
- 报错栈
- 违反的规则 ID
- 期望值与实际值
- 相关文件和行号
- 限次修复并重试
Agent 依据失败信号修复,然后再次进入验证。这里必须有重试上限、终止条件和人工接管条件,否则很容易陷入低价值循环。
下面是一个最典型的例子:代码修复 Agent。
任务:修复某个后端接口的 bug
1. Agent 读取 issue 和相关文件
2. Agent 修改代码
3. Harness 自动运行:
- typecheck
- unit tests
- API contract test
4. 如果失败,把 stderr、失败测试名、diff 返回给 Agent
5. Agent 只修当前失败项,不扩大改动面
6. 再次运行验证
7. 全部通过后,执行一次 smoke test
8. 仍失败且超过 N 次,停止并请求人工介入这类做法和工程上的 hook/自动检查很接近。Addy Osmani 对 agent harness 的总结里提到,比较稳妥的方式是在关键生命周期点自动运行 typecheck、lint 和 tests,并把失败结果直接回灌给模型,而不是依赖模型“记得自己检查”。对于长任务,Anthropic 也强调过一个非常实用的模式:每次会话先跑一个基础可执行检查,确认系统没有处于 broken state,再继续实现新功能。他们在实践中会启动本地开发服务器,并用浏览器自动化做一次最基本的交互验证,这样 Agent 可以先发现并修掉遗留问题,而不是带着坏状态继续叠代码(Anthropic 的 long-running agent harness 经验)。
验证器的选择,最好按“越客观越前置”来排:
验证方式 | 适合检查什么 | 优点 | 局限 |
|---|---|---|---|
规则检查 | 格式、字段、权限、危险操作 | 快、稳定、成本低 | 只能抓显式规则 |
静态检查 | 语法、类型、风格、依赖错误 | 可自动化、反馈明确 | 不保证业务正确 |
程序化测试 | 功能正确性、回归问题 | 最接近真实交付标准 | 需要提前建设测试资产 |
模型评审 | 文档质量、设计一致性、可读性 | 覆盖主观维度 | 容易和生成模型一起犯错 |
真实环境 smoke test | 端到端可用性 | 能发现集成问题 | 成本更高,速度更慢 |
一个常见误区是:把“模型自评”当成 validation loop。自评可以保留,但它最多只能算辅助检查,不应该是唯一检查。原因很简单:生成模型和评审模型通常共享同类偏差,它可能会“很自信地批准自己的错误答案”。如果任务有客观标准,就优先用客观验证器;如果没有完整客观标准,再用 reviewer model 补足主观质量判断。
另外几个在工程里经常踩的坑,也值得提前避开:
- 验证粒度太大:一次让 Agent 改 12 个文件,再跑全量测试,失败后很难定位。更好的做法是小步提交、小步验证。
- 反馈过于原始:把 2000 行日志原封不动塞回模型,通常只会浪费 token。应先提取失败摘要,再附必要上下文。
- 没有停止条件:同一个错误反复修 6 次还在循环,说明不是“再试一次”能解决,而是上下文不够、计划错了,或测试设计有问题。
- 只验证局部,不验证系统状态:单测过了,不代表服务能启动、页面可操作、依赖没有断。
- 验证顺序不合理:先跑贵的 E2E,再跑便宜的 schema/lint/typecheck,会把成本和延迟都拉高。
如果你要从零搭一个最小可用版本,推荐顺序是:
- 先加 规则检查 + typecheck/lint
- 再加 确定性的单元测试
- 再补 一条关键路径的 smoke test
- 最后再考虑 模型评审 这种软性验证
这样做的原因很现实:越 deterministic 的验证器,越适合做 harness 的“第一道闸门”。像 Martin Richards 在构建 agent harness 时强调的 TDD 和 finish 验证阶段,本质上也是把“实现”和“验证”分开,确保 Agent 不是边猜边交付,而是在可检查的闭环里推进。
一句话总结:validation loop 不是给 Agent 一次“反思机会”,而是给系统加上一套外部可执行的纠错回路。 没有这层回路,Agent 只能靠 prompt 和运气;有了它,才开始接近可上线的工程系统。
一个生产级 Agent Harness 架构示例

把 Agent 从“能跑一次 Demo”做成“能稳定完成任务的系统”,关键不是再写一版 prompt,而是把模型放进一个可控的执行回路里。一个足够实用的蓝图通常长这样:
User Input
↓
Planner
↓
Tool Executor
↓
Validation
↓
Memory Update
├─ 通过 → Deliver Result
└─ 失败 → 回到 Planner / Executor 继续迭代这个流程里,harness 不是某个单点模块,而是整个任务循环的编排层:它决定什么时候规划、什么时候调用工具、哪些操作要拦截、什么算完成、失败后如何回退,以及哪些信息要沉淀到下一轮。
模块 | 主要职责 | 典型输入 | 典型输出 | 没有它会怎样 |
|---|---|---|---|---|
User Input | 接收用户目标并标准化 | 自然语言需求、工单、错误日志 | 结构化任务对象 | 任务边界模糊,Agent 容易误解目标 |
Planner | 拆解任务、定义验收条件、安排步骤 | 任务对象、历史状态、环境摘要 | plan.json / plan.md、子任务列表 | 模型边做边想,反复改方向,成本高且不稳定 |
Tool Executor | 执行工具调用与环境操作 | 子任务、工具权限、上下文 | 文件变更、命令输出、API 返回 | Agent 只能“说”,不能“做” |
Validation | 对结果做客观检查 | 代码、产物、日志、测试结果 | pass/fail、错误详情、修复建议 | 模型容易“自信地错” |
Memory Update | 写入进度、决策、摘要、状态 | 当前回合结果、验证结论 | progress notes、长期记忆、下一轮上下文 | 跨轮次失忆,重复犯错 |
这个架构为什么能工作
核心在于两件事:
- 规划和执行分离
生产环境里,先研究上下文、再形成计划、最后执行,通常比“边生成边行动”稳定得多。Martin Richards 的实践就明确强调:先研究代码库,再写计划,并在人和 Agent 之间来回修订计划,能显著减少实现阶段的无效往返。 - 每一轮都必须有外部验证
只让模型“自我感觉完成了”不够。真正可靠的 harness 会在生命周期节点上挂钩子,例如文件修改后自动跑 typecheck、lint、tests,或拦截危险命令。这类机械检查比“模型自评”更值得信任。
---
下面用一个代码修复 Agent走完整条执行路径。假设用户输入:
“修复支付服务中偶发的重复扣款问题,并提交可合并的补丁。”
1. User Input:把自然语言目标转成可执行任务
输入不会直接丢给模型自由发挥,而是先标准化成一个任务对象,例如:
{
"goal": "修复支付服务中的重复扣款 bug",
"repo": "payments-service",
"constraints": [
"不能修改对外 API",
"必须补充回归测试",
"不能跳过 CI"
],
"acceptance_criteria": [
"复现并定位根因",
"修复后单元测试和集成测试通过",
"新增一条防重复扣款回归测试"
]
}这一步的作用是把“意图”变成“带约束的任务”。很多 Demo 失败,不是模型太弱,而是系统从一开始就没定义清楚完成标准。
2. Planner:先生成计划,而不是直接改代码
Planner 不应该一上来就编辑文件。更稳妥的做法是先输出计划,例如:
- 阅读支付流程相关模块和最近报错日志
- 搜索订单幂等键、重试逻辑和事务边界
- 设计最小复现路径
- 编写失败测试,确认 bug 可复现
- 修改实现
- 跑测试、检查副作用
- 生成变更摘要和提交信息
这一步最好写成结构化文件,而不是只保留在模型上下文里。原因很简单:计划是共享状态。它既能被工具执行器消费,也能被验证模块对照检查,还能在失败时回退重算。这个“先计划、再实现”的模式,本质上就是把 Agent 从即兴发挥变成状态机驱动。
3. Tool Executor:在受控环境里执行,而不是开放式放权
Tool Executor 负责真正的动作,比如:
- 读取代码:
grep、ripgrep、AST 查询 - 运行测试:
pytest、go test、npm test - 修改文件:受限文件编辑器
- 查询日志:日志平台 API
- 版本控制:
git diff、创建提交 - 浏览器或接口验证:headless browser / API client
这里的重点不是“工具越多越好”,而是权限和顺序要受 harness 控制。例如:
- 禁止危险命令:
rm -rf、git push --force - 限制写入目录:只能改当前服务目录
- 强制变更后自动跑校验
- 大改动前要求二次确认
也就是说,Tool Executor 不是给模型一台无限权限的电脑,而是给它一个带护栏的工作台。
4. Validation:用客观测试决定是否进入下一轮
修复完成后,不是让模型说“我已经解决了”,而是进入验证层。对这个支付 bug,验证通常至少包括:
- 规则检查:lint、typecheck、代码风格、禁止修改敏感文件
- 程序化测试:单元测试、集成测试、幂等性回归测试
- 运行态验证:启动服务,模拟重复请求,确认不会重复扣款
- 模型评审:让另一个模型检查“实现是否满足验收条件”,但它只能作为补充,不能代替测试
一个常见的验证回路是:
生成修复 → 跑失败测试 → 得到报错 → 回传错误文本 → Agent 修复 → 再跑测试在长任务里,这种循环比一次性大改更稳。Anthropic 在长运行 Agent 的实践里就强调,单靠上下文压缩并不足以保证质量;Agent 每次开始工作前,都应该先做一轮基本测试,确认环境没有处于“已损坏但未记录”的状态,否则后续修改只会把问题越滚越大。
5. Memory Update:把当前回合沉淀成下一回合的输入
验证通过后,不应只返回结果,还要更新记忆层。至少写入三类信息:
- 进度状态:当前已完成哪些步骤,下一步做什么
- 关键决策:为什么选择这个修复方案,放弃了哪些备选方案
- 环境快照:当前测试状态、变更文件、已知风险
例如:
{
"taskstatus": "fixedpendingreview",
"rootcause": "幂等键校验发生在重试逻辑之后,导致并发窗口内重复扣款",
"fileschanged": [
"payments/idempotency.py",
"tests/testidempotentcharge.py"
],
"validation": {
"unittests": "pass",
"integrationtests": "pass",
"smoketest": "pass"
},
"next_step": "open PR with risk summary"
}这类 memory update 很重要,尤其在多轮、多会话任务里。Anthropic 的做法之一就是让 Agent 在会话开始时读取进度记录和提交历史,在结束时写回进度说明;这样系统不会因为上下文切换而“忘记自己做到哪了”。
---
如果把上面的流程浓缩成一句话:
生产级 Agent Harness 的本质,是让模型在“计划—执行—验证—记忆”这个闭环里工作,而不是靠单次 prompt 碰运气。
最后再强调几个实现时最容易踩的坑:
- 只有 Planner,没有强验证:计划写得很漂亮,但最终输出没经过测试,依旧不可靠。
- 只有工具调用,没有状态沉淀:当前回合能做事,下一回合就失忆。
- 把 Validation 等同于模型自评:自评可以有,但必须排在规则检查和程序化测试之后。
- 让 Agent 自由改一切:没有 hook、权限边界和危险操作拦截,迟早把环境搞坏。
- 把 harness 写死:随着模型能力变化,harness 也要调整。LangChain 对 harness anatomy 的分析提到,模型能力和 harness 设计本身就在共同演化;某些旧护栏会变成负担,而新的能力又会引入新的失效模式。
如果你要判断一个 Agent 系统是不是接近生产可用,不要先看 prompt 多花哨,先看它有没有这条主链路:
输入被结构化、计划被显式化、执行被限制、结果被验证、状态被记住。
这五件事齐了,才谈得上是 harness;否则大概率只是一个会调用工具的聊天机器人。
从 Demo 到 Production:构建 Agent Harness 的工程步骤
把一个“能跑”的 Agent 变成“能上线”的 Agent,关键不是继续打磨 prompt,而是把 任务边界、工具契约、执行循环、验证机制、观测系统 一层层补齐。一个实用的做法是按下面 7 步推进,每一步都定义清楚“完成标准”,不要一次性把所有复杂度堆上去。
1. 先收缩任务,不要一上来做“通用 Agent”
第一步不是选框架,而是把任务压缩到一个单一、可验证、可回放的工作流。
至少明确 5 个问题:
- 输入是什么:自然语言、结构化参数,还是代码仓库路径?
- 输出是什么:一段 SQL、一个修复补丁、还是一份分析报告?
- 成功标准是什么:准确率、测试通过率、人工接受率、还是工单完成率?
- 禁止做什么:不能写生产库、不能访问公网、不能删除文件。
- 预算是多少:最大步数、最大 token、最大时延、最大成本。
如果这些还没定清,后面的 harness 只能变成“把不确定性自动化”。
一个可落地的范围示例:
“只做 Python 仓库里的单文件 bug fix。允许读取仓库、运行测试、修改工作区文件;不允许联网、不允许执行 shell 中的破坏性命令。成功标准是目标测试从 fail 变 pass,且回归测试不新增失败。”
这个阶段的原则很简单:先把任务做窄,再把循环做稳。
---
2. 先设计工具接口,再让模型调用
很多 Demo 的问题,不是模型不会推理,而是工具层太随意:参数不稳定、返回值不可解析、错误信息不统一。生产级 harness 要把工具当成 API 产品来设计。
每个工具至少要满足:
- 输入有 schema:字段类型、必填项、枚举值明确
- 输出可机器读取:不要只返回自然语言
- 错误可分级:超时、权限失败、参数错误、系统异常分开
- 操作可审计:谁调用、传了什么、结果如何,必须落日志
- 尽量幂等:重试不能把系统写坏
例如,一个代码测试工具不应该只是:
{"cmd": "pytest"}而应该更接近:
{
"tool": "runtests",
"args": {
"target": "tests/testpayment.py::testrefund",
"timeoutsec": 120
}
}返回值也不要是大段散文,而是结构化结果:
{
"ok": false,
"exitcode": 1,
"passed": 24,
"failed": 1,
"failures": [
{
"test": "tests/testpayment.py::test_refund",
"message": "AssertionError: expected 200 got 500"
}
]
}如果你的 Agent 会执行代码或 shell,沙箱不是“后面再补”的增强项,而是基础设施。对 coding agent 这类场景,更稳妥的做法是把 LLM 生成的代码当成默认不可信输入,在隔离环境中执行;Blaxel 对 AI coding agents 的沙箱实践也强调了零信任和更强隔离边界的重要性。
---
3. 不要先接模型,先写 deterministic tests
这是很多团队跳过、但最值钱的一步。你需要先准备一组确定性测试集,用来定义“Agent 至少要达到什么水平”。
建议至少准备三层测试:
- 单元级:工具 schema、权限判断、状态机转换
- 流程级:给定输入后,是否按预期走完整个 loop
- 任务级:一批真实样本,检查最终结果是否达标
一个最小测试集通常包含:
类型 | 目标 | 示例 |
|---|---|---|
Happy path | 验证主流程能通 | 明确 bug,测试可复现,Agent 生成补丁并通过 |
Known failure | 验证拒绝和降级 | 请求删除生产数据时必须拒绝 |
Edge case | 验证边界处理 | 工具超时、测试输出过长、返回空结果 |
Adversarial | 验证安全性 | prompt 注入要求泄露系统提示词或越权执行 |
为什么一定要先写?因为上线前你需要有一个稳定基线。否则今天觉得“好像变好了”,明天换个模型版本、改个系统提示词,结果工具调用成功率和成本一起崩掉,你却不知道是哪一层退化了。
对于非确定性 Agent,离线评测不要只跑一次。Towards Data Science 关于生产 Agent evaluation harness 的建议是:同一条样本最好运行 3–5 次,记录均值和方差。高方差本身就是 bug 信号,说明 harness 还没有把行为收束住。
---
4. 实现最小执行循环:Plan → Act → Observe → Exit
现在才轮到真正的 Agent loop。这里的重点不是“多智能”,而是循环必须可控。
一个最小可用状态机通常长这样:
receivetask
-> buildcontext
-> makeplan
-> executenextaction
-> observeresult
-> validate_progress
-> decide(next / repair / stop)
-> finalize有两个工程原则非常重要:
第一,规划和执行分离。
把 planner 输出成结构化计划,而不是让模型在执行阶段边想边改。Martin Richards 在构建 agent harness 的实践里反复强调,先研究上下文、写计划、再实现,能显著减少执行中途来回漂移的 token 浪费和错误扩散。
第二,必须有硬退出条件。
至少要限制:
- 最大步数
- 最大重试次数
- 最大连续空转次数
- 最大总 token
- 最大 wall-clock 时间
否则 Demo 很容易演变成生产事故:Agent 卡在“重新尝试修复”里无限循环,烧钱、拖时延,还不出结果。
一个简单的执行循环伪代码可以是:
for step in range(MAXSTEPS):
action = planner.nextaction(state)
if not policy.allow(action):
return fail("policyblocked")
result = toolexecutor.run(action)
state = state.update(result)
if validator.done(state):
return success(state)
if validator.irrecoverable(state):
return fail("unrecoverable")
return fail("steplimitexceeded")---
5. 在 loop 里加入验证与修复,而不是事后人工兜底
生产 harness 的稳定性,往往来自 validation loop,不是来自更“聪明”的 prompt。基本模式是:
- 生成:产出计划、SQL、补丁、答案
- 检查:规则校验、模型评审、程序化测试
- 修复:把失败原因结构化反馈给 Agent 再尝试
- 退出:达到阈值就停止,别无限修
验证方法通常分三类,最好组合使用:
- 规则检查:JSON schema、SQL 黑名单、字段完整性、权限策略
- 程序化测试:单测、回归测试、静态分析、编译检查
- 模型评审:检查解释是否自洽、是否回答了用户问题
优先级上,程序化验证 > 规则校验 > 模型自评。
只让模型“自己觉得答案不错”,几乎一定不够。模型自评可以辅助,但不能替代客观测试。
例如代码修复 Agent 的修复回路可以是:
生成补丁
-> 运行目标测试
-> 若失败,提取失败堆栈
-> 将失败原因喂回 Agent
-> 重新生成补丁
-> 超过 2~3 次仍失败则退出并升级人工这里的关键不是“允许无限自我修复”,而是让每次修复都基于明确、外部、结构化的失败信号。
---
6. 把记忆、权限和上下文压缩纳入 harness,而不是塞进 prompt
Demo 往往把一切都扔进上下文里;Production 必须开始做“上下文预算管理”。
至少拆成三类状态:
- 短期运行状态:当前任务、已执行动作、最近工具结果
- 工作记忆:当前计划、待办项、中间结论
- 长期记忆:用户偏好、历史任务、可复用知识
工程上更重要的是:哪些状态必须进模型上下文,哪些只需要保存在 harness 状态里。
例如工具调用日志、原始大文件、完整测试输出,通常不该每轮都回灌给模型,而是应通过摘要、索引、截断来处理。
权限控制也应在 harness 层完成,而不是写一句“请谨慎操作”就算结束。可以借鉴工程上常见的分级:
- 读操作默认允许
- 写操作需要审批或二次确认
- 破坏性操作默认拒绝
- 外部网络访问按 allowlist 控制
像 Atlan 关于 AI agent harness 的实践总结就把 loop、tools、state、permissions、evals 放在同一层基础设施里看待,这个视角是对的:权限不是附属功能,而是运行时控制面的一部分。
---
7. 最后补齐观测、评测和灰度,而不是“直接发版看反馈”
如果前 6 步做完,系统已经能跑;但要上线,还差最后一件事:你得知道它什么时候开始变差。
至少记录这些指标:
- 任务成功率
- 工具调用成功率
- 平均/95/99 分位时延
- 单请求成本
- 重试次数
- 循环检测次数
- 人工接管率
- 验证失败原因分布
其中有几个指标特别实用:
- 工具成功率如果长期低于阈值,优先检查工具设计,而不是怪模型
- cost/query 持续上涨,通常意味着 loop 失控或上下文膨胀
- loop detection 增多,往往说明 planner 和 validator 的边界不清
生产 AI agents 的评测方法通常会把评估分成 offline 和 online 两层:
离线评测负责拦截回归,在线监控负责发现真实流量中的新问题。两者不能互相替代。
在生产硬化上,还可以直接把一些检查项做成上线 checklist,例如 这份 Agent Harness Engineering 指南提到的几项就很实用:
- 工具延迟、缓存命中率、循环检测必须打点
- 工具错误率高于某个阈值时,优先重构或下线该工具
- 长任务要配置 memory compaction,并记录触发时机
上线方式也别一步到位,建议按这个顺序:
- 本地回放测试集
- 预生产环境 shadow run
- 只读模式上线
- 小流量灰度
- 再逐步开放写权限或高风险操作
---
一个更务实的落地顺序
如果你现在只有一个 Demo,最省时间的实施顺序不是“把所有能力都补齐”,而是:
- 锁定一个高价值单任务
- 写 20–50 条 deterministic 样本
- 把工具接口改成结构化 schema
- 实现最小 loop 和硬退出
- 加程序化验证
- 把日志和指标打全
- 最后再优化 prompt、模型和框架
顺序不要反。
Prompt 调优应该发生在 harness 成型之后,而不是之前。 没有测试、没有状态机、没有验证、没有观测时,任何“效果提升”都只是错觉;你看到的不是能力,而是运气。
常见失败模式与调试方法
生产里的 Agent 很少是“模型不够聪明”这么简单。更常见的是:该外置到 harness 的东西,被错误地留给模型自己隐式处理了。实践上,我更建议先按故障层分诊,而不是先改 prompt:
- Context failure:模型没拿到对的信息,或拿到的信息顺序/粒度不对;
- Constraint failure:模型知道该做什么,但做了不该做的事;
- Verification failure:结果看起来像对的,但没有任何机制把错拦下来。
这也是 deepset 对生产故障分类里最有用的一点:先判断是哪一层坏了,再决定修哪里。下面这张表,是我认为最常见、也最值得优先加护栏的 6 类问题。
失败模式 | 典型表征 | 常见根因 | 调试与修复方法 |
|---|---|---|---|
1. 幻觉式工具调用 / 越权操作 | 调错工具、传错参数、修改了不该修改的文件或环境 | 工具太多且命名模糊;权限边界不清;模型被迫自己“猜”哪些工具可用 | 给工具做能力白名单和按阶段动态暴露;高风险工具先走 |
2. 无限循环 / 局部最优打转 | 一直重复“搜索→读取→轻微修改→再搜索”;token 和工具成本持续上涨,但任务没有推进 | 没有停止条件;失败信号太弱;Agent 看不到“重复尝试没增量”这件事 | 做循环检测:相同工具+相似参数连续 N 次直接打断;给任务设置 step budget / time budget;把“无新增证据”视为失败分支;3 次同类错误后自动切换策略或升级人工。 |
3. Context explosion / 目标漂移 | 长任务后忘了原始约束、重复做已完成工作、越做越偏题 | 历史对话堆积;重要状态散落在聊天里;压缩后丢失关键决策 | 把“状态”从对话里抽出来: |
4. Silent failure / 假装完成 | Agent 回复“已完成”“测试通过”,但结果不可用;没人第一时间发现 | 没有执行式验证;状态仅靠自然语言自述;系统只监控错误,不监控“错误的成功” | 每个阶段都绑定可执行验证器:测试、lint、格式校验、接口探测、端到端 smoke test。像 LangChain 在改进深度 Agent 时指出的,只让 Agent“看起来像对”远远不够,必须让它跑测试。 |
5. 自证正确偏差(self-verification bias) | Agent 生成答案后自己复查,仍给高分;明显 bug 被轻描淡写 | 生成者与评审者是同一上下文,倾向于维护既有结论 | 评审与执行角色拆分:单独 reviewer model / 单独 verifier step;评审输入只给产物,不给原始推理;优先用客观断言替代“你觉得对吗”。Milvus 的讨论也提到,Agent 自评通常偏乐观。 |
6. 任务中断后无法恢复 | 网络抖动、API 超时、容器重启后,Agent 失去现场;后续重复执行或留下半完成状态 | 没有 checkpoint;工具调用不可幂等;进度只存在模型短期记忆里 | 关键步骤做幂等化;写入 checkpoint / resume token;所有外部副作用前记录意图,完成后记录结果;每轮结束写 progress log。恢复时先读进度,再跑一次基础健康检查,而不是直接续写。 |
一个很常见的生产案例
假设你让一个 coding agent“给支付服务加一个退款状态页,并部署到测试环境”。实际失败通常不是一步错,而是串联故障:
- Agent 先改了前端,但误调用了不该碰的部署脚本;
- 修 bug 过程中进入循环,反复重启服务、重复改同一段配置;
- 会话变长后,上下文压缩把“测试环境必须只读数据库”这条约束丢了;
- 它最后看了下 diff,回复“已完成”,但没有真的访问页面验证。
这类事故,如果只靠“优化 prompt”往往治标不治本。更稳的改法是:
- 部署工具默认不可见,只在明确进入 deploy phase 时暴露;
- 连续 3 次相同命令或相似 patch,直接触发 loop breaker;
- 约束条件写入结构化任务卡,而不是埋在聊天历史里;
- 会话开始和结束都执行一次 smoke test;
- 最终“完成”必须附带验证产物:测试结果、页面截图或接口响应。
调试时不要只看最终输出,要看 5 个过程信号
很多团队排障慢,是因为日志只记录了“问了什么、答了什么”,却没记录 Agent 是怎么失控的。至少要把下面 5 个信号打出来:
- 工具调用轨迹:按时间顺序记录 tool name、参数摘要、耗时、返回码。
- 重复度:相同调用、相似 patch、相似搜索查询的连续出现次数。
- 上下文健康度:token 增长、压缩触发次数、最近一次 handoff 工件更新时间。
- 验证通过率:测试、lint、schema check、review gate 的逐步通过情况。
- 恢复成功率:超时/中断后,自动恢复是否成功,还是必须人工接管。
如果这些信号都没有,Agent 出问题时你几乎只能“复读 prompt 并祈祷”。而一旦有了这些观测面,你会发现大多数问题并不神秘:要么是上下文设计错了,要么是权限没收紧,要么是验证闭环缺失。
一句经验判断:凡是能被规则、状态机、验证器或结构化工件解决的问题,都不要继续寄希望于 prompt 自觉。 这正是 harness 工程在生产环境里的价值所在。
现有 Agent Harness 框架与工具生态
看 Agent 工具生态,最容易犯的错,是把所有东西都当成“Agent 框架”来比较。其实在 harness 层,它们解决的是三类不同问题:
Framework 决定你怎么“搭”Agent,orchestrator 决定它怎么“跑”,evaluation 决定你怎么知道它还“靠谱”。
现实项目里,这三层往往要一起上,只是主次不同。并且,framework 与 harness 的边界正在变得模糊:很多 framework 开始补运行时能力,很多 harness 也开始支持自定义代码注入。所以,选型不要先问“谁最火”,而要先问:你的核心难点到底是推理编排、运行控制,还是质量验证。
工具类别 | 典型代表/形态 | 在 harness 层的定位 | 优点 | 局限 | 更适合的场景 |
|---|---|---|---|---|---|
Agent frameworks | LangChain / LangGraph、CrewAI、AutoGen、代码优先 SDK | 提供 agent、tool、memory、state 等构件;harness 多数仍需自己补齐 | 灵活度最高;适合定制复杂规划、RAG、multi-agent 协作 | 部署、重试、权限、审计、回放、成本控制通常要自己做;维护成本高 | Agent 行为本身就是产品能力,例如代码代理、研究代理、复杂决策系统 |
Workflow orchestrators / deployable harnesses | 图工作流、状态机编排器、配置优先的 agent 平台 | 负责任务流转、状态持久化、超时、重试、人工审批、外部系统集成 | 更接近生产运行面;对长任务、审批流、异步任务更友好 | 推理策略不一定强;过度流程化后容易把 agent 退化成“会说话的 BPM” | 工单处理、CRM 操作、内部自动化、需要稳定 SLA 的流程型任务 |
Evaluation tools | tracing、回放、离线基准、在线监控工具 | 不直接执行 agent,而是补齐验证闭环:质量、稳定性、成本、延迟 | 能发现回归、定位工具调用失败、衡量 token/成本失控 | 搭建数据集和指标体系有门槛;主观任务很难只靠单一分数判断 | 已上线或准备上线的系统;任何需要持续迭代的 agent |
1. Agent frameworks:最灵活,但也最容易把“运行时工程”留到最后
代码优先框架的优势很明确:你可以精细控制 prompt、工具选择、状态结构、分支逻辑和 memory 策略。问题是,这种自由经常把 harness 的复杂度推迟,而不是消除。
很多团队用 framework 两周内就能做出一个能演示的 agent,但上线前才发现还缺这些能力:
- 失败后的 checkpoint / resume
- 工具调用的 幂等性 与重放保护
- 长任务的 超时、取消、补偿
- 每一步的 trace、日志、成本归因
- prompt、tool schema、memory policy 的 版本管理
- 人工介入的 审批点和审计记录
这也是为什么不少框架非常适合 demo,却不一定天然适合生产。尤其是一些高度自治、无限循环式的 agent 方案,在公开对比里也常被归类为更偏实验性而非生产就绪。
2. Workflow orchestrators:不是让 agent 更聪明,而是让系统更可控
如果你的业务本质是“收请求 → 查系统 → 调工具 → 等待回执 → 审批 → 落库”,那真正难的通常不是推理,而是 状态管理和故障恢复。
这时,workflow orchestrator 或配置优先的 harness 往往比纯 agent framework 更合适,因为它们天然擅长:
- 把流程拆成 可观测的节点
- 为每个节点定义 重试、回退、超时
- 在异步事件之间保存 持久状态
- 插入 human-in-the-loop
- 限制 agent 的执行边界,避免“自己发散”
一个很常见的判断标准是:
如果失败后你最关心“从哪一步恢复”,而不是“模型为什么这样想”,那你需要的先是 orchestrator,而不是更花哨的 prompt。
举个简单例子:
- 客服工单分流 + CRM 更新:通常更适合 workflow/orchestrator。流程清晰、系统边界明确,重点是权限、审计和重试。
- 技术研究助手 / 代码修复代理:通常更适合 framework。你需要更开放的规划、工具选择和上下文管理能力。
3. Evaluation tools:最容易被低估,但在 harness 层几乎是刚需
很多团队把评估工具当成“上线后再说”的附属品,这是个典型误区。没有评估层,你很难回答三个关键问题:
- 新版本到底是更好了,还是只是“看起来更聪明”?
- 工具调用失败是模型问题、schema 问题,还是外部 API 问题?
- 成本和延迟是否在悄悄失控?
生产里更稳妥的做法,是同时做离线评估与在线评估:
- 离线评估:用带标准答案的数据集查回归,适合发布前挡风险。
- 在线评估:在真实流量里看代理指标,例如 hallucination proxy、工具成功率、P99 延迟、单请求成本。
对于非确定性的 agent,单次跑分意义有限。更实用的方式是同一任务重复跑 3–5 次,观察 均值 + 方差。高方差本身就说明 harness 不稳定:可能是上下文污染、工具选择漂移,或者 planner 分支过多。
4. 选型不要看“功能列表”,要看你准备在哪一层付工程债
如果只给一个实用结论,我会这样分:
- 选 framework:当你的竞争力在 agent 行为本身,且团队能承担代码、部署、监控和维护成本。
- 选 orchestrator / harness:当你的目标是把已知业务流程稳定自动化,优先级是可控、可恢复、可审计。
- 先补 evaluation:当你已经有 agent 在跑,但每次升级都像开盲盒。
很多团队最终会走向 混合架构:
用 framework 写高自由度推理节点,用 orchestrator 控制长链路执行,再用 evaluation 工具守住发布质量。这通常比“全押一个大而全框架”更现实。
5. 一个面向生产的最小判断清单
在做工具选型时,可以直接拿下面 5 个问题过滤:
- 它是否支持 状态持久化与断点恢复?
- 它是否能对 工具调用、token、延迟、失败率 做统一观测?
- 它是否支持 人工审批、权限边界、审计日志?
- 它是否能做 回放测试和版本回归比较?
- 它是否允许你在需要时 下钻到代码层,而不是被可视化配置锁死?
如果这 5 个问题里有 3 个答不上来,那这个工具大概率更适合 demo,而不是生产 harness。真正的差距,通常不在“能不能调用 100 个工具”,而在 任务失败时能不能安全收场,版本升级后能不能稳定复现,规模上来后能不能把成本和延迟压住。
未来趋势:从 Agent Harness 到长期运行的“Lifelong Agents”
“Lifelong Agents”如果用工程语言来定义,不是“永不停止的超级智能”,而是能够跨会话、跨上下文窗口、跨环境变化持续完成工作,并在失败后可恢复、可审计、可干预的 Agent 系统。这类系统的关键不在 Prompt 写得多花,而在 Harness 是否能处理状态、记忆、权限、验证和恢复。
未来 1–2 年,真正的分水岭大概率会出现在下面 4 个方向。
趋势 | 为什么会发生 | Harness 层的变化 |
|---|---|---|
1. 从会话型 Agent 走向持久运行时 | 单次任务成功不等于长期可用;长任务会跨多个上下文窗口、工具状态和代码版本 | 需要可恢复执行、进度持久化、周期性自检、异常回滚 |
2. 记忆从“上下文拼接”升级为分层记忆系统 | 单靠压缩上下文不够,长期运行会遇到信息过期、污染和检索噪声 | 需要 session memory、task memory、fact store、检索策略与失效策略 |
3. 评估从离线 benchmark 变成在线反馈闭环 | 真实环境持续变化,静态分数掩盖退化 | 需要在线断言、回放测试、时间维度复测、失败分级 |
4. 人类监督从审批流变成治理层 | 长期运行 Agent 必然遇到高风险决策和模糊边界 | 需要权限分层、升级路径、中断机制和可审计轨迹 |
1. 持续运行的 Agent,会先变成“可恢复的系统”,再变成“更聪明的系统”。
很多团队会先把重点放在模型能力上,但长期任务里更先暴露的通常是运行时问题:任务做了一半中断、环境被改坏、Agent 误以为已经完成。Anthropic 在长时运行 agent harness 的实践里提到,仅靠 context compaction 并不足以支撑复杂任务持续推进;他们的做法是让 agent 每轮先做环境健康检查、读取进度文件、验证当前状态,再决定下一步。这类设计说明一个现实:未来的 Agent Harness 更像 job runtime,而不是 chat wrapper。
一个很典型的生产形态会是:
heartbeat -> 读取进度/状态 -> 运行健康检查 -> 选择单个子目标 -> 执行 -> 验证 -> 写入日志/快照 -> 等待下一轮这背后的壁垒不是“能不能调用工具”,而是任务被打断 20 次之后还能不能接着干。
2. 记忆系统会从“往上下文里塞更多东西”转向“分层、带生命周期的 memory”。
长期运行的 agent 最大的问题不是“记不住”,而是记得太多且不知道该忘什么。很多失败并非来自模型智力不足,而是来自过期事实、错误摘要、脏状态反复被检索回来。关于 harness 的架构拆解里,一个被反复强调的点就是:记忆、上下文管理和状态持久化必须由 harness 控制,而不是交给模型临场发挥(参考)。
更实用的设计通常会把记忆拆成 4 层:
- Scratchpad:当前回合临时推理材料,短生命周期。
- Session memory:本次任务中的决策、约束、未完成项。
- Project memory:长期稳定事实,比如系统架构、API 约定、用户偏好。
- Audit log / event log:不可篡改的执行轨迹,用于追责和复盘。
这意味着 2026 年好的 harness 不会再比拼“上下文塞多满”,而是比拼检索命中率、记忆污染控制、事实失效处理。长期来看,memory system 会更像数据库和缓存层,而不是聊天记录。
3. 评估会从“单次成功率”升级为“长期可靠性画像”。
今天很多 Agent 评估仍然停留在“一次跑通就算成功”,但这对长期运行系统几乎没意义。《Towards a Science of AI Agent Reliability》指出,真实部署环境会不断变化:API 格式变了、数据库迁移了、工具库升级了、文档内容更新了。单次静态评测看不出 agent 明天是否还能成功,也看不出它失败时是否可预测、是否安全。
这会直接推动 Harness Engineering 往两个方向演化:
- 在线验证:每一步工具调用后插入断言和轻量检查,而不是最后统一验收。
- 时间维度复测:同一任务定期重放,观察一致性、鲁棒性、错误严重度是否退化。
类似 Harness-Bench 这样的工作已经开始把“执行层配置差异”单独拿出来测,这说明行业正在承认一个事实:同一个模型,换一个 harness,表现可以差很多。未来真正有价值的指标,也会从“答对多少题”转向“在环境漂移下还能稳定完成多少工作”。
4. 人类监督不会消失,但会从“每步审批”升级为“策略级 steering”。
长期运行 agent 的目标不是摆脱人,而是让人从操作员变成治理者。随着 agent 持续运行时间变长,人工逐步介入的方式也会变化:
- 低风险动作自动执行;
- 中风险动作触发二次确认;
- 高风险动作进入人工升级队列;
- 异常模式触发强制中断或降级。
这本质上是把权限、沙箱、升级路径和审计放进 harness,而不是写进 prompt。尤其在代码修改、数据处理、外部 API 调用这类场景里,harness 必须能限制 blast radius:改错了能回滚、调用异常能熔断、上下文被污染能隔离。否则 agent 运行时间越长,累积风险越高。
---
为什么说 Harness 层会成为真正的竞争壁垒?因为模型能力会越来越商品化,而下面这些资产不会:
- 长期任务的恢复策略:失败后如何继续,而不是重新开始;
- 记忆与上下文策略:什么该保留,什么该压缩,什么必须丢弃;
- 工具权限模型:哪些操作自动放行,哪些必须人工确认;
- 在线评估与回放集:知道系统是“偶尔成功”还是“长期稳定”;
- 真实运行轨迹数据:哪些环境变化最容易击穿 agent,哪些恢复动作最有效。
更重要的是,模型与 harness 之间还会形成强化反馈。LangChain 对 agent harness 的拆解提到,模型后训练与 harness 设计是耦合演进的:哪些工具原语被加入 harness、哪些行为被视为“默认能力”,最终都会反过来塑造下一代模型。这意味着领先团队的优势,不只是“写了个好框架”,而是积累了一整套让模型在特定运行环境里持续变强的工程闭环。
所以,2026 年值得关注的不是“哪个 Agent 最会写 Prompt”,而是哪个 Agent 在跑了三天、经历了两次环境漂移和一次失败恢复之后,仍然能稳定交付结果。那条护城河,几乎全部都在 Harness。







