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 在本地还是远程沙箱执行。它只依赖抽象服务或注册表。
这带来两个直接结果:
- 部署可以用配置替换实现,而不用复制 AgentLoop。
- 新行为通常通过事件或 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 日志之上。基础主线清楚后,再读它们会容易很多。
本篇练习
- 用一句话分别说明入口层、控制层和记录层的职责。
- 解释为什么一个 Turn 可能有三个 Step。
- 判断
assistant/message和agent/status哪个需要持久化,并说明原因。 - 打开
packages/examples/agent-spine-demo/README.zh.md,找到上表中的六个核心 Service 对应插件。
验收标准:不用看本文,能画出“用户 → Agent → LLM → Tool → Session → 用户”的简图,并指出其中哪些内容需要进入日志。