ARTICLE · 1190557
DSH 和 Pi 的插件机制对比
两个harness的产品定位
Pi和DSH这两个的定位都是:通过组合或者书写自己的插件,打造自己的agent。
Pi插件
概念:
Pi这个agent在运行过程中会抛出各种事件,就类似于大家写的codex或者claude code的hooks.json,只是Pi的这个事件抛出是在代码中的抛出的,你写的插件也是在代码中监听的。
会函数入参的方式给插件注册上下文对象,可以通过操作这个对象,修改agent。
Pi插件例子
Pi 的插件称为 Extension。入口接收 ExtensionAPI,调用注册函数,将问候能力提供给模型。
就是写一个函数,Pi在运行过程中对调用这个函数
// 伪代码exportdefaultfunction plugin(pi) {// 1. 通过扩展 API 修改 Agent 的工具配置 pi.setActiveTools(["read", "bash"])// 2. 监听生命周期 Hook,回调获得当前上下文 pi.on("session_start", async (event, ctx) => { ctx.ui.notify(`当前目录:${ctx.cwd}`) })// 3. 在工具执行前介入,通过返回值阻止调用 pi.on("tool_call", async (event, ctx) => {if (event.toolName === "bash") {const allowed = await ctx.ui.confirm("执行确认","是否允许执行命令?", )if (!allowed) {return { block: true, reason: "用户拒绝执行" } } } })}
加载插件
pi --extension ./greeting.ts
PI,DSH等harness没有和codex,grok build等harness用rust语言的原因
怀疑:
插件加载速度快:node是动态语言不需要前期编译,启动速度快,rust等静态语言需要预编译,吃内存,并且编辑中间产物大,node只有js文件本身的占用。
DSH插件
上面例子中 Pi 插件机制的局限
插件缺少统一的规范
pi的插件就是一个函数,然后调用pi这个对象完成各种能力
主要缺少:
统一的服务声明:插件 A 提供什么公共能力,B插件 应该通过哪个标准接口调用。
声明式依赖:B 需要 A 时,框架自动等待 A 就绪,并处理依赖消失与恢复。
插件级生命周期联动:服务提供方卸载时,如何处理消费者及其资源。
解耦不完全:Pi的harenss底层,loop,tools等模块并没有进行插件化解耦。
DSH的底层运行的Cordis框架怎么解决的
Cordis定义了插件的规范,并组织运行插件,把多个插件组织成一个可运行的应用。
例如给DSH写一个定时任务的插件
Service:统一声明插件提供的能力,让别的插件调用
插件 A 提供 scheduler 服务:
// 伪代码class SchedulerService extends Service {constructor(ctx) {super(ctx, 'scheduler') } create(task) {// 创建任务 }}
其他插件通过 ctx.scheduler 调用,不需要直接引用 A 的内部实现。
Inject:声明依赖,由框架决定何时启动
插件 B 声明自己需要 scheduler:
// 生命需要 scheduler serviceexportconst inject = ['scheduler']exportfunction apply(ctx) { ctx.scheduler.create({ name: '日报' })}
这样,Cordis就会自动帮你管理,插件启动顺序。
Fiber / Effect:统一管理插件资源
插件通过 Effect 登记资源清理:
exportfunction apply(ctx) { ctx.effect(() => {const timer = startScheduler()return () => timer.stop() })}
4.书写插件配置
# plugins.patch.yml- insert: # A:提供 scheduler 服务 - id: scheduler-servicename: '/absolute/path/to/scheduler-service.ts' # B:依赖并调用 scheduler 服务 - id: daily-reportname: '/absolute/path/to/daily-report.ts' # C:使用 Effect 管理定时器资源 - id: scheduler-timername: '/absolute/path/to/scheduler-timer.ts'
在已准备好的 DSH 源码开发环境中运行命令即可:
pnpm dsh web --patch ./greeting.patch.yml
在这种规范下,DSH做到了程度很高的模块化
Agent├── 执行控制│ ├── Agent Loop│ ├── 工具执行│ └── 子 Agent 调度├── 模型与上下文│ ├── 模型协议适配│ ├── 提示词组装│ └── 上下文压缩├── 会话管理│ ├── 会话状态│ ├── 历史记录│ └── 持久化与恢复└── 权限与隔离 ├── 权限策略 ├── 操作审批 └── 沙箱执行
所有的模块都是插件,彼此之间的调用通过 “inject” 字段注入,不和实际进行类交互。
并且你可以去替换这些核心插件,比如你想改Loop,就改loop。
DSH和PI的插件化能力对比
从上面可以看出,肯定是DSH目前的插件化机制更完善的,解耦更好的,这是Cordis框架带来的好处。
但是
从上面两处代码其实可以看出,Cordis就是一个框架,告诉你该怎么写这个插件,然后Cordis的runtime层帮你组织运行各种插件,让程序解耦,框架本身并没有提高插件化的能力上限(就是我的插件能做到啥程度),它只是封住了很多api,让你能通过这些api更简单的实现插件化。
比如:在不修改核心harness的情况下,你单独写一个插件
插件话的能力上限,还是取决于,现有的DSH和Pi给你暴露出来了多少 能力。
比如单独写一个插件
DSH
你写一个新的插件,还是依赖于DSH现有插件,给你提供哪些service接口,给你提供了哪些ctx。
Pi
你写一个新的插件,还是依赖于Pi现有的harness,给你提供哪些生命周期,哪些ctx。
如果你的功能不依赖于agent内部的暴露出来的信息,那Pi和DSH都是随便做的,如果以依赖,那肯定要受限于人家的暴露出来的能力。
暴露出来的能力对比:
核心能力两者基本上都暴露出来,因为我们业务开发主要就是围绕这些核心能力去写插件。所以两者在自定的插件能做什么上,应该区别不大。
插件数量对比

在考虑修改核心底层的下
DSH毕竟可以通过重写loop等底层插件,完成各种操作,增加暴露自己想要的能力,pi是不是可以的,这方面DSH是更好的。















































































