Pular para o conteúdo principal
D

dsh-memory-router

dcd887/dsh-memory-router

Intercepts DSH tool results before the model reads them: dedupe cache for repeated calls, a declarative decision graph that replaces large outputs with structured digests, an injection gate, and an honest token ledger that includes a negative-return account for over-compression. Measured 1400-1600 tokens saved per hit; the cumulative ledger is currently negative and the README says why, including that it cannot skip model turns.

Instalar

dsh plugin --profile web add github:dcd887/dsh-memory-router

README

dsh-memory-router

一个挂在 DSH 工具调用边界上的 Token 压缩插件:把模型「已经读过一次的东西」和「读不完的长输出」在进入上下文之前处理掉,并把省下的 token 记成账。

Node Tests Status License

dsh-memory-router demo


痛点

Agent 的每一次工具调用都有一次「回读」:工具返回 8000 字,模型为了决定下一步,要把这 8000 字重新读一遍。

三种情况下这是纯浪费:

情况浪费
同一个文件/命令被调用两次第二次的结果和第一次一样,模型又读一遍
工具返回 8000 字,模型只需要 4 个字段剩下 7900 字白读
状态类问题每次都要重新注入一遍现场同样的内容反复付费

HarnessMM(arXiv 2610.32813)把这类浪费称为「工具反馈后的无效推理回调」,并给出 EDG / GDR / CCS 三个机制。这个项目是那三个机制在 DSH 上的一个可运行等价实现。


三个模块

模块做什么状态
dedupe 去重同工具同参数,TTL 内直接复用上次的文本,工具不再执行生产验证
edg 决策图声明式规则:匹配工具+参数,守卫为真就跑本地动作,用结构化结果替换原始输出生产验证
liveState 会话延续自动落盘「在处理什么、动过哪些文件」,新会话第一轮注入生产验证
inject 注入闸门内容哈希去重 + 长度上限 + 账面行限频单测通过

真实数据

生产会话实测(不是模拟):

场景原生插件后省
读税则大表(8972 条)6031 字298 字(总条数 + 3 条样本 × 4 字段)1433 token
读盲推记录(26 KB)8035 字1707 字(标题骨架 + 错误行 + 结论行)1582 token
重复执行同一条命令重新执行命中缓存,未执行账本记 dedupe

十案例 A/B(node scripts/ab.mjs,可复现):

项数值
命中案例6/10
命中时平均节省96%
合计(挑过的样本,是上界)93%
按三成命中率折算约 29%

安装

git clone https://github.com/dcd887/dsh-memory-router.git
cd dsh-memory-router && npm test      # 6 个测试套件,92 项

以本地插件方式挂进 DSH profile:

# 在 profile 的 node_modules 下建链接(Windows 用 mklink /J)
ln -s /path/to/dsh-memory-router ~/.dsh/profiles/web/node_modules/@local/dsh-memory-router

重启 DSH 后生效。决策图配置放 ~/.dsh/memory-router/edg.json(示例见 edg.example.json)。

一个节点长这样:

{
  "id": "tax-table-pick-fields",
  "match": { "tool": "read", "args": { "file_path": { "$re": "hs_codes" } } },
  "guard": { "minChars": 2000 },
  "then": [
    { "act": "readJson", "path": "${args.file_path}", "limit": 3,
      "pick": ["hs_code", "name", "import_tariff", "category"] }
  ]
}

守卫只有五个纯本地算子(exitCode / fileExists / regex / minChars / jsonPath),动作只有四个只读动作(readJson / readFileSlice / grep / stat)。写 exec 会直接抛错——决策图写错,最坏结果是退化成没有它。


注入闸门(0.1.3)

「状态注入」是这套插件里唯一会主动往上下文里塞东西的模块,所以它也是唯一可能变成负收益的模块。

实测(7 天、87 次注入):注入花掉 21,945 token,同期压缩只省下 4,238,净亏 17,707。

原因是它把「每一条看起来像在问进度的话」都当成注入时机,同一段状态被反复灌了 87 次;而它的真实价值只在开新会话那一次。

0.1.3 给了三个闸门,默认值保持旧行为,不破坏已有配置:

配置项默认作用
inject.firstTurns0大于 0 时,只在会话前 N 条用户消息里注入
inject.oncePerSessionfalse为真时,每个会话最多注入一次
inject.strictStatefalse为真时只认 intent === 'state',不把 mixed 算进来

推荐档位(既保住「新会话自动带上次进度」,又把开销压到十分之一):

injectMode: 'context'
inject:
  firstTurns: 2
  oncePerSession: true
  strictState: true
  maxChars: 500

同一批使用强度下的估算:注入从 87 次 / 21,945 token,降到每会话最多 1 次 / 约 130 token。

闸门有专门的自测:tests/inject-gate.mjs(首条注入、二次不重复、第三条不注入、mixed 不注入)。

局限(请先读完这段再决定要不要 Star)

累计净收益目前是负的。

裁剪省 1223  ·  去重省 0(2 次)  ·  决策图省 3015(2 次)
重读赔 0     ·  注入花 14755     ·  净 −10517

原因有两个:

  1. 注入开销是历史累积的,其中大部分发生在「注入闸门」装上之前;
  2. 我的验证方式把它放大了:一直在同一类问句上反复测。

单次命中省 1400—1600 token 是真的;「长期净收益为正」还没被证明。

与论文的差距:不能跳过模型回合。

论文的 GDR 是「用持久决策替换后续推理回调」,我们做不到——DSH 的 PostToolDecision 只有 accept / block 两支,没有「执行下一个动作」这一支。

所以本项目做的是:插件自己执行确定性动作,把结果塞回这次工具调用的返回值。省的是上下文体积,不是回合数。

论文的 87.93% 是在 HumanEval 这种强重复链上测的,照搬那个数字会误导。我们这类写作与调研负载,能拿到的是个位数到十几个百分点的往返节省。

我们自己加了一个负收益科目:reread。

论文没有这一项。没有它,账本会系统性虚高——压缩越多,账面越省,实际可能越贵(模型压缩过头 → 重新读一遍 → 更贵)。所以每次压缩后同一调用再来,记一笔亏损。


架构

flowchart TD
    U[用户消息] --> IB[agent/inbox/claimed<br/>判意图 · 记现场]
    IB --> PS[agent/pre-step<br/>systemPrompt.context]
    PS --> G{注入闸门<br/>内容哈希 + TTL}
    G -->|内容没变| SKIP[不注入]
    G -->|内容变了| INJ[注入 live_state 或交接文档]
    INJ --> M[模型]
    M --> T[模型发起工具调用]
    T --> PE[tools/post-execute<br/>结果回来,模型还没读]
    PE --> D{① 去重<br/>同工具同参数}
    D -->|命中| CACHE[复用上次文本]
    D -->|未命中| E{② 决策图<br/>match + guard}
    E -->|命中| LOCAL[本地执行<br/>readJson / grep / stat]
    E -->|未命中| TRIM{③ 阈值压缩}
    TRIM --> OUT[最终文本进入上下文]
    CACHE --> OUT
    LOCAL --> OUT
    OUT --> LEDGER[(metrics.jsonl<br/>省 / 花 / 赔)]

处理顺序是固定的:去重 → 决策图 → 阈值压缩 → 缓存最终文本。任一步抛异常都回落,不阻断主循环。


记账口径

净收益 = 裁剪省 + 去重省 + 决策图省 − 重读赔 − 注入花 − 摘要花
科目记什么
trim压缩省下的
trim-skip省得太少没压的次数(不进账)
dedupe复用缓存省下的
edg本地执行替换输出省下的
reread负收益:上次压过,同一调用又来了
inject注入花的

查看账本:

node scripts/stats.mjs

目录

index.js            主逻辑(钩子接线、注入、去重、决策图、压缩)
lib/dedupe.js       哈希 + TTL LRU 缓存
lib/edg.js          决策图载入、匹配、守卫求值、本地动作
lib/live_state.js   会话延续状态
lib/metrics.js      CCS 记账
edg.example.json    十个节点的示例决策图
scripts/stats.mjs   账本汇总
scripts/ab.mjs      十案例 A/B(可复现)
tests/              6 套 92 项

License

MIT(提交前请补 LICENSE 文件)。

致谢

对标 HarnessMM: https://arxiv.org/abs/2610.32813

Plugins relacionados