CHAPTER 02 · STAGE 01 · 建立项目地图

先看全景:DeepSeek Harness 到底是什么

假设用户输入:“找到测试失败的原因并修复。”系统需要完成的事情远不止调用一次大模型。

从一个具体任务开始

假设用户输入:“找到测试失败的原因并修复。”系统需要完成的事情远不止调用一次大模型。

它要读取工作区说明,整理系统提示词,告诉模型当前有哪些工具,把用户消息写入会话记录,调用模型,解析模型返回的工具请求,检查权限,在沙箱或本地环境中执行命令,保存工具结果,再次调用模型,最终把回答展示给用户。如果进程中断,还可能恢复之前的会话。

DeepSeek Harness 是组织这些能力的 Agent 运行框架。它的核心特点不是“内置很多工具”,而是把模型、工具、日志、权限、沙箱、界面和持久化都做成可组合插件。

最简单的心智模型

可以先把整个系统分成六层:

入口层:CLI、Web、ACP、JSON-RPC
控制层:Agent、AgentLoop、Turn、Step、Inbox
模型层:System Prompt、Tool Schema、LLM Adapter
能力层:Tools、Shell、FS、Web、LSP、Subagent
记录层:Session Event Log、Persistence、Replay
策略层:Permission、Sandbox、Plan、Compaction、Guard

入口层负责“人或其他程序怎样与 Agent 交互”。控制层负责“什么时候请求模型、什么时候执行工具、什么时候停止”。模型层负责“模型实际收到什么”。能力层负责“Agent 能做什么”。记录层负责“发生过什么以及怎样恢复”。策略层负责“哪些行为允许执行、怎样限制资源、怎样处理过长上下文”。

这些层不是严格的运行时隔离,而是一张学习地图。一个插件可能同时注册 Service 和事件监听器,但你仍可以先判断它主要解决哪一类问题。

为什么说“一切都是插件”

普通应用常有一个不可替换的核心,其他模块只能调用它。DeepSeek Harness 运行在 Cordis 上,AgentLoop 本身也是插件,LLM 适配器也是插件,工具注册表、Session Store 和系统提示词组装器也都是插件。

插件通过共享 Context 取得 Service。例如 AgentLoop 不需要知道当前使用 JSONL 还是 SQLite,也不需要知道 Bash 在本地还是远程沙箱执行。它只依赖抽象服务或注册表。

这带来两个直接结果:

  1. 部署可以用配置替换实现,而不用复制 AgentLoop。
  2. 新行为通常通过事件或 Service 注册加入,而不是修改循环内部。

“插件化”不是把文件拆小,而是让依赖、生命周期和替换关系都能由运行时组合表达。

核心 Service

第一次阅读只记住下面六个 ctx key:

Service 负责什么 初始源码入口
ctx.sessions 内存中的 Session 和事件日志 packages/core/session
ctx.systemPrompt 组装提示词片段、动态上下文和工具 schema packages/core/system-prompt
ctx.tools 注册、筛选和执行模型工具 packages/core/tools
ctx.agents 管理活跃 Agent 和 Agent 事件 packages/core/agent
ctx.agentLoop 默认的具体循环实现 packages/core/agent-loop
ctx.llm 注册模型适配器并分发流式请求 packages/llm/llm

这六个 Service 组成最小主干。Shell、FS、Web、Subagent 等是可选能力;CLI、Web 和 ACP 是不同入口;持久化是把内存 Session 写到磁盘的另一条能力链。

一次对话的主线

用户消息先进入 Agent 的 Inbox。循环开始一个 Turn,领取待处理消息,然后开始 Step。Step 组装系统提示词和工具 schema,从 Session 日志推导模型历史,再通过 LLM Adapter 发出请求。

模型输出普通文本时,循环记录 assistant/chunk 和最终的 assistant/message。模型输出 Tool Call 时,循环记录 tool/call,通过 ctx.tools 执行,记录 tool/result,然后开始下一个 Step,让模型看到工具结果并继续推理。

Turn 可以包含多个 Step,因为一次用户请求可能需要多轮“模型决定 → 工具执行 → 模型继续”。当不再欠工具结果、没有新的 next-step 输入,循环关闭 Turn。

正式的时序图见 docs/agent-lifecycle.md。

三类事件不要混淆

Session 事件

Session 事件是持久事实,例如 turn/start、user/message、assistant/message、tool/call 和 tool/result。它们可以写入存储,用于恢复、回放、UI 和模型历史重建。

Agent 事件

Agent 事件是运行时控制点,例如 agent/pre-step、agent/request 和 agent/status。它们携带活跃 Agent,用于拦截、改写或观察正在进行的工作,但不是对话历史本身。

Capability 事件

能力事件属于某项能力的策略点,例如 tools/pre-execute、tools/post-execute 或 fs/*。它们让权限、超时、审计等插件插入现有执行过程。

判断一个事实是否需要重启后仍存在,是区分持久事件和运行时事件的有效方法。

“模型可见就必须可重建”

这是理解仓库设计最重要的规则之一。系统发给模型的历史、提示词和工具 schema 不能只存在于某个临时变量里;之后恢复 Session 时,系统必须能够从日志重建相同的模型输入。

因此,新的模型可见消息通常需要 SessionEvent。当前请求的系统提示词、工具列表和调用配置也通过 request/header 记录。这样,回放、持久化、调试和不变式检查才有共同的事实来源。

初学阶段暂时忽略什么

第一次学习可以先忽略:Web Client 的 UI 插件树、Typert 类型图生成、远程 API Gateway、不同持久化后端的物理格式、复杂子 Agent Provider、Windows 特殊实现和 vendored Cordis 内部源码。

这些部分重要,但都建立在插件、Service、事件、Turn/Step 和 Session 日志之上。基础主线清楚后,再读它们会容易很多。

本篇练习

  1. 用一句话分别说明入口层、控制层和记录层的职责。
  2. 解释为什么一个 Turn 可能有三个 Step。
  3. 判断 assistant/message 和 agent/status 哪个需要持久化,并说明原因。
  4. 打开 packages/examples/agent-spine-demo/README.zh.md,找到上表中的六个核心 Service 对应插件。

验收标准:不用看本文,能画出“用户 → Agent → LLM → Tool → Session → 用户”的简图,并指出其中哪些内容需要进入日志。