Blog

Agent Harness 的内部构造:一条请求如何走完十个子系统

逐个拆解 Agent Harness 的组件:从一条请求的完整链路讲起,说明十个子系统各自负责什么,并给出一张「出问题时该查哪个器官」的排查表。

Agent Harness 是什么回答的是定义。这些职责在代码里具体落在哪里,是另一个问题。这篇回答第二个。

研究 Harness 最有效的方式不是看功能列表,而是跟一条请求。从用户提交任务开始,到 Agent 停下为止,中间碰过的每个子系统都会显形,连同它们被碰到的顺序。

先看整体形状

循环在中间,是因为其他部分要么在给它喂输入,要么在记录它做了什么。这张图可以按三个环来读:执行循环是控制流;网关和工具注册表是它向外伸手的通道;Session 存储、记忆、账本和事件流是负责记住的东西。

一条请求的完整链路

用顺序描述同一个形状,通常比看框图更容易推理。

  1. 任务到达。 来自某个产品入口:控制台、浏览器扩展、API 调用或定时任务。
  2. Harness 打开或恢复一个 Session。 如果是接续之前的对话,加载已有状态,而不是从零开始。
  3. 组装上下文。 Harness 决定这一次调用真正要放进提示词的内容:系统指令、近期对话、检索出的记忆、工具 schema、Workspace 状态。这是一个预算分配决策,不是字符串拼接。
  4. 通过网关调用模型。 网关负责模型选择、流式输出和重试。
  5. 模型返回。 要么是最终答案,要么包含一个或多个工具调用。
  6. 工具调用经过权限闸门。 允许、拒绝,或挂起等待人工审批。
  7. 获批的调用在沙箱内执行,作用于 Workspace。
  8. 结果写入 Session,并折回上下文。循环回到第 3 步。
  9. 全程发出事件并记录用量,让这次运行可观测、可重放、可计费。

第 3 到第 8 步会反复执行,直到任务完成或触到上限。下面逐个说清每一步实际需要什么。

十个子系统

1. 执行循环

循环是唯一严格必需的部分。它调用模型、派发工具调用、追加结果,并决定何时停止。

真正需要工程判断的是停止条件。一个循环需要轮次上限、墙钟时间预算、token 预算,以及检测「在原地打转」的能力。缺了这些,一个迷糊的 Agent 会一直跑到有人发现账单为止。

2. 模型网关

一层很薄的提供方抽象,前提是你有意去建它。网关负责模型选择、凭证处理、流式输出与重试。

它防止的失败模式是:提供方的差异渗进循环。一旦 if provider == "x" 出现在控制流里,换模型就从改配置变成了重构。网关同时也是归一化 token 统计的天然位置,因为各家提供方的用量上报口径并不一致。

3. Session 存储

Session 是一段对话及其工作状态的持久记录。在 demo 里它是内存中的一个数组,在生产里它必须是一个有身份、被持久化的对象。

真正的 Session 存储和一份聊天记录的区别,在于它支持什么:进程重启后的恢复、在不破坏原分支的前提下做分叉、以及重建当时过程的重放。这三件事都要求以 Session 而不是进程作为事实来源。

这是最容易被低估的子系统。团队通常在一次部署中断了长任务之后,才发现自己需要它。

4. 上下文组装

上下文组装决定模型每一轮看到什么。它是影响输出质量杠杆最大的子系统,也最容易做错。

这里有三件事:选择,即当下哪些指令、记忆和工具 schema 是相关的;预算,即如何塞进有限的上下文窗口;压缩,即历史超出窗口时该摘要或丢弃什么。

把完整对话记录无脑塞进每次调用,一开始能用,直到不能用。而它不能用的表现不是一个报错,而是输出质量缓慢下滑,且很难归因。

5. Workspace

Workspace 是 Agent 真正能碰的东西:一个目录树、一个 Shell,通常还有版本控制。它决定了一个 Agent 是在「聊工作」还是在「做工作」。

它的设计问题是边界画在哪里。限定在单个仓库内的 Workspace 更安全也更简单;能触达宿主机文件系统的 Workspace 能力更强,但也更难推理其行为。

6. 工具注册表

注册表是「Agent 能调用什么」的清单。这里真正重要的设计选择是显式装配与自动发现的取舍。

自动装配看起来很省事,但它会侵蚀下游的一切。如果工具是在没有明确决策的情况下出现的,权限就无法审计,因为代码里根本不存在「某人决定过这个 Agent 可以做什么」的那个点。显式装配提供了那个决策点,也让权限闸门有一个稳定的校验对象。

每个工具还需要一份模型能读懂、Harness 能校验的 schema。一个返回非法结果的工具,比一个缺掉的工具更糟,因为模型会一直试图用它。

7. 权限闸门与沙箱

这是两套不同机制,经常被混为一谈。

权限闸门是策略:这个工具允许,那个需要审批,这个在这个上下文里禁止。它在执行之前运行,产出一个决策和一条审计记录。

沙箱是强制手段:让策略变成现实的进程边界。如果闸门规定 Agent 不能写出 Workspace,那么当 Agent 仍然尝试时,是沙箱让这条规定成立。

只有闸门没有沙箱,是一份建议书。只有沙箱没有闸门,是一道没人管谁翻的围栏。两者都要,而且人工审批链路应该走在闸门里面,而不是旁边。

8. 记忆

记忆是跨 Session 存活的东西,与「能跨进程存活但只属于一段对话」的 Session 状态不同。

难点不在存储,而在于:什么值得持久化、什么时候检索出来、以及如何避免检索到「上个月是对的、现在已经误导人」的内容。写进去却从未被正确检索出来的记忆,是纯粹的浪费。

9. 用量账本

每次模型调用都有成本,账本负责把成本归因到某个 Session、用户、产品或租户。

这个子系统常常是后期才补,然后突然变得紧急,通常是因为有人问上个月的基础设施账单为什么翻了三倍。归因能力很难事后补救,因为信息必须在调用发生时就采集,也就是在网关那里,token 数量真实存在的地方。

10. 事件流

事件流是「发生了什么」的记录:工具调用、审批、错误、产物、状态迁移。它让一次运行变得可解释,而不只是可重复。

它有两个消费者:排查意外结果的人,以及拿成本与活动对账的用量账本。如果事件是从散落各处的调用点随手发出的,这两边看到的都是残缺画面。

缺了哪个器官,会出现什么症状

这是最有实用价值的部分。生产环境的症状,往往能相当准确地对应到缺失的子系统。

你观察到的症状大概率缺失的子系统
部署之后 Agent 无法接着跑Session 存储只在内存里,没有持久化
长对话后输出质量下滑上下文组装没有压缩策略
没人能解释一次糟糕的结果事件流不完整或没有结构
无法按客户核算成本用量账本缺失,或事后才补
工具访问无法审计工具靠自动发现,没有显式装配
Agent 做出了超出预期的动作有权限闸门,但沙箱没有真正强制
重试调用产生了重复副作用循环在工具执行处没有幂等处理
一次运行永远不结束循环缺少轮次、时间或 token 上限
同样的上下文被反复检索又反复被忽略有记忆写入,没有检索策略
换模型需要改代码提供方逻辑从网关里泄漏了出来

设计的时候把这张表倒过来读:每一个症状,都是一条你现在选择满足、还是选择以后才发现的要求。

自研 Harness,还是采用现成的

诚实的说法是:Harness 起步不难,难在收尾。第一个版本,一个循环加上模型调用和几个工具,一个周末就能有;上面这些子系统,会吃掉之后的几个月。

自研还是采用的判断,通常归结为你认为哪些部分属于你的产品核心。Session 持久化、沙箱、权限策略、成本归因都是基础设施。它们很少是用户付钱给你的原因,而且它们都有可能在真实用户到来之后才以昂贵的方式暴露问题。

Downcity 的构造如何对应这份清单

Downcity 就是按这套拆解设计的,所以映射关系基本一一对应。

子系统Downcity 组件
执行循环、Session、WorkspaceAgent Harness
Session 生命周期与恢复Agent 生命周期
模型选择与提供方访问Federation 与 City 宿主
工具注册、成员与 PluginAgent Plugins
记忆作为可挂载能力memory plugin
权限闸门与审批链路权限模型
沙箱与数据边界安全总览
用量、Credits 与支付账号与支付
本地 CLI 操作与排查CLI
把运行时嵌进你的产品Agent SDK

这张表里有三个选择值得单独指出,因为它们是 Downcity 明确站边的地方。

工具是显式装配的,所以权限闸门永远有一个明确的决策可供校验。权限闸门与沙箱是两套机制,所以策略与强制可以分别推理。用量核算放在模型边界上,因为那是 token 数量真实存在、能支撑归因的唯一位置。

常见问题

执行循环就是 Agent 框架吗? 不是。框架帮你描述 Agent 行为,循环是执行它的运行时。你可以用框架来写这个循环,也可以不用。

一个 Harness 里一个 Agent 该支持多少 Session? 取决于产品需要多少,这正是 Session 必须是可寻址对象、而不能是进程全局变量的原因。多租户产品通常需要一个带租户隔离的 Session 存储。

上下文组装就是 RAG 吗? RAG 只是上下文组装的一个输入。组装还包括系统指令、工具 schema、Workspace 状态,以及在预算用尽时决定丢弃什么。

为什么要把权限闸门和沙箱分开? 因为它们回答不同问题。闸门回答「这件事该不该被允许」,产出策略决策与审计线索;沙箱回答「这件事物理上能不能发生」,产出强制力。把两者合并,会让两边都更难测试。

用量应该记在哪里? 在模型网关上,随调用一起记录。那是提供方上报的 token 数量真实存在的唯一位置。事后从日志里反推,最多只能是近似。

这些子系统能逐步加上去吗? 可以,多数团队也是这么做的。比较实际的顺序是:先做 Session 持久化,再做权限与沙箱,然后是用量归因。这三件事事后补的代价最高。

下一步

如果这套构造讲得通,想先看它跑起来,可以启动第一个 Agent,观察一个真实的 Session、Workspace 和工具边界。CLI 文档说明了你用来检查状态的命令。

如果你正在拿它对照自己的架构,白皮书讲了这些边界为什么该这样画,功能页则列出了每个子系统在实际产品里覆盖的范围。