第 5 章 动手实战(二):服务与依赖注入

服务(Service) 是一个插件提供、其他插件通过 ctx 消费的具名能力。在 harness 中,ctx.tools、ctx.llm、ctx.agents 都是服务。消费方只指定 'tools' 这样的能力名,而不导入其提供方——因此配置可以选择提供方,无需修改消费方。

本章对应官方教程第 3 章。

5.1 提供服务

创建 greeter.ts:

import { Service, type Context } from '@deepseek-ai/cordis'

declare module '@deepseek-ai/cordis' {
  interface Context {
    greeter: GreeterService
  }
}

export class GreeterService extends Service {
  constructor(ctx: Context) {
    super(ctx, 'greeter')
  }

  greet(who: string) {
    return `Hello, ${who}!`
  }
}

export const name = 'greeter'

export function apply(ctx: Context) {
  ctx.plugin(GreeterService)
}

两部分协同工作:

  • 运行时:super(ctx, 'greeter') 以名称 greeter 注册实例,任何插件此后可通过 ctx.greeter 访问。注册属于 effect,卸载提供方时会移除该服务。

  • 编译时:declare module '@deepseek-ai/cordis' 块用 TypeScript 声明合并把 greeter 加入 Context 接口,使 ctx.greeter 通过类型检查。它不生成任何代码——没有该声明时服务运行时仍工作,只是失去类型安全。

Service 子类本身就是插件(第 4 章的类形态),所以 ctx.plugin(GreeterService) 像挂载其他插件一样挂载它。

5.2 用 inject 消费服务

创建 consumer.ts:

import type { Context } from '@deepseek-ai/cordis'

export const name = 'consumer'
export const inject = ['greeter']

export function apply(ctx: Context) {
  console.log(ctx.greeter.greet('world'))
}

组合并运行(cordis.yml):

- name: './greeter.ts'
- name: './consumer.ts'

输出 Hello, world!。交换两行顺序后输出不变——决定插件何时启动的是依赖关系,而不是文件顺序。

5.3 依赖跟踪不会在加载后停止

inject 并非一次性的启动检查:

  • 所需服务在应用运行期间消失(提供方被卸载或热替换)→ 每个依赖插件也随之卸载;

  • 服务恢复后 → 依赖插件再次加载。

结合 effect,这能防止运行中的消费方保留对不可用服务的引用。这也是配置可以替换服务的原因:卸载 dsh-bash-local、挂载另一个 shell 提供方,所有注入 'shell' 的插件都会重启并使用新实现。

5.4 依赖缺失时的行为

尝试彻底移除 ./greeter.ts 再运行:

  • 消费方保持 PENDING,不输出任何内容,既不崩溃,也不会"运行一半";

  • PENDING 的 fiber 不会让 Node 事件循环保持活跃——如果组合里没有其他运行项,进程会静默以状态码 0 退出。

这是初学者最容易踩的坑,第 7 章会专门讲诊断方法。

5.5 可选依赖

inject 用于硬性依赖。如果某项功能缺失时插件仍应运行,请跳过 inject,在使用处探测:

export function apply(ctx: Context) {
  // 无提供方时为 undefined;插件照常运行
  const greeter = ctx.get('greeter')
  console.log(greeter?.greet('maybe') ?? 'no greeter available')
}

5.6 服务命名

一个应用中的服务名共用一个扁平命名空间。给自有服务添加有辨识度的前缀或命名空间(harness 已占用 tools、llm 等普通名称)。子系统页面上生成的 cordis-surface 区块列出 harness 注册的每一个名称。


依据官方教程第 3 章(03-services)整理。

DeepSeek Harness / 第 5 章 动手实战(二):服务与依赖注入 0 字 0 行 cosolar
2026-09-17T10:12:13.894199958Z 2026-09-17T10:39:03.316926298Z