---
title: "DeepSeek Harness 技术笔记：Cordis 插件内核、事件溯源与 Agent 执行"
description: "基于 DeepSeek Harness 官方架构与核心子系统源码，理解一切皆插件、Cordis、Agent Loop、SessionEvent、工具流水线和作用域隔离。"
---

> Documentation Index
> Fetch the complete documentation index at: https://docs.chengchencode.com/llms.txt
> Use this file to discover all available pages before exploring further.

# DeepSeek Harness 技术笔记：Cordis 插件内核、事件溯源与 Agent 执行

> **修改前的版本**
>
> 这是替换前保留的原文。现行版本在本栏「概览」。

> **学习目标**
>
> 读完本文，你应能理解：DeepSeek Harness 为什么说“一切皆插件”？Cordis 到底做什么？Agent Loop 如何与 SessionEvent、工具和模型适配器解耦？它和 Pi、yoagent 的取舍有何不同？

官方资料：[DeepSeek Harness](https://github.com/deepseek-ai/deepseek-harness) · [中文架构文档](https://github.com/deepseek-ai/deepseek-harness/blob/master/docs/architecture.zh.md) · [Cordis 入门](https://github.com/deepseek-ai/deepseek-harness/blob/master/docs/cordis-primer.zh.md) · [核心包](https://github.com/deepseek-ai/deepseek-harness/blob/master/docs/subsystems/core.zh.md) · [Session](https://github.com/deepseek-ai/deepseek-harness/blob/master/docs/subsystems/session.zh.md) · [工具](https://github.com/deepseek-ai/deepseek-harness/blob/master/docs/subsystems/tools.zh.md)

本文按 2026-10-15 官方 `master` 的架构文档、包说明整理。官方仍标注 Developer Preview，存在破坏性 API 变化风险。English Studio 部分仅是工程取舍分析，非已实现事实。

## 先建立心智模型

<strong>DeepSeek Harness（dsh）是一个基于 Cordis 的可组合 Agent Harness。</strong> 它不把模型、工具、会话、Loop、UI 都写成固定类的字段，而是把它们拆成在统一 Context 中注册服务的插件。扩展新功能时，原则上不是“修改大内核”，而是“安装或替换一项插件能力”。

与 yoagent “尽量让执行循环保持小核心”不同，dsh 更强调<strong>整个产品的插件化组合边界</strong>。

### Cordis 内核本身负责什么？

Cordis 负责插件的装载/卸载、依赖与上下文、服务注册、带类型的事件以及可逆副作用（effect）。它不直接承担英语学习、模型调用或工具执行业务。

插件向共享 Context 提供服务，按需依赖别的服务。注册项受生命周期管理，插件卸载时相应效果可以被撤销。

| 术语 | 直观理解 |
| --- | --- |
| Context（`ctx`） | 插件连接服务、事件和能力的共同入口 |
| Plugin | 向 Context 声明或实现一项能力 |
| Service / Seam | 对外公开接口，背后可由不同 Provider 实现 |
| Typed Events | 插件之间的事件型扩展点 |
| Scope / Realm | 限制能力和注册项作用范围 |
| Profile / Bundle | 按场景装配的插件配置集合 |

比如“将文件执行迁移到隔离沙箱”，可以通过更换相关文件系统和进程执行 Provider 来实现，而不应在每一种工具中分别硬编码远程执行逻辑。

> **一句话区分 Pi 与 dsh**
>
> Pi 优先复用“一套最小执行循环 + 应用扩展”；DeepSeek Harness 优先保证“模型、工具、循环、会话等都可作为插件替换”。两者都能够扩展，但系统级组合的力度不同。

## 一、核心包如何协作？

官方 [核心子系统文档](https://github.com/deepseek-ai/deepseek-harness/blob/master/docs/subsystems/core.zh.md) 将一次执行拆成多个相互配合的包：

| 组件 | Context 接口 | 负责什么 |
| --- | --- | --- |
| `core/session` | `ctx.sessions` | 追加式会话事件日志 |
| `core/system-prompt` | `ctx.systemPrompt` | 组装 Prompt 片段与工具 Schema |
| `core/tools` | `ctx.tools` | 工具注册、作用域及守卫执行 |
| `core/agent` | `ctx.agents` | 公共 Agent 接口和注册表 |
| `core/agent-loop` | `ctx.agentLoop` | 默认 Agent 驱动器 |
| `core/scope` | 非服务库 | 为能力按 Agent 限定作用域 |
| `llm/llm` | `ctx.llm` | 消息/流协议和模型适配器 |

其中一个细节尤其重要：<strong>外部消费者依赖 `ctx.agents`，而不是直接依赖具体 `agent-loop` 的实现。</strong> 默认 Loop 向注册表提供 AgentFactory。这使未来替换循环实现时，UI、工具与其它插件不需要全部跟着改。

dsh 的 `Profile` 由多层 Bundle 和用户配置组合而成，例如 Web、SDK 或 Headless 等不同启动方式。并不是每种运行模式都要加载完整 Web 产品。

## 二、Agent 的 Turn 与 Step 如何运转？

dsh 使用与部分 Agent 框架不同的粒度定义：

| 概念 | dsh 中的含义 |
| --- | --- |
| Session | 一份持久的 Agent 交互事件历史 |
| Turn | 从接纳任务到没有剩余工作的一轮执行，可能包含零个或多个 Step |
| Step | 一次模型请求以及该次请求产生的工具执行 |
| Agent | 有 `send`、`cancel`、`whenIdle` 等能力的运行句柄 |

因此不能把其它框架的 “Turn = 一次模型调用” 不加说明地移植过来。dsh 的 Step 更接近这个单次模型和工具的单位。

一次 Step 的简化执行路径：

1. `agent-loop` 从 Inbox 中认领输入，开启 `turn/start`。
2. `ctx.systemPrompt` 根据片段、变量和工具 Schema 构建上下文。
3. `agent/pre-step` 插件决定是否接受或修正当前输入。
4. `agent/request` 之后通过 `ctx.llm.prepareCall()` 解析实际模型路由，确定本次请求。
5. Stream 输出助手回复，工具调用经过受守卫的工具管线。
6. 模型可见的消息、工具结果等追加进 Session 日志；如果还有工作，进入下一 Step，否则触发 `agent/turn-stopping` 并关闭 Turn。

关键约束：<strong>每次模型看到的事实，应该能由持久日志重新构建。</strong> 这样才能做可信的回放、恢复和问题排查。真实实现对于取消、重试、失败的记录时机和请求冻结还有更严格约定，不能简单等同于“收到一个 token 就落盘一个 token”。

### 它如何拦截执行？

dsh 的事件主要分成三个领域：

| 事件域 | 职责 |
| --- | --- |
| `session/event` | 发送已经写入 Session Log 的持久事件 |
| `agent/*` | 观察和干预实时 Agent 行为：请求、步骤、状态、继续 |
| `tools/*` / `fs/*` 等 | 挂接工具、文件系统等具体能力的策略 |

部分事件采用 Waterfall（瀑布式）处理器：插件调用 `next()` 才能把执行继续交给后继逻辑。这不同于单纯的“监听通知”事件。比如 `tools/pre-execute` 参与工具准入，`tools/post-execute` 可以做结果处理；不要把它们都当成无返回值的观察器。

## 三、事件溯源：为什么 SessionEvent 是核心设计？

dsh 的 `Session` 不把一份“最终消息数组”作为唯一持久状态，而将交互写成<strong>仅追加的类型化 `SessionEvent` 日志</strong>。模型历史由事件投影而来。

典型持久事件包括：

- `turn/start` 与 `turn/end`
- `step/start` 与 `step/end`
- `user/message`、`assistant/message`、`tool/call`、`tool/result`
- 提示词和请求相关事件，以及其它插件注册的类型化事件

例如一次语法诊断任务：

~~~json
{
  "kind": "illustrative-learning-result",
  "session_id": "grammar-session-001",
  "weakness": "present_perfect_continuous",
  "outcome": "needs_practice"
}
~~~

上面的 JSON <strong>只是 English Studio 可以设计的业务事件示例</strong>，不是 dsh 官方 `SessionEventMap` 已定义的事件格式。真正集成时应通过框架提供的事件扩展契约登记并校验，而不是直接追加任意 JSON。

### 事件溯源带来什么？

| 需求 | 事件日志的优势 | 仍需注意 |
| --- | --- | --- |
| 重新打开历史 | 由日志重新投影模型上下文与 UI | 回放器须理解事件版本 |
| 恢复中断 | 识别已提交的 Turn/Step/结果 | 外部工具副作用仍需幂等 |
| 定位问题 | 追踪输入接纳、请求、结果和失败 | 需谨慎记录敏感数据 |
| 扩展业务事件 | 通过插件扩展 SessionEventMap | 不能随意破坏已有投影 |

实时模型流和已持久化历史也不是同一种东西：`agent/assistant-stream` 可以给 UI 推送临时 chunk，而稳定记录以最终落入日志的消息或 attempt 为准。若进程在提交前硬中断，不能假定流过的每个 token 都已持久化。

> **事件溯源不等于自动保证 Exactly-once**
>
> 记录用户已见过什么、工具何时返回，有助于恢复；但工具如果对外产生副作用，仍需为数据库写入、远程 API 和取消重试设计幂等与一致性边界。

## 四、工具为何也必须成为可替换的能力？

dsh 的工具不是裸函数数组。`ToolDefinition` 包含面向模型的 `ToolSchema`、规范化的输出契约、执行函数和可选运行元数据。

官方 [工具子系统](https://github.com/deepseek-ai/deepseek-harness/blob/master/docs/subsystems/tools.zh.md) 特别强调：

- 模型只能看到工具名、描述与参数 Schema 等允许字段，不能看到宿主专属的执行策略。
- 工具成功输出要符合声明的 JSON Schema，并通过投影生成模型可见内容。
- 工具执行经 `tools/pre-execute → tools/execute → tools/post-execute` 管线，可附加权限或输出修正。
- `isConcurrencySafe` 明确声明允许并发的工具；没有安全声明的操作会按独占约定执行。
- `maxParallelToolCalls` 限制并行安全工具的同时在途数量；独占工具仍需要排序屏障。

下面是官方文档中的配置思想（该 YAML 是 dsh 声明式配置示例，不是 Rust 配置）：

~~~yaml
- name: '@deepseek-ai/dsh-agent-loop'
  config:
    maxParallelToolCalls: 10
    agents:
      - id: 'main'
        provider: deepseek
        model: deepseek-chat
        reasoningEffort: high
        cwd: /workspace
~~~

这展示了 Loop 也只是被挂载的一项插件。调用方还可以通过 `ctx.agents.create()` 和 `ctx.agents.resume()` 创建与恢复 Agent，拿到有明确 `dispose()` 责任的 `AgentHandle`。这比“全局单例里放所有 Agent 状态”更利于隔离与回收。

## 五、Extension 和插件边界怎么设计？

可以用三个问题判断该写在哪一层：

| 需求 | 最接近的扩展点 | 不推荐 |
| --- | --- | --- |
| 更换模型后端 | `ctx.llm` 的 Adapter | 在 Agent Loop 内硬编码不同 API |
| 新增语法查询工具 | `ctx.tools` 注册 ToolDefinition | 把业务逻辑混进 `agent-loop` |
| 会话按不同用户隔离 | Agent Scope 和业务身份规则 | 所有 Agent 复用同一可变全局上下文 |
| 新增长期学习记忆 | 独立 Memory Service 与 Tool/事件适配 | 强迫 Session Log 承载全部长期业务数据库 |
| 添加进度和审计 | 事件消费者、投影或遥测扩展 | 每个工具都手写一套日志系统 |

插件注册还有生命周期：加载后注册服务和事件，在卸载时应撤销效果；Agent 自己的 Scope 负责本地注册项随销毁而清理。

## 六、对 English Studio 的借鉴与取舍

你已经选择了 Rust Harness 和独立 OpenViking 服务，因此不必为了借鉴 Cordis 就整体迁移到 TypeScript dsh。真正值得复用的是其<strong>能力边界</strong>：

| dsh 的原理 | English Studio 的建议 |
| --- | --- |
| 基于接口访问 Agent | Rust `AgentRuntime` 公开统一创建、执行、取消和恢复能力 |
| Provider / Tool / Loop 解耦 | 模型适配、工具注册、循环由接口或 Trait 隔离 |
| SessionEvent 持久事实 | Durable Session 保存会话与工具状态，支持派生与追踪 |
| Agent Scope | 听力/语法/词汇三个 Agent 独立消息与工具权限 |
| Tool Schema + Guard | 对修改学习数据的工具进行参数校验、授权和幂等 |
| Plugins + Lifecycle | OpenViking Gateway 等服务可独立托管及关闭 |
| 组合配置 | 以 `AgentDefinition` 配置不同专业 Agent，而不是复制三套 Harness |

### 特别注意三个系统不要合并

- <strong>Session Store：</strong>当前 Agent 的事件和消息历史。
- <strong>OpenViking：</strong>跨会话长期学习记忆及检索。
- <strong>Learning DB：</strong>错题、词汇卡、正确率、复习排期等结构化业务事实。

这三者可以关联，但职责不相同。dsh 的 Event Sourcing 不代表需要取消 English Studio 的结构化 Learning DB。

> **什么时候考虑 Cordis 式插件平台？**
>
> 当你确实需要让不同应用形态自由组合/替换模型、沙箱、工具、会话、插件和 UI，且有相应复杂度预算时，这套理念价值很大。如果当前只有少量专业英语 Agent，一个精简 Rust Runtime 加明确服务接口往往更简单。

## 总结：记住三个结论

1. <strong>组合：</strong>“一切皆插件”意味着服务由 Context 发现，由 Profile/Bundle 组合，由生命周期管理。
2. <strong>执行：</strong>Agent Loop 只是实现公共 Agent 接口的一个 Provider；Turn 包含多个 Step，工具经过受保护的管线。
3. <strong>状态：</strong>SessionEvent 是持久事实；模型上下文和 UI 状态由投影派生，真实工具副作用仍需业务级恢复策略。

进一步阅读：[官方架构说明](https://github.com/deepseek-ai/deepseek-harness/blob/master/docs/architecture.zh.md) · [Cordis 基础](https://github.com/deepseek-ai/deepseek-harness/blob/master/docs/cordis-primer.zh.md) · [核心 Agent 语义](https://github.com/deepseek-ai/deepseek-harness/blob/master/docs/subsystems/core.zh.md) · [Pi Harness 技术笔记](/notes/pi-harness/) · [yoagent 技术笔记](/notes/yoagent/)

Source: https://docs.chengchencode.com/notes/deepseek-harness/previous/index.mdx
