什么是 Agent Harness:AI 应用开发里的工程化运行时外壳
什么是 Agent Harness:AI 应用开发里的工程化运行时外壳
这里说的
Harness,不是测试框架,也不是某个产品名,而是把 agent 的决策、工具、状态、权限和观测接成一个能上线的运行层。为了便于理解,本文先把它画成“围着 loop 的运行层”;但在 Codex 这类系统里,harness 往往会把 loop、运行逻辑、协议、状态和执行环境一起包进去,所以别把它理解成单纯的外壳。
先说结论:Harness 是不是旧东西换了个新名字
某种程度上,是。
如果你看完很多关于 harness 的文章,第一反应是“这不就是以前 agent 里的上下文、工具、权限、状态、日志、沙箱、审批这些东西吗”,这个判断并不离谱。Harness 不是一个新的模型算法,也不是一种新的 agent 推理方式。它没有发明 context,也没有发明 tool calling,更没有发明权限系统。
它真正表达的是另一个问题: 当 agent 从 demo 进入真实业务系统后,这些原本零散存在的工程能力,必须被组织成一层统一的运行边界。
以前我们可能会分别说:
- prompt 要怎么写
- context 要怎么拼
- tool 要怎么注册
- memory 要怎么存
- 权限要怎么判断
- 日志要怎么记录
- 失败要怎么 retry
这些都对,但它们是散的。Agent Harness 这个词的价值,是把这些散点放到同一个视角下: 一个会调用工具、会产生副作用、会持续执行的 agent,如何被安全、稳定、可观测、可恢复地跑起来。
所以不要把 harness 理解成“一个神秘新概念”。更准确的理解是:
Harness 不是新 Agent,而是对 Agent 工程化运行层的命名。它把上下文、工具、权限、状态、观测、恢复这些老问题,统一放到“Agent 怎么安全稳定跑起来”这个问题下重新组织。
这也是为什么最近很多人开始频繁提 harness。不是因为以前没有这些能力,而是因为 agent 真正开始接业务系统、接代码仓库、接外部工具以后,大家发现只讲 prompt、function calling、agent loop 已经不够了。真正难的不是让 agent 走一步,而是让它每走一步都能被控制、被记录、被暂停、被恢复。
先从一个很具体的事故开始。
用户说: “昨天买的会员退掉。”
模型看到了两条订单,选错了其中一条,直接调用退款接口。事后回看,问题不在“模型不会推理”,而在系统没有把这次动作接住: 没有先问清订单,没有审批,没有幂等键,也没有完整审计轨迹。
这篇文章想回答的,就是这件事背后的系统问题。
如果换成后端工程师更熟悉的说法,这其实是在问: 一段会调用外部副作用的智能流程,能不能像一个靠谱的业务服务一样被控制、被审计、被恢复。模型可以给方向,但真正决定系统能不能上线的,是这层运行时有没有把动作接稳。
读完这篇文章,你最好能带走三个判断:
- 看到一个 Agent 设计时,能判断它缺的是模型能力,还是缺运行边界
- 遇到 bad case 时,能知道该查上下文、工具权限、状态、trace 还是恢复逻辑
- 真要自己做时,能从一个最小 harness 开始,而不是一上来造平台
1. 为什么这个词容易让人困惑
你可能在三种地方见过 harness。
第一种是测试工程里的 test harness,它负责驱动测试、准备数据、收集结果。
第二种是评测里的 eval harness,它负责批量跑样本、比对模型或 prompt 版本。
第三种是本文要讲的 agent harness,它负责把模型的决策接进真实业务流程。
这里容易混淆的一点是,OpenAI 在 Codex 语境里把 harness 说得更宽,它不仅是外壳,还包括支撑整个 Codex 体验的 agent loop and logic。为了便于理解,本文会先把它简化成“围在 loop 外面的运行层”,但你要记住,在生产系统里,这条边界没有那么硬。
一句话先定住:
Agent Loop负责决定下一步,Agent Harness负责让这一步真的、安全地发生。
更直白一点,loop 是“脑子在想”,harness 是“手脚在做,但每一步都被系统接住”。这也是为什么同样叫 harness,在不同语境里重点不一样: 测试 harness 关心可重复,评测 harness 关心可比较,agent harness 关心可执行、可恢复、可追责。
还有一种更接近产品实现的叫法,是把某个产品自己的运行层也直接称为 harness。OpenAI 在 Codex 里用的就是这个更宽的语义: 既有 agent loop,也有支撑它运行的逻辑、协议和持久化能力。所以下文为了讲清楚概念,会先用“围着 loop 的工程层”来解释,但你在别的文章里看到同一个词时,要先判断作者是在讲抽象概念,还是在讲具体产品实现。
2. Agent Loop 是核心,但不是全部
如果把 agent 想成一个会做事的系统,loop 就是它的心跳。
最小循环大概长这样:
while not done and budget_left > 0:
context = build_context()
action = model.plan(context)
if action.needs_tool:
result = tool.execute(action)
memory.append(result)
elif action.is_final:
break
elif action.requires_approval:
pause_for_human()
这段代码能说明核心思想,但它还不是一个可上线系统。真实运行里,loop 不是无限循环,它会在这些条件下结束:
- 得到最终输出
- 完成工具调用
- 被人工审批打断
- 发生错误
- 达到最大 turn 数
- 超过时间或预算
所以更准确地说,loop 是核心控制回路,harness 负责整套运行生命周期。前者决定“要做什么”,后者决定“怎么做、何时停、出错怎么办、停了怎么接着跑”。
再往前走一步,你会发现 agent 不是一个固定 pipeline,而是一串根据上下文和工具状态不断改变的事件。模型每一步都可能选不同动作,所以 harness 的价值不是把它变成死板流程,而是在每一步都放上控制点,把“可能出错的智能行为”转成“可管理的业务事件”。
3. Agent Harness 到底解决什么问题
还是回到退款事故。
如果没有 harness,模型一旦看到“退会员”就可能直接猜订单、直接走退款动作。
有了 harness,系统会先把这次动作拆开,再分别处理。
把这件事拆开之后,你会发现它其实不是一个动作,而是一串需要分别判断的问题: 这是不是退款意图,是否存在多个候选订单,这个动作会不会改变外部状态,需不需要人工确认,最后留下来的证据能不能解释刚才为什么这么做。harness 的意义,就是把这些判断分到不同的控制点上。
并不是所有 AI 应用都需要完整 harness。如果只是一次性问答、没有工具、没有副作用、没有长任务,普通的 RAG 或 chat backend 就够了。真正需要认真设计 harness 的场景,通常会出现这些信号:
- Agent 会调用会改变外部状态的工具,比如退款、发邮件、下单、改配置、写文件
- 任务不是一次回答就结束,而是会持续多轮执行
- 失败后不能简单重跑,因为可能已经产生副作用
- 业务要求能解释、能审计、能追责
- 用户、管理员或系统需要在中途审批、暂停、恢复
- 多个客户端都要看到同一条任务进度,比如 Web、CLI、IDE 同时接入
换句话说,harness 不是给所有 demo 准备的,而是给“Agent 已经开始做真实动作”的系统准备的。
3.1 安全
安全不是“加个 sandbox 就完了”。真正的前沿做法通常是分层的:
- 默认只读
- 写操作需要显式批准
- 高风险命令走 allow / ask / deny
- 敏感路径和 parent directory 直接拒绝
- 网络出口默认受限
- 密钥不放在模型生成代码运行的环境里
Claude Code 的安全文档就是典型例子: 默认只读,编辑文件、跑测试、执行 bash 都需要明确授权;新 MCP server、网络请求、可疑命令也都有额外门禁。安全的重点不是“阻止一切”,而是把风险做成可控边界。
这里真正值得学的不是“它限制很多”,而是它默认把模型看成不可信执行者。对于 prompt injection、误触发命令、误写文件、泄露密钥这些问题,最合理的防线不是事后补救,而是让危险动作从一开始就走审批和隔离。
如果输入里混进了 prompt injection,或者模型把一段 shell 命令误当成普通文本,最好的做法不是期待它自己“想明白”,而是让执行层先把命令关住。默认只读、最小权限、敏感路径拒绝、网络出口限制,本质上是在把“即便模型出错,也不会立刻造成外部损失”作为系统假设。
3.2 状态
很多人会把“记住聊天记录”当成状态管理,其实这不够。
这里至少要分清三层:
thread,一段持久会话checkpoint,某个执行时刻的快照memory,跨任务的长期事实或检索存储
聊天历史只是 conversation memory,任务恢复靠的是 execution checkpoint。后者才决定一个被中断的任务能不能从上次成功的位置接着跑。
这也是很多人第一次做 agent 时最容易混淆的地方。对话记录只是“我说过什么”,checkpoint 才是“系统做到哪一步了”。如果任务已经查过订单、也拿到退款资格,但还没真正调用退款接口,那恢复时就不能从头再问一遍,而应该从这一步继续往下走。
实际实现里,thread_id 往往就是恢复主键。你只要知道这个主键,就能找回这条任务流上次成功的 checkpoint;反过来,如果没有这个主键,你只能把它当成一次新的会话重新跑。
3.3 上下文
模型不应该一开始看到全部东西。
这就是 progressive disclosure 的意思: 先给最小任务上下文,再按需补充工具、文档、技能、检索结果和摘要。否则模型看到的东西太多,会被噪音带偏;太少,又缺关键线索。
所以 harness 负责决定:
- 哪些文件进上下文
- 哪些规则来自
AGENTS.md/CLAUDE.md/ skills - 哪些内容通过检索补进来
- 哪些历史只保留摘要
这背后有一个很实际的原则: 模型看到的上下文越多,不代表它越稳。很多时候,过量上下文只会制造噪音。好的 harness 不是把所有材料都塞给模型,而是按任务阶段逐步揭示它真正需要的那部分信息。
比如修 bug 的前半程,模型可能只需要失败测试、相关文件和一段政策说明;直到它真的决定要改代码,才需要写权限、patch 规则和测试命令。这样比一开始把整个仓库都塞给它更稳,也更容易排错。
3.4 工具
工具不是简单的 function calling。
生产里,工具通常要经过发现、命名、校验和过滤。MCP 的价值就在这里: 它把 tools、resources、prompts、notifications 和生命周期管理标准化了,方便 AI 应用发现外部能力并建立稳定连接。
但 MCP 不是完整 harness,它只管协议和上下文交换,不决定你是否该允许模型执行这件事。权限、审批、状态、恢复和审计仍然属于 harness。
这点很重要。协议解决的是“怎么接”,不解决“该不该接”。所以你可以把 MCP 理解成工具接入层,但你不能把它误认为安全边界。真正决定模型能不能调用某个工具的,是 harness 里的 policy 和 approval 逻辑。
MCP 的架构本身也说明了这一点。它是 host / client / server 的状态化协议,host 负责连接权限和用户同意,client 负责和具体 server 交换能力。这很适合做工具发现和上下文交换,但仍然不是你的业务审批系统,更不是你自己的权限边界。
3.5 可观测
日志不是 trace,trace 也不只是打印。
真正有用的是结构化事件流,比如:
run_idturn_iditem_idtool_call_id- 模型版本
- prompt 来源
- 工具输入和输出
- 审批决定
- 延迟和 token
- 错误栈
- 脱敏和保留策略
这样你才能回答一个真实问题: “为什么它会退掉这单?” 而不是只能看见“最后输出错了”。
一个最小的事件长这样就够用了:
{
"run_id": "r_123",
"thread_id": "t_456",
"turn_id": "turn_01",
"event": "tool_call",
"tool": "lookup_order",
"status": "ok",
"latency_ms": 84
}
有了这种结构化事件,你就能继续追问: 它先查了几笔订单,为什么最后选了这笔,在哪一步触发了审批,哪一次重试真正产生了副作用。
对后端工程师来说,这一层对应的不是普通日志,而是可审计的事件流。它要能串起 run、turn、tool call、approval、error、retry 这些节点,否则你只能知道“坏了”,不知道“怎么坏的”。
3.6 恢复
恢复不是“重新跑一遍”。
真实系统里,恢复要面对两个问题:
- 能不能从最后一个成功 checkpoint 接着跑
- 已经发生的副作用要不要重放
这就是 rehydration 和 durable execution 的意义。LangGraph 的 persistence 讲得很清楚: checkpoint 让你能按 thread 保存状态,interrupts 让你能中断并恢复,pending writes 让成功的节点不用重跑,replay 则能回看历史执行。对于退款、发邮件、写数据库、改文件这种副作用动作,恢复一定要带幂等键、effect log 和补偿逻辑。
这也是为什么“重新跑一遍”不是恢复策略。真正的恢复要先判断副作用有没有已经发生。比如退款已经成功,但返回没写回去,这时你不该再退一次,而应该去查询状态、补齐记录,或者走补偿。harness 的恢复能力,核心就在于能分清“命令没执行”“命令执行了但响应丢了”“命令执行成功但下游写失败”这三种不同情况。
所以恢复策略不是“重跑”,而是“先确认副作用是否已经发生,再决定是继续、查询、补偿还是回滚”。能幂等的工具可以重试,不能幂等的工具就必须靠 effect log 和状态查询来兜住。
Harness 不是为了让 agent 更聪明,而是为了让一次不可控的模型行动,变成可检查、可阻断、可回放的流程。
4. 一个 Agent Harness 在工程上包含哪些模块
你不应该把 harness 理解成一个类或者一个函数,它更像一条控制链。
如果顺着一次请求来读,会更容易理解它到底在做什么:先由 Context Builder 和 Guardrails 决定模型该看到什么,再由 Policy Engine 决定这件事能不能做,接着模型给出下一步动作,随后 Tool Registry 和 Sandbox Executor 把这个动作变成可控执行,最后 State Store、Trace Logger 和 Verifier 把过程和结果都收住。
从任务开始到结束,大致会经过这些模块:
Context Builder,决定模型看到什么Guardrails,检查输入、输出和工具调用是否越界Policy Engine,做 allow / ask / deny 决策Model / Planner,决定下一步动作Tool Registry和MCP Adapter,发现并调用外部能力Sandbox Executor和Workspace Manifest,在受控工作区里执行文件、命令和依赖State Store和Checkpoint,保存任务状态和恢复点Event Stream / Protocol,把一次请求拆成连续事件Trace Logger和Audit,记录全过程Verifier / Finalizer,做最终检查和收尾
这里还要把 Guardrails 和 Policy Engine 分开看。前者更像格式和边界检查,负责判断输入、输出和工具参数有没有越界;后者更像业务决策,负责判断“即使没越界,这件事现在能不能做”。很多系统之所以越写越乱,就是把这两层混成了一层。
如果你今天只想做一个最小可用 harness,不需要平台化。最少只要四样东西就够了:
- 工具白名单和 schema 校验
- 侧效动作的审批规则
- 带
thread_id的 checkpoint 存储 - 结构化事件和脱敏日志
这已经足够把一个 demo 从“能跑”推进到“能审、能断、能恢复”。
一个足够小但仍然靠谱的运行状态,通常会长得像这样:
{
"run_id": "r_123",
"thread_id": "t_456",
"turn_id": "turn_02",
"phase": "waiting_for_approval",
"retry_budget": 2,
"pending_side_effects": ["refund"]
}
这不是为了炫技,而是为了让恢复、审批和审计都能落在同一份状态上。你一眼就能知道它现在卡在哪里,是在等人批,还是在执行工具,还是已经做完只差收尾。
如果把它落到一个普通后端里,最小实现甚至可以先从三张表开始:
| 表 / 集合 | 记录什么 | 解决什么问题 |
|---|---|---|
agent_run |
run_id、thread_id、用户、状态、当前阶段、retry budget |
知道任务现在在哪里 |
agent_event |
每次模型输出、工具调用、审批、错误、重试 | 能复盘为什么这样执行 |
agent_approval |
需要人工确认的动作、审批人、结果、原因 | 把高风险动作拦在执行前 |
再加一个工具注册配置,记录每个工具是 query tool 还是 mutation tool、需要什么权限、是否允许自动执行。做到这一步,已经比“模型直接 function calling”强很多了。
换句话说,harness 的起点不是平台,而是可控的事件边界。只要你能把一次智能动作拆成输入、决策、批准、执行、记录、校验这几个节点,你就已经在做 harness 了。
5. 流程图:从用户任务到工具执行再到结果返回
还是拿退款任务走一遍。
这张图真正表达的是: 一次 agent 请求不是一条直线,而是一串带边界的事件。
如果少掉其中任何一层,问题都会立刻变具体: 没有 Context Builder,模型只能靠猜;没有 Policy + Approval,模型可能越权;没有 State Store,你一中断就要重来;没有 Verifier,半成品就会被当成最终结果发出去。
拿退款任务走一遍会更直观。用户说“昨天买的会员退掉”,系统先从订单服务和政策文档里构造上下文,然后模型判断这是一个需要查询和审批的副作用请求。接着 policy 决定它能不能继续,如果能,就在 sandbox 里执行退款,并把交易号、输出和事件流都记录下来。如果失败,retry budget 决定是重试还是终止;如果需要人工确认,run 会暂停,等批准后再从同一个 thread 继续。
OpenAI 的 Codex App Server 就是这么设计的。它不是简单的 request/response,而是一个双向 JSON-RPC 协议。一次 client request 可以带来多次 event update,必要时服务器还可以反向发起 approval request。它把一次工作拆成 Item、Turn 和 Thread:
Item是最小输入输出单元,比如用户消息、工具调用、批准请求、diffTurn是一次用户输入触发的工作轮次Thread是可恢复的持久会话
这也是为什么生产级 harness 需要协议层,而不只是后端 wrapper。
再看第二张图,为什么要把 harness 和 compute 分开。
这张图背后的原则很简单: 凭据和控制放在 harness 里,模型生成的代码只在 sandbox 里跑。这样才能同时做安全、持久化和扩展。
这也是很多生产系统会把 client 看到的“界面状态”和 server 端的“真实运行状态”分开的原因。前者可以流式展示,后者必须可恢复、可审计、可中断。harness 就是那个负责真实状态的地方。
6. 实战场景一:客服 Agent 如何用 Harness 管住工具调用
先看退款。客服场景最容易让人误判 harness 的作用,以为它只是“加个审批按钮”。实际上,最难的不是审批本身,而是模型在业务状态不清的时候,不能替业务先做决定。
用户说“昨天买的会员退掉”,对人类来说,这句话通常已经足够;但对系统来说,这还远远不够。昨天可能买了两笔会员,可能来自不同渠道,可能分别处在不同退款规则里。模型如果直接猜单号并调用退款接口,问题就不再是“答错一句话”,而是把一笔真实资金动作执行到了错误订单上。
所以一个成熟的客服 harness,第一步通常不是退款,而是把请求拆成可以验证的动作。它先查订单,再比对政策,再判断这个动作是不是会改变外部状态。能只读查询的,尽量只读查询;一旦进入退款、取消订单、发通知、改 CRM 这类 mutation tool,系统就要把它当成一笔业务变更,而不是一段普通推理。高风险步骤往往需要用户确认或者人工审批,工具调用还要带幂等键,避免网络重试、进程恢复或客户端重连时重复执行。
这里最重要的能力不是“更像人”,而是“知道什么时候不能继续猜”。一个好的客服 harness 会允许模型在信息不足时追问,也会允许它在有多个候选订单时把候选项列给用户,而不是强行选一个最像的。它还要把每一步的依据写进审计轨迹: 查到了哪笔订单,依据的是哪条退款规则,为什么这个动作被批准或拒绝,后续如果出错又该从哪里恢复。
如果退款接口已经在外部系统里成功了,但本地状态保存失败,harness 不能简单地再调一次。它需要先判断这一步是否已经产生了副作用,然后选择查询状态、做补偿,或者继续恢复,而不是盲目重放。对客服 Agent 来说,harness 的价值就是把“业务动作的正确性”放在“回答看起来顺不顺”之前。
真正落地时,可以把退款工具分成两类。lookup_order、get_refund_policy、check_refund_status 是 query tool,可以自动调用;initiate_refund、cancel_subscription、send_refund_email 是 mutation tool,默认要过 policy。policy 至少要检查订单归属、退款金额、是否已退过、是否超过时限、是否命中风控、是否有幂等键。这样做以后,模型仍然可以负责理解用户意图,但它不能绕过业务规则直接改状态。
如果再往工程实现走一步,客服 harness 更像一个状态机,而不是一个聊天机器人:
| 状态 | 允许模型做什么 | Harness 要拦什么 |
|---|---|---|
intent_detected |
判断用户是不是要退款 | 不能直接退款 |
order_candidates_found |
展示候选订单、要求用户确认 | 不能替用户猜订单 |
policy_checked |
解释退款资格和限制 | 不能绕过规则给承诺 |
approval_pending |
等用户或人工审批 | 不能执行 mutation tool |
refund_submitted |
查询退款结果、解释进度 | 不能重复提交同一退款 |
reconciled |
汇总结果并关闭任务 | 不能丢失交易号和审计信息 |
这张表说明了一个关键点: harness 不是简单地“允许/拒绝工具调用”,而是在控制任务所处的业务阶段。处在 order_candidates_found 阶段时,即使模型很自信,系统也应该阻止它调用 initiate_refund;处在 refund_submitted 阶段时,系统要优先查退款状态,而不是再次提交退款。
审计记录也应该围绕这个状态机来设计。一次退款至少要留下: 用户原始请求、候选订单列表、用户确认的订单、命中的退款规则、审批结果、幂等键、外部退款交易号、最终状态。如果后续财务、客服主管或用户追问“为什么退的是这笔订单”,系统能把整条链路拿出来,而不是只说“模型当时这么判断的”。
7. 实战场景二:代码 Agent 如何用 Harness 管住文件、命令和测试
代码 Agent 的风险比客服 Agent 更直接,因为它不只是说错话,它会改文件、跑命令、影响仓库状态。你让它修一个登录接口 bug,它面对的不是一段静态文本,而是一整个会变化的工作区、依赖环境和测试链路。
没有 harness 时,常见问题不是模型“不会写代码”,而是它很容易在仓库里失控: 读错文件、改到无关模块、跑测试超时后不知道自己做到哪一步、把 .env、密钥或父目录误碰到,最后只留下一个看起来像样但其实不可回放的 diff。对后端工程师来说,这类风险很熟悉,因为它本质上就是“有副作用的自动化”缺乏边界。
有 harness 时,流程会更像一个受控的工作流,而不是一个自由奔跑的脚本。默认只读,只有明确的工作区可以写;workspace manifest 会把输入、输出和挂载路径讲清楚,让模型知道它能碰哪里、不能碰哪里;bash 和网络请求默认受限,必要时走审批;patch 不是悄悄写进文件系统,而是作为事件被记录下来,这样你才能知道它为什么改了这一行、又为什么回滚;测试在 sandbox 里跑,命令失败会生成可以恢复的状态,而不是把整个会话直接抹掉。
这也是 Claude Code 安全文档值得看的原因。它强调默认只读、写入范围限制、bash 审批、网络请求审批、可疑命令检测,以及新 MCP server 的 trust verification。它要解决的不是“尽量小心一点”,而是把代码 agent 的危险动作做成 fail-closed 的权限系统。换句话说,代码 Agent 的核心不是“能不能生成 patch”,而是“这个 patch 在真实仓库里能不能被追踪、被暂停、被恢复、被审计”。
如果把这类任务讲得更工程化一点,harness 其实在做四件事: 它限制模型的写入面,限制命令的执行面,保留每次修改的证据链,并确保一旦中途失败还能从最后一个 checkpoint 接着跑。这个闭环比“模型能不能写出正确代码”更重要,因为生产系统最怕的不是一次写错,而是写错之后没有人知道它是怎么错的。
所以代码 Agent 的验收标准也不应该只是“它最后回答修好了”。更靠谱的验收应该包括: 它改了哪些文件,是否超出允许范围;它跑了哪些命令,是否有超时或失败;失败后它有没有根据测试输出继续修复;最终 diff 能不能解释;如果用户中断任务,下次能不能从已有 patch 和测试结果继续。换句话说,代码 Agent 的结果不是一段回答,而是一条可审计的工程轨迹。
代码 Agent 的 harness 可以按五个阶段理解:
| 阶段 | Agent 在做什么 | Harness 在管什么 |
|---|---|---|
| 观察 | 读错误日志、测试输出、相关文件 | 只读权限、上下文裁剪、敏感文件过滤 |
| 定位 | 推断问题可能在哪个模块 | 最大 turn 数、工具选择、trace 记录 |
| 修改 | 生成 patch、编辑文件 | 写入范围、diff 事件、禁止改无关目录 |
| 验证 | 运行单测、集成测试、lint | 命令审批、超时、资源限制、失败记录 |
| 收尾 | 总结变更、说明测试结果 | verifier、最终 diff、未完成风险提示 |
这套阶段化思路很实用。比如测试失败后,模型可能会倾向于继续大范围修改代码,直到测试通过为止。但 harness 应该限制它的搜索半径: 先要求它解释失败原因,再允许修改相关文件;如果连续多次测试失败,就暂停并要求用户确认方向,而不是让它无限试错。对真实仓库来说,控制“能改多少”和“失败后怎么停”,比让模型多跑几轮更重要。
代码场景还有一个容易被忽略的点: patch 本身也是状态。模型改过哪些文件、为什么改、后来有没有回滚,都应该进入 event stream。否则你只看到最终 diff,却不知道它中间有没有尝试过危险方向。一个成熟的代码 Agent,不只是能生成代码,还要能让开发者相信它的操作过程没有失控。
8. 生产案例:OpenAI Codex、Agents SDK、Claude Code、LangGraph 给我们的启发
这一章不是产品清单,而是回答一个更具体的问题: 这些系统各自证明了 harness 的哪一层能力。它们不是在讲同一个故事,而是在从不同角度告诉你,agent 真正上线时缺的到底是什么。
OpenAI Codex 说明,agent 体验不是一次 request-response,而是一条可以被持续追踪的事件流。一个 client request 会拆成多个 item,turn 和 thread 让会话可以恢复、可以 fork、可以接着看下去。对 UI 来说,这意味着你不只是要显示最终答案,还要能显示进度、diff、审批和中断点;对后端来说,这意味着协议必须围绕事件和状态,而不是围绕“某个一次性的回答”来设计。
Agents SDK 进一步说明,harness 不只是包一层工具调用,它本身可以成为标准运行层。OpenAI 在 2026 年 4 月的更新里把 model-native harness、sandbox execution、Manifest、MCP、skills、AGENTS.md、shell、apply patch、snapshotting 和 rehydration 放到一个统一语境里,核心意思其实很简单: 让模型更接近它擅长的工作方式,同时把安全、耐久和扩展性从应用代码里抽出来。这里最值得借走的,不是某个 API 名字,而是“把 compute 和 harness 分开”这件事。
Claude Code 则把另一个事实讲得很直接: 安全不是补丁,而是默认值。它默认只读,写文件和跑命令都要明确授权,网络请求也要审批,新 codebase 和新的 MCP server 还要做 trust verification。这个设计告诉你,agent 不是先自由行动再补安全,而是先把默认权限设成收敛,再允许必要的放行。对真实团队来说,这种 fail-closed 的思路往往比“多加几个 if 判断”更重要。
LangGraph 说明 durable execution 不是一句好听的话,而是 checkpoint、thread、interrupt、replay 和 pending writes 这些明确语义的集合。它的价值在于,图执行到一半失败时,不需要把所有已经成功的步骤重做一遍;人类介入时,也不需要丢掉现有状态。你可以把它理解成一个更适合 agent 的状态机: 它不怕中断,因为它从一开始就假设会中断。
MCP 的启发正好是反过来的。它把 tools、resources、prompts 和 notifications 的发现与通信标准化了,但它不是完整 harness,更不是你的安全边界本身。也就是说,MCP 很适合作为工具和上下文的协议层,却不能替你决定能不能退款、能不能发邮件、能不能执行命令。这个边界如果没想清楚,就很容易把“能连接工具”误当成“能安全上线”。
这几套系统共同说明的,不是“模型更强”,而是“模型外面的 runtime 更成熟”。如果你把它们放在一起看,就会发现真正的生产实践都在往同一个方向收敛: 事件化、可恢复、可审批、可审计、可分层。
把这些案例抽象掉产品名,可以得到四条更有用的工程原则:
| 原则 | 来自哪些实践 | 对自己的系统意味着什么 |
|---|---|---|
| 请求要事件化 | Codex 的 thread / turn / item、App Server 的双向协议 | 不要只存最终回答,要存执行过程 |
| 权限要默认收敛 | Claude Code 默认只读、bash 和网络请求审批 | mutation tool 不应该默认自动执行 |
| 状态要能恢复 | Agents SDK 的 snapshot / rehydration、LangGraph checkpoint | 长任务必须有 checkpoint 和 resume 语义 |
| 工具协议不等于安全 | MCP 负责工具和上下文交换 | policy、approval、audit 仍然要自己做 |
这四条原则比记住某个产品 API 更重要。因为你最终可能不用 Codex App Server,也可能不用 LangGraph,但只要你的 Agent 会长期运行、会调工具、会产生副作用,这些问题都会回到你的系统里。
所以看生产案例时,不要只问“它用了什么框架”,而要问“它把哪类风险放到了哪个边界上”。Codex 把多端体验收进同一套事件协议,Claude Code 把危险动作收进权限系统,LangGraph 把长任务收进 checkpoint,MCP 把工具接入收进协议。你自己的 harness,也应该先回答同样的问题: 上下文边界在哪,工具边界在哪,执行边界在哪,恢复边界在哪。
9. 做这些是在重复造轮子吗
不会,前提是你知道什么该复用,什么必须自己做。很多团队一开始就把“造轮子”理解错了,以为只要自己实现了某个 agent 流程,就等于重复造了一整套平台。其实真正需要避免的是重复造基础设施,而不是重复定义自己的业务边界。
模型 API、sandbox provider、checkpoint/persistence、tracing backend、MCP client/server、通用 agent SDK 这些东西,通常都应该尽量复用。它们更像运行底座,不太应该每个团队都重新写一遍。你可以选一个足够稳定的 SDK,把执行、状态和工具接入先跑通,再把精力放在业务流程和权限边界上。
真正应该自己做的,是业务审批规则、工具权限矩阵、失败补偿逻辑、审计留存策略、红线路径和面向用户的审批交互。原因很简单: 这些东西不是通用技术问题,而是业务责任。一个退款动作该不该走人工审批,不是框架能替你决定的;一个工单什么时候算真正关闭,也不是通用 harness 能替你定义的。
同样重要的是,不要假设 MCP 等于安全边界,不要假设 sandbox 等于业务正确性,也不要假设 trace 自动等于可恢复。协议能帮你把工具接上,sandbox 能帮你把命令关住,trace 能帮你把过程记录下来,但它们都不会替你判断这个动作是否符合你的业务规则。真正能把系统做稳的,是你把这些底座和自己的规则叠在一起,而不是拿其中任意一个能力当全部答案。
如果只是把一个 agent 接进一个后端流程,SDK 往往就够了。
如果你需要多个客户端形态、稳定协议、事件流、审批、兼容性和长期维护,那就要开始认真看协议层,比如 Codex App Server 这种设计。区别不在于“要不要自己造轮子”,而在于你造的是业务逻辑,还是基础设施。
可以粗略按团队阶段来判断:
| 阶段 | 推荐做法 | 重点 |
|---|---|---|
| Demo / 内部工具 | 用 SDK + 简单工具白名单 + 基础日志 | 先证明任务链路有价值 |
| 业务试点 | 加审批、状态表、结构化事件、幂等键 | 先把副作用和恢复管住 |
| 多客户端 / 长任务产品 | 设计事件协议、checkpoint、权限系统、审计策略 | 把 harness 变成稳定运行层 |
这张表的意思不是鼓励你造平台,而是提醒你不要跳级。很多团队真正需要的不是完整 harness 平台,而是先把最危险的几个动作管住。
10. AI 应用开发岗位应该借鉴什么
对 AI 应用开发者来说,真正有价值的不是 harness 这个词本身,而是一套判断问题的方式。backend 工程师看到它,会自然想到 request lifecycle、state machine、idempotency、retry、audit;AI 应用开发者则要再往前走一步,把 prompt、工具、状态和权限放进同一个运行图里看。
以后遇到“模型不对”的时候,先不要急着改 prompt。先问三个更基础的问题: 它看到的上下文是不是对的,它能做的动作是不是太危险,它失败以后能不能恢复、回放和审计。很多看起来像模型能力不足的问题,最后都会落到 harness 的设计上。上下文裁剪错了,模型就会选错工具;权限放太松了,模型就会把猜测变成副作用;状态和 checkpoint 没设计好,任务中断后就只能从头重来。
这也是为什么单 agent 往往应该先跑通。OpenAI 的实践和那份实用指南都在强调同一件事: 先把单 agent + 工具 + 规则做强,再考虑多 agent。多 agent 当然能解决职责拆分、并行子任务和复杂工作流的问题,但它也会带来新的协调成本。很多团队之所以觉得“agent 做不稳”,并不是因为 agent 不够多,而是因为连一个 agent 的上下文、权限和恢复语义都还没设计好。
如果你真的需要拆成多 agent,通常是因为工具开始爆炸,分支开始过多,模型经常选错工具,或者职责边界已经足够清楚,值得让不同 agent 处理不同子任务。这个时候,manager pattern 更像中央控制和统一输出,handoff pattern 更像任务接力和领域拆分。它们都不是为了“更高级”,而是为了让复杂度分层出现。
所以 AI 应用开发岗位真正要学的,不是把模型接上某个 API 就结束,而是学会把模型放进一个能被控制的业务系统里。你能不能设计权限,能不能设计状态,能不能设计恢复,能不能在出错时知道问题出在上下文、工具还是执行层,这些才是从 demo 走向产品的分水岭。
一个很实用的排查方法是: 遇到 bad case 时,不要先说“模型不行”,先按下面这张表定位。
| 现象 | 优先怀疑 | 该看什么 |
|---|---|---|
| 模型答非所问 | 上下文构造 | 检索结果、摘要、系统指令、历史裁剪 |
| 选错工具 | 工具注册和描述 | tool schema、工具命名、工具过滤 |
| 做了不该做的动作 | policy / approval | allow / ask / deny 规则、权限矩阵 |
| 重复退款、重复发邮件 | 恢复和幂等 | idempotency key、effect log、retry 记录 |
| 出错后无法复现 | trace / event | run_id、turn_id、tool_call_id、错误栈 |
| 中断后只能重来 | checkpoint | thread_id、checkpoint、pending writes |
这张表就是 harness 思维的实际价值。它把“模型表现不好”拆成可以定位、可以改、可以验证的工程问题。
11. 总结
如果只记住三件事,就够了。
Agent Loop 让 agent 能行动;Agent Harness 让它在真实系统里安全、稳定、可观测、可恢复地行动;真正的 Agent 工程,不是让模型多走一步,而是让它每走一步都可控、可查、可退。
所以你以后再看一个 AI 应用,别先问“模型聪不聪明”,先问它的边界在哪里、它的副作用怎么管、它失败后怎么回来。只要这三个问题答不出来,它大概率还是 demo,不是可以放心交付的系统。
最直接的行动建议是: 下次你做一个会调用工具的 Agent,不要先急着加更多工具。先给每个工具标上 query / mutation,给 mutation tool 加审批和幂等键,给每一步写结构化 event,再保存一个能恢复的 checkpoint。做到这些,你已经开始把 Agent 从“能演示”往“能上线”推进了。
参考资料
更多推荐
所有评论(0)