Harness 是什么?为什么强大的模型不等于好用的 Coding Agent
过去两年,我们讨论 AI 编程工具时,最常问的问题往往是:“GPT 和 Claude 谁更强?”“Codex 这次是不是又换了一个更好的模型?”“为什么某个模型 Benchmark 分数很高,实际写代码却没有想象中好用?”
这些问题当然重要,但进入 Agent 时代后,只看模型已经越来越难解释真实的产品体验。
今天的 Codex 不只是一个“会写代码的模型”。你给它一句“帮我找到这个项目里的登录 Bug,修掉并跑完测试”,它可以自己搜索仓库、读取文件、修改代码、调用 Shell、分析测试失败、再次修改,最后给出 diff 和验证结果。这个过程中,模型负责理解和推理,但真正让整个任务跑起来的,是模型外围的一套运行系统。
这套系统,就是 Harness。
一句话来说:
Harness 是包在 AI 模型外面,负责组织上下文、提供工具、维护状态、控制权限,并驱动模型一步一步完成任务的运行系统。
如果模型是“大脑”,Harness 更像是神经系统、工作台、操作规程和执行控制系统的组合。模型决定“下一步应该做什么”,Harness 则负责“让这一步真的发生,并把结果重新送回模型”。
这也是为什么今天评价 Codex 这样的 Agent,仅仅问“底层是什么模型”已经不够了。更准确的评价单位正在变成:
一、为什么一个很强的模型,依然不等于一个好用的 Agent
先做一个简单的思想实验。
假设我们拿到一个非常强的代码模型,并给它这样一个任务:
登录成功以后,用户偶尔会再次跳回 /login。 请找到原因、修复问题,并运行相关测试。
如果只是裸模型,它能做的事情其实非常有限。模型可以根据你提供的代码进行分析,也可以告诉你“建议检查 session 初始化和 middleware 的执行顺序”,但它并不知道你的硬盘上有什么文件,不知道当前 Git 状态,不知道 auth.ts 在什么位置,更不能真的执行:
BASH
npm test
因为语言模型本质上仍然是在完成一件事:接收输入 Token,根据当前上下文进行推理,然后输出新的 Token,或者生成一个结构化的 Tool Call。
真正让 Codex 从“一个会回答代码问题的模型”变成“一个能自己进入项目完成工作的 Agent”,需要外围系统补齐大量能力:搜索代码、读取文件、编辑代码、执行 Shell、保存任务状态、限制危险命令、把工具执行结果返回给模型,以及决定什么时候继续、什么时候结束。
OpenAI 在介绍 Codex 时,直接把底层这套逻辑称为 Codex harness。Codex 的 Web、CLI、IDE 和桌面 App 虽然入口不同,但底层可以共享同一套 harness;其中最核心的部分之一就是 Agent Loop,也就是协调用户、模型与工具不断交互的执行循环。

这张图里最值得注意的一点,是 Model 并没有直接连接操作系统。
模型真正输出的是“意图”。比如,它可能判断下一步应该搜索某个函数、读取某个文件,或者运行一次测试。Harness 接收到这个意图后,会决定应该调用哪个工具、参数是否合法、操作是否需要用户批准,然后在真实环境或 Sandbox 中执行,最后再把执行结果送回模型。
这意味着,“模型会不会调用工具”其实只描述了 Agent 能力的一半。
真正决定一个 Agent 好不好用的问题是:工具怎么定义、上下文怎么组织、结果怎么反馈、失败怎么恢复、权限怎么控制,以及一整个任务能不能形成闭环。
换句话说,模型提供的是智能,而 Harness 负责把这种智能变成可执行的工作流。
二、Harness 到底是什么?用 Codex 把它拆开看
如果把一个成熟的 Coding Agent 拆开,Harness 通常至少要管理六类东西:Instructions、Context、Tools、State、Permissions 和 Verification。这些模块并不一定在代码里严格对应六个独立服务,但从理解 Agent 的角度,这个划分非常有用。
Instructions 解决的是“模型应该遵守什么规则”。 传统 Chatbot 时代,我们很容易把这一切叫作 Prompt;但在 Coding Agent 中,Instructions 已经远不止 System Prompt。它可能来自系统规则、AGENTS.md、项目规范、Skill、工具说明以及当前任务约束。比如一个项目可以规定:“数据库 schema 变化必须生成 migration;代码修改后必须执行测试;禁止直接修改 production 配置。”Harness 的职责,是在合适的时机让模型持续看到这些约束。
Context 解决的是“这一刻模型应该知道什么”。 一个大型仓库可能有几十万甚至几百万行代码,显然不能一次全部送给模型。用户只说“修复登录 Bug”时,Harness 可能先搜索 auth、session、middleware,找到几份候选文件,然后只读取其中相关片段。模型进一步发现需要检查 session.test.ts 时,再发起下一次读取。于是 Context 不再是一个静态 Prompt,而是一个不断变化的运行状态:

这也是为什么现在越来越多工程讨论从 Prompt Engineering 转向 Context Engineering。真正困难的问题已经不是“这句话应该怎么问”,而是“Agent 执行到第 37 步的时候,模型应该看到哪些东西”。
Tools 解决的是“模型怎么影响现实世界”。 模型并不会真的执行 npm test。它产生的更像是一个结构化请求:
JSON
{
"tool": "shell",
"command": "npm test"
}
Harness 收到这个请求以后,才会检查参数和权限,在合适的执行环境里启动命令,把 stdout、stderr 和 exit code 收集回来,然后转换成下一轮模型能够理解的 observation。
一个极简 Tool Executor 可以想象成:
PYTHON
def execute_tool(call):
if call.name == "read_file":
return filesystem.read(call.arguments["path"])
if call.name == "shell":
check_permission(call)
return sandbox.run(call.arguments["command"])
if call.name == "git_diff":
return git.diff()
State 解决的是“Agent 做到哪里了”。 如果任务只持续十几秒,状态管理没有那么明显;但一旦 Agent 工作几十分钟甚至数小时,就必须知道已经读过哪些文件、哪些方案失败过、当前 Git 状态是什么、哪些 TODO 已经完成,以及 Session 被中断以后如何继续。Long-horizon Agent 真正困难的地方之一,就是需要把聊天记录、运行状态、文件 Artifact 和执行历史区分开,而不是简单把所有内容无限追加进 Context Window。
Permissions 和 Sandbox 解决的是“Agent 可以做什么”。 npm test 也许可以自动执行,但 rm -rf、修改系统文件、访问网络、删除大量代码或者 git push --force 就不能采用同样的策略。因此一个实际 Harness 通常需要 Policy Layer:
PYTHON
risk = policy.evaluate(tool_call)
if risk == "safe":
return execute(tool_call)
if risk == "requires_approval":
if ask_user(tool_call):
return execute(tool_call)
return ToolDenied()
return ToolDenied()
所以你在 Codex 里看到“是否允许执行这个命令”的确认窗口时,那不是模型突然决定尊重你的意见,而是 Harness 的权限系统介入了。
Verification 解决的是“模型说完成了,到底算不算完成”。 这是 Agent 和普通 Chatbot 一个非常重要的区别。模型完全可能自信地说“Bug 已经修复”,但测试仍然失败。因此,更成熟的 Harness 不会把模型的完成声明直接当成最终状态,而会继续执行 Test、Lint、Type Check 或其他验证流程;如果验证失败,结果会重新回到 Agent Loop,让模型根据新的反馈继续分析和修改。
整个过程可以概括成一个持续收敛的闭环:
理解任务 → 制定计划 → 执行动作 → 观察结果 → 验证结果 → 通过则结束;未通过则反馈错误、重新规划,再进入下一轮执行。
这里真正重要的不是“模型能不能生成一个看起来正确的答案”,而是系统是否能够用外部、可验证的信号判断任务有没有真的完成。Verification 把 Agent 从“自认为完成”变成了“经过验证后完成”,而这也是 Harness 能够支撑长时间自主执行的关键机制之一。
三、从一个 Bug 修复过程,看清 Agent Loop 是怎么工作的
理解 Harness 最直观的方法,不是继续背概念,而是观察一次 Codex 如何完成真实任务。
还是前面的登录 Bug。用户只输入一句:
修复登录后偶尔被重定向回登录页的问题,并运行测试。
Harness 首先不会直接要求模型“写答案”,而是根据当前任务和工具能力构造 Context。模型可能先要求搜索:
PLAINTEXT
auth
session
middleware
redirect
/login搜索结果返回以后,Harness 把候选文件和部分代码交回模型。模型继续请求读取:
PLAINTEXT
src/auth/session.ts
src/middleware.ts
src/pages/login.tsx
tests/session.test.ts这时模型才形成一个初步判断:可能是 middleware 在 session refresh 完成之前就执行了 redirect 判断。
模型生成一次编辑操作。Harness 修改文件,随后模型要求:
BASH
npm testHarness 在 Sandbox 中启动测试。如果结果是:
SHELL
FAIL tests/session.test.ts
Expected:
/dashboard
Received:
/login这个错误不会直接展示给用户后就结束,而是重新进入 Agent Loop。Harness 把测试日志写入当前 State,并在下一轮 Context 中告诉模型:“刚才的修改没有通过测试。”
模型拿到新的 Observation 后,会再次分析当前状态。它可能发现,真正的问题并不在 redirect 逻辑本身,而是 session 刷新后的 Cookie 没有在同一个请求周期里生效。于是模型提出第二次修改方案,Harness 再次编辑代码并重新运行测试。
这一步很重要,因为它说明 Coding Agent 并不是“模型思考一次,然后把答案输出出来”。在真实任务中,模型的判断会不断受到外部执行结果的修正:搜索结果会改变它对代码结构的理解,测试失败会推翻之前的假设,新的错误日志又会成为下一轮推理的输入。
因此,一次看似简单的 Bug 修复,实际上是一个不断收敛的执行循环:
理解任务 → 搜索和读取代码 → 分析问题 → 修改代码 → 执行测试 → 根据测试结果决定结束,或者带着新的错误信息进入下一轮分析和修改。
如果测试通过,Harness 可以继续生成 diff、整理修改内容并输出最终结果;如果测试失败,失败日志不会被当成任务终点,而会重新成为 Agent 的 Observation,触发下一轮规划和执行。
也就是说,Agent Loop 的关键并不是“循环调用模型”,而是让模型推理和真实环境反馈持续发生往返。模型每一轮看到的世界,都可能因为上一轮工具执行而发生变化。

这张图把一次典型的 Coding Agent 执行过程展开了:从 Context Retrieval、读取相关文件,到代码修改、测试执行,再到失败后的日志分析与重试。可以看到,真正把这些步骤连接起来的并不是模型本身,而是 Harness 对 Context、Tool Call、Execution Result 和 Verification 的持续编排。
如果把这个过程进一步压缩成代码,就可以看到一个最小 Harness 的基本骨架:
PYTHON
def run_agent(task):
state = AgentState(task=task)
while not state.finished:
context = build_context(state)
response = model.generate(
instructions=load_instructions(),
context=context,
tools=available_tools(),
)
if response.tool_call:
call = response.tool_call
if requires_approval(call):
if not request_user_approval(call):
state.add_observation("Tool call denied.")
continue
result = execute_in_sandbox(call)
state.add_tool_result(result)
continue
if response.final_answer:
result = verify(state)
if result.success:
state.finished = True
return response.final_answer
state.add_observation(
f"Verification failed:\n{result.output}"
)这段代码最重要的地方其实不是 model.generate(),而是模型前后那几个函数:
TEXT
build_context()
load_instructions()
available_tools()
requires_approval()
execute_in_sandbox()
verify()
AgentState它们共同决定了模型处于什么环境、能够采取什么动作、如何理解执行结果,以及失败后还能不能继续。
也就是说,模型能力越接近,我们越会发现实际产品差异开始来自这些“模型以外”的地方。
四、Codex App Server 揭示了一个关键事实:产品不是 Harness,Harness 也不是模型
讨论 Harness 时,一个很容易混淆的问题是:我们本地安装的 Codex App、Codex CLI,能不能直接叫 Harness?
日常交流里这么说通常不会造成太大问题,但如果从工程架构上看,最好把 产品、Harness 和模型 分开理解。
Codex 是一个很好的例子。今天我们可以通过 CLI、IDE Extension、Web 和 macOS App 使用 Codex,这些入口的界面、交互方式和运行环境明显不同,但 OpenAI 明确说明,它们底层可以复用同一套 Codex harness。换句话说,CLI 和桌面 App 并不是各自重新实现了一套 Agent,它们可以共享负责 Agent Loop、Tool Execution、状态持久化和权限控制的核心运行逻辑。

这也是 Codex App Server 存在的意义。它位于产品界面和 Codex Core 之间,把底层 Agent 能力暴露成一套稳定的双向协议,使不同客户端都能够驱动同一套 Harness。
这里有一个容易被忽略的细节:Agent 的交互并不是传统 API 那种简单的“一次 Request 对应一次 Response”。
用户提交“运行测试并修复失败用例”以后,中间可能连续发生代码搜索、文件读取、Shell 执行、权限确认、Diff 生成以及多轮模型推理。客户端必须不断接收这些中间事件,有时还需要在任务进行到一半时回复一个 Approval Request,然后 Agent 才能继续执行。
因此,Codex App Server 把一次 Agent Session 拆成了三个基本概念:Thread、Turn 和 Item。Thread 是一个可以保存、恢复甚至 Fork 的长期 Agent Session;Turn 是一次用户输入触发的一轮工作;Item 则是其中的原子事件,例如用户消息、Agent Message、Tool Execution、Approval Request 或代码 Diff。
这个设计其实说明了一件很重要的事:当 Agent 从“回答问题”升级到“持续工作”,我们需要管理的已经不仅仅是 Prompt 和 Response,而是一个有生命周期、有状态、有事件流的运行系统。
因此,更准确的 Agent 分层可以理解为:
层次 | 在 Codex 中可以对应什么 |
|---|---|
交互与产品层 | CLI、IDE、Web、macOS App,以及 Codex 产品体验 |
Harness / 控制层 | Agent Loop、Context、State、Permission、Verification |
协议与工具层 | JSON-RPC、MCP、Shell、Files、Git、Browser |
Model | 负责理解、推理、规划和生成的模型 |
Environment | Repository、OS、Sandbox、Network |

这也解释了为什么 MCP 不是 Harness。MCP 更像一种工具连接协议,让 Harness 能以标准方式发现和调用外部能力;Shell、Browser、Git 是具体工具;Model 提供推理能力;而 Harness 的职责,是决定模型在什么上下文中运行、可以调用什么工具、执行结果如何反馈,以及整个任务什么时候应该继续或结束。
所以,如果一定要回答“Codex 是不是 Harness”,更准确的说法应该是:
Codex 是一个完整的 Agent 产品,而 Codex Harness 是这个产品内部负责组织和驱动 Agent 工作的核心运行层。
这个区别看起来只是术语问题,但它会直接影响我们怎样判断一个 AI 产品到底强在哪里。很多时候,产品体验的提升并不是单纯来自换了一个更强的模型,而是来自 Harness 对 Context、Tools、State 和 Verification 的持续优化。
五、为什么 Harness Engineering 会成为 Agent 时代的新工程问题
如果模型越来越聪明,一个很自然的问题是:未来是不是不断升级模型就够了?
现实正在给出一个有点反直觉的答案:模型越强,Harness 反而越重要。
当模型本身能力不足时,系统最大的瓶颈确实是 Model。一个连项目代码都无法正确理解的模型,再精细的 Tool Router 也发挥不了太大作用。但当模型已经可以可靠完成代码理解、规划和 Tool Use 以后,失败原因会逐渐转移到模型外围。
模型可能已经知道该怎么修,但 Harness 没有找到真正相关的文件;模型写出了正确代码,但环境里没有办法启动应用并验证结果;模型可以继续工作,但当前 Context 已经被几百轮历史日志淹没;模型认为任务完成了,但系统没有 Verifier 去发现 Integration Test 仍然失败。
这就是 Harness Engineering 开始出现的背景。
OpenAI 在 2026 年公开过一个很有代表性的实验:他们从一个空 Git Repository 开始,让 Codex 构建并维护一个真实的软件产品,同时规定人类工程师不直接提交代码。五个月后,这个项目已经达到百万行级别,并产生了大约 1500 个 Pull Request。OpenAI 总结这次实验时强调,人类工程师的工作开始从“亲自写代码”,转向设计环境、表达意图,以及建立让 Agent 可以可靠工作的反馈循环。
这其实是 Harness Engineering 最值得注意的地方。
如果 Codex 看不到应用运行状态,工程师真正需要解决的不是“再写一个更聪明的 Prompt”,而是让运行状态对 Agent 可见。OpenAI 的团队为此把 Chrome DevTools Protocol 接入 Agent Runtime,使 Codex 能够自己启动应用、读取 DOM、截图和操作网页;他们又把 Logs、Metrics 和 Traces 暴露给 Codex,让 Agent 可以直接验证性能和运行行为。
于是,“确保服务启动时间低于 800ms”这样的要求,才有可能真正形成闭环:Agent 修改代码以后,可以自己启动服务、读取指标、发现瓶颈、再次修改并重新测量,而不是写完代码以后告诉用户“理论上应该更快”。
这里其实出现了一条很清楚的技术演进路线:
Prompt Engineering → Context Engineering → Harness Engineering。
Prompt Engineering 关心的是“这句话怎样写,模型回答得更好”;Context Engineering 关心的是“模型这一刻应该看到什么信息”;Harness Engineering 再进一步,它关注的是:
怎样设计整个运行环境,让模型能够长期、可靠地完成任务?
而当任务从几分钟延长到几小时,这个问题会变得更加突出。
OpenAI 在上述项目中提到,他们已经经常看到单个 Codex Run 连续处理同一个任务超过六小时。 Anthropic 在研究 long-running agents 时也指出,仅仅拥有更大的 Context Window 并不能解决长期执行问题:Agent 仍然需要维护 Progress、保存 Artifact、进行 Context Compaction,并在新的 Session 中可靠恢复工作。
这时候,Harness 需要处理的东西越来越像一个传统 Runtime:状态持久化、Checkpoint、Compaction、Retry、Timeout、任务进度、Artifact、Observability 和 Recovery。
也正是在这一阶段,Harness 和 Agent Runtime 两个概念开始逐渐靠近。
一个短任务的 Harness 可以只是一层 Tool Calling 包装;一个真正的 Long-horizon Harness,则需要回答“任务进行到哪里了”“哪些方案已经失败”“当前环境是什么状态”“中断后如何继续”等一系列传统应用运行时才需要面对的问题。
再往前走一步,就会来到目前非常值得关注的 Self-improving Harness。
想象一个 Agent 系统运行了几万个任务以后,Eval 和 Verifier 发现它经常在某类任务上做错 Tool Routing。过去,需要工程师手工修改 Tool Description 或 System Prompt;未来,这个过程可能变成:系统分析失败轨迹,自动修改 Instructions、Tool Description、Routing、Workflow 或 Memory Policy,再跑一轮 Eval,只有确认性能提升后才保留修改。
这里的“Self-improving Agent”并不一定意味着模型修改自己的神经网络参数。
很多情况下,更现实的路线反而是:
模型不变,但 Agent 开始优化包在自己外面的 Harness。
这也是为什么 Harness 很可能比今天大量 Prompt 技巧更值得长期关注。Prompt 很容易随着模型升级而失效,而 Context、Tools、Verification、State 和 Runtime 这些问题,只要 Agent 还需要真实地完成工作,就不会消失。
六、以后评价 Codex 这样的 Agent,别只问它用了什么模型
理解 Harness 之后,我们评价 Coding Agent 的方式也应该发生变化。
当然,Model 仍然非常重要。模型决定了代码理解、推理和规划能力的上限。但真正进入实际工程场景后,我更关心的是模型周围到底有什么。
首先是 Context。它怎样从大型代码库中找到真正相关的信息?Context 过长以后如何处理?Agent 工作几个小时后,是否仍然能够保留任务目标、关键决策和当前进度?
第二是 Tools。Agent 能不能读写文件、运行 Shell、控制 Browser、查询 Git 和外部系统?Tool Schema 是否足够清晰?当一个工具失败时,系统有没有能力重新规划,而不是原地报错?
第三是 State 和 Agent Loop。一个任务能持续多久?Session 中断后能否恢复?任务进度、执行结果和 Artifact 是否真正持久化?还是所有所谓“记忆”都只是塞在当前聊天记录里?
第四是 Verification。这往往是区分“Demo Agent”和“生产 Agent”的关键。模型说“完成了”以后,系统会不会真的运行测试、Type Check、Lint 或 UI Validation?最终完成条件来自模型自己的判断,还是来自外部可验证信号?
最后是 Security 和 Observability。Shell 到底运行在哪里?默认有没有网络?什么操作必须经过 Approval?当 Agent 卡住时,我们能不能看出问题出在模型、Context、Tool Call,还是外部环境?
如果把这些问题压缩成一个更简单的判断框架,可以记住:
Agent 的实际能力,不只是 Model,而是 Model × Harness × Tools × Environment。
同一个模型,放进不同 Harness 里,完全可能呈现出两个不同级别的 Agent。
一个 Harness 可能只提供 File Read 和简单 Shell;另一个则拥有成熟的 Context Retrieval、Sandbox、Browser、Persistent State、Verifier 和自动恢复能力。即使模型完全相同,最终能完成的任务跨度也可能非常大。
所以未来再比较 Codex、Claude Code 或其他 Coding Agent 时,“底层用了什么模型”仍然值得问,但它应该只是第一个问题,而不是最后一个问题。
如果一定要用一句话总结 Harness,我会这样说:
模型决定 Agent 能有多聪明;Harness 决定这份聪明能不能稳定地变成工作结果。
过去几年,我们主要解决的是让模型“回答得更好”。
Agent 时代真正开始解决的问题,则是:
怎样让模型可靠地工作。
而从回答问题到完成工作的这段距离,正是 Harness 存在的意义。
参考资料
OpenAI — Harness engineering: leveraging Codex in an agent-first world:从真实的大规模 Codex 项目解释为什么工程团队开始从“写代码”转向“设计 Agent 可以工作的环境与反馈循环”。阅读原文
OpenAI — Unlocking the Codex harness: how we built the App Server:解释 Codex Harness、Codex App Server,以及 Thread / Turn / Item 等运行时概念,是理解“产品和 Harness 为什么不是一回事”的关键材料。阅读原文
OpenAI — Unrolling the Codex agent loop:从模型、工具和用户之间的交互入手,解释 Codex Agent Loop 的核心工作方式。阅读原文
OpenAI — Introducing the Agents API:可以观察 OpenAI 如何把 Context Management、Tools、Subagents 和长时间运行能力逐步抽象成标准 Agent Runtime。阅读原文
Anthropic — Effective harnesses for long-running agents:非常适合理解 Long-horizon Agent 为什么需要 Progress、Artifact、Context Compaction 和跨 Session 恢复能力。阅读原文

