Agent Harness:把一切装起来

Agent; Agent Harness; 架构设计; 生命周期 2200 words 11 min read
This post is not yet available in English. Showing the original.

从第 2 篇到第 9 篇,我们一块一块地拆

、工具契约、提示词与缓存、短期记忆、长期记忆、状态持久化、规划、多智能体委派。每一篇都是一个独立的部件。这一篇要做的事很简单,也很难——把它们装成一个能跑的程序

这个”装起来的东西”有个名字:Agent Harness(智能体运行时框架)。专题二里我们已经从 Claude Code 的角度拆过它;这一篇换个视角,讲清楚它的通用骨架——不绑任何具体实现。

一句话定位:模型负责判断,Harness 负责结构

一、什么是 Harness,什么不是

这是理解 Harness 的第一道坎,也是最容易糊涂的地方。

Harness 拥有的

、提示词构建器、工具注册表与分发器、记忆管理器、持久化层、钩子系统、事件总线、模型路由器,以及把这些接起来的生命周期。

Harness 不拥有的

相信什么、它积累了哪些具体技能、某个工具的具体提示词、“该解决哪些任务”的业务逻辑。这些是应用代码

判断一个东西属于哪边,有个好用的准则:

如果移除某个功能会破坏”这个系统能解决什么任务”,它是应用代码。 如果移除它会破坏”这个系统还能不能跑”,它是 Harness。

同一套 Harness,今天能托管一个代码探索 Agent,明天能托管一个客服 Agent,后天能托管一个数据分析 Agent——而 Harness 本身一行都不用改。这就是 Harness 与应用代码分离的价值。

在 agent开发系列横评的 16 个项目里,Paperclip 是这个分离做得最干净的参考

本身根本不调用模型,它只负责派生适配器进程(应用)并编排它们。OpenCode 也是同样的切法——server/services(Harness)与 agent 定义(应用)分开。

二、组件清单

一个生产级 Harness 通常有十个左右的核心服务,而它们几乎逐一对应我们前面讲过的章节:

组件职责对应篇目
循环控制器 Loop Controller驱动 observe→plan→act→reflect→stop第 2 篇
工具注册表 + 分发器工具的单一事实来源 + 校验管线第 3 篇
提示词构建器 Prompt Builder稳定前缀 + 易变尾部的装配第 4 篇
记忆管理器 + 压缩器三视图、裁剪、去重、摘要第 5 篇
记忆存储 + 写入器长期记忆的检索与写入第 6 篇
持久化 + 运行状态机 + 检查点步边界提交、崩溃恢复第 7 篇
规划器 Planner循环之上的规划层第 8 篇
委派 Delegationsupervisor 在循环里,specialist 由它派生第 9 篇
钩子 / 事件总线 / 模型路由 / 追踪 / 配置横切的”管道层”本篇 + 后续

Harness 就是这张组件图,前面每一篇是图里的一块。 把它们摆到一起,Agent 运行时的全貌就出来了。

三、组合

跨项目看,把这些服务接起来的方式主要有三种,按正式程度递增:

  • 闭包工厂(Closure Factories)
    ,接收依赖、返回一组方法。接线在 main/app.ts 里一次性完成。Paperclip 用这个——小、显式、传假对象就能测试。
  • 服务注册表(Service Registry)
    ,消费者按名字查。当有很多同类东西(工具、agent、provider)时好用。
  • 分层依赖注入(Layered DI)
    ,运行时按序解析。OpenCode 用 Effect 的 Layer.effect 正是干这个。

反模式警告

Harness 是把三种混着用的——有的服务注入、有的注册、有的当单例 import。选一种,坚持到底。

一个带类型的 Harness 长这样——所有服务作为字段,所有依赖显式声明:

type Harness = {
  config:      Config;
  bus:         EventBus;
  hooks:       HookRunner;
  tracer:      TraceSink;
  prompt:      PromptBuilder;    // 第 4 篇
  memory:      MemoryManager;    // 第 5-6 篇
  tools:       ToolRegistry;     // 第 3 篇
  loop:        LoopController;   // 第 2 篇
  state:       RunStateStore;    // 第 7 篇
  checkpoints: CheckpointStore;  // 第 7 篇
  router:      ModelRouter;      // 后续:成本与模型策略
};

四、生命周期
→ tick → shutdown

Harness 有三个阶段,每个都有自己的规则。大多数 Harness 的 bug 都藏在阶段之间的边界上——服务在 boot 没完成时就被用了、drain 开始后还在接新请求、shutdown 没等运行状态机写完检查点就退了。

stateDiagram-v2
    [*] --> Boot
    Boot --> Ready : 服务初始化完成 + 健康检查通过
    Ready --> Tick : 用户消息 / 定时触发
    Tick --> Tick : 下一个请求
    Tick --> Draining : SIGINT / SIGTERM
    Ready --> Draining : SIGINT / SIGTERM
    Draining --> Shutdown : 在途任务排空或到达截止时间
    Shutdown --> [*]

Bootstrap 顺序不是随意的

启动序列的每一步都依赖前一步。跨项目验证过的可行顺序:

  1. 加载并解析配置文件(带环境变量覆盖)。
  2. 校验配置 schema;出错立即失败,并一次性列出所有错误
  3. 替换环境变量,解析 $secret: 引用。
  4. 打开数据库;跑完待执行的 migration。
  5. 初始化各服务(按依赖顺序)。
  6. 加载插件/技能/钩子。
  7. 启动后台任务(心跳、定时器、孤儿回收)。
  8. 健康检查全绿,才开始接请求。

顺序错了就是那类”开发时不出现、上线才炸”的 bug

、插件加载器在第一次模型调用后才跑、心跳在 migration 完成前就启动了。

Tick

Ready 之后,每来一个用户消息或定时触发,就是一次 tick。tick 内部就是第 2 篇的对话循环——只不过现在它跑在一个有配置、有持久化、有钩子、有追踪的完整环境里。第 7 篇的步边界提交,正是发生在每个 tick 里的每一步之后。

Shutdown

收到 SIGINT/SIGTERM,不能直接退。要进入 draining 状态

,等在途任务跑到步边界并写完检查点,或到达截止时间强制退出。没有优雅 shutdown,崩溃恢复(第 7 篇)就形同虚设——因为你根本没给状态机写盘的机会。

五、Harness 是脚手架,不是纪念碑

Anthropic 在长时运行应用的文章里有一句话点破了 Harness 的本质:

Harness 里的每一个组件,都编码了一个”模型自己做不到什么”的假设。

这句话的分量在于它的推论

,一些当初”模型做不到、必须由 Harness 兜底”的假设会失效。如果不带着这个框架去看,Harness 会在模型早就不需要某个功能之后,还长年累月地堆着它。

所以 Harness 不是永久的纪念碑,而是应该随模型演进的脚手架。今天你为”模型不会自己停”写的步数上限、为”模型会重复读文件”写的去重逻辑、为”模型分不清事实和流程”写的记忆分层——其中一部分,在下一代模型上可能就是多余的。

这也呼应了 agent开发系列横评 16 个项目后得出的一条核心结论:

协议比实现重要。 MCP、Plugin SDK、Runnable 接口这些协议,才是长期价值的锚点。具体实现会随模型迭代而变,但把”变化的部分”和”稳定的部分”隔离开的那条接口线,会一直在。

小结

  • Harness = 模型的运行时;模型带来判断,Harness 带来结构。
  • 判断准则
    ”能解决什么任务”的是应用代码,破坏”还能不能跑”的是 Harness。
  • 组件清单基本就是前面每一篇的部件,加上钩子/总线/路由/追踪/配置这些横切管道。
  • 生命周期 bootstrap→tick→shutdown,bug 多在阶段边界。
  • Harness 是脚手架
    ”模型做不到什么”的假设,随模型变强要敢于拆掉多余的部件。

前十篇搭完了 Agent 的”内部骨架”。接下来三篇转向对外与生产

、连接器与 MCP、Skills 与子代理,以及把 Agent 推上生产环境要面对的后端、可观测、成本与安全。


主要参考

ch11(The Agent Harness)· claude-code-book ch15(构建你自己的 Agent Harness)· agent开发/9执行层 · agent开发/项目差异对比总览(Paperclip/OpenCode 的 Harness 与应用分离)· Anthropic「Building effective agents / long-running apps」