Agent Loop 周围有哪些核心服务?
Agent Loop 不是万能机器,它究竟依靠哪些稳定服务才能工作?
把 Agent Loop 画在正中间很容易产生一种误会:好像它是一台包办一切的超级机器。实际上,Agent Loop 更像项目经理,它负责推动流程,却不亲自保存数据库、不亲自实现模型协议,也不亲自拥有全部工具。
Agent Loop 是总调度员,不是所有能力的集合
当用户提交任务后,Agent Loop 要完成一连串协调:
- 找到对应 Session;
- 读取已有上下文;
- 组装系统提示词和工具说明;
- 调用选中的模型;
- 发现模型提出了工具调用;
- 让工具系统执行;
- 把结果写进 Session;
- 判断是否还需要下一步。
它能做这些,并不是因为所有实现都写在 Loop 内部,而是因为它可以访问一组稳定服务。

图 6-1:图中的服务名代表稳定职责;Provider 与工具插件可以替换或增加。
六个核心角色分别负责什么
| 服务 | 通俗职责 | 它不负责什么 |
|---|---|---|
| ctx.agents | 管理活跃 Agent,让 UI/API 能找到并投递任务 | 不直接调用模型 |
| ctx.sessions | 保存 SessionEvent,并从日志重建会话上下文 | 不决定下一步行动 |
| ctx.llm | 注册模型 Provider,并把统一请求转成具体模型调用 | 不执行文件或终端工具 |
| ctx.tools | 保存当前作用域可见的工具,并管理执行流水线 | 不决定何时调用工具 |
| ctx.systemPrompt | 收集提示词段落和工具说明,组装模型看到的系统内容 | 不保存完整会话历史 |
| ctx.agentLoop | 提供默认循环驱动,创建并推进 Agent | 不把所有能力实现写死在内部 |
这些 ctx 名称可以继续理解成“稳定岗位”。Agent Loop 依赖的是岗位职责,不是某个固定员工。
把六个角色逐个拆开看
表格适合建立全貌,但真正读源码时,你会不断遇到这些名字。下面把它们放回一次真实任务中分别理解。
ctx.agents:Agent 的接待台与在岗名册
ctx.agents 保存的是当前进程中“正在工作的 Agent”。Web UI、API 或子 Agent 系统想找某个 Agent,不需要知道默认循环类叫什么,只需要拿 Session id 到这里查询。
它还提供统一的创建与恢复入口。默认 Agent Loop 会把自己的创建能力登记进来,因此上层只依赖 ctx.agents,就能请求“创建一个 Agent”或“恢复一个 Agent”。以后即使换掉默认循环,上层入口也不用跟着改。
但这张名册不是长期记忆。进程退出后,在岗 Agent 会消失;真正可以跨重启保存的历史属于 ctx.sessions。可以把二者区分成:ctx.agents 管“现在谁在线”,ctx.sessions 管“这个 Session 发生过什么”。
ctx.sessions:按时间保存事实的档案室
ctx.sessions 负责创建、查找和管理 Session。每个 Session 内部不是只有最终对话,而是一条持续追加的 SessionEvent 日志:用户消息、模型回复、工具调用和工具结果都会按顺序留下记录。
Agent Loop 每推进一步都会向 Session 写事实;下一步开始前,又从这些事实重建模型需要的历史。持久化插件可以把日志写到 JSONL 或 SQLite,但无论后端怎样变化,Agent Loop 面对的仍是同一套 Session 接口。
所以 ctx.sessions 解决的不是“把聊天气泡存起来”这么简单,而是保证模型上下文、页面恢复和运行审计都能回到同一个事实来源。
ctx.llm:模型线路的统一总机
ctx.llm 不等于 DeepSeek 模型本身。它更像一台总机:不同模型插件把 Provider 路线注册进来,Agent Loop 只提交统一格式的请求,再由总机找到对应适配器。
例如两个 Agent 可以分别选择 DeepSeek 与 GPT 兼容 Provider。它们的 API 字段、认证方法和流式返回格式可能不同,但适配器会把差异挡在 ctx.llm 后面。Loop 最终看到的仍是统一的内容块、Tool Call 和完成状态。
这里也能看出“服务存在”和“具体路线可用”的区别:Host 中必须先有 ctx.llm 这个注册与调度服务;真正发请求时,还必须有与 provider 名称匹配的适配器。只有总机、没有接通任何线路,电话仍然打不出去。
ctx.tools:工具目录加统一执行通道
ctx.tools 一半像应用商店,一半像安检通道。插件把工具名称、说明、参数格式和执行函数注册进来;Agent Loop 在每个 Step 开始前,只取当前 Agent 作用域能够看见的那部分工具。
当模型提出 Tool Call 后,ctx.tools 还要负责参数校验、执行前审批、安全 Guard、真正执行和结果处理。文件、终端、网页工具虽然来自不同插件,却不必各自重做一遍权限与日志流程。
与 LLM Provider 不同,多个工具通常不是候选关系。一个编码 Agent 往往同时需要读文件、改文件、运行终端和搜索网页,因此同一个工具服务下面可以并列挂很多贡献。
ctx.systemPrompt:每一步开会前的材料组装员
系统提示词并不是启动时生成一次就永远不变。ctx.systemPrompt 会收集 Persona、项目规则、运行时目录、模式说明和工具 Schema,并在每个 Step 前组装出这次模型真正看到的系统内容。
把提示词拆成多个贡献有两个好处:第一,不同插件只维护自己负责的规则;第二,不同 Preset 和 Agent 作用域可以看到不同组合。代码 Agent 与内容 Agent 即使共享同一个 Host,最终拿到的系统内容也可以完全不同。
ctx.systemPrompt 只负责“本次请求前面应该放什么”,完整对话历史仍由 SessionEvent 投影而来。一个管会议资料,一个管项目档案,不应混成同一份巨大字符串。
ctx.agentLoop:把五项服务串成工作循环
ctx.agentLoop 是默认的具体循环驱动。它依赖前面五个接口服务,把“找到 Agent、读取 Session、组装 Prompt、调用模型、执行工具、记录结果”连成持续运行的 Turn 和 Step。
它还把创建能力注册到 ctx.agents,因此 UI 与 API 面向 Agent 接口编程,不需要直接依赖默认循环包。换句话说,ctx.agentLoop 是当前负责带队的项目经理,ctx.agents 才是公司对外公布的统一办事窗口。
下面的图把“启动依赖”和“任务流向”分开了:五个服务先让 Loop 具备工作条件;真正运行时,任务才从入口经过 Agent、模型和工具,并持续写入 Session。
到底哪些是“必须有”,哪些只是“可以增加”
默认 Agent Loop 在激活时明确依赖 agents、sessions、llm、tools 和 systemPrompt 五项接口服务,少一项都会停在等待或启动失败。这就是默认 Harness 的最小工作骨架。
但“服务必须存在”不等于“里面必须预装固定数量的实现”:
- ctx.llm 必须存在,但可以注册 DeepSeek、GPT 或其他 Provider;真正请求的路线必须可解析;
- ctx.tools 必须存在,但可以没有业务工具,也可以同时注册几十项工具;
- ctx.systemPrompt 必须存在,但提示词段落可以按部署和 Preset 自由组合;
- 持久化、Web UI、遥测和审批等能力很重要,却不是所有运行形态都必须使用同一实现;
- headless 与 Web 的入口不同,但都可以复用同一套 Agent、Session 和循环接口。
所以完整性来自两层:先把默认 Loop 依赖的接口服务凑齐,再根据当前 Profile 与 Preset,为这些服务注册真正可用的实现和贡献。
两种不同的“可扩展”
不同服务的扩展方式并不完全相同。
LLM 服务后面可以注册多个 Provider,例如 DeepSeek、OpenAI 兼容 Provider 或 Replay Provider。具体 Agent 再选择 provider 和 model。
Tools 服务后面则经常同时存在多个工具:文件、终端、网页、LSP、子 Agent。它们不是相互替换,而是共同组成模型本次可见的工具列表。
System Prompt 也类似。基础规则、技能说明和工作区信息可以由多个插件分别贡献,最后共同组装。
所以“一个槽位挂多个插件”可能有两种含义:
- 多个候选实现,运行时选择其中一个,例如模型 Provider;
- 多个贡献同时生效,例如工具和提示词段落。
Capability Seam:接口、实现和使用方
DSH 把可替换能力进一步拆成三个角色:
- Service Definition:定义统一接口;
- Service Provider:提供具体实现;
- Consumer:使用能力,常见形式是模型可见的工具。
以文件系统为例:
- Definition 规定读取、写入等接口;
- Provider 可以是本地文件系统,也可以是 E2B 沙箱;
- Consumer 是模型看到的文件读取或编辑工具。
这样更换 Provider 时,工具和 Agent Loop 不必跟着重写。就像把公司的仓库从本地机房迁到云端,员工仍通过同一套仓储系统申请和读取物料。
真正稳定的是能力接口,不是某个具体实现;真正面向模型的是 Consumer,不一定是底层 Provider。
UI 为什么只接 ctx.agents
UI 的职责是接收输入、展示状态和结果。它不应该越过 Agent Loop 直接调用 ctx.llm。
如果 UI 直接操作模型,工具调用、SessionEvent、权限检查和恢复逻辑都会被绕过。系统很快会出现两条不一致的运行路径。
因此 UI/API 通常通过 ctx.agents 找到活跃 Agent,再把消息放入 Agent 的收件箱。运行结果通过 SessionEvent 和实时事件返回给界面。
入口保持简单,调度逻辑集中在 Agent 体系中,这也是多个入口能够共享同一套行为的原因:Web、CLI、API 看起来不同,后面接的是同一套 Agent 能力。
“核心服务”不等于不可替换核心
这些服务之所以叫核心,是因为默认 Agent Loop 需要它们协作,而不是因为它们拥有特殊地位。
在 DSH 里,Agent Loop 自己也是插件。你可以更换模型 Provider、增删工具、重组提示词,甚至用另一种驱动实现 Agent 接口。
下一课,我们把视线从 Host 的公共服务移到单个 Agent:Preset 怎样在同一套宿主上,为不同 Session 准备不同的工具和人格?
Preset:同一套 Harness 如何运行不同 Agent?
为什么同一个Harness系统里,可以同时运行工具和提示词完全不同的 Agent?