Corids框架基础——“一切皆插件”
理解DeepSeek harness的地基cord插框架
本章目标:
- 什么是插件化架构
- 为什么是Agent框架要这么设计
- 5个核心设计思想
先问一个问题:什么是 Agent?
想象你要开一家AI 公司,这家公司的目标是:接收客户任务 → 思考 → 调用工具 → 返回结果。
客户说:“帮我查一下天气,然后写一首关于天气的诗”
Agent 要做的事:
- 理解任务(需要 LLM 大脑)
- 调用"查天气"工具
- 拿到天气数据
- 调用"写诗"能力
- 返回结果
问题来了:如果每做一个新 Agent,你都要从零写"怎么调工具"、“怎么管对话”、“怎么换模型”……太累了!
Cordis 就是解决这个的——它是一套"公司管理框架",让你像搭积木一样组装 Agent。
| Cordis 概念 | 公司类比 | 具体例子 |
|---|---|---|
| 插件 (Plugin) | 一个员工/部门 | “工具部”、“模型部”、“安全部” |
| 上下文 (Context) | 公司通讯录/公告板 | 每个员工通过通讯录找到其他部门 |
| 服务 (Service) | 部门提供的能力 | “工具部”提供ctx.tools(各种工具) |
| 事件 (Event) | 公司广播/邮件 | “有新任务了!”、“工具调用完毕!” |
| 依赖注入 (inject) | 入职前提 | “安全部”成立的前提是“工具部”已经存在 |
| 效果 (Effect) | 入职/离职手续 | 员工离职时自动交接工作、归还工牌 |
核心概念:Cordis的五大思想
Cordis 是整个 dsh 框架的底层哲学。想象它是一个**“插件操作系统”**——每个功能都是一个可插拔的模块。
思想一:插件 = 员工
插件是一个实现服务的对象,在coredis中,插件有三种形态。
// 形态 1:函数插件(最常用)exportconstname='my-plugin'// 可选,用于诊断export functionapply(ctx:Context){// 在这里注册你的贡献}// 形态 2:对象插件exportconstmyPlugin={name:'my-object-plugin',apply(ctx:Context){/* ... */}}// 形态 3:类插件(Service 子类)classMyServiceextendsService{constructor(ctx:Context){super(ctx,'myService')// 注册服务名}}一句话理解:插件就是一个"员工",入职时告诉公司"我能干什么"。
思想二:Context = 公司通讯录
Context是所有服务的容器插件,通过Context找到它需要的服务,而不是直接import具体实现。
没有Cordis的情况(混乱):"我要用搜索功能!"→ 你newSearchTool()→ 你newDeepSeekLLM()→ 你newFileSaver()每个地方都要自己找具体的人,换个人就要改代码 有Cordis的情况(有序):"我要用搜索功能!"→ ctx.tools.execute('search',...)你不管具体是谁在做,通讯录(ctx)帮你找到一句话:Context 让你"找人办事"不用知道具体是谁,只认岗位不认人。
思想三:依赖注入 = 入职前提
插件通过inject字段声明它需要什么服务,coredis会等所有依赖就绪后才加载插件
场景:你想成立"安全审计部",但它必须等"工具部"成立后才能工作 (因为安全部要审计工具调用) 没有依赖注入: 你手动安排:先加载工具部 → 再加载安全部 万一顺序错了,安全部找不到工具部,崩溃! 有依赖注入:// 安全部的定义exportconstinject=['tools']// "我需要 tools 部先成立"Cordis自动处理:1.看到安全部需要 tools2.先加载 tools3.tools 就绪后,再加载安全部一句话:你只声明"我需要谁",框架自动安排顺序。
思想四:事件 = 公司广播
事件是插件间松耦合通信的方式,Core Redis有5种调度模式。
emit——广播通知
// 服务方:发射事件classStatsServiceextendsService{bump(name:string){constnext=(this.counts.get(name)??0)+1this.counts.set(name,next)this.ctx.emit('stats/report',name,next)// 广播}}// 消费方:监听事件export functionapply(ctx:Context){ctx.on('stats/report',(name,count)=>{console.log(`${name}->${count}`)})}一句话:事件 = “我做了件事,谁关心谁听着”,发布者不知道也不关心谁在听
思想五:效果 = 自动离职交接
Cordis最精妙的设计
场景:一个插件被卸载(比如配置改了、热更新) 没有效果管理: 插件卸载了,但它注册的监听器还在 → 内存泄漏 它启动的定时器还在 → 定时器报错 它注册的 service 还在 → 别人调用时崩溃 有效管理(Cordis): export functionapply(ctx){// 注册监听器 → 卸载时自动移除ctx.on('event',handler)// 启动定时器 → 卸载时自动清理ctx.effect(()=>{consttimer=setInterval(...)return()=>clearInterval(timer)// ← 这个函数在卸载时自动执行})}Fiber状态机(插件的生命周期)
PENDING ──→ LOADING ──→ ACTIVE ──→ UNLOADING ──→ DISPOSED
│
└──→ FAILED
PENDING:等待依赖服务就绪
LOADING / ACTIVE:apply() 执行中 / 已完成
FAILED:加载失败
UNLOADING / DISPOSED:清理中 / 已清理
深入理解:Waterfall = 审批流程
Waterfall 是 dsh 中使用最频繁的模式,理解它就理解了 Agent 的"拦截链"
这是最难理解的,用公司审批类比:
场景:员工要调用一个危险工具(比如删除文件),需要审批 直接调用: tool.delete(file)→ 直接执行,没有拦截Waterfall(审批链): 员工发起请求 → 经过层层审批 → 最终执行或拒绝 调用 ctx.waterfall('approval/request', '删除文件')│ ▼ ┌─────────────────────────────┐ │ 第1层:日志记录员 │ │"有人要删除文件,我先记一笔"│ │ 调用next()→ 继续往下 │ └─────────────────────────────┘ │ ▼ ┌─────────────────────────────┐ │ 第2层:安全策略员 │ │"删除文件?让我看看规则..."│ │ 规则说:不能删除系统文件! │ │ 直接返回"拒绝"✂️ │ ← 不调用next(),链断了! └─────────────────────────────┘ │ ▼(返回值往上走)┌─────────────────────────────┐ │ 第1层收到"拒绝"│ │"哦被拒绝了,我也返回拒绝"│ └─────────────────────────────┘ │ ▼ 最终结果:拒绝执行执行流程:
调用 ctx.waterfall(‘approval/request’, ‘rm -rf /’)
│
▼
┌─────────────────────────┐
│ 监听器 1 (日志记录) │
│ 调用 next() ──────────┼──┐
└─────────────────────────┘ │
▼
┌─────────────────────────┐
│ 监听器 2 (安全策略) │
│ 发现是危险工具 │
│ 直接返回 false ✂️ │ ← 短路!不调用 next()
└─────────────────────────┘
│
▼ (返回值上浮)
┌─────────────────────────┐
│ 监听器 1 收到 false │
│ 记录日志,返回 false │
└─────────────────────────┘
│
▼
最终结果: false (拒绝执行)
总结
| 概念 | 一句话解释 | 类比 |
|---|---|---|
| Context | 插件之间互相找到对方的渠道 | 公司通讯录 |
| Service | 一个插件提供的能力 | 部门(如工具部) |
| Event (emit) | 广播通知,谁监听谁收到 | 公司大喇叭 |
| Waterfall | 层层处理,可以中途拦截 | 审批流程 |
| Effect | 注册的东西,卸载时自动清理 | 离职交接 |
Cordis 的五大思想:
插件 = 员工 → 每个功能是一个独立模块
Context = 通讯录 → 通过它找到其他服务
依赖注入 = 入职前提 → 声明需要什么,框架自动安排
事件 = 广播 → 松耦合通信
效果 = 自动清理 → 生命周期自动管理
"Cordis 有五个核心机制:插件即服务
每个插件是一个函数或类,通过 apply(ctx) 向系统注册自己的能力。上下文定位
插件之间不直接 import,而是通过 ctx 找到需要的服务。比如 ctx.tools 找到工具注册器。这样消费者和提供者完全解耦——换实现不改代码。依赖注入
插件用 inject 声明需要什么服务,框架自动按依赖顺序加载。配置文件里的顺序无所谓。类型化事件
插件间通过事件通信。ctx.emit 广播通知,谁监听谁收到。发布者不知道谁在听,新增监听者不改发布者代码。可逆效果
所有注册都是’效果’,插件卸载时自动清理——监听器移除、定时器停止,不会内存泄漏。"
总结
一句话概括
Cordis 是 dsh 的底层框架,通过插件化 + 事件驱动 + 依赖注入实现"一切皆插件、一切可替换"。
核心知识点
- 五大思想
| 思想 | 一句话 | 类比 |
|—|—|—|
|插件即服务|apply(ctx)注册能力 | 员工入职报到 |
|上下文定位|ctx找服务,不直接import| 公司通讯录 |
|依赖注入|inject声明依赖,框架负责排序 | 入职前提 |
|类型化事件|emit广播 /waterfall审批 | 公司大喇叭 / 审批流程 |
|可逆效果| 注册的东西,卸载时自动清理 | 离职交接 | - 两种关键事件模式
emit(广播):大喇叭通知,谁监听谁收到,发布者不知道谁在听
waterfall(审批链):层层处理,next()=放行,不调用next()=拦截
3. Fiber 生命周期
PENDING → LOADING → ACTIVE → UNLOADING → DISPOSED
面试怎么答
“Cordis 的核心是插件化架构。插件通过 apply(ctx) 向系统注册能力,通过 ctx 查找其他服务(解耦),通过 inject 声明依赖(框架自动排序)。通信靠事件——emit 做广播通知,waterfall 做可拦截的审批链。所有注册都是’效果’,卸载时自动清理,支持热加载。”
一页速记
五大思想:插件即服务 / 上下文定位 / 依赖注入 / 类型化事件 / 可逆效果
事件模式:emit(广播) / waterfall(审批链, next()=放行, 不调用=拦截)
生命周期:PENDING → LOADING → ACTIVE → UNLOADING → DISPOSED
效果管理:ctx.effect(() => { … return 清理函数 })