← 全部插件 · 当前:dsh-handoff
典型场景:一次跑很久的任务——大型迁移、反复失败重试、无人值守的长目标——把上下文顶到窗口上限, 而同会话压缩已经压不动了:装了大型工具集的 envelope 本身就占掉一大块、一次超大工具调用是个不可分单元、 或者「摘要的摘要」逐轮失真。这时候你要的不是把历史摘要得更狠,而是换一条干净会话接着干, 并且说清楚为什么换、换过去的是什么。
它不自作主张。三重门同时打开才动手:压力真的到顶、观测到同会话压缩已经失败、agent 正处于空闲边界。 判定只用会话日志证据,不靠对服务的探测。
包产物 0.1.0(私有 Gitea registry,latest)· 许可 MIT · 零运行时依赖 · 分发:私有 registry / git 依赖(钉标签)· 源码仓库(私有,需账号或会员)
DSH 已经会在接近窗口上限时原地压缩(阈值 0.8 × 窗口),并在提供方确认溢出后压缩重试。这覆盖了绝大多数长会话。 本插件补的是它结构上覆盖不了的部分——这四条都写在压缩自己的 README 里,不是猜测:
| 情形 | 为什么压缩没用 |
|---|---|
| envelope 主导 | 压缩只压缩派生历史,绝不缩减系统提示词、工具 schema 或会话前缀。装了大型 MCP 工具集时,那部分本身就吃掉可观比例。 |
| 不可分单元 | 平衡摘要无法拆分一个不可分单元。一次超大工具结果、一条超长粘贴,压缩会直接判定「没有可压的区间」,什么都不做。 |
| 摘要的摘要 | 每次压缩都把上次的摘要当普通历史再压一次。多轮之后信息逐级失真,而 token 仍会再次触顶。 |
| 收敛失败 | 压缩重试用尽后只记一条警告,然后携带超预算历史继续——下一次请求就可能在提供方侧溢出。 |
交接的代价明显更高:一次摘要调用、一条新会话、以及用户的注意力被打断。所以默认立场是不交接, 只有压缩被证明无解时才动手。这也意味着它在正常使用里很少触发——稀有是设计正确的证据,不是没生效。
handoffRatio(默认 0.92)× 路由模型的 contextWindowcontextWindow 就不动——不猜compaction/* 事件)agent/status === idle 动手:没有 driver 在调度、没有打开的轮次maxChainDepth(默认 8)拒绝继续接力tool/result,不再往下接力
判定只用日志证据。这一点是被实测纠正过来的:曾经有一条「宿主层探不到压缩服务 ⇒ 没有后端」的证据,
而 dsh-web-app 模式恰恰会禁用宿主层的压缩行、把后端挂到 agent preset 上——
探测结果永远是「缺失」,而压缩工作得好好的。那条证据单独就能放行,于是插件会去抢压缩的活,
正是整个设计要避免的那一个失败。现在它被删掉了,并有一条回归测试守着。
后继会话是空上下文——这正是目的。它拿到的是第一份、也是唯一一份交接材料,由一条 <session-handoff> 包裹的消息投递:
| ① 模型摘要 | 逐字回放前任的 system 与工具 schema,再加固定指令——这样辅助调用成为会话的真正前缀,提供方热前缀缓存可复用。八段结构:原始意图 / 当前工作 / 下一步 / 文件 / 错误 / 未完成 / 关键上下文 / 前任会话。 |
|---|---|
| ② 确定性回退 | 模型调用失败时不放弃交接,而是从日志重建简报:最后一条用户输入、最近的助手输出、被读改过的文件、未完成的待办、未结清的审批、最近的错误。质量更差,但永远可用——而且「上下文满了」与「模型正在挣扎」是相关的。 |
| ③ 按优先级裁剪 | 超出预算时先砍「错误与修复」「未完成任务」,最后才动别的;而「原始意图」与「下一步」永不丢弃——丢了它们,简报就不值得换一条会话,宁可放弃交接。 |
不在公共 npm 上:dsh-handoff 这个名字已被他人占用,所以用 scoped 名
@hakehuang/dsh-handoff,包放在私有 Gitea registry 上,
需要账号或会员;匿名请求返回 401。三种方式:
# 一次性:让这个 scope 走私有源
npm config set @hakehuang:registry https://zephyrrtos.cn:3000/api/packages/hakehuang/npm/
# 装进 DSH profile(在 profile 目录里)
npm i @hakehuang/dsh-handoff
npm i "git+https://zephyrrtos.cn:3000/hakehuang/dsh-handoff.git#pkg-v0.1.0"
⚠️ git 依赖会把整个仓库交给消费者(git 不认 files 字段);
registry 路径则只交出打包白名单里的 17 个文件。要边界就钉 registry,要方便就钉标签。
# profile 的 package.json
"dependencies": { "@hakehuang/dsh-handoff": "link:../../path/to/dsh-handoff" }
# ~/.dsh/profiles/<profile>/cordis.patch.yml
- insert:
- id: handoff
name: '@hakehuang/dsh-handoff'
config:
handoffRatio: 0.92
⚠️ name 必须逐字等于包名。装载器按包名解析这一行,写错的话静默挂不上,不报错。
装完重启 DSH,日志里应出现:
handoff: successors will be created from the root context, so they survive this plugin being reloaded
handoff: ready — handoffRatio=0.92 maxChainDepth=8 mode=auto ledger=…
没有这两行就是没挂上。装上后可用 /handoff status 查看当前会话的实测判定与原因;
/handoff 可以立刻交接一次(跳过压力判定,但不跳过护栏)。
handoffRatio | 0.92 · 门 1 阈值,必须 > 压缩的 0.8,否则装载失败 |
|---|---|
minGainRatio | 0.05 · 单次压缩释放少于窗口这个比例算「停滞」 |
maxChainDepth | 8 · 一条谱系最多被接力多少次 |
maxBriefTokens | 8192 · 简报硬预算 |
allowDeterministicFallback | true · 模型摘要失败时退回确定性简报;设为 false 则宁可不交接 |
carryGoal | false · 在新会话新建等价目标并武装续行。默认关:它会放大「跑偏」 |
toolFilter | null · 可收紧后继会话的可用工具 |
mode | auto · 自动交接;manual 只响应 /handoff |
这一节照实写,因为「只讲优势不讲局限」正是本站点其他工具要防的那种失真。
contextWindow = 1,000,000,一条极长对话也才到 45%(约 44.9 万 token)handoffRatio: 0.92 ≈ 92 万 token,约等于那条对话的两倍——在你的实际用法里它几乎不会触发cwd(后继会在侧栏看不见)dsh-web-app profile 上实测过;其他模式未验证
发布凭据的现状:目前用管理员账户密码发布,这是本项目自己登记过的治理问题
(建议换成只勾 write:package 的 Gitea 令牌)。这也意味着消费者拿到的是私有源,匿名不可装。
| 源码仓库 | hakehuang/dsh-handoff(私有)· 标签 pkg-v0.1.0 锚定产包提交 |
|---|---|
| 发布与验证记录 | 仓库内 PUBLISHING.md(含消费者视角验证:装出 1 个包、17 个文件、插件行等于包名、不含台账) |
| 设计文档 | 仓库内 DESIGN.md——每个「为什么不那样做」都有实测依据 |
| 全部插件 | /plugins.html |
| 问题与讨论 | 问 Zephyr 智能体 · 或加入工作组 |