dsh-super-memory
Manifest valid★ 1dsh-super-memory(超级记忆)
跨压缩记忆插件:在同一个超长会话里被上下文压缩过几次之后,你早先问过 / 定过的事,模型先看到它,再作为参考结合这次的新条件综合回答——不会当新问题从零重来。
只做同一个会话内、跨压缩这一段。跨会话记忆不在范围内(那是另一类插件的事)。 插件自包含:不依赖任何其他记忆插件,不访问互联网,默认一次模型都不调用。
1. 这是什么 / 解决什么问题
一句话:上下文压缩会把早先的对话从模型眼前拿走(原文仍在磁盘上),这个插件在被拿走的那一刻把它存到你本机,之后只在相关的时候把那几段递回给模型。
一个真实场景:
- 同一件事在会话开头被讨论、定过一条结论,之后几轮里又被反复提起、修改,最后定稿。
- 聊到几百万 token 之后,上下文被自动压缩了两次,那些轮次连同当时的结论一起从窗口里消失。
- 你说:「按我们之前定下来的做法来吧」。
- 没有这个插件:模型只能反问「什么做法?」或者猜一个。 有这个插件:本地检索命中那些旧结论,把它作为参考注入——模型看到「此前是 X」,再结合你这次说的新条件回答。
它不做什么:
- 不做跨会话记忆。换一个会话就换一份记忆,互不可见(记忆按「会话所属工作区 + 会话 id」存放)。
- 不能替用户发消息。插件拿到的会话句柄没有 append 能力(实测边界,不是自我限制)——它能做的只有「把资料排进你下一轮的上下文」(见第 4 节),不能凭空让主模型立刻重答,也不会往会话里插一条消息。
- 不改写你的对话、不碰 DSH 的原始会话日志(只读)。
- 不读密钥、不自己连网(要用模型时经 DSH 的模型服务)。
2. 两种检索方式(默认只用第一种)
| ① 本地检索(默认) | ② 大模型辅助检索(可选) | |
|---|---|---|
| 什么时候用 | 每次提问都做 | 只有你在面板 ⑦ 里选了档位才启用 |
| 怎么找 | 本地词法打分:中文字符二元组 + 英文词,标题/关键词/正文分权重(BM25),过阈值才算命中 | ①压缩入库时让模型给每块补几个「你以后可能怎么问这块内容」的短词;②你点 ✕ 时让模型把你的问法改写成关键词,并判定哪一条真的强相关 |
| 花什么 | 0 模型调用、0 token;未命中一分 token 都不花(连注入文本都不产生) | 按次计费,走 DSH 的模型服务;压缩时每批一次调用,点一次 ✕ 一次调用(相同提问 30 天内不重复调用) |
| 延迟 | 提问热路径零延迟 | 压缩时的调用你在等压缩,感觉不到;点 ✕ 时最多等 8 秒(界面会显示「检索中」) |
| 命中率 | 词对得上就命中;「换个说法问」容易漏 | 明显更高(尤其是口语化、同义改写的问法) |
| 怎么切换 | 默认就是它 | 面板 ⑦「使用方式」三选一:不调用大模型(默认) / 调用主模型 / 调用指定模型 |
两条路可以同时存在,但提问时的检索永远是本地的:模型只在「压缩入库扩写」和「你点 ✕」这两个时机被调用,绝不进提问热路径。模型改出来的词也要自己过本地阈值——它没法凭一句话把不相关的内容塞进你的上下文。
3. 三件事怎么工作
| # | 时机 | 做什么 | 成本 |
|---|---|---|---|
| ① | 压缩时 | 把这次被压掉的内容本地入库:一层是 DSH 已经写好的摘要,按小节切开存(短、准、结论级);另一层是被压掉那段的对话原文(你问的 + 助手答的文字),外加模型读过的文件 / 检索结果原文(read / grep / glob / web_fetch / history_read)。 | 0 模型调用(选了模型档位才会给这些块补关键词) |
| ② | 压缩后 | 注入一份目录级「本会话此前脉络」(话题 — 结论),让新窗口一开场就知道过去聊过哪几大块。挑不出值得留的就一点都不注入。 | ≤ 300 token / 次压缩,自适应,可为 0 |
| ③ | 提问时 | 先在本地比一次关联性,命中才注入参考。 | 命中 ≤ 700 token / 轮(≤2 条、每条 ≤300 字符、每条至少 50 字符才值得注入);未命中 0 token |
几个刻意的取舍:
- 注入块抬头写着「历史只作参考,不作结论」:如果这次的新条件和旧结论冲突,回答要以新条件为准,并明确说「此前是 X,这次因为 Y 改为 Z」,不许静默改口。
- 压缩时不做任何模型提炼:摘要直接用 DSH 生成好的那份,L2 直接抄原文。想省钱的人不该被"入库也要调模型"绑架。
- 原文只在你要的时候才展开:日常注入的是压缩过的一行行结论;要逐字原文请用
history_read工具(只在用户明确要求时调用)。
4. 「✕」的完整语义
会话里每条助手回答下面都有一个 ✕(只在这个会话被压缩过之后才出现;没压缩过就没什么可找的)。点它的完整行为是:
- 立刻在会话窗口里出现一行「xxx(辅助模型)检索中,请稍后…」——不是弹窗,就在对话里;这一行期间没有按钮(结果还没出来,不给操作)。
- 插件用「你上一个问题」在本会话已压缩的历史里找候选,然后(选了辅助模型时)让模型把你的问法改写成关键词再找一遍,最后让模型判定哪一条是"强相关"。
- 强相关的定义是「会改变答案的那种」,不是「有点关系」。模型只回一个编号,回 0 = 都不强相关——弱模型有"老好人"倾向,硬挑一条正是最危险的失败模式,所以这一条被明确允许。
- 结果分两种:
- 有强相关 → 会话里显示「已用 xxx(辅助模型)搜索已压缩的历史:找到相关内容」,并给出两个按钮:
- 「打开原文」:在右侧栏打开一份逐字摘抄的 Markdown——只有强相关的那几段、只含提问与回答(不含工具调用与思考),纯代码生成、0 token;
- 「知道了」:把这一行收起来。 同时这段资料被排进你下一轮的注入:你直接继续提问(随口说一句也行)即可,主模型会带着它重新回答。
- 都不强相关 → 会话里如实显示「未搜索到强相关内容」,不注入任何东西,也不给「打开原文」按钮(没有相关段落就不产出文件)。
- 有强相关 → 会话里显示「已用 xxx(辅助模型)搜索已压缩的历史:找到相关内容」,并给出两个按钮:
- 必须说清楚的一点:插件不能让主模型"立刻重答"。它没有往会话里发消息的能力,唯一能做的是把资料排进你下一轮的上下文。所以点完 ✕ 之后的效果是——下一次提问时模型带着这段历史回答。界面上的那句话(「资料已备好 —— 直接继续提问即可」)就是这个意思。
另外两个细节:
- 本地检索已经强命中(粗筛最高分 ≥ 阈值 ×1.5)时,会跳过这次查询改写并如实告诉你「这次本地已经直接找到内容,跳过了辅助模型的查询改写(省一次调用)」。这是纯省钱,不影响结果。
- 检索调用最多等 8 秒(面板可调)。8 秒是上限不是每次都等满;真超时意味着这一次白点了——超时只写诊断日志,不影响你的正常提问。
5. 成本账
单次口径(全部是上限,不是配额):
| 项 | 数字 |
|---|---|
| 压缩后总览 | ≤ 300 token / 次压缩,可为 0(实测一次:190 token / 8 行 / 747 字符) |
| 单轮注入 | ≤ 700 token、≤ 2 条、每条 ≤ 300 字符(每条下限 50 字符)。700 才真的放得下"两条满额中文块 + 抬头"(2×≈255 + ≈105);设成 500 时预算会先砍掉第二条 = 实际只注入 1 条 |
| 实测一次注入 | 128 / 167 / 395 / 458 token(视命中几条、正文多长) |
| 入库扩写 | 每块只送前 600 字符、每批 8 块、单次输出上限 240 token(格式不对整批丢弃,保留原词) |
| 查询改写 | 单次输出上限 120 token |
| 每日调用上限 | 默认 0 = 不限(想设就填,到上限停用到次日,面板会显示原因) |
「已删除」的那条闸门:早期版本有一个会话累计注入上限(默认窗口的 2%)。2026-10-07 按实测删掉了它——一天只有 19~21 次辅助调用,整场超长会话的插件总开销约 6 万 token,而主模型累积已用 5.6 亿 token(约 0.01%)。而累计闸门唯一稳定的效果是「命中率莫名其妙下降」:用户不会想到是自己多点了几次 ✕ 把额度花完了,只会觉得这插件后来不灵了。成本控制改由单次口径负责(上面那张表)。
量级表(用于判断"到底贵不贵"):
| 场景 | 辅助花费 |
|---|---|
| 5 次压缩 + 20 次 ✕(同一会话,估算) | ≈ 3.3 万 token |
| 同一场会话里主模型的累积用量 | 数亿 token |
| 占比 | ≈ 0.01% |
说明:3.3 万 token 来自
scripts/measure-savings.mjs在真实会话日志上重放生产代码路径的实测(该脚本会把每次压缩都过一遍入库管线并模拟扩写调用);"数亿"是主模型侧的累计口径,两者不是同一把尺子的同一段,只用于看数量级。 上表的"实测一次注入 / 一次总览"数字来自scripts/inspect.mjs对同一份真实记忆库(122 块:摘要 60 + 原文 62,共 116,747 字符)的试检索输出,可用同一条命令复现。
不想花这钱:面板 ⑦ 选「不调用大模型」即可,插件的核心功能(压缩入库、检索、注入)完全不受影响,只是"换个说法问"更容易漏。
6. 架构与术语
三个层次(术语第一次出现时的解释):
- L0 = DSH 原生会话日志(
$DSH_HOME/sessions/**/session.v4.jsonl.zstd,多帧 zstd 压缩包)。插件只读它,永不写入。 - L1 = 摘要块:压缩时 DSH 已经生成好的摘要文本,按 Markdown 小节切开存下来,短、准、结论级。
- L2 = 对话原文块:从"被压掉的那段"里整理出来的对话文字(你问的 + 助手答的),带标题与关键词,另有读类工具的结果原文。
为什么 L2 不能直接用 L0 顶替(这是刻意的冗余,不是重复劳动):
- L0 是压缩包 + 机器格式:提问热路径要现场解多帧 zstd、再解析事件流,太慢——热路径必须以毫秒计。
- L0 里噪声是正文的 3.3 倍:实测工具结果的体量是对话正文的 3.3 倍。直接拿 L0 检索,等于让文件内容、命令输出把真正的对话结论淹没掉。
- 标题与关键词只存在于 L2:检索打分靠「标题 + 关键词 + 正文」三种字段加权(标题权重 3、关键词权重 4、正文权重 1)。L0 里没有"这段在讲什么"的标题,也没有"你以后可能怎么问它"的关键词——那两样是入库时现算的。
配套机制(都是为了让"注入"这件事本身不产生额外开销):
- 指纹去重:每块内容有一个 16 位内容指纹(sha1 前 16 位),同一个块永不重复注入;相似度 ≥0.6 的块也不再塞。
- 同话题冷却:连续两轮话题高度相似(词集 Jaccard ≥0.35)时跳过评分。
- 参考块挂住不放:命中过的参考块在本次压缩窗口内一直显示。撤掉它会让整份运行上下文快照的文本变化,DSH 会再追加一份(实测 1054 字符 ≈ 252 token)——挂着反而更省。
- 总览在窗口内保持稳定:同理,文本不变就不会反复追加快照。
7. 可选:模型辅助(默认不调用)
面板 ⑦「使用方式」三选一:
| 档位 | 含义 |
|---|---|
| 不调用大模型(默认) | 纯本地。提问零延迟、零 token;压缩后仍会注入总览与命中的参考。 |
| 调用主模型 | 用你当前会话的主模型补问法、换关键词;成本随主模型走。 |
| 调用指定模型 | 这两处改用你指定的模型(例如主对话用 pro、这里用更便宜的 flash)。 |
- 只在两个时机调用:① 压缩入库时给块补关键词;② 你点 ✕ 时改写查询 + 判定强相关。提问时的检索永远本地。
- 不指定、也不继承思考强度:插件不给模型传思考强度参数,也不继承主对话的设置——想调就到「设置 → 模型」里对该型号设置。插件里没有这个旋钮。
- 失败一律静默降级:提供方没注册、凭据缺失、限流、超时、输出不是合法 JSON,全部只写诊断日志,插件表现与"没这个功能"完全一致。
- 失败冷却:限流 / 额度类暂停 10 分钟,提供方/凭据类暂停 30 分钟;按路径(入库 / 检索)分别记,互不牵连。
- 超时:入库调用默认 8000ms;✕ 路径默认 8000ms(原为 4000ms,实测思考型模型 4 秒常常只吐思考、正文一个字都没有,那次点击就白费了)。都是上限,模型正常返回就立刻结束。
- 相同输入缓存:同一个提问 + 同一个路由的改写结果缓存 30 天(最多 200 条)。
- 日调用上限:默认 0 = 不限;填了才启用。
- 调用留痕:只记录元数据(时间、路径、成败、耗时、估算 token),写在全局数据目录的
dsh-super-memory.llm.jsonl——不含任何对话内容。
8. 隐私与边界声明
本节逐条列出插件实际用到的主机能力与数据边界,全部可在仓库源码中核对。没有列在这里的能力,插件不会使用。
依赖
- 没有任何第三方 npm 运行时依赖:
package.json里只有peerDependencies,没有dependencies/devDependencies/optionalDependencies。 peerDependencies声明的@deepseek-ai/*都是 DSH 宿主自身提供的包,由宿主在运行时注入。- 通过
ctx.inject拿到的服务:必需tools、systemPrompt、webServer;可选llm(拿不到时模型辅助功能自动消失,其余照常工作)。
读什么
- 只读会话日志
$DSH_HOME/sessions/**(多帧 zstd 逐帧解码):绝不写入、绝不修改、绝不删除。 - 只读会话标题投影缓存
$DSH_HOME/storages/session_projcache/sessions/<会话id>.json(用于在面板里显示与 DSH 侧栏一致的会话标题)。 - 状态查询对会话日志只做 stat 取大小,不读内容。
写什么
- 记忆本体只写会话所属工作区内的记忆目录,默认
<工作区>\.dsh-compaction-memory\:
<workspace>/.dsh-compaction-memory/
<sessionId>.jsonl 该会话的压缩记忆(一行一条块)
_trash/<时间>_<会话>/ 回收站(blocks.jsonl + manifest.json)
_audit.jsonl 删除 / 还原 / 清空的审计
_pairs.jsonl 未命中诊断的人工配对记录(不参与检索)
_readable/excerpts/*.md 「打开原文」生成的逐字摘抄(只在点 ✕ 时产生)
- 全局数据目录(默认
$DSH_HOME,可用环境变量DSH_SUPER_MEMORY_HOME指到任意目录)里只有这几个小文件:
| 文件 | 内容 |
|---|---|
dsh-super-memory.settings.json | 设置(跨工作区生效,含面板登记的已知工作区列表) |
dsh-super-memory.diag.jsonl | 诊断日志(默认上限 4000 行,超了自动压缩) |
dsh-super-memory.llm-usage.json | 今日调用次数与 token 估算 |
dsh-super-memory.llm-cache.json | 查询改写缓存(30 天、最多 200 条) |
dsh-super-memory.llm.jsonl | 模型调用留痕(只有元数据) |
- 除此之外不写任何地方。
storeDir可配置,但含..的配置会被拒绝并回落默认值。
删什么
- 所有删除路径先经
assertInside()校验——目标必须严格位于记忆目录之内(等于根目录也拒绝,..、绝对路径、符号链接逃逸一律拒绝)。 - 默认走回收站语义:条目移入
_trash/可还原,并写审计日志。只有两种情况是永久删除:trashEnabled显式设为false后的删除,以及面板的「清空回收站 / 彻底删除」——两者都写审计。
网络
- 不访问互联网。代码里没有任何对公网地址的请求(无硬编码外部域名 / URL),
lib/不引入node:http(s)/net/dns/tls。 - 面板(浏览器半边)里的
fetch全部指向本机 DSH 的插件接口/api/dsh-super-memory/*,同源同进程,不产生对外流量。 - 唯一"网外"行为是:你在 ⑦ 里主动选了模型档位之后,插件通过 DSH 的
llm服务调用你自己配置的模型。
命令执行
- 全仓库只有一处
spawn(lib/routes.js的revealInFileManager()),唯一用途是「在系统文件管理器里定位文件」(面板的「浏览」按钮):Windows 用explorer.exe /select,,macOS 用open -R,其它用xdg-open。 - 该
spawn不经过 shell,不接受任何来自请求的字符串作为可执行文件名或参数;目标路径由服务端自己用记忆目录根 + 会话 id 拼出,越界工作区直接 403。
凭据
- 不读取、不存储、不转发任何密钥或凭据。仓库内没有
.env读取、没有 keyring / 凭据存储访问。 - 模型调用一律经由 DSH 的
llm服务,插件侧只填provider+model两个字符串名字;真正的鉴权与密钥由 DSH 掌握。
失败边界
- 任何失败都只写诊断日志,不抛出到宿主、不阻塞对话:检索未命中、模型不可用、输出格式不合要求等全部在内部消化,最坏情况就是不注入参考,回答照常进行。
- 超时、冷却、缓存、日上限四道闸门之外,还有单次输出上限与每批块数上限——模型多说几句的代价不会失控(输出超限或格式不对就整批丢弃,保留原有词频,绝不写坏数据)。
关于 locale/*.json 与"是否需要构建"
locale/zh.json、locale/en.json是手写源文件,不是构建产物;随包分发并被exports的./locale/*.json导出。- 本仓库没有构建步骤:不存在 build / prepare / prepublish 之类会产出运行产物的脚本。
npm test只是自检,不产出任何运行产物,也不被运行时依赖。
本节写法说明:以上只写能从本仓库代码与
package.json核实的事实。若将来新增了本节未列出的能力,请同步更新本节。
9. 安装与使用
环境:DSH 0.1.7-rc.2 / 0.2.0-rc.1 及以上(见 package.json 的 peerDependencies;已在 0.2.0-rc.2 客户端实测)。
安装(三种方式任选):
# ① 插件市场:搜 dsh-super-memory 直接装(若你的 DSH 版本带市场)
# ② 从本地目录装(<本机插件目录> 换成你 clone / 解压出来的路径)
plugin_manager action: install_bundle target: <本机插件目录>
# ③ 从 npm 装(已发布时)
plugin_manager action: install_bundle target: dsh-super-memory
装完重启 DSH 客户端:宿主插件代码在进程里缓存,不重启不生效。成功标志:设置里出现「超级记忆」这一节,工具列表里出现 history_read。
卸载:plugin_manager action: remove_bundle target: dsh-super-memory。
卸载不会自动删除工作区里的 .dsh-compaction-memory 与全局目录里的那几个文件,要手动清理(位置见第 8 节)。
面板怎么用(顶部三句话就够):
- 它是干什么的:压缩时把被压掉的内容存到你本机,之后只把相关的那几段递回给模型;最多占 700 token/轮,挑不出相关的就一点都不注入。
- 要不要额外调用辅助大模型:在 ⑦ 里三选一(默认不调用)。调用辅助大模型需要你先在 DSH 官方模型接口里接入对应模型;插件不存密钥、不改写你的对话,只读会话日志。
- 平时不用管它——觉得它忘了本应提到过的历史内容,就点回答下面的 ✕ 让它再找一遍。
面板按"插件实际做的三件事"分板块(①②③),后面是 ④ 被保存的压缩内容、⑤ 回收站、⑥ 诊断与路径、⑦ 模型辅助。所有调参项收在顶部右侧的「参数设置」里(默认折叠):普通用户不展开也完全能用,想抠成本 / 命中率的人展开就能逐项调。所有参数改完即时生效,不需要重启。
常见问题:
- 插件刚重启时点 ✕ 会不会失败? 一般不会。面板拿不到工作区时,宿主会从会话日志里的 cwd 兜底;只有连 cwd 都读不到(比如这个会话还没被插件见过)才会报「未知工作区」——那时随便发一条消息让它见到本会话,再点一次即可。
- 命中率低怎么办? 三条路,按性价比排序:① 在 ⑦ 里选「调用指定模型」并挂一个便宜模型(命中率提升最明显);② 到「参数设置 → 检索参数」把「命中阈值」从 0.28 调低一点(更省 → 更容易命中,代价是可能注入不太相关的内容);③ 确认「入库」两项都开着(关了就真的没东西可检索)。
- 不想花 token 怎么关? ⑦ 选「不调用大模型」= 0 模型调用;再想彻底不动上下文,把 ① 的「压缩后注入总览」和 ③ 的「提问时注入记忆」两个开关关掉(入库照旧,已有记忆仍可检索,只是不再注入)。总开关关掉 = 不写入本地、不注入任何内容。
- 记忆在哪儿? 在你的项目文件夹里(
<工作区>\.dsh-compaction-memory\)。如果那个工作区是 git 仓库,面板 ④ 会检测到并给一个「帮我加忽略规则」按钮——记忆里是对话原文,提交上去就收不回来了。
10. 开发者:设计与教训
这一轮修掉的真缺陷(都配了会变红的断言)
| 缺陷 | 后果 | 修法 |
|---|---|---|
| 路径穿越写入 | 会话 id 来自请求体,..\..\.. 之类会逃出记忆目录写到任意位置 | 文件名只留安全字符 + 落盘前断言目标仍在 root 之内(双重设防) |
| ✕ 的资料只在第一步可见 | 组装上下文每个 step 都跑一次,清空点写在那里 → 真正作答的那一步看不到它 | 清空点搬到 turn/end,且只在"跨轮"时清 |
| 跨会话串味注入 | 记忆库按工作区放,同工作区里躺着多个会话的块 → 模型说出你根本没说过的事 | /diagnose 的候选只取目标会话的记录,没有 session 字段的旧记录一律不匹配 |
回填 NaN 导致冷启动看不到块 | const added = backfill(...) 而内部是 += await … → added 恒为 NaN → 索引永不刷新 | 去掉错误的 await 链,写出真实计数并在落盘后失效索引 |
outChars 恒 0 | 思考型模型只吐 reasoning 不吐正文 → 留痕里输出量永远是 0,"没输出还是没记账"分不清 | 按分片累加真实长度,并把"思考"与"正文"分开记 |
| 毫秒级陈旧判定 | 用 Date.now() 判断"结果是不是上一轮的",同一毫秒内发布的新结果被误判成陈旧整块不显示 | 改用发布序号(自增),不做时间比较 |
| Hooks 顺序 / TDZ | ① 提前 return 之前漏掉 hook → 组件卸载,✕ 按钮凭空消失;② onClick 引用了定义在它之后的 const run → 点击抛 TDZ 且被静默吞掉,表现是"点了没反应" | 所有 hook 提到提前 return 之前;run 保持函数声明(提升),并加源码级断言钉死 |
npm test 覆盖不到宿主半边 | lib/host.js 一度被写成 UTF-16 乱码而测试全绿——因为三套脚本只 import 了少数几个模块 | 新增 scripts/host-smoke.mjs:每个文件非空 / 无 BOM / 无 NUL + 逐个 node --check + 宿主入口与关键模块真的能 import + 导出契约在位 |
踩坑经验
- 不要用 PowerShell 做大段文本写入:历史上把
lib/host.js写成过 UTF-16 + NUL 的乱码(npm test还全绿)。改文件一律用精确替换工具,改完node --check。 - 并发编辑会互相覆盖:同一文件被两个进程同时改写时,后写的整份覆盖前一份。多件事要改同一个文件时串行做。
- 桩渲染两大坑:①
useRef必须像真 React 一样跨渲染复用同一个对象(每次新建会让"只在首次渲染记一个值"的组件每帧重置,于是测试里的陈旧判定和真机行为不一致);② 桩fetch要按 URL 分派(宿主就是多个接口),全都回同一个 payload 会让跨接口读取永远读到undefined。 - hook 槽位下标是数组下标,不是"第几个 useState":种错槽位(比如把
openLlm写成 6)会去改另一个状态,断言看着还在、其实形同虚设。 - 注入块必须稳定:任何一节上下文变化都会让 DSH 追加整份运行上下文快照;"撤掉一个块"也要花钱。
11. 变更历史
| 版本 | 关键变化 |
|---|---|
| 0.1.0 | 三件事落地:压缩时入库(L1 摘要 + L2 只留对话文字)、压缩后注入 ≤300 token 总览、提问时本地检索命中才注入。记忆存在会话所属工作区。 |
| 0.1.1 → 0.1.2 | 修「本轮第一个请求看不到召回」:改从 agent/inbox/spliced 事件抓提问(它比运行上下文求值早几毫秒);新增 stickyRecall(命中块挂住不放,省一次快照追加)。 |
| 0.1.3 → 0.1.6 | 面板按"三件事"重构成板块;④ 拆出「被保存的压缩内容」+「回收站」,会话标题与 DSH 侧栏一致;全局数据目录可用 DSH_SUPER_MEMORY_HOME 搬离系统盘。 |
| 0.2.0 | 审计驱动的安全与正确性修复:路径穿越(连 root 本身也拒绝)、写操作 CSRF 守卫、sessionLogBytes 契约、改设置丢工作区名单、?limit=abc 传播成 slice(0,-NaN)、history_read 回落日志加 64MB 闸、states 加 LRU。新增 scripts/unit.mjs(失败即退出码 1)。 |
| 0.2.1 | 成本口径正式改为「用户选档位 + 单次严格控制」;删除思考强度设置键与继承路径(那是 bug:又慢又贵);工作区自动识别;新增宿主冒烟守卫 host-smoke.mjs。 |
| 0.2.2 | 辅助成本砍掉一半:工具结果块不再送去扩写(它靠文件名/标题就能检索到,而体量是对话正文的 3.3 倍)、每批 5→8 块、扩写输出上限 300→240;本地已强命中时跳过查询改写(省一次调用,结果不变);点 ✕ 立刻显示「检索中」、✕ 超时 4s→8s;留痕与用量开始记录 token 估算;修掉陈旧判定的毫秒比较;删除会话累计上限(默认日上限改 0 = 不限)。 |
已取消的功能(以及为什么)
| 曾计划 / 曾存在 | 现状 | 理由 |
|---|---|---|
| 每次压缩生成一份"整会话可读 MD" | 已取消(连 0 token 也不做) | 会话窗口本来就能翻到历史——真正的问题是模型记不住,不是你看不到。为此多写一份文件不解决任何问题。保留下来的只有「点 ✕ 时的逐字摘抄」(短、按需、0 token)。 |
| 候选人工挑选界面 | 已取消 | 让用户从 10 条候选里挑一条,等于把检索算法的活派给用户;而且他挑的时候也未必知道哪条"会改变答案"。改成让辅助模型判强相关,判不出来就如实说没有。 |
| 「未命中诊断」界面 | 界面已取消 | "内容没进库 / 在库里没排上来 / 还在被压掉的原文里"是我们的排障信息(诊断日志里本来就有);用户真正的问题是"这段到底聊没聊过",而他点 ✕ 时辅助模型会直接回答这个问题。宿主侧的 /diagnose 路由保留给 ✕ 复用。 |
会话累计注入上限(sessionBudgetRatio) | 已删除 | 见第 5 节:它唯一的稳定效果是"命中率莫名下降",因为用户不会把自己点 ✕ 的消耗归因到额度上。成本改由单次口径负责。 |
思考强度设置键(llmReasoningEffort) | 已删除 | 插件不需要这个概念,也不该继承主对话的(继承会又慢又贵,实测确认是 bug)。要调就去 DSH 官方「设置 → 模型」。 |
未实现 / 不打算做:跨会话记忆;本地向量检索;把注入内容写回会话(平台没有 append 能力)。
12. 自检脚本
需要 Node ≥ 22.15(原始会话日志是多帧 zstd,用到 zstdDecompressSync;低版本会直接报"不支持 zstd")。DSH 自带的 node 在 <DSH 安装目录>/resources/runtime/primary-runtime/dependencies/node/bin/node.exe。
npm test 会真的报错(退出码非 0),它跑四套:① 单元测试(纯函数 + 历次真实 bug 的回归)、② 面板静态自检(设置键 / CSS 类名 / API 路径 / 板块齐全 / 数字框精度)、③ 面板渲染冒烟(用桩 React 真正渲染面板整棵树)、④ 宿主半边冒烟(每个文件非空无 BOM 无 NUL、逐个 node --check、宿主入口真能加载、导出契约在位)。
其余脚本用真实会话日志做端到端验证:
# 0) 四套自检(不需要会话日志;`npm test` 就是这四条)
node scripts/unit.mjs
node scripts/panel-check.mjs
node scripts/panel-render.mjs
node scripts/host-smoke.mjs
# 1) 纯离线:真实日志跑通「压缩入库 → 检索 → 总览 → 注入样例」+ minScore 标定表
node scripts/selftest.mjs <session.v4.jsonl.zstd>
# 2) 宿主半边联调:假 cordis ctx + 真实日志,跑通入库/总览/命中/未命中/开关/面板 API/history_read
node scripts/harness.mjs <session.v4.jsonl.zstd> [临时工作区]
# 3) 验收证据导出:库条目统计、总览预览、试检索命中、最近打分日志
node scripts/inspect.mjs "<工作区>" "<一个旧话题>"
# 4) 省钱实测 + 阈值标定:扩写调用 / 输入 token 的 A/B,以及「强命中跳过改写」门槛
node scripts/measure-savings.mjs <session.v4.jsonl.zstd> [--json]
# 5) 时序排障:压缩事件、投影里还剩哪些用户消息、每份运行上下文快照的字符/token
node scripts/compactions.mjs <sessionId> [fromSeq toSeq | --snap | --raw <seq>]
# 6) L2 抽取完整度审核:日志里"应当收进来的文字"vs 插件实际入库字符数
node scripts/shadow-audit.mjs <sessionId> <fromSeq> <toSeq> [插件记账的rawChars]
会话日志位置:$DSH_HOME/sessions/**/session.v4.jsonl.zstd(多帧 zstd,脚本里按帧解码)。
设计论证(不写进界面,只留在这里)
minScore为什么是 0.28:真实会话(3.8 MB / 5388 事件 / 2 次压缩)实测——相关历史问题的 L2 块 top 分落在 0.72–1.50、L1 摘要块 0.02–0.48;完全不相关的问题 0.00–0.14。两者分得很开,0.28 取在中间偏保守的一侧。跑几天后可以看诊断日志里hit:false reason=below-threshold的topScore分布,漏召多就往下调。- 「强命中跳过改写」为什么是 1.5×:在某真实会话的 92 条历史提问上标定——按 1.5×minScore(0.42)会有 29 条跳过改写,而 2× 只多跳 1 条、3× 多跳 9 条却会把"擦边命中"一起跳掉(那才是真正需要辅助模型的场景)。取
max(0, minScore, minScore × 1.5):即使有人把阈值设成 0,也不会出现"任何候选都算强命中"。
License
MIT
Comments
Loading…