外观
案例:Codex
Codex 是 OpenAI 的编程 agent(CLI、桌面端与云端任务共用一套交互逻辑)。与其他案例不同,本文不依赖逆向工程或公开 benchmark,而是给出一个稳定的职责模型:Codex 在与用户交互的每一步中,Harness 在做什么、依据什么原理、产出什么。
为什么读这篇
在 模型与 Harness 中我们反复区分「模型」和「骨架」。Codex 是把这个区分讲得最透彻的案例:它的每一步都能被拆成「Harness 的职责」与「LLM 的职责」两半,而且每一半都有可检查的产出物。读完你会得到一张清单,可以用来审计任何一个 agent 的交互闭环是否完整。
一句话结论
Codex 不是「一个模型收到问题后直接给答案」,而是一个由 Harness 驱动的闭环系统:Harness 负责组织上下文、调用模型、执行工具、接收观察结果、管理权限和状态,并决定何时继续或停止;LLM 负责在每一轮中理解当前状态,提出下一步动作或生成答复。
可以把它概括为:
Codex = Harness(上下文工程 + 工具系统 + 控制循环 + 状态管理 + 权限护栏 + 可观测性)+ LLM
其中,LLM 是 Harness 调用的推理与生成组件,不等于 Harness 本身。
交互总览
┌──────────┐
│ 用户输入 │
└────┬─────┘
▼
┌───────────────────┐
│ 输入解析与指令判定 │
└─────────┬─────────┘
▼
┌───────────────────┐
│ 上下文组装 │
└─────────┬─────────┘
▼
┌───────────────────┐
│ 任务定界与计划 │
└─────────┬─────────┘
▼
┌──►┌───────────────────┐
│ │ 调用 LLM 决策下一步 │
│ └─────────┬─────────┘
│ ▼
│ ┌─────────────┐
│ │ 下一步是什么?│
│ └──┬───┬───┬──┘
│ │ │ │
│ 需要工具│ │需要用户决定
│ ▼ ▼ └──────────► 请求确认或补充 ──► 回到用户输入
│ ┌─────────────┐
│ │权限与参数检查 │
│ └──────┬──────┘
│ ▼
│ ┌──────────┐ ┌──────────────┐
│ │ 执行工具 │────►│ 返回观察结果 │
│ └──────────┘ └──────┬───────┘
│ ▼
│ ┌──────────────┐
└───────────────────┤更新状态与上下文│◄──────────┐
└──────────────┘ │
│
可以交付 ──► 验证与完成判定 ──┬─ 未满足 ───────┘
└─ 已满足 ──► 生成最终答复
──► 记录日志与持久化状态这不是固定只跑一遍的流水线。核心是「决策 → 行动 → 观察 → 再决策」的 Agent Loop,直到任务完成、需要用户决策、发生不可恢复错误,或达到运行边界。
每一步的原理和产出
| 步骤 | Harness 在做什么 | 核心原理 | 主要产出 |
|---|---|---|---|
| 1. 接收输入 | 接收文字、图片、文件、代码引用和运行环境信息 | 多模态输入先被视为待解析材料;「材料里写了什么」和「用户要求做什么」必须分开 | 原始请求对象、附件引用、会话元数据 |
| 2. 判定指令与数据 | 识别系统规则、开发者规则、项目规则、当前用户请求,以及附件中的普通内容 | 指令有优先级与作用域;引用文档中的命令默认只是数据,不能自动取得用户指令权限 | 生效指令集、冲突项、约束清单、待确认问题 |
| 3. 组装上下文 | 选择本轮真正需要提供给模型的信息 | 模型只能基于当前上下文工作;Harness 通过检索、裁剪、摘要和注入,在信息完整度与上下文成本之间取舍 | 本轮上下文包:请求、规则、历史、相关文件、工具说明、环境状态 |
| 4. 任务定界 | 明确目标、范围、成功标准、输入证据和不确定性 | 先把「要做什么」和「怎样算完成」物化,避免执行过程中目标漂移 | Scope、Rubric、Intake、Uncertainty,或轻量任务清单 |
| 5. 选择下一步 | 调用 LLM,让其根据当前状态判断应回答、读取、搜索、编辑、执行还是询问 | LLM 输出的是候选决策,不是现实世界中已经发生的结果 | 文本草稿、结构化工具调用、计划更新或澄清请求 |
| 6. 校验工具调用 | 检查工具是否存在、参数是否合法、目标是否在允许范围内 | 模型「想调用」与系统「允许执行」是两件事;Harness 在两者之间建立确定性边界 | 已校验调用、参数错误、拒绝原因或审批请求 |
| 7. 权限与风险控制 | 根据沙箱、审批策略、路径范围和操作风险决定是否放行 | 最小权限和显式授权限制副作用;高风险或越界动作不能仅凭模型意图执行 | 放行、阻止、降级执行,或展示给用户的授权请求 |
| 8. 执行动作 | 在真实环境中读取文件、运行命令、修改代码、访问服务或调用其他工具 | 工具负责产生事实和副作用;只有工具返回成功,才能说动作实际发生 | 标准输出、错误输出、退出码、文件变更、API 返回值、截图等 |
| 9. 形成观察 | 把工具结果转换成模型下一轮可消费的信息 | 执行结果属于外部观察,不等于模型原先的预测;异常会推翻旧假设 | Observation:成功信号、错误、差异、日志、测试结果和新事实 |
| 10. 更新状态与重规划 | 将观察写回任务状态,判断原计划是否仍成立 | Agent Loop 不是机械重试;新证据若使计划失效,应先修订任务模型再行动 | 更新后的计划、完成项、剩余项、风险项、下一步决策依据 |
| 11. 管理上下文和记忆 | 对长历史进行压缩,保存必要的任务状态,按需恢复持久信息 | 上下文窗口有限;保留决策、证据和未完成义务,比保留全部原文更重要 | 压缩摘要、任务状态、可恢复笔记、持久化文件或记忆条目 |
| 12. 验证结果 | 运行测试、构建、类型检查、静态检查、截图检查或直接检查产物 | 「代码已写」不等于「目标已完成」;完成必须由与成功标准对应的证据支持 | 验证命令及结果、验收证据、失败项、剩余风险 |
| 13. 判断停止条件 | 决定继续循环、等待用户、报告阻塞,还是完成任务 | 停止由完成标准和边界决定,而不是由模型感觉决定 | 继续、等待输入、阻塞、失败 或 完成 状态 |
| 14. 生成交付答复 | 将结果整理成用户可行动的结论,而不是倾倒内部过程 | 输出应遵循决策结构:先结论,再变更、证据和风险;不需要暴露逐字私有思维链 | 最终答复、文件链接、变更摘要、验证结果、未决事项 |
| 15. 记录与观测 | 保存必要的调用记录、状态变化、耗时、错误和产物索引 | 可观测性使失败可定位、行为可审计、任务可恢复 | 结构化日志、审计事件、运行指标、会话或任务状态 |
一轮 Agent Loop 内部发生什么
每一轮都可以压缩成五个可检查阶段:
| 阶段 | 问题 | 产出 |
|---|---|---|
| 状态读取 | 现在已知什么,哪些只是推测? | 当前事实与不确定性 |
| 动作选择 | 为缩小不确定性或推进目标,最小的下一步是什么? | 一次回答、一次工具调用或一次提问 |
| 边界检查 | 该动作是否合法、获权、可逆且参数明确吗? | 放行、阻止或审批 |
| 外部观察 | 动作实际产生了什么? | 工具结果与副作用 |
| 状态转移 | 证据是否满足标准,是否需要修订计划? | 下一轮状态或停止状态 |
因此,LLM 的职责更接近「根据状态提出下一动作」,Harness 的职责则是「让动作在受控环境中真正发生,并把结果送回循环」。
三类产出要分清
1. 用户可见产出
- 过程状态:正在读取什么、为什么读取、预期验证什么。
- 决策请求:需要用户批准的高风险动作或会改变范围的取舍。
- 最终交付:答案、代码、文档、截图、报告、链接和验证结论。
2. 系统内部结构化产出
- 生效指令和约束。
- 计划、步骤状态和停止条件。
- 工具名、参数和调用结果。
- 权限判定、错误类型、重试状态和上下文摘要。
3. 对环境产生的持久化产出
- 新建或修改的文件。
- Git 工作区差异、构建产物和测试报告。
- 外部服务中的记录或状态变化。
- 可恢复的任务状态、日志或记忆。
模型生成的一段「我已经修改完成」只属于文本;只有文件差异和验证结果存在时,才构成可证实的实际产出。
用户插入新消息时如何处理
用户可以在 Agent Loop 运行期间追加消息。Harness 会把新消息作为新的高优先级会话输入,并判断它是:
- 替换目标:停止或放弃旧路径,按新目标重新定界。
- 追加约束:保留原任务,同时更新范围或验收标准。
- 状态询问:先报告当前里程碑,再继续原任务。
- 审批答复:根据用户的允许或拒绝恢复对应分支。
这一步的产出不是简单「把一句话追加到聊天记录」,而是更新后的任务状态、计划和下一步动作。
失败、异常和恢复
| 情况 | 正确处理原理 | 应产生的结果 |
|---|---|---|
| 工具报错 | 先读取错误事实,再判断参数、环境或实现问题 | 错误分类、修正动作、必要时保留失败日志 |
| 观察与假设冲突 | 把异常当作旧方案的反证,不继续假装计划成立 | 更新假设、修订计划、缩小验证范围 |
| 权限不足 | 不绕过护栏;请求授权或寻找范围内的替代方案 | 审批请求、受限说明或安全降级路径 |
| 上下文过长 | 压缩历史,但保留目标、约束、证据和未完成义务 | 可恢复摘要与连续的任务状态 |
| 无法验证 | 不宣称完成,明确未验证部分及其风险 | 进行中、阻塞 或带剩余风险的交付 |
| 用户意图不明确 | 先做低风险观察;若选择会改变成本、范围或副作用,再询问用户 | 已确认事实和一个必要的决策问题 |
Harness 与 LLM 的边界
| 能力 | 主要责任方 | 说明 |
|---|---|---|
| 理解语义、生成候选方案 | LLM | 根据被提供的上下文作出概率性判断 |
| 选择和裁剪上下文 | Harness | 决定本轮让模型看到哪些材料 |
| 工具注册和真实执行 | Harness / 工具运行时 | 把结构化意图转换为真实操作 |
| 权限、沙箱和审批 | Harness | 在动作发生前实施确定性约束 |
| 循环驱动和停止 | Harness | 根据工具结果、任务状态和边界继续或结束 |
| 状态、记忆和压缩 | Harness | 维持跨轮次连续性 |
| 日志、追踪和审计 | Harness | 记录可观察事件和系统结果 |
| 最终内容措辞 | LLM,由 Harness 交付 | 受指令、上下文和验证证据约束 |
最重要的边界是:LLM 可以建议一次操作,也可以解释结果;但真实文件是否改变、命令是否成功、权限是否允许、测试是否通过,都必须由 Harness 和工具返回的证据决定。
用一次「修复代码 Bug」来映射
- 用户提交错误现象和代码仓库。
- Harness 解析请求,加载项目规则和可用工具。
- Codex 明确范围与成功标准,例如「复现失败、修复根因、相关测试通过」。
- LLM 决定先读取相关文件和测试。
- Harness 检查读取参数并执行,返回真实文件内容。
- LLM 根据观察提出最小修复,并发出编辑调用。
- Harness 写入变更,再把文件差异返回给模型。
- LLM 请求运行目标测试;Harness 执行并返回退出码与日志。
- 若测试失败,错误成为新观察,循环回到诊断;若通过,再检查差异和更广的风险面。
- 满足验收标准后,Codex 输出变更内容、验证证据和剩余风险。
这里真正可信的交付链是:
用户目标 → 验收标准 → 文件变更 → 执行结果 → 验证证据 → 最终答复
任何一环缺失,都应被视为尚未完全闭环。
从 Codex 学到的
- 每一步都要有「产出物」才算发生。Codex 的 15 步里每一步都定义了具体产出——上下文包、已校验调用、Observation、验收证据。审计自己的 harness 时,如果某一步说不出产出物是什么,这一步很可能只是「模型的感觉」而不是受控的系统行为。
- 「想调用」和「允许执行」之间要有确定性边界。LLM 的决策是概率性的候选动作,工具校验、权限门控是确定性的闸门。把这两层混在一起,等于让模型的幻觉直接写入现实世界。
- 停止条件属于系统,不属于模型。
继续/等待输入/阻塞/完成由完成标准和运行边界决定。一个说「我觉得做完了」就停的 agent,会在最难的任务上恰恰停在交付之前。 - 交付链的每一环都要能被证据替换。「用户目标 → 验收标准 → 文件变更 → 执行结果 → 验证证据 → 最终答复」这条链上,任何一环只能用下一环的证据支撑,不能用上一环的声明支撑。
说明边界
本文描述的是 Codex 类 Agent 的稳定职责模型,适用于 CLI、桌面端和云端任务的共同交互逻辑。不同版本在工具名称、权限策略、上下文压缩算法、记忆范围和日志实现上可能不同。
「思考」在这里指可解释的任务判断,例如计划、假设、依据和下一步,不代表公开模型逐字的私有思维链。对用户真正有价值且可验证的,是决策结论、执行动作、外部观察和验证证据。
延伸阅读
- 核心组件:智能体循环——「决策 → 行动 → 观察」循环的一般形式
- 核心组件:上下文工程——第 3 步「组装上下文」与第 11 步「压缩与记忆」的完整展开
- 核心组件:权限、安全与人类在环——第 6、7 步的确定性边界如何设计
- 核心组件:评测与可观测性——第 12、15 步的验证与审计如何落地
- 案例:Claude Code——同一职责模型在另一个产品里的具体实现:工具调用 + 权限门控 + 子代理
- 案例:Aider——把「编辑落盘」这一个环节做到极致的对照样本