dsh-busyloop
DSH agent-loop 引擎:宿主 LLM 通道(官方 ctx.llm)+ 轻量 loop 骨架 + 工具表注入。
能力等价,范式不同:装上 busyloop,dsh 就有一样的 agent 循环能力(LLM 多轮 + 工具调度); 但不保证像 codex 一样工作(AGENTS.md/工具协议/会话格式) —— 那是 codexstyle 层的事。
架构(三件套)
dsh-busyloop ──────────── 引擎(能力:LLM 适配器 + loop + 宿主工具调度)
├── dsh-busyloop-codex ──── 范式①:桥接 codex CLI(外包)
└── dsh-busyloop-codexstyle ─ 范式②:自研 codex 风格(规划中)
设计原则
- LLM 适配器不是手刻、不是外接:直接消费宿主
ctx.llm(@deepseek-ai/dsh-llm的 LlmRuntime),类型来自官方包,运行时由宿主提供 - 不造独立运行时:全部活在 dsh 宿主内,发布物在"纯 dsh + 官方包"环境可跑(bundle 自包含)
- 工具表注入:宿主
ctx.tools只读不可枚举,loop 的工具由调用方提供(schema + execute)
用法
import { createBusyLoop, hostLlm } from '@snow-the/dsh-busyloop'
// 插件内:
const engine = createBusyLoop(ctx) // ctx.llm 宿主服务
const result = await engine.run({
provider: 'deepseek',
model: 'deepseek-chat',
prompt: '东京天气如何?用 get_weather 工具查询后一句话回答',
tools: [weatherTool],
onEvent: (ev) => console.log(ev), // turn / tool / done
})
// { output, turns, toolCalls, usage, finish }
纯库方式:hostLlm(ctx.llm) → runBusyLoop(llm, options)。
HTTP 端点(经 ctx.webServer 注册)
宿主一旦提供 webServer(dsh-web-app 就会),以下端点即生效:
| 端点 | 方法 | 说明 |
|---|---|---|
/api/busyloop/health |
GET | 引擎存活 + hostLlm 状态 |
/api/busyloop/providers |
GET | 宿主已注册的 LLM provider 列表 |
/api/busyloop/channels |
GET | 已配置的通道 |
/api/busyloop/channel |
POST | 新增/修改通道 |
/api/busyloop/credentials |
GET | 凭据来源(不回显密钥) |
/api/busyloop/credential |
POST | 写入凭据 |
/api/busyloop/test |
POST | 通道连通性测试 |
历史更正。 本 README 早先写着「官方宿主当前未注入
ctx.http.mount,这些端点默认不暴露(404)」—— 那是当时的真实情况,但已不再成立,而且端点名也早已改前缀。根因是
ctx.http不是 DSH 的服务(官方 ctx 服务面里没有它),所以那条路由从来没挂上过,还白背了一个hono依赖。现在改为官方的ctx.inject(['webServer'], …),并且:
- 不能用
ctx.get('webServer')—— 它是时点快照;webServer 若后挂载,你就永远漏注册,且不报错。- 每个端点先过 Host/Origin + 浏览器鉴权围栏(
connection.requestRejection),伪造请求 403、匿名请求 401; 围栏读不到时失败关闭(503),不会静默放行。
测试金字塔(14 个测试)
- 单元/集成(
npm test,node --test 串行):fake chunks(不碰真模型)——多轮循环、工具参数解析、结果回填、maxTurns 截断、未知/抛错工具、providers/health 端点、sessionId 透传 - 宿主解析链验证:dist 放入宿主 node_modules 树加载(官方包全部宿主解析)
- 真机 E2E(
node e2e/e2e-host.mjs,手动运行,需要真 API key):真实 DeepSeekAdapter + 真实模型(纯对话 + 多轮工具调用,cacheReadTokens命中验证)
发布
pnpm build # tsc 类型 + esbuild bundle(官方包 external,宿主提供)
npm test # node --test
npm publish # GitHub Packages(@snow-the 下同款流程)
安装(重要)
dependencies 与 bundles 都要加:只在 dependencies 里加会被 pnpm 装入 node_modules 但不会被挂载(loader 只应用 bundles 里声明的包的 bundle patch);必须同时把
@snow-the/dsh-busyloop加进dsh.profile.bundles,它的 cordis.patch.yml 才会生效、busyloop_run工具才会注册。宿主包不声明为运行时依赖:
@deepseek-ai/dsh-tools是宿主提供的包(经 dsh-base bundle 装入),本插件只把它声明为 peerDependency。把它放进 dependencies 会把 dsh-tools 拉进 profile 的依赖图,在"安装后立即重启"的窗口期可能让宿主工具调度器(Symbol 键)解析到第二个模块实例,导致首次工具调用崩溃(Cannot read properties of undefined (reading 'prepare'))——这是 0.1.6 的教训(2026-08-22 崩溃),0.1.7 起改为 peer 声明。重启时序:
pnpm install必须完整跑完(等到提示 Done)再重启 dsh web;不要在安装进行中重启。
No comments yet. Be the first to write one.