兰 亭 墨 苑
期货 · 量化 · AI · 终身学习
首页
归档
编辑文章
标题 *
URL 别名 *
内容 *
(支持 Markdown 格式)
# DeepSeek Harness 与 Cordis 微内核架构:从零到一的 Agent 插件开发指南 参考链接:https://deepseek-harness.github.io/deepseek-harness/develop/basic/ ## 目录 1. **导言:AI Agent 时代的软件工程挑战** 2. **底层设计哲学:为什么是 Cordis 微内核?** 3. **核心机制拆解** * 依赖注入与插件上下文(`ctx`) * 生命周期与副作用清理(Fiber 与 Effect) * 超级事件总线与五种分发模式 * 配置系统与 YAML 动态求值 4. **Harness 高阶架构设计** * 能力的三层拆分范式(Definition / Provider / Consumer) * LLM 适配器与 StreamChunk 状态机 5. **插件开发实战:从零构建与集成** * 实战 1:定义并注册 Tool 工具 * 实战 2:编写旁路 Log 监听器 * 实战 3:Profile 组合与启动 6. **工程化与分发部署** * 组合包(Bundle)与 Profile 机制 * 本地 `link` 与 npm / Git 分发陷阱 7. **总结与展望** --- ## 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 实例传入入口函数: ```ts 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.tools`、`ctx.llm` 或自定义注册的 Service。 * **副作用管理**:通过 `ctx.effect()` 手动挂载清理钩子。 * **事件订阅与发布**:调用 `ctx.on()`、`ctx.emit()`。 * **作用域隔离**:使用 `ctx.plugin()` 或 `ctx.isolate()` 挂载子插件。 --- ### 3.2 生命周期与副作用清理(Fiber 与 Effect) Cordis 内部通过状态机追踪插件的生命周期: $$\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): ```ts 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](https://deepseek-harness.github.io/deepseek-harness/develop/basic/config#%E5%8F%AF%E9%85%8D%E7%BD%AE%E2%80%8B) 声明强类型契约: ```ts 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`: ```yaml - 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)](https://www.google.com/search?q=https://deepseek-harness.github.io/deepseek-harness/develop/advanced/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`) ```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`) ```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`) 在配置中挂载提供方与插件: ```yaml - name: '@deepseek-ai/dsh-system-prompt' - name: '@deepseek-ai/dsh-tools' - name: './tool-logger.ts' - name: './greet-tool.ts' ``` 运行单文件启动器: ```bash node --import tsx ../../vendor/cordis/bin.js ``` 输出日志: ```text [tool-logger] greet -> Hello, Cordis! tool replied: [{"type":"text","text":"Hello, Cordis!"}] ``` `tool-logger` 与 `greet-tool` 之间没有一行直接的代码依赖,全靠系统的 `tools` 服务与 `tools/result` 事件完成了解耦协作! --- ## 6. 工程化与分发部署 当插件需要在多台机器或团队内部发布时,使用裸脚本就不够严谨了。Harness 引入了 **组合包(Bundle)与 Profile** 机制。 ### 6.1 两个 Manifest 概念 根据 [打包与安装插件文档](https://deepseek-harness.github.io/deepseek-harness/develop/basic/publish#%E4%B8%A4%E4%B8%AA%E6%A6%82%E5%BF%B5%EF%BC%8C%E4%B8%A4%E7%A7%8D-manifest),系统中明确分清了两个角色: 1. **组合包(Bundle)**:回答“这个包贡献什么?”。它是一个带 `cordis.patch.yml` 的 npm 包,在其 `package.json` 中声明: ```json { "name": "dsh-hello-plugin", "dsh": { "bundle": { "patch": "./cordis.patch.yml" } } } ``` 2. **Profile**:位于 `$DSH_HOME/profiles/<name>` 下,回答“这套配置由哪些组合包组合而成?”。由 CLI 自动维护。 安装插件到指定 Profile: ```bash 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` 中显式授权: ```yaml allowBuilds: dsh-hello-plugin: true ``` 或者将代码编译后发布到 npm 注册表/提供 `.tgz` 包,免去构建权限授权。 --- ## 7. 总结与展望 DeepSeek Harness 通过将 **Cordis 的微内核控制反转架构** 与 **Agent 领域的三层能力拆分理念** 相结合,为构建大规模、高鲁棒性的 AI 智能体应用提供了一套极具前瞻性的工程范式。 掌握了 `ctx` 的依赖注入、生命周期控制、事件流水线以及层级配置组装,你就能像搭积木一样,轻松构建出高复用、易测试、能够适应各种复杂沙箱环境的专业级 AI Agent 系统!
配图 (可多选)
选择新图片文件或拖拽到此处
标签
更新文章
删除文章