欢迎光临
我们一直在努力

为什么用 Cordis 做 AI Agent 运行时:从 QQ 机器人框架到 DeepSeek Harness

DeepSeek Harness 系列第六篇。Cordis 原本是 Koishi(QQ 机器人框架)的底层——一个国人开发的"小众"插件框架。DeepSeek 为什么选它来支撑 50+ 包的 Agent 运行时?

背景:Cordis 是什么

Cordis 是 cordiverse/cordis 项目,最初为 Koishi(一个 QQ/Discord/Telegram 机器人框架)设计的插件运行时。它的核心只有约 2000 行 TypeScript,提供:

  • Service:命名的 ctx 键,任何插件可以提供/消费
  • Fiber:插件的生命周期状态机(PENDING → LOADING → ACTIVE → DISPOSED)
  • Effect:注册即 disposer 的副作用管理
  • inject:声明式依赖等待
  • Events:类型化的 emit/waterfall/serial/parallel 事件

Harness 将其 vendor 进仓库(vendor/cordis,版本 4.0.0-rc.7),rescope 为 @deepseek-ai/cordis,并追加了 18 项本地修改(生命周期加固、事务性配置加载、JSDoc 等)。

为什么不用"主流"方案

方案 A:InversifyJS / tsyringe / NestJS

传统 DI 容器做的事:

// InversifyJS 风格
@injectable()
class ToolRegistry {
constructor(@inject('LLM') private llm: LLMService) {}
}
container.bind('ToolRegistry').to(ToolRegistry)
const registry = container.get('ToolRegistry')

问题:

  • 没有自动 dispose:你 bind 了一个 service,谁来负责 unbind + cleanup?传统 DI 要么不管(leak),要么需要手动 lifecycle hook
  • 没有 dependency-driven reload:如果 LLM provider 热替换了,依赖它的 ToolRegistry 应该自动重启——传统 DI 做不到
  • 配置和实例化分离:cordis.yml 一行就是一个 plugin 实例,改配置触发 HMR——传统 DI 的配置通常是启动时一次性的
  • Cordis 的关键差异:它不只是 IoC,它是一个有 lifecycle + reactivity 的 plugin orchestrator。

    方案 B:自己写一个

    50+ 包的 monorepo,如果从头设计:

    • Plugin registry + lifecycle state machine
    • Dependency graph + topological sort
    • Hot reload protocol
    • Effect tracking + ordered disposal
    • Typed event system with waterfall semantics
    • YAML config loader + patch/overlay composition

    这大概是 6000-8000 行基础设施代码,加上持续的 edge case 修复。Cordis 把这些已经做了,而且在 Koishi 生态中经过了真实的多插件并发运行验证。

    方案 C:不用 DI,直接 import

    // 直接 import 方式
    import { toolRegistry } from './tool-registry'
    import { llmService } from './llm-service'
    // 每个模块直接引用具体实现

    这在 Agent 框架中是不可行的,因为 Harness 的核心卖点就是可替换性——用户必须能通过配置换掉任何一层。直接 import 把依赖硬编码到了编译时。

    Cordis 解决了什么 Agent 特有问题

    1. Service 消失时的 graceful degradation

    AI Agent 运行时有一个独特场景:service 可以运行时消失。

    • MCP Server 崩溃 → ctx.tools 中的 MCP tool 被注销
    • LLM Provider 因为 rate limit 暂时不可用
    • 文件系统 watcher 被操作系统杀死

    传统 DI 的假设是:一旦 bind,service 就一直在。Cordis 的假设是:service 可以随时出现和消失。

    export const inject = ['tools']

    export function apply(ctx: Context) {
    // 如果 ctx.tools 的 provider 被卸载(比如 HMR 替换):
    // 1. 本插件自动进入 DISPOSED
    // 2. 所有通过 ctx 注册的 effect 逆序执行
    // 3. 当新的 tools provider 加载后,本插件自动重新 apply
    }

    这个行为是 Cordis 框架级保证的——每个使用 inject 的插件都自动具备这个能力,不需要手动写重连逻辑。

    2. 声明式组合 + HMR

    AI Agent 的开发循环要求极快的反馈:改了一个 tool 的 prompt,想立刻看效果。

    Cordis 的 cordis.yml + HMR plugin 提供了这个能力:

    id: mytool
    name: './src/my-tool.ts'
    config:
    temperature: 0.7

    修改 temperature → Cordis loader 检测变化 → 旧 fiber dispose(所有 effect 清理)→ 新 fiber 用新 config 加载。整个过程不重启进程,不丢失其他 session 状态。

    传统 DI 框架没有"文件变化 → 局部热替换 → 依赖自动重建"这条路径。你要么重启整个进程,要么自己写一套 HMR 协议。

    3. 配置层的 patch/overlay 组合

    Harness 的 Profile + Bundle 系统:

    base bundle → plugin bundles → profile patch → home patch → CLI overlay

    每一层都是 Cordis 配置的一组 patch 操作。这个组合模型直接来自 Cordis loader 的 Include + patch 机制——Harness 只是定义了层的优先级规则。

    如果自己实现,这个"多层配置合并 + 引用已安装 npm 包中的 patch 文件"的逻辑相当复杂。

    4. Effect-scoped 资源管理

    Agent 运行时有大量需要生命周期管理的资源:

    • MCP 连接(要断线重连、要清理 tool 注册)
    • PTY session(要维护状态、要在 Agent 结束时 kill)
    • File watcher(要在 workspace 切换时重建)
    • Background job(要在 session 结束时取消)

    全都通过同一个模式管理:

    ctx.effect(() => {
    const resource = acquire()
    return () => resource.release()
    })

    不需要为每种资源设计不同的 lifecycle hook。这比 NestJS 的 OnModuleInit / OnModuleDestroy 更通用——因为 effect 可以嵌套、可以条件性执行、可以在运行时动态添加。

    Cordis Waterfall:around-middleware for events

    Cordis 的 waterfall 事件是 Harness 架构中大量使用的模式:

    // tools/pre-execute 是 waterfall 事件
    ctx.waterfall('tools/pre-execute', async (toolName, args, next) => {
    // 在 tool 执行前做权限检查
    if (!hasPermission(toolName)) {
    return { denied: true, reason: 'No permission' } // 短路,不调 next()
    }
    // 放行
    return next() // 必须调用,否则后续 listener 不执行
    })

    这个模型和 Koa/Express 的 middleware 类似,但应用在事件系统中:

    • agent/pre-step:决定模型这一步看到什么
    • agent/request:拦截或修改即将发出的 LLM 请求
    • llm/stream:处理流式响应
    • tools/pre-execute / tools/execute / tools/post-execute:工具执行管道

    每个 waterfall 的 listener 可以选择:

    • 调 next() 放行(大多数情况)
    • 不调 next() 短路(策略拒绝)
    • 调 next() 但修改参数或包装返回值(around advice)

    这比纯 emit/subscribe 强大得多——它允许多个独立插件组成一个决策链,而不需要知道彼此的存在。

    Vendor 的代价和收益

    代价

    Harness 做了 18 项本地修改,维护成本不低:

    • Fiber lifecycle hardening:修复了 3 个 reentrant disposal 的 race condition
    • Transactional config reload:让配置变更失败时能回滚
    • Lazy config resolution:!!js 表达式在 inject 激活后才求值
    • Include patch semantics:让后一个 patch 能覆盖前一个 patch 插入的行

    每次 upstream 更新,都要 re-apply 这些修改。

    收益

    • 完全可审计:vendor 在仓库里,不依赖外部注册表的版本
    • 可 patch:遇到 upstream 不接受的修改,不需要 fork + 长期维护
    • 版本锁定:不会被上游 breaking change 意外破坏
    • 性能调优:可以针对 Harness 的使用模式优化热路径

    为什么"小众"不是问题

    Cordis 在 npm 上的下载量可能不如 InversifyJS,但:

  • 代码量小:核心 ~2000 行,可以完整 review
  • 语义明确:5 个核心概念(plugin, service, inject, effect, event),没有 magic
  • TypeScript-first:完整的类型推导,declaration merging 让扩展点 type-safe
  • 实战验证:Koishi 生态有上百个插件并发运行的经验
  • DeepSeek 自己维护:vendor 后由 Harness 团队负责,不依赖外部 maintainer
  • 对于一个 AI Agent 运行时来说,最重要的框架特性不是"生态大",而是:

    • 插件可以安全地注册/注销资源 ✓
    • Service 消失时依赖方自动重启 ✓
    • 声明式配置组合 + 热替换 ✓
    • Around-middleware 事件管道 ✓

    Cordis 恰好把这四件事做好了,而"主流"DI 框架做的是另一组事情(HTTP 路由、请求生命周期、模块化)。

    对 Agent 框架选型的启示

    如果你也在构建 Agent 运行时,Cordis 的设计给出几个值得借鉴的方向:

  • DI 不够,需要 lifecycle:传统 IoC 解决"谁创建谁"的问题,但 Agent 运行时需要"谁什么时候活着"的管理
  • 配置即组合:Agent 的能力集应该是配置决定的,不是代码决定的
  • Effect tracking 比 manual cleanup 可靠:资源泄露是 long-running Agent 的大敌
  • Around-middleware 比 hook callback 强:决策链(permission → approval → timeout → execution)需要有序短路能力
  • 参考链接

    • Cordis 入门
    • Cordis 框架教程
    • vendor/README.md(manifest + 修改日志)
    • cordiverse/cordis(上游仓库)
    • Koishi(Cordis 最早的实战平台)

    DeepSeek Harness 系列文章:

    • 第六篇:为什么用 Cordis 做 AI Agent 运行时(本文)
    赞(0)
    未经允许不得转载:171主机测评 » 为什么用 Cordis 做 AI Agent 运行时:从 QQ 机器人框架到 DeepSeek Harness
    分享到: 更多 (0)

    评论 抢沙发

    • 昵称 (必填)
    • 邮箱 (必填)
    • 网址