从能跑到可用:一个 Agent Harness 还差哪些工程闭环?

从零实现 Agent Harness 系列 · 第 09 篇

这一篇做一次阶段性架构回看。

前八篇讲的是 Agent Harness 如何跑起来;这一篇讲的是它要走向产品级,还需要补上哪些工程闭环。

真正可用的 Agent,不只是能调用工具、能完成任务,还要能隔离执行、恢复失败、控制权限、记录状态,并让用户放心交给它长期运行。

前言

到第八篇为止,这个系列已经把一个教学版 Agent Harness 的主线基本走完了。

前八篇分别解决的是这些问题:

主题 解决的问题
01 Loop 与工具调用 Agent 怎么从"想"变成"做"?
02 Plan / TodoWrite 复杂任务怎么变得可见、可控、可恢复?
03 Hooks 与权限 工具执行怎么拦截、确认和审计?
04 Skills 专业能力怎么按需加载?
05 Context / Memory 上下文快满了怎么办?
06 长任务系统 跨轮、后台、定时任务怎么托管?
07 Multi-Agent 多个 Agent 怎么分工并收敛?
08 MCP 外部能力怎么标准化接入?

这些内容连起来,回答的是:

text 复制代码
Agent Harness 的关键机制是什么

也就是:

  • Agent Loop 到底是什么
  • 工具怎么接
  • Plan 为什么重要
  • 长上下文怎么处理
  • MCP 怎么接进来

这些问题解决的是:

text 复制代码
Agent 为什么能工作

这一篇不再新增一个单点组件。

它要做的是阶段性回看:把前八篇的机制放到一个更完整的产品级架构里,看看真正可用的 Agent 还差哪些工程层。

因为产品级 Agent 关注的,往往是另一组问题:

  • 出错以后怎么办
  • 怎么防止误操作
  • 多个任务怎么隔离
  • 提示词怎么分层
  • 状态怎么恢复
  • 怎么观测和审计

也就是说,教学版强调的是:

text 复制代码
机制清楚

产品级更强调的是:

text 复制代码
系统可靠

所以这篇文章想回答的问题是:

从"一个能讲明白原理的 Agent Harness",到"一个真正能放心交给用户的 Agent",中间还需要补哪些层?

这也是这一篇的定位:

text 复制代码
前八篇讲 Agent Harness 的机制
这一篇讲产品级 Agent 的工程闭环

一、System Prompt:从一段说明书到运行时上下文骨架

很多教学版代码里,system prompt 都比较短。

这很好,因为主线清楚。

但一到真实系统,prompt 通常要开始分层。

1. 稳定规则层

比如:

  • 你是谁
  • 你的边界是什么
  • 哪些事必须遵守
  • 哪些行为永远不能做

这一层最好稳定,不应该每一轮都被临时状态挤来挤去。

2. 动态能力索引层

比如:

  • 当前有哪些 skills
  • 当前有哪些 tools
  • 当前连接了哪些 MCP server
  • 当前哪些能力需要审批

这一层会随着运行时变化。

它不是"世界知识",而是 Agent 当前能使用的能力菜单。

3. 运行时状态层

比如:

  • 当前任务目标
  • 当前 todo
  • 当前约束
  • 当前环境信息
  • 最近一次失败和恢复策略

这一层最容易变化,也最容易膨胀。

如果这三层全糊在一起,prompt 会越来越难维护。更麻烦的是,一旦系统出了问题,你很难判断到底是规则不清、能力暴露错了,还是状态注入乱了。

所以产品级 Agent 很重要的一步,是把 prompt 从"一整段文字"升级成"运行时装配出来的上下文骨架"。

可以把它理解成:

text 复制代码
System Prompt = 稳定规则 + 动态能力 + 运行时状态

这不是为了把 prompt 写得更长,而是为了让每一部分都有清楚的来源、生命周期和优先级。

4. 现实产品里的例子:Claude Code 和 Codex

这里可以看两个真实产品的公开设计。

注意,我们不是在说它们内部真正的 system prompt 长什么样。那通常不是公开信息。

但从公开文档里,已经能看到一个很清楚的趋势:

text 复制代码
产品级 Agent 不会只靠一段 system prompt 管住所有行为
而是把规则、项目上下文、工具能力、权限策略、运行环境分层管理

Claude Code:把项目上下文和系统约束拆开

先看 Claude Code。

Claude Code 里有 CLAUDE.md、memory、skills、hooks、permissions 这些机制。它们看起来都在"影响 Agent 行为",但位置并不一样。

可以用一个具体例子理解。

假设一个项目里希望 Agent 遵守这些规则:

  • 这个仓库用 pnpm,不要用 npm
  • 修改后要跑 pnpm test
  • 不要直接提交 commit
  • 不允许执行 rm -rf 这类危险命令
  • 遇到数据库迁移要先让用户确认

如果全部写进 system prompt,大概会变成一段越来越长的说明书:

text 复制代码
你在这个项目里工作。
请使用 pnpm。
改完请运行 pnpm test。
不要提交 commit。
不要执行危险删除命令。
数据库迁移前请确认。
......

这能起作用,但问题是:它把三类完全不同的东西混在了一起。

第一类是项目知识。

比如:

text 复制代码
这个项目用 pnpm
测试命令是 pnpm test
代码风格遵循某个约定

这类信息适合放在 CLAUDE.md 或 memory 里,作为项目级上下文,让模型在推理时知道"这个仓库怎么工作"。

一个简单的 CLAUDE.md 可以这样写:

markdown 复制代码
# CLAUDE.md

## Project conventions

- Use `pnpm`, not `npm`.
- Run `pnpm test` after changing application code.
- Run `pnpm lint` before summarizing code changes.
- Frontend code lives under `apps/web/`.
- Do not edit `db/migrations/` unless the user explicitly asks.

## Workflow

- Explain any risky change before making it.
- Prefer small focused diffs.
- When tests fail, summarize the failing command and the suspected cause.

它看起来像 prompt,但定位不一样。

CLAUDE.md 是项目上下文:告诉 Claude Code 这个仓库怎么构建、怎么测试、有哪些约定。它会影响模型判断,但它不是强制执行层。

Claude Code 的文档里还区分了不同作用域:

text 复制代码
~/.claude/CLAUDE.md        # 个人偏好,所有项目通用
./CLAUDE.md                # 项目级说明,适合提交到仓库
./.claude/CLAUDE.md        # 另一种项目级位置
./CLAUDE.local.md          # 个人本地说明,通常不提交

在大仓库里,还可以用 .claude/rules/ 拆规则。

比如:

text 复制代码
.claude/rules/testing.md
.claude/rules/security.md
.claude/rules/frontend/react.md

还可以做路径级规则:

markdown 复制代码
---
paths:
  - "src/api/**/*.ts"
---

# API rules

- All API handlers must validate input.
- Use the standard error response format.
- Include OpenAPI comments for public endpoints.

这就解决了一个很实际的问题:不是所有规则都应该每轮加载,也不是所有规则都适用于整个仓库。

放在 Harness 的视角里,它对应的是:

text 复制代码
全局偏好 -> user context
项目约定 -> project context
路径规则 -> scoped context
当前任务 -> runtime state

第二类是可复用能力。

比如:

text 复制代码
怎么做 code review
怎么整理 release note
怎么检查一个 PR

这类更适合做成 skills。模型需要时再加载,不必每一轮都塞进 system prompt。

第三类是硬约束。

比如:

text 复制代码
不能执行危险命令
某些工具调用必须确认
某些动作要记录日志

这类就不应该只靠 prompt 提醒,而应该交给 hooks 和 permissions。因为它们不是"建议模型怎么做",而是"系统是否允许这么做"。

这说明它的结构大致不是:

text 复制代码
一个超长 system prompt 解决所有问题

而更像:

text 复制代码
基础行为规则
  + 项目上下文 CLAUDE.md
  + 自动记忆
  + skills 能力包
  + hooks 生命周期拦截
  + permissions 权限策略

也就是说,Claude Code 把"告诉模型怎么做"和"系统是否允许这么做"分开了。

CLAUDE.md 可以提醒模型项目规范,但如果要硬性阻止某个动作,更适合用 hook 或 permission 这类系统层机制。

这个拆分对我们写 Harness 很有启发:

text 复制代码
项目约定进 context
可复用流程进 skill
安全边界进 policy / hook
当前任务状态进 runtime state

system prompt 仍然重要,但它不再承担全部责任。

Codex:把项目指令、沙箱和审批拆成不同层

再看 Codex。

Codex 也有类似的分层思路。公开文档里,AGENTS.md 用来给项目提供额外说明和约定;sandbox 和 approval policy 用来限制命令、文件系统、网络访问等执行边界。

还是用一个具体场景。

假设你让 Codex 做这件事:

text 复制代码
帮我修一下登录页的失败测试,必要时可以改代码,但不要碰数据库迁移。

这件事里面至少有四类信息。

第一类是项目规则。

比如:

text 复制代码
测试命令是什么
代码风格是什么
哪些目录是前端代码
哪些文件不要随便改

这类适合放在 AGENTS.md 里,让每次进入仓库时都有稳定的项目约定。

一个很小的 AGENTS.md 可能长这样:

markdown 复制代码
# AGENTS.md

## Repository expectations

- Use `pnpm`, not `npm`.
- Run `pnpm test` after changing application code.
- Run `pnpm lint` before summarizing code changes.
- Do not modify files under `db/migrations/` unless the user explicitly asks.
- Document behavior changes in `docs/`.

这类文件的重点不是"写得像 prompt 一样华丽",而是把仓库里的工作约定稳定放在一个地方。

Codex 的公开文档里还提到,AGENTS.md 是有层级的。

比如:

text 复制代码
~/.codex/AGENTS.md                 # 个人全局偏好
repo/AGENTS.md                     # 仓库级约定
repo/apps/web/AGENTS.md            # 前端子目录约定
repo/services/payments/AGENTS.md   # 支付服务约定

如果当前任务在 services/payments/ 下面,Codex 可以把全局约定、仓库约定和支付服务的局部约定一起作为项目上下文。

这就比把所有规则都写进一个 system prompt 更自然:

text 复制代码
通用工作习惯放全局
仓库规则放 repo 根目录
局部模块规则放子目录
当前任务状态由 runtime 注入

第二类是当前任务状态。

比如:

text 复制代码
这次目标是修登录页测试
已经读过哪些文件
当前失败点是什么
下一步准备跑什么命令

这类属于 runtime state,随着任务推进不断变化。

第三类是可用能力。

比如:

text 复制代码
能读文件
能改文件
能跑测试
能调用 MCP 工具
能启动 subagent

这类由 tools 和运行时工具池控制,不应该混在项目说明里。

第四类是执行边界。

比如:

text 复制代码
当前 sandbox 允许写哪些目录
网络能不能访问
哪些命令需要 approval
是否允许跨出当前 workspace

这类由 sandbox 和 approval policy 控制。

这说明 Codex 的运行时也不是只靠 prompt:

text 复制代码
AGENTS.md:项目规则和上下文
tools:当前可用能力
sandbox:技术边界
approval policy:哪些动作需要确认
runtime state:当前任务、工作区、命令结果、文件变化

换句话说,模型可以"想做一件事",但 runtime 会决定:

  • 这个工具是否存在
  • 当前环境是否允许
  • 是否需要用户批准
  • 结果如何回写到上下文
  • 文件变化是否能被追踪和恢复

这就是产品级 Agent 和教学版 prompt 最大的差异:

text 复制代码
教学版把很多规则写进 prompt
产品级把不同性质的规则拆到不同控制面

更具体一点,可以拆成五层:

text 复制代码
prompt plane:定义 Agent 的角色、边界和基本行为方式
context plane:注入项目说明、历史记忆、当前任务状态
tool plane:决定这一轮模型能看到和调用哪些能力
policy plane:判断哪些动作允许、哪些动作需要审批、哪些动作禁止
state plane:记录任务进度、工具结果、错误现场和文件变化

这样拆开以后,很多事情就不会互相打架。

项目规范变了,改 context 或 AGENTS.md / CLAUDE.md

新增一个外部系统,改 tool plane。

某类命令要拦截,改 policy。

任务失败以后要恢复,查 state。

system prompt 仍然重要,但它不再是所有规则的垃圾桶。它更像最上层的行为宪法,负责稳定地告诉模型"你是谁、你怎么工作、你的边界是什么"。至于项目知识、工具列表、权限审批、运行状态,都应该交给更适合的系统层去管理。

所以我们在自己写 Harness 时,不需要一上来复刻 Claude Code 或 Codex 的复杂度,但至少要学到这个方向:

产品级 Agent 的关键,不是把 system prompt 写得越来越长,而是把上下文、工具、权限和状态拆成可维护的运行时结构。

二、错误恢复:从返回报错到可恢复执行

教学版里,工具失败时常见的处理方式是:

text 复制代码
把错误文本返回给模型
让模型自己决定下一步

这在入门阶段完全合理。

但产品级系统还要继续往前走一步:错误恢复不能只靠模型临场发挥。

1. 区分错误类型

不是所有错误都一样。

至少要区分:

  • 用户输入问题
  • 工具参数问题
  • 环境问题
  • 权限拒绝
  • 临时失败
  • 不可恢复失败

比如,同样是工具失败:

  • 参数缺字段,可以让模型修正后重试
  • 网络超时,可以按策略重试
  • 权限拒绝,应该进入审批或终止
  • 删除生产数据被拦截,不能让模型换一种方式绕过去

如果系统只返回一段字符串,模型可能看懂了,也可能没看懂。产品级系统不能把所有判断都压在"模型应该会理解"上。

2. 决定恢复策略

不同错误应该对应不同处理:

  • 重试
  • 换路径
  • 请求用户确认
  • 回滚
  • 降级执行
  • 终止当前任务

这里的关键是:恢复策略应该是系统能力,而不只是提示词建议。

比如一个工具声明自己是临时网络失败,runtime 可以允许最多重试两次;如果是权限拒绝,runtime 就应该直接进入审批流,而不是让模型自己猜下一步。

3. 保留恢复现场

如果 Agent 做到一半崩了,系统最好能知道:

  • 它做到哪一步
  • 哪个工具失败了
  • 当时的输入是什么
  • 哪些任务已经完成
  • 哪些任务还没有完成

这就是为什么真实系统里,"错误恢复"通常不是一个函数,而是一整套状态管理和策略体系。

一个可恢复的 Agent,不只是会说"抱歉我失败了",而是能回答:

text 复制代码
失败在哪里
为什么失败
还能不能恢复
恢复应该从哪一步继续

4. 现实产品里的例子:审批失败、会话恢复与检查点

这里不要一上来想"怎么让模型更聪明",先看失败现场本身。

假设 Agent 正在执行:

text 复制代码
修复测试
  -> 发现需要改配置
  -> 准备写入受保护目录
  -> 触发权限限制

教学版 Harness 很可能只是把错误返回给模型:

text 复制代码
Permission denied

然后让模型自己判断下一步。

问题是,这条错误太薄了。

它没有告诉系统:

  • 这是临时失败,还是权限失败?
  • 失败发生在哪个任务步骤?
  • 是应该重试,还是应该请求审批?
  • 如果用户稍后批准,能不能从这里继续?

所以产品级系统里,错误恢复通常分成三层。

第一层是错误分类。

text 复制代码
错误类型:permission_denied
失败动作:写入受保护目录
当前任务:修复测试

第二层是恢复决策。

text 复制代码
permission_denied:
  -> 如果动作可审批,请求用户确认
  -> 如果有安全替代路径,换路径
  -> 如果动作被策略禁止,终止当前分支

第三层是现场保留。

text 复制代码
task_id:当前任务
step_id:失败步骤
tool_name:哪个工具失败
tool_args:当时的参数
error_type:失败类型
recovery_options:可选恢复策略

这样,Agent 不只是知道"失败了",而是知道"失败在哪里,以及从哪里恢复"。

Claude Code 和 Codex 的产品机制,都能看到这种分层思路。

Claude Code 里的 checkpoint、session resume、hooks、permissions,分别对应不同问题:

text 复制代码
checkpoint:保留可以回看的工作点
session resume:让任务不是一次性对话
hooks:在工具调用前后插入系统逻辑
permissions:把允许/拒绝变成运行时判断

Codex 里的 sandbox 和 approval policy,则更直接地把"能不能做"拆开:

text 复制代码
sandbox:技术边界,决定动作能不能直接执行
approval policy:交互边界,决定什么时候必须问用户
parent workflow:子任务失败后,把失败结果带回主流程

这就比"把 traceback 交给模型看"稳定得多。

放到自己的 Harness 里,最小实现可以先不用很复杂:

text 复制代码
tool_result = {
  ok: false,
  error_type: "permission_denied",
  message: "cannot write protected path",
  task_id: "fix-tests",
  step_id: "update-config",
  recovery_options: ["request_approval", "choose_safe_path", "stop"]
}

然后 runtime 做两件事:

  1. 把失败写进 task/todo 状态。
  2. 下一轮把"失败类型 + 失败步骤 + 可选恢复策略"交给模型。

这样模型仍然负责推理下一步,但恢复边界由系统提供,而不是全靠模型临场猜。

三、隔离环境:Agent 不能总在同一个工作区里动手

教学版 Harness 通常直接在当前目录里读文件、写文件、跑命令。

这很适合讲原理,因为读者一眼就能看到:

text 复制代码
模型决定调用工具
工具就在当前目录执行
结果再回到模型

但产品级 Agent 一旦开始处理真实任务,就不能只问"能不能执行",还要问:

text 复制代码
在哪里执行
影响哪些文件
能不能回滚
会不会和别的任务互相干扰

这就是隔离环境要解决的问题。

1. 隔离的不是目录,而是任务影响范围

先看一个简单场景。

你同时让 Agent 做两件事:

text 复制代码
任务 A:修复登录页测试
任务 B:探索把状态管理从 Redux 迁到 Zustand

这两个任务都可能改到同一批前端文件。

如果它们都直接在主工作区里执行,很快会出现问题:

  • 任务 A 的修复和任务 B 的实验混在一起
  • 测试失败时不知道是谁引入的
  • 用户想保留修复、丢弃实验,会很麻烦
  • 后台 scheduled task 可能污染正在开发的分支

所以隔离环境的第一层含义,不是"换个目录这么简单",而是给每个任务划出自己的影响范围。

一个产品级 Agent 至少要能回答:

text 复制代码
这个任务在哪里执行?
它改了哪些文件?
这些改动属于哪个 task?
如果失败,能不能丢弃?
如果成功,怎么合回主线?

2. 隔离可以分成几层

不同系统会用不同隔离手段。

大致可以分成四层:

隔离方式 解决的问题 适合场景
临时目录 避免临时文件污染主目录 数据处理、文件生成
Git worktree 隔离代码改动和 diff 代码修复、重构实验、并行任务
容器 / 沙箱 限制命令、网络、依赖和系统资源 不可信命令、复杂环境
只读挂载 / 受控写入 控制哪些路径能读写 高风险项目、生产配置

所以 worktree 不是隔离环境的全部。

它只是代码任务里特别常用、也特别容易理解的一层。

3. worktree 是什么

在 Git 里,一个 repository 默认只有一个工作目录。你切分支、改文件、跑测试,都是在这个目录里发生。

git worktree 允许同一个 Git 仓库同时拥有多个工作目录。

它们共享同一份 Git 历史,但每个目录可以 checkout 到不同分支,有自己独立的文件状态。

可以把它想成:

text 复制代码
同一个仓库
  -> main/                 # 用户当前工作目录
  -> ../repo-fix-login/     # 修登录测试的工作目录
  -> ../repo-zustand-exp/   # 做重构实验的工作目录

这不是简单复制一份仓库。

更准确地说,它是给同一个仓库开了几张独立工作台:

text 复制代码
Git 历史共享
文件修改隔离
分支可以不同
diff 可以单独看
不想要的工作台可以删掉

放到 Agent 里,worktree 的价值就很直接:

text 复制代码
一个 task 一个 workspace
一个 workspace 一组改动
一组改动可以被查看、测试、丢弃或合并

4. 现实产品里的例子:Codex worktree

Codex app 的公开文档里,worktree 就是一个明确的产品机制。

它解决的不是 Git 技巧问题,而是 Agent 并行执行的问题。

比如同一个项目里:

text 复制代码
main workspace:用户当前工作目录
worktree A:Codex 修复登录页测试
worktree B:Codex 做 Zustand 实验
background worktree:定时任务后台运行

这样每条任务线都有独立文件变化。

任务 A 跑测试失败,不会影响任务 B。

任务 B 改得太激进,也不会污染主工作区。

用户可以单独检查任务 A 的 diff,也可以直接丢弃任务 B 的实验 worktree。

对应到 Harness 设计里,关键抽象不是 git worktree 这个命令,而是这条链路:

text 复制代码
task_id
  -> workspace_id
  -> execution_boundary
  -> diff / artifacts
  -> merge_or_discard

也就是说,Agent 不只是有一个"当前目录",而是每个任务都有自己的执行边界。

5. 这和 sandbox、permission 的关系

worktree 解决的是:

text 复制代码
这次任务的文件改动落在哪里

sandbox / permission 解决的是:

text 复制代码
这次任务技术上能访问什么、能执行什么

这两者不是替代关系。

更接近产品级的结构通常是:

text 复制代码
task
  -> workspace / worktree     # 隔离改动
  -> sandbox                  # 限制环境能力
  -> permission policy        # 判断是否需要审批
  -> audit log                # 记录做过什么

所以第三章真正要表达的是:

Agent 不只需要知道"要做什么",还要知道"在哪里做、能改哪里、改动怎么收回来"。

四、权限策略:从轻量拦截到审批与审计系统

教学版里,我们已经通过 Hooks 看到了一个重要方向:

text 复制代码
权限逻辑不要写死在每个工具里
而要作为横切策略统一挂上去

但产品级通常还要更进一步。

因为真实系统里的权限问题会更复杂:

  • 哪些命令可以直接执行
  • 哪些操作必须询问用户
  • 哪些文件永远不能读
  • 哪些外部系统需要额外认证
  • 不同用户角色能用哪些工具
  • 哪些操作在开发环境允许,在生产环境禁止

所以最终你往往需要的不是几个 if,而是一套更清晰的策略层:

  • 策略判断
  • 审批入口
  • 审计记录
  • 结果追踪

也就是说:

text 复制代码
Agent 不是"能做什么都做"
而是"能做的事也要在策略边界内做"

这里还有一个容易被忽略的点:审批不是打断体验的补丁,而是产品级 Agent 的交互协议。

用户真正需要的不是"每一步都问我",而是:

  • 小风险自动执行
  • 中风险清楚说明后确认
  • 高风险直接拦截
  • 所有关键动作可回看

这就要求权限系统和工具元数据、运行环境、用户身份、审计日志一起工作。

现实产品里的例子:hooks、permissions、sandbox 和 approval

假设你希望 Agent 可以自动读文件、跑测试,但遇到下面这些动作必须停下来:

  • 删除文件
  • 修改 .env
  • 执行数据库迁移
  • 调用生产环境部署工具
  • 访问外网

如果只写进 prompt:

text 复制代码
请不要做危险操作,必要时先问我。

它仍然只是一个行为建议。

Claude Code 的 hooks 很适合理解产品级做法。

它可以在工具调用前后、权限请求、MCP 工具调用等生命周期节点执行用户定义的逻辑。也就是说,权限不只是写在 prompt 里的"请小心",而是可以变成运行时拦截点。

例如:

text 复制代码
PreToolUse hook:工具调用前检查
PermissionRequest hook:权限弹窗前决定是否允许
PostToolUse hook:工具调用后记录或处理结果

Codex 也把这件事拆成两层:

  • sandbox 决定技术上能不能访问文件、网络、命令
  • approval policy 决定什么时候必须停下来问用户

比如同一个 curl 命令,在不同配置下可能是:

text 复制代码
network disabled:技术上直接不允许
network enabled + approval:先问用户
trusted domain:允许执行
unknown domain:拦截或审批

这两个东西配合起来,比"在 system prompt 里提醒模型不要乱来"可靠得多。

对我们自己的 Harness 来说,最小可实现版本可以是:

text 复制代码
工具调用请求
  -> before_tool hooks
  -> permission policy
  -> approval request
  -> 执行工具
  -> after_tool hooks
  -> 写审计日志

有了这层之后,工具系统就不只是"函数分发表",而开始变成一个受策略控制的执行层。

五、状态外置:上下文不是数据库

前面几篇讲过的很多机制,最后都会落到同一个问题上:关键状态不能只放在模型上下文里。

Plan 需要保存任务进度。

Memory 需要保存长期经验。

后台任务和定时任务需要保存下一次运行时间。

错误恢复需要保存失败步骤和恢复现场。

产品级 Agent 至少会逐步把下面这些状态外置出去:

  • todo / task
  • 长期记忆
  • cron 配置
  • 后台任务状态
  • 子智能体消息
  • transcript 摘要
  • 工具调用记录
  • 错误恢复检查点

原因很简单:

text 复制代码
上下文不是数据库

模型看到的上下文,适合做当前推理。

但系统如果要长期可靠运行,关键状态必须能:

  • 持久化
  • 查询
  • 恢复
  • 审计

这也是为什么"可恢复"几乎总是和"状态外置"绑在一起。

如果任务状态只存在于模型上下文里,那么一旦上下文被 compact、进程重启、后台任务跨轮执行,系统就会失去连续性。

真正可用的 Agent runtime,通常会把状态拆成两类:

text 复制代码
模型上下文:用于当前推理
外部状态:用于长期事实和系统恢复

这两者不是替代关系,而是配合关系。

现实产品里的例子:memory、settings、task 和 compact

这里也可以用一个长任务场景。

用户说:

text 复制代码
帮我这周每天检查一次 CI 失败,如果是 flaky test 就记录下来,周五给我总结。

这个任务不可能只靠当前上下文完成。

系统至少要记住:

  • 任务是什么
  • 下次什么时候运行
  • 每次检查到了什么
  • 哪些失败已经判断为 flaky
  • 周五要生成什么总结

Claude Code 里能看到几类不同的状态:

  • CLAUDE.md 和 memory 用来保存项目约定、经验和偏好
  • settings 里有 permissions、hooks、MCP server 等配置
  • 交互界面里有 context indicator,也支持自动 compact 或手动 /compact

这说明它不会把所有东西都塞在一次模型上下文里。

有些东西是项目上下文,有些东西是用户配置,有些东西是会话状态,有些东西是运行时压缩后的摘要。

Codex 也类似。公开术语里,task 是一个持久工作单元,包含上下文、消息、结果和动作;AGENTS.md 是持久指令;memories 是跨会话可复用的本地上下文;worktree 和 diff 则保存任务实际产生的文件变化。

这些机制对应到 Harness 里,可以拆成:

text 复制代码
messages:当前推理上下文
task store:任务状态
memory store:长期经验
config store:权限和工具配置
artifact store:文件变化、日志、结果
schedule store:定时触发信息

这样 compact 不会让任务失忆,进程重启也不会让系统不知道自己做过什么。

六、Multi-Agent 收敛:不只是派出去,还要收回来

很多人第一次做多 Agent,会很兴奋地让大家同时开工。

但产品级系统更关注另一个问题:

text 复制代码
结果怎么收回来
冲突怎么处理
谁来做最终决策

也就是说,多 Agent 不是越多越好,而是:

  • 分工边界要清楚
  • 上下文隔离要清楚
  • 工作环境隔离要清楚
  • 汇总协议要清楚

否则很容易变成一堆并发噪音。

一个更稳的 Multi-Agent 系统,至少要回答四个问题:

  1. 谁有权派发任务?
  2. 子 Agent 的结果用什么格式回流?
  3. 多个结果冲突时谁裁决?
  4. 子 Agent 是否允许直接修改主工作区?

如果这些问题没有回答,多 Agent 看起来是在协作,实际上只是把不确定性并发放大了。

所以真正成熟的多 Agent,不只是"能派出去",还得"能稳定收回来"。

现实产品里的例子:Codex subagents

假设用户给了一个复杂任务:

text 复制代码
帮我评估这个 PR:检查代码风险、测试覆盖、性能影响,并给出是否建议合并。

这个任务天然可以并行:

text 复制代码
reviewer:看代码风险
tester:看测试覆盖
perf:看性能影响

但如果三个 Agent 各自返回一大段文字,主 Agent 仍然会很难收敛。

Codex 的 subagents 文档很适合用来解释这个问题。

它不是简单地说"可以开很多个 Agent",而是强调:可以生成多个专门的子 Agent 并行工作,然后把结果收集到一个响应里。

这里的关键不是"多",而是:

text 复制代码
分工明确
权限继承
结果汇总
父流程裁决

也就是说,产品级 Multi-Agent 不是让每个 Agent 各自跑完、各自说一段话就结束,而是要有一个父流程负责:

  • 派发任务
  • 限定子任务边界
  • 收集结果
  • 合并结论
  • 处理审批和失败

Codex 文档还提到,subagent 会继承当前 sandbox policy。这个细节很重要:子 Agent 不是逃出主系统边界的"自由执行者",它仍然在父任务的权限和环境约束里工作。

这对我们自己的 Harness 有一个直接启发:

text 复制代码
spawn_subagent 时,不只传 prompt
还要传 role、task_id、workspace、permission_scope、return_schema

更进一步,返回结果最好也结构化:

text 复制代码
status:completed / blocked / failed
findings:发现了什么
risk_level:风险等级
suggested_next_step:建议主 Agent 怎么处理
artifacts:相关文件或日志

否则多 Agent 只是多几个对话,不是一个可控协作系统。

七、MCP 边界:它解决接入,不自动解决架构

这一点也很重要。

很多人接触到 MCP,会有一种"系统终于完整了"的感觉。

其实 MCP 解决的是:

text 复制代码
外部能力如何标准化接进 LLM 应用

它很重要,但它不是:

  • 任务系统
  • 权限系统
  • 审批系统
  • 错误恢复系统
  • 环境隔离系统
  • 状态恢复系统

所以把 MCP 接进来,系统只是"外部能力来源更标准化了",不代表其他工程问题自动消失。

甚至可以反过来说:MCP 接得越多,越需要前面那些工程机制。

因为外部能力一多,系统就更需要知道:

  • 哪些工具可信
  • 哪些工具危险
  • 哪些工具只读
  • 哪些调用失败可以重试
  • 哪些调用必须记录审计

MCP 扩展的是能力边界。

产品级架构要补的是控制边界。

现实产品里的例子:MCP 工具接入也需要权限

假设你给 Agent 接了三个 MCP server:

text 复制代码
docs:查内部文档
jira:读写工单
deploy:触发部署

如果只是把它们的工具全部加入工具池,模型一下子就多了很多能力。

但这些能力风险完全不同:

text 复制代码
docs.search:只读,风险低
jira.update_ticket:会改外部系统,风险中
deploy.production:可能影响线上,风险高

Claude Code 的 MCP 文档里有一个很关键的点:MCP server 可以提供 tools、prompts、resources,而且还支持动态更新能力列表。

这很强,但也带来一个问题:

text 复制代码
工具可以动态变多
风险也会动态变多

所以 Claude Code 的安全文档会强调 MCP server 的信任来源,也支持给 MCP server 配置 permissions。

Anthropic 的 MCP connector 文档也有类似思路:连接远程 MCP server 时,可以 allowlist、denylist 或配置单个工具。

Codex 术语里也把 MCP resource、MCP server、MCP tool 分开描述,本质上也是在说明:外部能力不是一个模糊整体,而是要拆成可读资源、可调用工具和服务来源来管理。

所以在自己的 Harness 里,connect_mcp 不应该只是:

text 复制代码
发现工具
加入工具池

更完整一点应该是:

text 复制代码
发现工具
  -> 标记 server 来源
  -> 区分 resource / prompt / tool
  -> 标记 tool 风险级别
  -> 套用 allow/deny 策略
  -> 高风险工具进入 approval
  -> 写入审计记录
  -> 下一轮暴露给模型

这也是为什么 MCP 是外部能力标准,不是完整 Agent 架构本身。

八、完整架构:产品级差异在执行闭环

把前面几件事放在一起,你会发现产品级 Agent 真正的提升,不是多了某个单点 feature,而是闭环更完整了。

一个更接近产品级的闭环,通常至少会包括:

text 复制代码
任务创建
  -> prompt 装配
  -> 计划与执行
  -> 工具调用
  -> 权限检查
  -> 状态落盘
  -> 错误分类
  -> 恢复策略
  -> 长任务异步推进
  -> 定时唤醒
  -> 审计与回放

如果缺任何一环,系统都可能在真实环境里掉链子。

比如:

  • 没有 prompt 分层,系统会越来越难维护
  • 没有状态落盘,长任务会失去连续性
  • 没有错误分类,失败后只能靠模型猜
  • 没有 worktree 或沙箱,并发执行会污染主环境
  • 没有审计,用户很难知道 Agent 到底做过什么

这也是这篇文章想强调的核心:

text 复制代码
产品级不是单点能力更炫
而是执行闭环更稳

现实产品里的例子:Codex 和 Claude Code 都在做"组合系统"

最后把场景拉完整一点。

假设用户说:

text 复制代码
每天早上检查 main 分支 CI。
如果失败,定位原因;
如果是简单测试问题,开一个修复分支;
如果需要改生产配置,先问我;
周五给我一份总结。

这已经不是"调用一个工具"能解决的任务。

它需要一整套 runtime:

text 复制代码
定时触发:每天早上运行
上下文装配:知道项目规则和历史失败
隔离环境:在 worktree 里做修复
工具调用:跑测试、读日志、改文件
权限审批:生产配置必须确认
错误恢复:CI 服务失败要重试或记录
状态外置:保留每天的检查结果
结果汇总:周五生成总结

把前面这些产品机制放在一起看,会发现 Claude Code 和 Codex 都不是只提供一个聊天框。

Claude Code 有:

  • 项目上下文和 memory
  • skills
  • hooks
  • permissions
  • MCP
  • 会话恢复和 compact

Codex 有:

  • AGENTS.md
  • tools
  • sandbox
  • approval policy
  • worktree
  • subagents
  • automations / scheduled tasks
  • task 和 thread

这些东西单独看都是 feature。

但放在一起,它们其实是在组成一个 Agent runtime:

text 复制代码
上下文系统
  + 工具系统
  + 权限系统
  + 隔离环境
  + 状态系统
  + 协作系统
  + 外部能力接入

这就是产品级 Agent 的真实形态。

不是一个更聪明的 prompt,也不是一个更长的工具列表,而是一套能把模型推理、工具执行、权限边界、状态恢复和用户控制连接起来的运行时。

小结

一个 Agent Harness 能跑起来,说明它已经具备了最基本的执行闭环:

text 复制代码
模型理解任务
  -> 选择工具
  -> 执行动作
  -> 看结果
  -> 继续推进

但"能跑"不等于"可用"。

真正可用的 Agent,还要补上另一组工程闭环:

text 复制代码
prompt 要分层
错误要能恢复
环境要能隔离
权限要能控制
状态要能持久化
结果要能审计和回放

这些机制看起来不像模型能力本身,但它们决定了用户敢不敢把真实任务交给 Agent。

所以这一篇的重点不是再加一个新功能,而是换一个视角看前八篇:

text 复制代码
前八篇解决 Agent 怎么工作
这一篇开始讨论 Agent 怎么被可靠地运行

这也是从教学 Harness 走向产品级 Runtime 的关键一步。

相关推荐
QN1幻化引擎2 小时前
Gravity-Anchored Cognitive Field Architecture: The DalinX V8/V10 Implementation
java·前端·算法
学计算机的计算基2 小时前
LeetCode 图论四题精讲:BFS、拓扑排序、Trie 树的模板与优化
java·笔记·算法
浩瀚地学2 小时前
【面试算法笔记】0202-链表-基本功能实现
java·经验分享·笔记·算法·面试
tkevinjd3 小时前
416分割等和子集
java·python·算法·leetcode·职场和发展
Keven_113 小时前
算法札记:Tarjan与拓扑序(Topo)的关系
算法·拓扑·tarjan
ShallWeL3 小时前
AUC、F1、召回率怎么选
人工智能·算法·机器学习
水龙吟啸3 小时前
华为2026.6.3机考选择题+编程题【速刷敲黑板】
人工智能·深度学习·算法·华为
学究天人3 小时前
数学公理体系大全:第十四章 向量空间与模:线性代数的公理化与推广
线性代数·算法·矩阵·动态规划·抽象代数
zzz_23684 小时前
【Java实习面试算法冲刺】回溯
java·算法·面试