系列第二篇。上一篇建立了 Harness = Agent 操作系统的框架,这篇把操作系统拆开看:工具怎么接、权限怎么卡、内存怎么管、生命周期怎么控。

An image to describe post


上一篇我把 Harness 定义成了「Agent 的运行时环境」——模型在上面跑,但能做什么、不能做什么、记住什么、怎么开始怎么结束,全由 Harness 决定。

这个定义在概念层面够了,但要真的理解一个 Harness 怎么设计、好的 Harness 和烂的 Harness 差在哪,得钻进四个核心维度去看。这四个维度不是并列关系——它们相互咬合,一个维度没做好,其他三个都会被拖下水。

An image to describe post


工具集成:Agent 的手该怎么长

工具是 Harness 给 Agent 最直观的东西。没有工具,模型就是关在笼子里的脑袋。

但「给工具」这件事远没有听起来那么简单。我一开始以为就是列个清单——读文件、写文件、跑命令、调 API——然后把清单喂给模型就行了。后来看了 Claude Code 和 Stripe Minions 的架构才意识到,工具设计有三个层次的问题,而且一个比一个深。

第一层:给什么工具。 这不是越多越好。Claude Code 的工具集其实很克制:文件读写、终端命令执行、网络搜索、Git 操作,基本就这些。Stripe 的 Minions 有个中央 MCP 服务器挂了 400 多个内部工具,但不是每个 Minion 都能用全部——具体到某个任务,只暴露相关的子集。这里的设计原则是:工具集越小,Agent 选错工具的概率越低;工具集越大,Agent 的自由度越高。具体怎么平衡,取决于任务的确定性——修一个已知类型的 bug 和做一个开放式的架构重构,需要的工具集完全不同。

第二层:工具怎么描述。 每给一个工具,Harness 要附带一份描述告诉模型「这个东西是干嘛的、参数是什么、什么情况下该用它」。这份描述的质量直接影响 Agent 的工具选择能力。写得太模糊,模型会瞎选;写得太啰嗦,占宝贵的上下文窗口。我看到的一个有意思的实践是:不让人类写工具描述,而是让 Agent 在用完一个工具之后自己总结「这个工具适合什么场景」,然后把这个总结存下来供后续会话使用。这是自举式的工具发现。

第三层:工具的权限层级。 这才是 Harness 工程里真正考验设计功力的地方。不是所有的工具都平等——读文件和删文件都是「文件操作」,但风险天差地别。Claude Code 的设计给了我一个很清晰的参考:每个工具调用在到达执行层之前,要过一道权限检查。它使用分层权限系统——不同类型的操作有不同的审批阈值:读文件可能自动放行,删文件必须等人确认;终端命令也按风险分级;网络请求有独立的白名单机制。Agent 每次想调一个工具,Harness 不问模型「你觉得可以吗?」——因为模型不会拒绝——而是直接查权限表:这个操作在当前任务里是否被允许?有没有触发需要人类确认的阈值?

这个设计思路跟操作系统的系统调用权限模型非常像:用户态程序想做什么,不是自己说了算,是内核根据权限位来裁决。Harness 就是 Agent 的内核。


安全边界:最难的不是划圈,是划在哪

安全边界可能是 Harness Engineering 里最被低估的维度。这不是因为安全不重要,而是因为很多人误以为「加个沙箱就行了」。

沙箱当然有用。Stripe Minions 的每个任务都在独立 VM 里跑,VM 启动大约 10 秒,预装好代码和服务,网络完全隔离、生产环境完全不可达。这种隔离就是权限系统本身——因为 Agent 根本碰不到生产环境,所以不需要在每一步都检查「你能不能碰生产环境」。这是用架构做安全,而不是用规则做安全。

但沙箱只是答案的一半。另一半是:Agent 在沙箱里产出的东西,怎么安全地出去?

这就是「人在回路」的设计。Claude Code 在涉及破坏性操作时,会暂停并等人确认——删文件、force push、修改关键配置,都得人点头。但这里有一个微妙的设计问题:如果每次都要人确认,Agent 就不是「自主干活」了,是「你盯着它干活」。好的 Harness 会把操作分级——

  • 低风险操作(读文件、跑只读命令)自动放行

  • 中风险操作(修改非关键代码、创建分支)记录日志但自动执行

  • 高风险操作(删文件、修改生产配置、涉及安全的操作)必须等人确认

这个分级的难点在于,什么算「高风险」是上下文相关的。删一个刚创建的临时文件,和删一个核心模块的源码,在 Harness 眼里可能都是「删除操作」。区分它们的不是 Harness 本身,而是 Harness 要能理解任务上下文——这就涉及到更高级的设计:Harness 要能读代码仓库的结构、理解文件之间的依赖关系、判断一个文件的「关键度」。这已经不是传统安全边界的概念了,是 Harness 要有一定程度的「代码理解能力」。

X 上有工程师用过一个我很认同的比喻:Harness 的“沙箱属性”——圈里随便跑,圈外碰都别碰。但怎么画这个圈、画多大、什么时候可以临时扩大,全是工程决策,而且目前没有标准答案。


上下文管理:Agent 的工作记忆瓶颈

这是四个维度里我觉得最被忽视、但实际上影响最大的一个。

Agent 干活靠上下文窗口,而上下文窗口是有限的硬约束。一个 bug 修复任务,涉及几十个文件、上百次工具调用——每次工具调用的输入输出、Agent 自己的推理链、中间决策,全要塞进同一个上下文。窗口满了,就得丢东西。丢了什么、怎么丢,就是 Harness 的上下文管理策略。

我自己理解这个问题的时候,觉得可以借鉴人脑的记忆模型:

An image to describe post

工作记忆(working memory) 像人脑的前额叶——当前正在处理的、必须保持在注意力范围内的信息。对 Agent 来说,就是当前步骤直接相关的文件内容、最近的几次工具调用结果、当前的推理状态。这部分信息必须精确、完整,不能丢。

短期记忆(short-term memory) 像人脑的海马体——跟当前任务有关但不是立刻要用的信息。比如之前已经改过但跟当前步骤关系不大的文件、更早的推理步骤中已经验证过的假设。这部分信息可以压缩存储——不丢,但不占完整空间。

长期记忆(long-term memory) 像人脑的皮层——跨任务的持久知识。比如这个代码仓库的架构约定、常见 bug 模式、团队偏好。这部分信息应该在任务之间持久化——写到文件里、存到向量数据库里、记到 CLAUDE.md 或类似的项目级记忆文件里。

一个好的 Harness 本质上是这三种记忆的管理器:判断什么信息属于哪一层,什么时候从一层转移到另一层,什么时候可以彻底丢掉。

我看到的具体实践里,有几种不同的策略。最简单的就是把超出窗口的旧消息直接截断——粗暴,但不会出错。稍好一点的是用模型自己来做摘要压缩——把一大段对话历史压缩成几句要点存着。更高级的做法是用外部存储(向量数据库、知识图谱)把长期信息结构化存储,Agent 根据需要检索而不是硬塞进上下文。

但这里有一个陷阱:压缩和检索都可能丢信息。被压缩掉的细节可能恰好是后面需要的关键线索;检索回来的信息可能跟当前上下文不匹配。上下文管理的本质不是「怎么存更多」,而是「怎么丢更少」。这个方向目前还在很早期的阶段,没有公认的最佳实践。


生命周期管理:Agent 从生到死的状态机

这个维度最容易被当成「工程细节」而忽略,但我觉得它直接影响 Agent 能不能真正在生产环境里跑。

Agent 的一次任务不是一秒就能跑完的——短则几分钟,长则几小时。中间可能发生什么事?

中断与恢复。 任务跑到一半,上下文窗口满了或者出错了,Agent 挂掉了。重启之后它能不能接上?这不仅需要保存「做到了哪一步」,还需要恢复当时的思维状态——之前推理到一半的结论、还没验证的假设、工具调用的中间结果。Harness 需要给 Agent 做 checkpoint,就像游戏的存档点。Claude Code 支持跨会话的长任务,就是因为它有持久化的会话状态。

暂停与等待。 Agent 碰到需要人确认的操作,暂停等人。人确认了之后,Harness 要让 Agent 无缝继续——不能让它忘了之前在想什么。这听起来简单,但实现起来意味着 Harness 要在暂停期间保持完整的上下文快照。

多 Agent 协调。 一个任务被拆成多个子任务,每个子任务由独立的 Agent 在独立的 sandbox 里并行跑。它们的上下文是隔离的——Agent A 改了文件 X,Agent B 怎么知道?顺序怎么控制?冲突怎么处理?这些问题不是 Loop 层面的(Loop 管的是单个 Agent 的行为),而是 Harness 层面的——Harness 要给多个 Agent 提供一个共享的状态空间,同时保证隔离性。

Claude Code 的 worktree 机制是一个很好的例子:每个 Agent 在自己的 git worktree 上工作,互不干扰。跑完之后 Harness 负责合并、处理冲突、协调顺序。这种设计把「多 Agent 并发」的复杂度从 Agent 推理层搬到了 Harness 工程层——Agent 不需要知道有其他 Agent 在跑,Harness 替它们管。


好的 Harness 到底长什么样

四个维度拆完,我有一个感受:好的 Harness 不是「功能最全」的 Harness,而是「设计最干净」的 Harness。

工具给得克制——刚好够用,不多不少。安全边界划得果断——宁可多问一次人,不要事后擦屁股。上下文管得聪明——知道什么该留什么该丢。生命周期控得完整——不管 Agent 跑多久、挂几次,任务状态始终可追溯。

Martin Fowler 在他的 Harness Engineering 文章里提到过一个概念叫「steering loop」——Harness 不只是 Agent 的容器,它还要持续地监控和引导 Agent 的行为。这个概念跟 Loop Engineering 有交集——Harness 负责「能不能」,Loop 负责「怎么走」——但 Harness 层面的 steering 更偏向基础设施:资源够不够、权限对不对、上下文有没有溢出、Agent 是不是在兜圈子。这些是 Harness 自己就能做的事情,不需要等 Loop 来发现。

下一站,Loop Engineering。Harness 给了 Agent 全套装备,但怎么用这些装备走出一条路,是 Loop 的事。


下一篇预告:Loop Engineering 深入——验证机制怎么搭、失败怎么分类处理、自我提示怎么设计才有效。把你修 bug 的「边做边想」模式工程设计进 Agent 里。