QP内容库 Logo
首页
文章
文档
默认分类
关于
登录 →
QP内容库 Logo
首页 文章
文档
默认分类 关于
登录
  1. 首页
  2. 文档
  3. DeepSeek Harness
  4. Cordis 框架教程
  5. 2. 生命周期与 effect

2. 生命周期与 effect

  • Cordis 框架教程
  • 发布于 2026-09-30
  • 0 次阅读
目录
当前文章没有目录

English | 中文

Cordis 插件可能因修改配置、热重载、显式资源释放或所需服务消失而卸载。通过 Cordis API 建立的注册属于 effect,会在所属插件卸载时撤销;在这些 API 之外管理的资源必须包装在 ctx.effect() 中。

Effect

对于 Cordis 尚未管理的资源,例如定时器、连接或 watcher,应将其包装在 ctx.effect() 中并返回 disposer(资源释放函数):

创建 lifecycle.ts,将它放在 tmp/cordis-tutorial 中:

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

export const name = 'lifecycle-demo'

function heartbeat(ctx: Context) {
  console.log('heartbeat plugin loading')
  ctx.effect(() => {
    const timer = setInterval(() => console.log('tick'), 200)
    return () => {
      clearInterval(timer)
      console.log('heartbeat cleaned up')
    }
  })
}

export function apply(ctx: Context) {
  // Mount a child plugin and keep its fiber to dispose it later.
  const fiber = ctx.plugin(heartbeat)
  // The demo timer is itself an effect: if THIS plugin is unloaded first,
  // the pending callback is cancelled instead of firing on a dead app.
  ctx.effect(() => {
    const timer = setTimeout(async () => {
      await fiber.dispose()
      console.log('disposed')
      process.exit(0)
    }, 700)
    return () => clearTimeout(timer)
  })
}

让 cordis.yml 指向该文件:

- name: './lifecycle.ts'

运行(node --import tsx ../../vendor/cordis/bin.js)后会得到:

heartbeat plugin loading
tick
tick
tick
heartbeat cleaned up
disposed

请留意三点:

  • ctx.plugin(heartbeat) 会把一个来自代码的函数挂载为插件,这与 YAML loader 为每个配置项执行的操作相同。函数插件不需要 apply 方法:Cordis 会直接调用该函数,其名称只用于诊断。只有对象形态才要求 apply 方法,例如 ctx.plugin({ apply(ctx) { /* ... */ } })。调用会返回一个 fiber,即一个已加载插件实例的运行时句柄。
  • effect 主体在加载期间运行;它返回的 disposer 在卸载期间运行。对于生命周期与插件一致的资源,你绝不需要自行调用 disposer。
  • fiber.dispose() 会等该插件的所有清理工作(包括异步 disposer)完成后才结束,并递归卸载它挂载的所有子插件。

Fiber 状态机

每个已加载插件实例都拥有一个 fiber,并在以下状态之间转换:

PENDING → LOADING → ACTIVE → UNLOADING → DISPOSED
                 ↘ FAILED
  • PENDING:已经声明,但所需服务(第 3 章)尚不可用。
  • LOADING / ACTIVE:apply 正在运行/已经完成。
  • FAILED:apply 或配置校验抛出异常。
  • UNLOADING / DISPOSED:disposer 正在运行/一切均已拆除。

你会在第 6 章再次遇到 PENDING,它通常就是「为什么我的插件没有输出」的答案。

已经属于 effect 的操作

你很少需要亲自编写 ctx.effect(),因为内置注册 API 本身已经是 effect:

  • ctx.on(event, listener):监听器会在卸载时移除(第 4 章)。
  • ctx.plugin(child):子插件会随父插件一同 dispose(资源释放)。
  • 服务注册属于 effect。ctx.tools.register(...) 等 harness 注册表也会把返回的 disposer 附着到调用插件上,因此会自动撤销(第 7 章)。

对于 Cordis 不管理的资源,应在 ctx.effect() 内获取它,并返回用于释放资源的 disposer。此后 Cordis 会在卸载期间调用该释放逻辑,热重载时也不例外。

有一项顺序注意事项:disposer 会按注册顺序的逆序启动,但多个异步 disposer 会并发运行。如果拆除步骤必须按顺序执行,请把它们放在同一个 disposer 中,并在其中依次等待每步完成。

下一章:服务:插件如何共享功能。

相关文章

Cordis 教程

English | 中文 Cordis 是 DeepSeek Harness 底层的插件框架:它是一个小型运行时,其中的每项能力,包括工具、LLM(大语言模型)适配器、文件访问乃至 agent loop(智能体循环)本身,都是挂载到共享上下文中的插件。本教程通过动手实践讲解 Cordis:每一章都是

7. 进入 harness

English | 中文 本章会向 harness 的 tools 服务注册一个可由模型调用的工具,通过 harness 工具流水线执行它,并观察结果事件。整个示例无需密钥,也不会调用模型。 工具插件 创建 greet-tool.ts,将它放在 tmp/cordis-tutorial 中: impo

6. 组合与 HMR(热模块替换)

English | 中文 到目前为止构建的每项能力都是插件,cordis.yml 则选择应用的插件树。本章会改变这种组合、热重载一个插件,并诊断始终无法加载的插件。 Cordis 配置项不只有名称 Cordis 配置项除了 name 和 config,还接受其他元数据: - id: greeter

5. 配置

English | 中文 cordis.yml 中的每个 Cordis 配置项都可以携带 config 块,插件则声明一个 schema,在运行 apply 前验证该块。错误配置会导致加载失败,并给出准确的错误:插件绝不会在配置不完整时启动。 可配置插件 创建 config-demo.ts,并将其放

4. 事件

English | 中文 服务支持直接调用;事件让插件无需知道有哪些插件正在监听,就能发出通知。harness 使用事件处理工具结果、模型请求和审批决定等交互。 声明、发出与监听 创建 stats.ts,将它放在 tmp/cordis-tutorial 中。它是一项负责计数并在每次变化时发出通知的服

3. 服务

English | 中文 服务是一个插件提供、其他插件通过 ctx 消费的具名能力。在 harness 中,ctx.tools、ctx.llm 和 ctx.agents 都是服务。消费方只指定 'tools' 之类的能力,而不导入其提供方,因此配置可以选择提供方,无需修改消费方。 提供服务 创建 g

目录
当前文章没有目录

星沉月落夜闻香,素手出锋芒。

Copyright © 2026 quping.com All Rights Reserved. Powered by Halo.
鲁ICP备09092435号