DeepSeek Harness 与 Cordis 微内核架构:从零到一的 Ag

DeepSeek Harness 与 Cordis 微内核架构:从零到一的 Agent 插件开发指南

参考链接:https://deepseek-harness.github.io/deepseek-harness/develop/basic/

目录

  1. 导言:AI Agent 时代的软件工程挑战
  2. 底层设计哲学:为什么是 Cordis 微内核?
  3. 核心机制拆解
  • 依赖注入与插件上下文(ctx
  • 生命周期与副作用清理(Fiber 与 Effect)
  • 超级事件总线与五种分发模式
  • 配置系统与 YAML 动态求值
  1. Harness 高阶架构设计
  • 能力的三层拆分范式(Definition / Provider / Consumer)
  • LLM 适配器与 StreamChunk 状态机
  1. 插件开发实战:从零构建与集成
  • 实战 1:定义并注册 Tool 工具
  • 实战 2:编写旁路 Log 监听器
  • 实战 3:Profile 组合与启动
  1. 工程化与分发部署
  • 组合包(Bundle)与 Profile 机制
  • 本地 link 与 npm / Git 分发陷阱
  1. 总结与展望

1. 导言:AI Agent 时代的软件工程挑战

随着大语言模型(LLM)能力的演进,构建简单的 AI 应用已不再具备门槛。然而,当应用场景走向复杂的 AI Agent(智能体) 系统时,开发者往往会遭遇严重的软件工程挑战:

  • 能力耦合严重:Tools(工具)、Prompt(系统提示词)、LLM 交互与本地 Shell 执行环境交织在一起,难以测试和独立替换。
  • 环境切换困难:开发环境需要在本地终端跑命令,而生产环境需要无缝切到隔离的 Docker 或 Sandbox 中。
  • 资源泄漏与状态混乱:热重载(HMR)或动态卸载插件时,残留的定时器、未解绑的事件监听器导致系统内存飙升。

DeepSeek Harness 正是为解决这些痛点而生的 AI Agent 框架。它建立在 Cordis 这个高度解耦、具备严格生命周期管理的 TypeScript 依赖注入微内核之上,引入了高度优雅的插件机制与分发体系。


2. 底层设计哲学:为什么是 Cordis 微内核?

传统的微服务或单体 Agent 框架往往采用强依赖注册机制。而在 DeepSeek Harness 中,所有的扩展能力均以 插件(Plugin) 的形式存在。

Cordis 的核心哲学是 控制反转(IoC)与生命周期感知

  1. 控制反转(IoC):插件不需要自己去实例化复杂的依赖服务。你只需要在插件头部静态声明所需依赖(例如 export const inject = ['tools', 'llm']),框架就会确保底层服务就绪后再初始化当前插件。
  2. 生命周期闭环:每一个插件在运行时都是一个受控的 Fiber(运行节点)。当插件被卸载或重新加载(HMR)时,其产生的每一个副作用(Effect)都会被优雅地“逆序回卷”并清理。

3. 核心机制拆解

3.1 依赖注入与插件上下文(ctx

在 Harness 插件中,ctx(Context,上下文)是插件访问系统能力的唯一接口。

ctx 是谁创建的?

ctx 由 Cordis 的根容器(Root Context / Loader)在系统启动时创建,并在加载插件时为该插件派生(fork)出一个专属的子 Context 实例传入入口函数:

import type { Context } from '@deepseek-ai/cordis'  
  
export const name = 'my-plugin'  
export const inject = ['tools'] // 声明依赖的服务  
  
export function apply(ctx: Context) {  
  // ctx 是框架自动注入的专属实例  
  ctx.tools.register(...)  
}  
  

ctx 包含的核心能力:

  • 服务访问:调用 ctx.toolsctx.llm 或自定义注册的 Service。
  • 副作用管理:通过 ctx.effect() 手动挂载清理钩子。
  • 事件订阅与发布:调用 ctx.on()ctx.emit()
  • 作用域隔离:使用 ctx.plugin()ctx.isolate() 挂载子插件。

3.2 生命周期与副作用清理(Fiber 与 Effect)

Cordis 内部通过状态机追踪插件的生命周期:

PENDING依赖就绪LOADING挂载完成ACTIVE卸载/HMRUNLOADING清理完毕DISPOSED\text{PENDING} \xrightarrow{\text{依赖就绪}} \text{LOADING} \xrightarrow{\text{挂载完成}} \text{ACTIVE} \xrightarrow{\text{卸载/HMR}} \text{UNLOADING} \xrightarrow{\text{清理完毕}} \text{DISPOSED}

当插件处于 UNLOADING 状态时,框架必须安全释放其占用的资源:

  • 原生 Effect 自动清理:通过 ctx.on() 监听的事件、通过 ctx.tools.register() 挂载的工具,在插件销毁时无需手动写解绑代码,框架会自动安全注销
  • 非框架资源清理(ctx.effect:针对持久性外部资源(如 Socket 连接、setInterval 定时器),需要包裹在 ctx.effect() 中并返回清理回调(Disposer):
ctx.effect(() => {  
  const timer = setInterval(() => console.log('heartbeat'), 1000)  
    
  // 返回的函数会在插件卸载或 HMR 时自动触发  
  return () => clearInterval(timer)  
})  
  

3.3 超级事件总线与五种分发模式

事件机制是 Harness 内部实现“零耦合协作”的桥梁。不同于普通 EventEmitter,Cordis 针对异步与拦截场景提供了 5 种分发模式:

模式 对应 API 通俗比喻与运行规则 典型应用场景
广播(emit) ctx.emit() 大喇叭广播:同步触发所有监听器,忽略返回值。 日志上报、指标采集 (stats/report)
并发(parallel) await ctx.parallel() 小组同步并发:所有人同时开始做,等全部完成才继续。 异步并行初始化、多方数据预加载
短路接力(serial) await ctx.serial() 接力棒(异步):按顺序执行,一旦某个监听器返回非空值则直接终止并返回结果。 多级缓存查找、策略寻址
同步短路(bail) ctx.bail() 接力棒(同步):逻辑同 serial 但全过程同步。 权限拦截(任意一插件否决则中断)
洋葱模型(waterfall) ctx.waterfall() 流水线安检:每个听众需显式调用 next() 传递控制权,可拦截或重写数据。 请求/响应拦截器、中间件

⚠️ Waterfall 核心铁律:只负责观察或记录的 waterfall 监听器,**必须显式调用 next()**,否则下游的逻辑会被直接截流短路。


3.4 配置系统与 YAML 动态求值

Harness 遵循 Fail-Fast(阻断式报错) 原则,绝不允许插件带病启动。

1. Schema 强类型校验

插件通常使用 Schemastery 声明强类型契约:

import { Schema } from '@deepseek-ai/cordis'  
  
export interface Config {  
  port: number  
}  
  
export const Config: Schema<Config> = Schema.object({  
  port: Schema.number().default(8080),  
})  
  

若用户在 cordis.yml 中传入非法数据类型,Loader 会直接拒绝启动并抛出定位到具体 JSON Path 的错误。

2. !!js 动态求值标签

为了在静态 YAML 中支持动态环境感知,Harness Loader 扩展了自定义 YAML 标签 !!js

- name: './my-plugin.ts'  
  config:  
    greeting: !!js process.env.DEMO_GREETING ?? 'Hello'  
  disabled: !!js process.platform === 'win32'  
  
  • **不加 !!js**:字符串会被解析为普通的文本死代码 "process.env.DEMO_GREETING"
  • **加上 !!js**:Loader 加载该文件时会实时执行该 JS 表达式,将计算后的值注入插件。

4. Harness 高阶架构设计

4.1 能力的三层拆分范式

在大模型 Agent 系统中,为了实现底层的“多端无缝切换”,Harness 提出了非常巧妙的 三层拆分架构(DIP 依赖倒置原则的极致应用)

       ┌───────────────────────┐  
       │   Service Definition  │ (例如 dsh-shell: 定义 Shell 服务契约)  
       └───────────────────────┘  
           ▲               ▲  
           │               │  
    ┌──────────────┐   ┌──────────────┐  
    │   Provider   │   │   Consumer   │ (例如 dsh-tool-bash: 将 Shell 封装为 Tool 供 LLM 调用)  
    │(dsh-bash-loc)│   │(dsh-tool-ba) │  
    └──────────────┘   └──────────────┘  
  (例如 本地 Exec 实现)  
  
  1. Service Definition(定义层):仅定义 TypeScript 抽象接口与服务名,不写任何实现。
  2. Service Provider(提供方):实现该接口。开发环境可以引入 @deepseek-ai/dsh-bash-local(本地执行);生产环境替换为 dsh-bash-docker(容器沙箱执行)。
  3. Consumer / Tool(消费方):将服务包装为模型可见的 Tool,负责描述 Prompt、定义 JSON Schema 及渲染输出结果(Render)。

优势:Consumer 和 Provider 完全解耦。优化底层 Docker 性能或替换 Exec 实现,上层大模型的 Prompt 和工具定义无感;调整提示词,底层安全沙箱逻辑也无需重新测试。


4.2 LLM 适配器与 StreamChunk 状态机

不同的模型厂商(DeepSeek、OpenAI、Anthropic)接口协议各异。Harness 的 LLM 适配器(Adapter) 充当桥梁,统一输出为基于 AsyncIterable 的 StreamChunk

[block-start (text)] ──> [text-delta] ──> [block-end]  
[block-start (tool-call)] ──> [tool-call-delta] ──> [block-end] ──> [finish]  
  

适配器严格要求:

  • 不能静默丢弃参数:若底层 API 不支持某字段,必须抛出明确的 LlmError
  • 必须支持取消:必须将 options.signal 与底层 HTTP 请求绑定,当 Agent 中断决策时立刻释放网络连接。

5. 插件开发实战:从零构建与集成

下面通过一个完整的案例,展示两个互不相识的插件如何在 Harness 中协同工作。

实战 1:定义并注册 Tool 工具 (greet-tool.ts)

import type { Context } from '@deepseek-ai/cordis'  
import { defineTool } from '@deepseek-ai/dsh-tools'  
import { CallId } from '@deepseek-ai/dsh-llm'  
  
export const name = 'greet-tool'  
export const inject = ['tools'] // 注入 tools 服务  
  
export function apply(ctx: Context) {  
  // 1. 注册打招呼工具  
  ctx.tools.register(  
    defineTool({  
      name: 'greet',  
      description: 'Greet the named person.',  
      parameters: {  
        name: { type: 'string', required: true, description: 'Who to greet' },  
      },  
      output: {  
        schema: { type: 'string' },  
        render: (_args, value) => [{ type: 'text', text: value }],  
      },  
      async execute(args) {  
        return `Hello, ${args.name}!`  
      },  
    })  
  )  
  
  // 2. 模拟 LLM 发起调用  
  void (async () => {  
    const result = await ctx.tools.execute({  
      callId: CallId('demo-1'),  
      name: 'greet',  
      arguments: { name: 'Cordis' },  
      signal: new AbortController().signal,  
    })  
    console.log('tool replied:', JSON.stringify(result.content))  
  })()  
}  
  

实战 2:编写旁路 Log 监听器 (tool-logger.ts)

import type { Context } from '@deepseek-ai/cordis'  
import type {} from '@deepseek-ai/dsh-tools' // 触发类型合并声明  
  
export const name = 'tool-logger'  
export const inject = ['tools']  
  
export function apply(ctx: Context) {  
  // 监听系统总线上的工具执行结果事件  
  ctx.on('tools/result', (exec, result) => {  
    const text = result.content  
      .map(block => (block.type === 'text' ? block.text : ''))  
      .join('')  
    console.log(`[tool-logger] ${exec.name} -> ${text}`)  
  })  
}  
  

实战 3:Profile 组合与启动 (cordis.yml)

在配置中挂载提供方与插件:

- name: '@deepseek-ai/dsh-system-prompt'  
- name: '@deepseek-ai/dsh-tools'  
- name: './tool-logger.ts'  
- name: './greet-tool.ts'  
  

运行单文件启动器:

node --import tsx ../../vendor/cordis/bin.js  
  

输出日志:

[tool-logger] greet -> Hello, Cordis!  
tool replied: [{"type":"text","text":"Hello, Cordis!"}]  
  

tool-loggergreet-tool 之间没有一行直接的代码依赖,全靠系统的 tools 服务与 tools/result 事件完成了解耦协作!


6. 工程化与分发部署

当插件需要在多台机器或团队内部发布时,使用裸脚本就不够严谨了。Harness 引入了 组合包(Bundle)与 Profile 机制。

6.1 两个 Manifest 概念

根据 打包与安装插件文档,系统中明确分清了两个角色:

  1. 组合包(Bundle):回答“这个包贡献什么?”。它是一个带 cordis.patch.yml 的 npm 包,在其 package.json 中声明:
{  
  "name": "dsh-hello-plugin",  
  "dsh": { "bundle": { "patch": "./cordis.patch.yml" } }  
}  
  
  1. Profile:位于 $DSH_HOME/profiles/<name> 下,回答“这套配置由哪些组合包组合而成?”。由 CLI 自动维护。

安装插件到指定 Profile:

dsh plugin --profile demo add ./hello-plugin  
  

6.2 本地 link 与 npm / Git 分发对比

在 Harness 中,不同安装方式的物理与运行机制如下:

[开发模式: link:] ──> 操作系统软链接 ──> 源码保存 ──> 触发 HMR 热重载  
[发布模式: npm]   ──> Registry 下载 ──> 预构建产物(lib/) ──> 稳定运行  
[源码模式: Git]   ──> 拉取 GitHub 源码 ──> 触发 prepare 脚本 ──> 需要 allowBuilds 授权  
  

本地软链接 (link:)

  • 执行 dsh plugin --profile demo add ./hello-plugin 后,package.json 中添加 "dsh-hello-plugin": "link:..."
  • 修改本地代码保存后,配合 HMR 机制实时生效,无需重新打包。

从 Git 直接安装的“构建陷阱”

若执行 dsh plugin --profile demo add github:you/hello-plugin

  • Git 安装拉取的是源码而非构建产物(没有 lib/ 输出)。
  • pnpm \ge 10 默认会阻止自动执行 prepare 构建脚本
  • 解决方案:必须在 Profile 的 pnpm-workspace.yaml 中显式授权:
allowBuilds:  
  dsh-hello-plugin: true  
  

或者将代码编译后发布到 npm 注册表/提供 .tgz 包,免去构建权限授权。


7. 总结与展望

DeepSeek Harness 通过将 Cordis 的微内核控制反转架构Agent 领域的三层能力拆分理念 相结合,为构建大规模、高鲁棒性的 AI 智能体应用提供了一套极具前瞻性的工程范式。

掌握了 ctx 的依赖注入、生命周期控制、事件流水线以及层级配置组装,你就能像搭积木一样,轻松构建出高复用、易测试、能够适应各种复杂沙箱环境的专业级 AI Agent 系统!