夜雨聆风学习资料网

ARTICLE · 1190557

DSH 和 Pi 的插件机制对比

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 暴露方式
Pi 暴露方式
判断
模型调用与协议适配
LLM Service、Adapter
Provider 注册、模型 API
两者都开放
工具注册与执行
Tool Service、执行流水线
工具注册、调用与结果 Hook
两者都开放,但修改权限不同
提示词与上下文
组装服务、消息投影、会话记录
提示词和上下文变换 Hook
两者都开放,Pi 更直接支持临时变换
会话读写与持久化
Session、持久化与查询服务
会话 API;SDK 可替换存储边界
两者都开放,历史结构不同
上下文压缩
可替换 Compaction 引擎
压缩 Hook、可返回自定义压缩结果
两者都开放
执行开始、停止、继续
Agent 句柄、Inbox、执行事件
控制 API、生命周期 Hook
两者都开放,执行边界不同
审批与执行限制
审批、工具策略、沙箱服务
工具拦截及扩展实现
DSH 的独立能力接口更完整
替换 Agent Loop
注册 Agent Factory,消费者依赖 Agent 接口
Extension API 中未见等价的 Loop 提供方接口
DSH 更明确
请求原始 payload、HTTP headers
通用接口未见完整等同项,需进入 Adapter 层
提供相应 Provider Hook
Pi 在这部分暴露更直接
任意修改内部调度算法
未全部开放
未全部开放
都需要扩展点或修改实现

插件数量对比

在考虑修改核心底层的下

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

相关学习资料