← 全部文章← All writing

智能体

精读 Codex Harness:从 Agent Loop 到能长期工作的 Agent

一个模型可以很聪明,但一个 Agent 要在真实环境里连续工作几十分钟、几小时,甚至跨越好几个上下文窗口,光靠模型「想得对」是不够的。它还得能运行、暂停、观察、恢复、协调、验证和记忆。这些事不归模型管,归 Harness 管。

这篇是我和 ChatGPT 在一场很长的技术讨论里一起写出来的。起点是读 Codex 开源的 Harness,后来越聊越深,变成一个更大的问题:如果把 Agent 当成能独立承担工作的数字团队,该怎么给它设计制度、知识体系和运行系统?

先说清楚证据等级。文中的内容分三种:源码可证的,是 2026 年 10 月能检索到的 Codex 开源文件和 OpenAI 官方工程文章里写明的;架构抽象,是为了讲清楚而画的逻辑图,不是源码依赖图;设计提议,是我们在讨论里推出来的方案,Codex 并没有原生实现。三种我会逐处标明。代码会更新,具体实现以当时版本的仓库为准。

如果只记一句话:LLM 提供开放性智能,Harness 提供可执行的运行结构,两者组合起来,才是真正可用的 Agent。

Harness 不是一段 Prompt

Harness 是模型和外部世界之间的执行、状态与控制系统。

假设用户说:「找出登录模块的 Bug,修好它,并保证测试通过。」LLM 能判断该看哪个文件、生成补丁、解释错误;但读文件、起进程、批权限、写日志、处理中断,都是运行时在做。

系统核心骨架架构抽象
用户需求「修复登录 Bug,并运行测试」
↓
Harness · 控制与执行层线程 · 上下文 · 权限 · 工具 · 状态 · 事件
⇅
LLM · 推理引擎分析、计划、提出工具调用
本地世界 · 执行环境Shell、文件、Git、测试、MCP
LLM 和工具之间不是一次性的:Harness 会多次把工具结果交回模型,形成反馈循环。

Harness 里其实有两条运行路径:

  • 模型驱动:需要语义判断时,Harness 调 LLM,让它提出工具调用或给出最终答复。比如模型输出 read_file("login.ts"),Harness 验权、读取、把文本交回去。
  • 程序驱动:用户点了停止、检测到超时、要限制并发、要把状态写进 SQLite,Harness 直接跑写好的程序逻辑,不需要再问 LLM。比如收到中断事件,用 CancellationToken 取消任务并清理,这一步不产生任何模型调用。

所以准确的说法是:Harness 是一个嵌入了 LLM 的软件系统,而不是一个所有代码都由 LLM 临时生成的系统。这是我们讨论里反复确认的一条。

「Agent Runtime」和「Agent Harness」有大量重叠。可以把 Runtime 看作偏执行的内核,Harness 看作包含执行、上下文、工具、安全与协作的整体;这只是方便设计的区分,业界没有统一定义。OpenAI 官方对 Harness 的描述也是围绕 Agent Loop 及相关逻辑展开的 [1]。

Codex 是怎么拆开的

Codex CLI 的 Rust workspace 有很多 crate,数量随主分支变化,不值得死记。值得记的是几条关键边界:codex-core 承载主要的 Agent 逻辑,codex-protocol 定义内部数据协议,codex-api 对接模型,codex-app-server 给客户端提供交互协议,另外还有持久化、TUI、沙箱和补丁等组件 [2]。

从交互界面到执行内核逻辑分层 · 非依赖图
CLI / TUI终端入口
IDE编辑器交互
Desktop / Web图形产品形态
⇅ JSON-RPC
codex-app-server线程管理 · 事件翻译 · 客户端协议
⇅
codex-coreSession · run_turn · Context · Tool Dispatch · Memory 编排
⇅
codex-api模型调用接口
codex-protocol内部 Op / Event 类型
State / Rollout本地状态与事件历史
为讲清概念画的逻辑分层。实际源码依赖更复杂,不同客户端的接入路径也在演进。

一个系统既有桌面端又有 CLI 时,最不该做的就是给每个界面各写一套 Agent Loop。App Server 把底层能力用双向 JSON-RPC 暴露出去:一条客户端请求可以对应很多条流式事件,服务端还能反过来向客户端请求审批。这样「界面」和「Agent 的执行生命周期」就分开了 [1]。

还有个很 Unix 的细节:codex-arg0。程序根据启动时用的可执行文件名进入不同入口,比如 apply_patch 和 Linux 沙箱 helper 会被分派到不同逻辑 [3]。它不是在「告诉 LLM 自己是谁」,而是在进程层面做多入口复用。

Task、Turn、Sampling:一条指令怎么变成几十步工作

用户只提一次需求,Codex 会持续请求模型、执行工具、观察结果,直到这一轮结束。

常见的说法是 Task Loop → Turn Loop → Sampling Loop,方便建立直觉,但源码里并不是三个同名的 while 循环整齐嵌套。更准确地说:Session 的 submission loop 负责分发操作;SessionTask 管任务的生命周期;run_turn 在一轮之内连续发起模型采样请求 [4]。

概念 回答的问题 主要由谁控制
Session / Task 任务何时开始、被取消、结束? Harness 的任务机制
Turn 这一轮还要不要继续? 模型信号 + Harness 状态
Sampling 这次模型请求产生了哪些响应项? 模型生成,Harness 接收处理
Tool Call 执行哪个工具、什么参数? 模型提议,Harness 执行

拿修登录 Bug 举例,一个 Turn 里可能发生这样五次 Sampling(演示轨迹,真实的 Codex 不会预先定好次数):

Sampling 发生了什么 模型的输出
01 分析请求 模型首次拿到任务和项目环境,决定先读登录模块 read_file("src/login.ts")
02 定位问题 Harness 把文件内容交回,模型发现验证流程漏了一个异常分支 read_file("src/auth.ts")
03 生成修复 模型基于新信息提出补丁,Harness 检查作用范围并应用 apply_patch({ ... })
04 跑测试 Harness 执行测试、收集退出码和日志,模型下一次会看到真实反馈 run("npm test -- login")
05 结束 测试通过且没有待处理指令,模型给出最终答复,Harness 结束这个 Turn Final Message · turn/completed

用伪代码写出来是这样(不是 Codex 原样代码):

async fn run_turn() {
    loop {
        let context = build_model_input();
        let result = run_sampling_request(context).await;
        if result.has_tool_calls() {
            execute_and_record(result.tool_calls()).await;
        }
        if !needs_follow_up(result, pending_input) {
            break;
        }
    }
}

Sampling 的次数谁决定?不是主 Agent 事先分配「十次」,也不是用户指定。LLM 在一次请求里产生工具调用等响应,Harness 执行后,根据 needs_follow_up、待处理输入、Hook 和停止条件,动态决定要不要再请求一次 [4]。

还有一点:Task 结束不等于任务成功。模型说「我修好了」和测试真的通过,是两件事。后者需要 Harness 去读外部的验证证据。在高可靠系统里,测试、验收和资源预算都应该独立于模型的自我声明。

并行执行,有序收集

一次 Sampling 里,模型可以给出多个 Tool Call。能不能并发,取决于工具和执行环境,而不是「每个调用都要再 Sampling 一次」。

模型一次要读三份互不相关的文件,可以同时执行;两个调用都要改同一条数据库记录,就得小心。Codex 的工具运行时用读写锁等机制,区分可以并发的工具和需要独占执行的工具 [5]。

三个工具的并发时间线示意耗时
Tool A · 读文件 · 3s
A
Tool B · 读日志 · 1s
B
Tool C · 读配置 · 2s
C
实际完成顺序 B → C → A · 交回模型的顺序 A → B → C
耗时是虚构的。Codex 用 FuturesOrdered 等结构保持工具请求的原始顺序,避免完成时间打乱结果;这不代表所有工具都会无条件并行。

这里顺便把 Tool 和 Skill 分开:Tool 是已经实现好的执行能力,比如读文件、跑 Shell;Skill 更像工作手册,告诉模型按什么流程使用现有工具,有时附带脚本、模板和参考资料。

一个很有用的分界:LLM 可能输出 read_file(path="login.ts") 这样的结构化调用,根本不需要写代码;也可能生成一条 Shell 命令,交给 Harness 已有的命令执行工具。至于取消任务、写日志、处理超时,通常是 Harness 自己的 Rust 程序在做。

Op / Event:客户端怎么看见 Agent 在干什么

Codex 不会等任务全部做完,才把一个巨大的结果一次性返回。客户端给 Harness 发操作(Operation),Harness 持续给客户端发事件(Event)。

要注意有两层协议:Rust core 内部的 Op / Event 类型,和 App Server 对外的 JSON-RPC 方法、通知。两者语义相关,但不是同一套名字 [1]。

双向协议与流式事件Client ↔ App Server ↔ Core
Desktop输入、停止、审批
App ServerJSON-RPC 翻译层
Codex Core运行 Agent Loop
↕ 请求 / 通知
turn/startitem/startedoutputDeltaturn/completed
一条请求可能产生大量通知,但事件数不等于 Sampling 次数:命令输出可以一行行流式推送,不需要每行都重新调用模型。

事件驱动也是 Multi-Agent 协作的基础。子任务完成、工具失败、审批请求、用户中断,这些变化先被运行时接收、记录、分发;只有需要重新判断时,才触发新的模型推理。

SubAgent:什么时候拆,什么时候等

Sampling 是同一个 Agent 内部的推理迭代;SubAgent 是另一个有独立上下文和工作流程的执行者。并行不等于一定更快。

假设一个网站要做数据库、后端、前端三块。全交给主 Agent 串行做很直观,但慢;拆给三个 SubAgent 可以并行,但接口约定和写入范围必须先说清楚。关键不是先决定「开三个 Agent」,而是先看任务的依赖 DAG 和共享资源冲突。

主 Agent 与 SubAgent 的工作拓扑架构抽象
主 Agent · 项目经理拆解任务 · 定义接口 · 决定何时协调、何时汇合
↙ ↓ ↘
Sub A数据库 / Schema
Sub B后端 / API
Sub C前端 / UI
↓ Mailbox / Join ↓
主 Agent 汇合验证处理依赖变更 · 集成与测试
把主 Agent 类比成项目经理,而不是能改所有制度的 CEO,这是我们的解释,不是 Codex 官方的定义。

Codex 的 Multi-Agent 规格里有 spawn_agent、send_message、followup_task、wait_agent、interrupt_agent 等工具。值得注意的是,V2 的 wait_agent 可以等 Mailbox 更新、用户新输入或超时,并不一定要等所有子 Agent 做完 [6]。

情况 建议机制 为什么
A、B 各自独立读日志 并行 没有写冲突,可以同时推进
后端依赖还没定的 Schema 先定契约再并行 避免基于过时结构返工
三个 Agent 都要改同一个文件 协调写入范围 避免补丁互相覆盖、更新丢失
主 Agent 没有独立工作,又依赖结果 wait_agent 不需要靠 LLM 反复轮询「做完了吗」

Mailbox 是协作机制,不是共享大脑。send_message 发消息但不主动触发新 Turn;followup_task 在目标空闲时可以触发后续 Turn。这让主 Agent 可以暂停自己的 Sampling,而 SubAgent 继续干活 [6]。

子 Agent 继承多少上下文,背后的抽象是上下文隔离:完整继承便于接着做,但有成本和污染;少继承利于聚焦;从空白上下文启动最干净,但需要更完整的任务描述。具体选项和默认行为以当时版本的工具协议为准,不要把某一版的「三档继承」当成稳定的官方接口。

我们得出的项目管理原则:独立任务并行,强依赖任务串行;主 Agent 有关键路径上的活就继续做,没活可做才等待。子 Agent 发现会影响别人的异常,先报告主 Agent,由它决定是否重新分配;运行时负责传消息和执行中断。

World State:让模型知道世界变了什么

几十次 Sampling 之间,AGENTS.md、权限和环境配置都可能变。怎么把变化同步给模型,又尽量不破坏已有上下文?

Codex 的 WorldStateSection 给不同类型的模型可见状态定义了稳定 ID、Snapshot 和 render_diff。Harness 可以对照上一次快照,判断哪些内容需要更新 [7]。这里的 diff 是结构化的状态比较和差异化呈现,不等于所有变化都只给模型发几行文本。

状态同步:修改 AGENTS.md基于源码的语义演示
上一次快照 · Section: agents_md规则:不允许修改生产数据库 目录:/app
↓ 本次模型可见的更新
规则没变No update,Snapshot 未变,保留先前内容。不重复发送相同指令。
规则变了Replacement notice:旧规则不再适用,附上更新后的完整规则(允许在测试环境改配置,仍禁止修改生产数据库)。
对 AGENTS.md 来说,当前实现在变更时会生成替换通知和更新后的完整文本,而不只是行级补丁 [13]。

为什么这和 Prefix KV Cache 有关

两次模型输入开头的 token 序列完全一样时,服务端有机会复用之前算过的前缀 KV Cache。保留旧消息、在尾部追加新信息,通常比反复改开头更容易命中缓存;但前缀相同只是必要条件,不保证一定命中 [8]。

相同前缀如何跨 Sampling 复用示意 · 不按比例
Sampling 01
稳定指令环境用户任务
Sampling 02 · 只在尾部追加
稳定指令环境用户任务新内容
如果前面的环境信息被改写
稳定指令改写后续不匹配新
蓝色是可复用的前缀。KV Cache 省的是重复计算,那些历史 token 照样占着上下文窗口。

所以:World State 优化状态同步,Prompt Caching 优化重复前缀的计算,Compaction 解决上下文增长。三者不是替代关系。

上下文满了之后怎么办

这是我觉得整套设计里最有意思的部分:从「压缩过去」慢慢转向「保存状态,重新组织工作集」。

第一条路:交接式 Compaction

Codex 的压缩提示词要求模型为接手任务的另一个 LLM 写一份交接摘要:保留进度、关键决策、约束、剩余步骤和重要引用 [9]。它要回答的不是「之前聊了什么」,而是「下一位执行者怎么接着干」。

区别很具体。「数据库模块基本完成」是摘要;「改完了哪些文件、哪条测试失败、失败原因是什么、哪些用户约束不能违反」才是能接着干的交接。两者都可能丢细节,但交接至少明确地为「任务恢复」这个目标做了优化。

第二条路:Token Budget,直接开新窗口

Codex 的 compact_token_budget.rs 里明确有这样一种模式:跳过模型或服务端的摘要压缩,调用 start_new_context_window 装一个新窗口。它仍然挂在 Compaction 的生命周期里,好复用 Hook 和事件机制;这不代表传统 Compaction 被取消了 [10]。

两种跨窗口方案两种策略,不是每个会话都同时启用
传统 Compaction
Window 01 · 长历史对话 + 工具输出 + 失败日志
↓ LLM 交接摘要
Window 02 · 摘要 + 新输入上下文变短,重要细节可能丢
新窗口 + 外部记忆
Window 01 · 历史与当前工作执行中维护 Notes,保留 History
↓ 新上下文窗口
Thread Hint短提示
Notes任务笔记
History原始证据
↓ 按需读取
Window 02 · 新工作集不搬全部历史,靠恢复提示和外部检索

三种材料各管一件事:Thread Hint 是简短的恢复线索;Notes 是模型整理的当前任务笔记(进度、决策、待办);History 是可追溯的原始会话和工具记录。在相关扩展可用的场景里,新窗口先拿到恢复提示,再按需读细节。具体接口、启用条件和保留策略因版本和配置而异,不能假设每个 Codex 产品形态都有同样的工具。

这套方案最重要的限制也在这里:「历史还在」不等于「模型会想起来」。模型没写进 Notes、也不知道该去 History 搜什么,信息虽然还在,却恢复不了。问题从存储变成了检索决策。

上下文容量的问题可以靠外部存储缓解;但「什么时候该想起去找什么」,仍然是智能系统的核心问题。

长期记忆:不只记住对话,还要沉淀经验

Codex Memories 的 README 介绍了一个两阶段机制:符合条件的根 Session 启动时,后台异步触发记忆任务。Phase 1 读取符合条件的 Rollout,让模型提取 raw_memory 和 rollout_summary,存进状态数据库;Phase 2 在全局锁保护下挑选候选、整理文件工作区,再由专门的 Consolidation Agent 产出更高密度的长期记忆 [11]。

Codex 两阶段 Memory 流水线基于源码的简化
Rollouts历史会话 + 工具记录
Phase 1模型提取 · 存进 DB · 可受限并发
Phase 2筛选 · 巩固 · 输出文件 · 串行
↓
memory_summary.md高密度导航
MEMORY.md合并后的经验
Skills / Summaries具体流程与出处
目录与流程以公开 README 为准。

SQLite、Markdown 和 KG 是什么关系

要分清「记录的格式」和「查询的数据库」。Codex 的记忆不是全存成 Markdown:状态数据库承载结构化的任务记录和元数据,Rollout 历史有自己的持久化格式,MEMORY.md 这类文件放对 LLM 友好的长文知识。Markdown 是表达层,SQLite 是嵌入式关系数据库,Neo4j 是图数据库。KG 是一种知识模型,并不要求一定用 Neo4j。

存储 适合放什么
SQLite · 本地状态 单用户、本地运行时的状态索引、元数据与事务;嵌在应用里,不需要独立数据库服务
Markdown / 文件 · 叙述记忆 复杂解释、工作交接、可复用流程,人能读的 Notes 和 Skills
PostgreSQL · 共享服务(提议) 服务端共享的 Agent 平台:事务、版本、集中权限、并发写入;KG 也可以用关系表表示
Neo4j · 图(可选) 多跳关系、路径查询、复杂图遍历真正成为核心需求时再评估

不是「一百万用户就必须上 PostgreSQL」。一百万个用户各自在本地跑独立的 Agent,每人一个 SQLite,完全合理。真正决定选型的,是状态要不要集中共享、跨机器访问、高并发更新。

长期记忆该知道什么

三类:稳定的用户和项目偏好、能复用的任务经验、已经验证过的环境事实。但每一条都要带有效范围:适用于哪个项目、哪个环境、什么时间、什么版本。一次探索里的猜测,不能自动升级成组织制度。

Codex 的巩固过程会参考 usage_count、last_usage 和生成时间等信号,也允许把稳定的重复流程进一步提炼成 Skill [11][12]。但要记住:被反复使用,不等于被证明正确。

让 KG、RAG、上下文窗口和 Agent Loop 接起来

这四个概念不是四个互相竞争的产品,而是四段各司其职的数据流。知识图谱维护实体和关系;RAG 是检索增强的过程,可以查文档、向量、结构化数据库或图;上下文窗口是当前这次模型请求能直接看到的工作集;Agent Loop 决定什么时候问模型下一步。把它们接起来的,是 Harness 里的检索策略和上下文构建器。

Memory-aware Agent Runtime设计提议 · 非 Codex 内置
Harness / Agent RuntimeTask 与 Agent Loop · 检索策略 · Context Builder · 记忆写入与校验
↕ 按事件触发 / 模型主动调用
检索层(RAG)全文 / 向量搜索 · KG 关系查询 · 来源与版本过滤
↕
Notes工作经验与复杂语义
Knowledge Graph事实、关系与依赖
History事件、原始证据
↓ Context Builder
LLM 上下文窗口固定规则 + 当前任务 · 精选相关知识 · 最近执行历史 · 可用工具定义
这是我们讨论出的目标架构,Codex 并没有原生集成完整的 KG + GraphRAG。

不是每次 Sampling 都要全量 RAG

常见误区是「每次调模型之前,先把整个知识库检索一遍」。这会带来额外的延迟、成本和上下文噪声。我们更倾向三种触发方式:

触发方式 什么时候检索 谁决定
强制检索 新任务、关键约束、跨窗口恢复 Harness 的确定性规则
事件驱动 Schema 变化、依赖过期、测试失败 状态事件 + 查询策略
Agent 驱动 模型碰到未知问题,需要更多背景 LLM 自己调用检索工具

放到一个任务的不同阶段:

阶段 检索怎么做
开始规划 强制检索:读取生效的规则、任务目标、关键实体关系和当前依赖;未知部分让 LLM 自己探索
持续执行 跳过或选择性检索:World State 和相关版本没变就不重复检索,只在出现新文件、Schema 变更等事件时针对性查询
测试失败 事件驱动 + Agent 驱动:优先查错误码、相关的历史失败和版本变化;模型认为还缺信息,再自己扩大范围
跨窗口恢复 强制的检查点检索:必须恢复任务目标、最新决策、关键约束、待办和必要的证据索引,再按需查 Notes / History

KG 怎么参与调度

假设 KG 里记着:Backend API 依赖 Schema v1。Agent A 把 Schema 升到 v2,Harness 可以查依赖链,生成一个 DEPENDENCY_OUTDATED 事件交给主 Agent 处理。这时 KG 就不只是回答问题的资料库,而是依赖检查和任务协调的事实依据;前提是关系、版本和来源都经过验证。

MATCH (service)-[dep:DEPENDS_ON]->(schema)
WHERE schema.id = 'schema_v1'
  AND dep.status = 'active'
RETURN service.id, dep.valid_from, dep.evidence_id;

这里有个很重要的认识:Agent 的检索应该感知状态,而不只是感知问题。模型此刻是在规划、修错、整合子任务还是恢复上下文,决定了它该召回哪一类信息。

记忆不只是保存,还要有事实、证据和版本

「Agent 说过什么」和「组织现在确认什么是真的」,绝不能混为一谈。

设想 Agent A 从 staging.yaml 读到最大连接数是 100,Agent B 在生产实例上查到是 200。两者未必冲突,可能只是环境不同。就算都来自生产环境,也要分清配置文件里的声明值和数据库正在生效的运行值。

从候选知识到权威事实设计提议 · 写入流水线
ObservationAgent / 工具提供候选事实与证据
↓
Proposal · 待验证实体、属性、环境、来源、观察时间
↓ 验证 · 解决冲突
Verified Fact · 权威层版本、有效期、关联证据、依赖关系
↓ 变更通知
依赖它的任务 / Agent收到通知、检查兼容性,必要时重新协调

我们建议把知识状态拆开:proposed、verified、conflicted、superseded、invalidated。还要区分 observed_at(什么时候观察到)和 valid_from(事实从什么时候生效)。一条事实被频繁引用,不会因此自动变成对的。

{
  "entity": "production.db.connection_pool",
  "attribute": "max_connections",
  "value": 200,
  "status": "verified",
  "observed_at": "2026-10-27T10:02:00+08:00",
  "valid_from": null,
  "evidence": "sql-query/run-182",
  "version": 2
}

这里涉及两种不同的「一致性」:数据库事务保证并发写入不互相覆盖;证据验证保证写进去的事实可信。前者可以用 SQLite / PostgreSQL 的事务、版本号、乐观锁解决;后者不是 ACID 能自动解决的。

所以我们选的是混合记忆,一条知识有三种视图:

视图 回答的问题 放什么
叙述 Notes 当时为什么这么决定? 复杂语义、背景与经验
结构化 KG 现在什么成立? 实体、关系、时间和版本
事件日志 究竟发生了什么? 不能被静默覆盖的证据

自然语言 Notes 不需要全转成三元组;KG 也不该靠 Markdown 去模拟关系数据库。让自然语言保留复杂语义,让结构化事实支撑检索、验证和依赖查询,让原始事件负责追溯。

Harness 像制度,主 Agent 像项目经理

一个好的 Agent 系统和一个运作良好的远程团队,解决的是同一个组织问题:自治和协调怎么同时成立?

我们聊过远程办公团队的例子。如果所有人完全自由安排时间、没有任何规矩,协作会乱;如果把每个人的每一分钟都规定死,又会扼杀自主性。更好的方式是保留较大的个人自由,同时明确少量必须协同的规则,比如每天四小时的核心协作时间。

放到 Agent 世界里,可以把固定时间的协调换成事件驱动的检查点:任务开始时对齐目标;依赖变化时通知;关键操作前检查权限;跨窗口时保存进度;完成时验收结果。

Agent 组织的治理层级设计提议
治理 / 制度权限、资源、边界、验收标准 · 高于具体 Agent
↓
主 Agent · 项目经理项目计划、依赖、委派、冲突协调、交付
↓
SubAgent A独立任务
SubAgent B独立任务
SubAgent C独立任务
⇅
共享记忆 / 能力注册表Notes + KG + 证据 + Skills + Tools
治理权、任务调度权和知识管理权相互分离。

强约束边界,弱约束过程,强验证结果。

外部知识不能自动获得指令权限

RAG 和外部文档里的 Prompt Injection 也是要面对的:一份恶意文档可能假装拥有更高优先级,诱导模型调用危险工具。可信的 Harness 不能只靠「请忽略恶意指令」来防御,而要把外部知识和控制策略隔离开,由真实的权限、工具授权和沙箱在执行的位置施加硬约束。

这也影响记忆:从不可信文档里提取的内容可以记成候选事实,但不能自动变成公司的安全制度,更不能修改 Harness 的执行权限。一句话:不可信的知识可以参与决策,但不能授予权限。

从 Memory 到 Skill 再到 Tool:Agent 怎么真正积累能力

一套系统的进步,不该只是「记住上次干过什么」,还应该减少下一次的重复推理。

假设 Coding Agent 每周都要检查一次数据库 Schema 兼容性。第一次,它可能临时写个 Python 比较脚本、读日志、生成报告;下一次又重复差不多的步骤。随着流程被证明可靠,可以先把它沉淀成工作步骤,再把其中确定的部分固化成可复用的代码。

能力沉淀:知识 → 流程 → 程序设计提议
Memory经验 / 事实 / 教训
Skill可复用流程 / 检查表
Tool已验证的程序能力
↓
评测与能力注册表基准测试 · 适用范围 · 版本 · 失败回滚
不是所有 Memory 都该变成 Skill,也不是所有 Skill 都该变成 Tool。箭头是可能的演化路径,不是自动晋升链。

Codex 的记忆巩固提示词里已经有「把有价值的、重复出现的流程提炼成 Skill」的指导 [12]。但「模型写了一个 Skill」不等于「系统安全地部署了一个新工具」:新 Tool 涉及代码测试、注册、授权、版本控制和可回滚的发布。

情况 更合适的形态
某个项目的偏好、重要业务约束 Memory / KG
需要 LLM 参与判断的多步流程 Skill / SOP
稳定的输入输出转换、格式校验 已验证的 Tool
经常变化、还没有成熟流程的问题 继续交给前沿大模型探索

这正好对上我一直认的「异构智能」分工:确定的世界交给传统程序,已经定义清楚的概率判断交给专门的决策模型,开放世界的问题交给前沿大模型;Harness 管它们之间的协作和边界。

把所有组件放进一张图

这不是 Codex 官方的「终极架构」,而是我们从源码出发、结合组织管理和知识治理推出来的一张蓝图。

Agent Operating System · 概念蓝图设计提议
控制平面 · 治理Policy · Auth · Budget · Approval · Verification
↓
编排主 Agent · Task DAG · Event Bus · Mailbox
执行运行时Task / Turn / Sampling · Tools · Sandbox
⇅
上下文工程World State · 检索门控 · Context Builder · Compaction
⇅
叙述Notes · Skills
知识KG · 有效期
证据Event Log · History
⇅
模型生态前沿大模型 · 决策模型 · 确定性程序
箭头表示信息和控制关系,不是程序调用图。可信的控制平面不允许普通检索内容直接修改规则。

如果从零实现,最小可行版要什么

第一版不需要五种数据库,也不需要一上来就做复杂的 GraphRAG。先把 Agent Loop、事件日志、明确的权限、状态管理和可恢复的任务检查点做可靠;记忆用 Markdown + SQLite 就能起步。真出现共享访问和复杂事务需求,再考虑 PostgreSQL;复杂多跳图查询真成了瓶颈,再评估 Neo4j。

  • 可取消的 Task 与持续的 Turn
  • 工具调用与结果留痕
  • 上下文增量更新与压缩恢复
  • 可复用的 Notes 与历史索引
  • 规则和知识之间的信任边界
  • 清晰的任务验收与测试

衡量它有没有变好,不是看模型的回答是否更流畅,而是看任务最终成功率、关键约束遗漏率、记忆召回质量、重复工作比例、延迟和实际成本。这是下一阶段真正值得做的实验。

未来的 Agent 可能更像一个不断学习的组织:有自己的工作记忆、组织知识、工具平台和协作纪律;有时要自主探索,有时必须守规矩。最好的 Harness 不会替模型想所有事,而是让智能在清晰的边界里持续产生价值。

速查表

术语 一句话定义
Harness 承载模型、工具、状态、控制和执行逻辑的整体系统
Agent Runtime 偏执行引擎的叫法,现实中和 Harness 高度重叠
Task / Session 任务的生命周期,以及它所在的会话环境
Turn 由一次用户请求驱动的一段连续 Agent 工作
Sampling 对模型的一次生成请求,以及对响应的处理
Tool Call 模型提出的调用请求,由 Harness 的工具实现执行
SubAgent 有独立执行上下文、可以被委派任务的 Agent
Mailbox Agent 之间异步通信与唤醒的机制
World State 模型需要知道的、类型化的运行环境状态及其更新
KV Cache 对相同输入前缀复用的注意力中间状态
Compaction 通过缩减或重建上下文,让任务继续跑下去
Thread Hint 跨窗口继续任务的简短恢复线索
Notes / History 可读的工作笔记 / 可追溯的执行记录
KG / RAG 结构化的知识网络 / 从各种来源检索来增强模型的过程
Memory Consolidation 把分散的会话记忆去重、更新、巩固成长期知识
Skill / Tool 复用工作方法 / 复用已实现的程序能力

参考资料

本文是围绕开源技术的独立解读,不代表 OpenAI 官方的产品文档。文中的图都是重新绘制的,代码只用了简化片段或伪代码。源码更新后,以对应版本的仓库为准。

  1. OpenAI · Unlocking the Codex harness: how we built the App Server
  2. Codex Rust workspace · Cargo.toml
  3. codex-arg0 · 多入口执行分派
  4. codex-core · run_turn 与 sampling 逻辑
  5. codex-core · 工具调用的并行执行
  6. Codex · Multi-Agent 工具与协作说明
  7. World State · Section / Snapshot / Diff
  8. OpenAI API · Prompt Caching
  9. Codex · Compaction 交接提示词
  10. Codex · Token Budget 新窗口实现
  11. Codex Memories · Phase 1 / Phase 2 说明
  12. Codex · Memory Consolidation 提示词
  13. World State · AGENTS.md 状态更新
  14. OpenAI · Unrolling the Codex agent loop
  15. OpenAI Developers · Codex as a platform