DeepSeek Harness Plugin Hub

发布与管理完整 Harness Profiles,发现适合你的插件。

探索

插件目录环境预设文档中心动态

社区

发布插件联系我们报告问题

相关链接

Plugin Hub GitHubDeepSeek Harness 官方项目系统状态隐私说明
© 2026 DeepSeek Harness Plugin HubPowered byPaxTech

独立、非官方社区项目,与 DeepSeek 官方无隶属、授权或背书关系。

Attention Health — DeepSeek Harness 插件(DSH Plugin)
← Plugins
A

dsh-attention-health

Attention Health

上下文健康监测 + 内容退化守卫 + 零模型交接文档(host + web UI 双面)

插件会安装到这里;不确定时保持 web。

npx -y @deepseek-ai/dsh plugin --profile web add github:donghangxunlang-cmd/dsh-attention-health#99ef870a3ca86bb0e92eeb0b989e6e376bb9b8b1
README兼容性版本

兼容性与来源证明

Attention Health 以 dsh-attention-health 发布,当前版本为 0.1.0。Plugin Hub 会校验它的 manifest,并保存精确安装来源,便于复现安装结果。

DSH 兼容范围
*
运行环境
web
发布来源
github
Registry 更新时间
2026/9/20

版本

0.1.0stable
2026/9/20

相关插件

正在加载相关插件…

最新版
0.1.0
DSH
*
HMR
重启进程
Tree shaking
未声明可安全裁剪
解包体积
未提供
文件数
未提供
Surface
web
许可证
MIT
发布源
github
GitHub
★ 0
周下载
0
最近提交
2026/9/20
查看源码 ↗
README Badge

点击下方 Badge 复制 Markdown,粘贴到 README 即可。

这是你的 Plugin?认领权益 · 优先安全扫描

验证 package.json 声明的 GitHub 仓库,即可管理这个公开页面。认领后,Hub 会优先安排当前版本的安全扫描,并在通过后公开展示结果。

认领这个 Plugin →
报告问题
DeepSeek Harness Plugin Hub
ProfilesPlugins分类动态文档登录管理 Profiles
ProfilesPlugins分类动态文档登录

相关插件

继续浏览 productivity-workflow 分类下经过校验的插件。

Acp App@deepseek-ai/dsh-acp-appdsh ACP 配置文件包:基于 dsh-base 的仅限自动化的 JSON-RPC stdio 和进程生命周期管理Client Ui Task Board@linxin666/dsh-client-ui-task-board面向 DSH Web GUI 的主机权威任务面板,支持实际会话执行、主机 cron 调度以及可选的跨平台空闲睡眠保护;以挂载方式提供,无需修改 DSH 源代码。Web All@linxin666/dsh-web-allDSH Web UI 全家桶聚合插件:一键安装全部功能插件(task-board / git-graph / pet / remote-web-ui / web-ui-settings / skin-center / community-plugins / compat shim)。compat 桥接层已并入本包(src/client),无需独立 compat npm 包。Agent Teams@nanmicoder/dsh-agent-teamsAgentTeams for DeepSeek Harness:通过自然语言驱动多智能体团队协作(队长、成员、具有依赖关系的任务、消息传递),并在 Web GUI 中提供树状监视器

README

dsh-attention-health

给 DSH(DeepSeek Harness)做上下文健康监测 + 零模型交接文档生成。

当前状态:三个阶段均已落地并实测生效(离线工具 → host 插件 projection → Web UI 提示条)。


安装

标准 DSH 插件包(host + 浏览器双面,一个包):

dsh plugin --profile web add -w dsh-attention-health
  • -w 不能省:profile 自身是 pnpm workspace 根,否则会报 ERR_PNPM_ADDING_TO_ROOT;
  • 包管理器请用与 profile 一致的 pnpm 主版本(profile 的 node_modules/.modules.yaml 记着 创建它的版本;版本不一致会报 ERR_PNPM_VIRTUAL_STORE_DIR_MAX_LENGTH_DIFF);
  • 从源码/GitHub 安装:dsh plugin --profile web add -w github:<你的用户名>/dsh-attention-health;
  • 卸载:dsh plugin --profile web remove -w dsh-attention-health。

装完必须重启 DSH(插件行不会热加载;浏览器半只需刷新页面)。 本机实测:dsh-node.err.log 出现 [attention-health] projection "attentionHealth" registered, 且页面启动图里出现 <包名>/client.js。

关于本项目

由 AI 主导开发——需求、验收标准与真实场景测试由作者提出,代码、测试与文档由 AI 在长程会话中编写, 并经过多轮独立审查与真实数据校准(670 项自动化测试;阈值来自 2,244 个真实思考块与 79 个会话实测)。 它不是随手生成的玩具,也不是专业团队打磨的产品。

维护状态:个人项目,维护能力有限;欢迎 issue 与 PR,但不承诺 SLA。


这是什么

三个能力:

  1. 实时思考守卫(2026-09-16 新增)—— 在生成过程中逐帧检测思考的复读 / 空转, 命中就立刻中止本轮并提示原因。这是本项目唯一会主动干预的能力。
  2. 会话健康自检 —— 从会话日志机械推断注意力退化风险
  3. 交接文档生成 —— 从会话日志机械提炼出可直接交给新会话的结构化 Markdown

除守卫外,其余能力都不调用任何模型;守卫本身也只做字符串判据,不额外发起模型请求。

核心设计立场:要交接的时刻,正是模型已经不可靠的时刻。所以交接文档绝不能靠 LLM 写——让退化的模型总结它退化的原因,产出必然是垃圾。会话日志是 append-only 的事件溯源记录,数据本身完好,与模型状态无关。

与生态插件的分工(2026-09-16 定位收缩)

DSH 生态里已有更成熟的上下文洞察工具,本插件刻意不重复它们:

你想要的用哪个
上下文占用 / 构成 / 趋势、逐请求浏览、成本估算(元)dsh-context —— 它读官方 token-meter 投影,比本插件自算的更准更细
一键导出交接文档(LLM 总结式,交互更直接)dsh-handoff-button 等
按规模 / 成本 / 缓存判断"要不要新开"dsh-context-compass 等(已覆盖,UI 更完整)
按内容退化信号(思考打转 / 输出复读 / 守卫命中)判断"要不要交接" + 零模型交接文档本插件(生态里没有替代)

所以它只做生态不做的三件事:

  1. 退化 / 异常检测 —— 思考打转、输出复读、符号堆砌(真实事故:单思考块 53.5 万字符、 同行重复 69,667 次 ≈ 一次 ¥1.02)。
  2. 由检测得出的行动建议 —— 继续 / 交接。生态里的会话健康工具(如 dsh-context-compass) 主要看规模 / 成本;本插件看的是内容退化(思考打转 / 输出复读 / 守卫命中), 并据此主动给出"该交接了"的结论。
  3. 零模型交接材料 —— 交接时刻正是模型不可靠的时刻,所以这份材料绝不用 LLM 写。 这与"LLM 总结式"竞品是不同取舍,不是单纯的优劣。

成本明细只在结论是交接时作为依据显示,其余情况交给 dsh-context。


用法

# 健康自检:当前工作目录的最新会话
node handoff.mjs --health

# 健康自检:指定会话(支持 ID 前缀)
node handoff.mjs d346d3e8 --health

# 生成交接文档到文件
node handoff.mjs -o HANDOFF.md

# 指定会话 + 限制规模
node handoff.mjs 6938bea6 --max-turns 30 --max-requests 20 -o HANDOFF.md

# 列出会话
node handoff.mjs --list

# 生成 + 打印解析统计
node handoff.mjs --stats -o HANDOFF.md

# 汇总历史交接记录(阈值回溯素材,零 token;见下文「校准留痕」)
node handoff.mjs --calibration

--health 输出示例(2026-09-15 实测,对象是本机真实发生过的退化事故会话):

会话        :session-SAMPLE-9f44ebc9-a5fd-c8e9fc6ed02d
健康评分    :50 / 100 — 上下文偏大
上下文规模  :353,386 token
占声明窗口  :35.3%(窗口 1,000,000)
有效占用    :58.9%(有效窗口 600,000,系数 0.6)
思考异常    :1 次(打转 1 / 其他 0),最长同一行重复 69,667 次
发现:
  - 已过有效窗口一半(评分 −25)
  - 思考过程出现打转:1 次(最长同一行重复 69,667 次)(评分 −25)

上面是 2026-09-16 边界设定后的真实输出。同一条会话在旧系数(0.5)下是 30 分 / 上下文过大, 现在 50 分 / 上下文偏大 —— 因为有效窗口从 500K 变成 600K,分母变大、判定变宽。 这是有意的:Chroma 的 Context Rot 研究给的是「声明窗口的 50%~60%」(见下文第 3 节), 0.5 取的是区间下端、0.6 取上端,同时让"何时劝交接"不再早到离谱(见「双口径」一节)。

那次事故:模型在思考过程里把「好。」重复了 69,667 次,reasoningTokens 打满 256,000 才停 —— 而同一轮的最终回复完全正常,所以只看输出的旧判据一个字都没报。 现在思考退化单列一行,并且参与评分(见下文「退化检测」)。

等级文案(正常 / 留意上下文 / 上下文偏大 / 上下文过大)只描述上下文规模, 不含行动建议;要不要交接,看交接文档里的「维度 A 质量(异常信号)」与命中率成本。 评分为启发式指标(阈值内部校准、未做回溯验证):看同一会话的趋势可以,不代表精确测量。


生成的交接文档包含什么

章节内容数据来源
0. 为什么要交接健康评分、上下文规模、风险信号usage + 事件分析
1. 会话标识ID、标题、cwd、preset、模型、窗口、时长header + request/context
2. 当前目标goal 的 objective / phase / 轮次goal/change
3. 用户请求主线全部人类输入(含问答回答)user/message + ask_user_question 结果
4. 各轮进度概要每轮的请求、结果、工具数、错误数、结束原因turn/* + assistant/message
5. 涉及的文件路径 + 操作类型与次数tool/call 的 arguments
6. 待办事项checkbox 列表todo/write
7. 遇到的错误工具失败 + 轮次异常结束tool/result.isError + turn/end.reason
8. 工作量统计轮次、步骤、工具分布、token全量事件

会话内一键交接(UI 按钮,2026-09-15 新增)

会话里健康提示条展开后有一个「复制交接内容」按钮,点一下:

  1. 浏览器请求 host 半注册的本地路由 GET /attention-health/handoff?sessionId=…;
  2. host 半用纯规则折叠该会话的事件流(活会话读内存、冷会话读压缩日志), 产出精简交接 Markdown —— 不调用任何模型、零 token;
  3. 内容直接进剪贴板,界面提示复制了多少字符 / 多少行,粘贴到新会话即可。

实测:当前会话 3ms 出结果、4516 字符;最大会话(9745 事件)也是 4ms 级别。

它是怎么实现的(接口都查证过)

环节用的接口说明
生成内容host 半 webServer.register({kind:'prefix', path:'/attention-health', handler})原生 (req,res) 通道。官方 @Remote/ctx.remote 需要 typert 生成链,本机无源码 checkout 无法构建,故不用
读会话事件活会话 ctx.sessions.get(id).snapshotEvents();冷会话 ctx.sessionQuery.readSession(id)活会话优先,拿到的是内存中最新内容
复制剪贴板navigator.clipboard.writeText,兜底 document.execCommand('copy')两条都失败时明确报错,不假装成功

⚠️ 注册路由必须用 ctx.inject(['webServer'], cb),不能写成 ctx.get('webServer'): profile 的加载条目是并行初始化的,插件常常在 webserver 之前跑完 apply, 那一刻 ctx.get 只能拿到 undefined,路由就悄悄丢了(浏览器点按钮报 HTTP 404)。 这个坑真实踩过,test/plugin-test.mjs 里有一项专门守着它(mock 的 get('webServer') 故意返回 undefined,只有走 inject 才能注册成功)。

为什么不用"自动新开会话并预填"

曾经实现过:客户端 uiWorkspace.startSession() 新开会话 + 新会话 slot props 的 inputActions.setDraft(text) 写入草稿。这两个接口本身确实存在(都是官方 contract), 但实测客户端 ctx.get('uiWorkspace') 在 apply 阶段同样拿不到服务(与 host 侧同一个 时序问题),功能不可用。按用户决定改为剪贴板方案:不依赖任何服务时序、一步到位、零副作用。

失败时不会静默降级:按钮旁会显示具体原因(后端错误 / 剪贴板被拒等)。

交接内容结构(2026-09-15 提炼质量增强)

文档头之后(「给接手方」操作指引 → 零模型声明 → 隐私提示 → 生成时间 / 数据来源):

小节内容要点
为什么交接(健康度)评分、上下文规模、有效占用,并附评分机制说明(分数只由单会话上下文规模驱动、与时间无关;质量维度需伴随退化信号才给交接结论:≥70% 有效 + 退化 → 建议交接、≥90% 有效 + 退化 → 立即新开会话;只有规模大、无退化则交给压缩/成本维度)
会话ID、最新标题(含曾用标题)、cwd、preset、模型、时间跨度
最新目标优先最新 goal/change;无 goal 时取最近一条用户请求;首尾差异大时并列「最初目标 / 最新目标」并提示以最新为准
用户请求主线超长正文(>320 字)自动压缩成摘要,原文进附录 B
关键决策与未决问题从助手文本与问答回答里抽句,去重、按时间排序、标注来源轮次与角色。两级词表:主表是 25 个取舍词(根因 / 结论 / 决定 / 改为 / 放弃 / 必须 / 风险…);当整场会话主表命中不足 3 条时,自动用通用结论词(发现 / 推荐 / 因此 / 最优 / 汇总 / 合计)兜底补抽 —— 咨询、报表、生活对话里的数字结论(如"散件合计约 11900 元")靠这一级补回。文档里会写明本会话是否启用了兜底
各轮进度最近若干轮的请求 / 结果 / 工具 / 错误 / 结束原因
涉及的文件文件工具清单(精确)+「可能涉及(来自命令)」(从 pwsh/bash 文本启发式挑出的写文件动作)
待办最后一次 todo/write
遇到的错误按错误码聚合:× N、出现轮次,以及"第 X 轮之后未再出现"或"最近一轮仍出现(未解决)"
下一步规则推导(未闭合轮次、未解决错误、进行中待办…)
统计总量 + 各轮上下文增长表(增量 / 输出 token / 工具结果字符 / 主要来源),并列出增长最快的轮次及成因判定(大工具输出 vs 模型长输出)
附录 A最近 1~2 轮完整原文(不截断,总长上限 20,000 字符,超出在尾部截断并标注);自动跳过"继续 / 好了"这类指令型轮次,并在标题注明跳过了哪几轮
附录 B超长用户请求原文(上限:完整档 12,000 字符;精简档只展开第 1 条 ≈900 字符,超出截断并标注)

全部为本地规则:零模型调用、零 token、不联网。

压缩成本 / 交接时机(双口径,2026-09-16 边界设定)

两个维度各用一个坐标系,运行时并列展示、各自标注口径,互不换算:

维度口径阈值
A 质量有效窗口(0.6×声明)规模且退化才动手:≥90% 有效 且检出退化信号(重复调用 / 输出退化 / 思考打转 / 思考异常)→ 立即新开;≥70% 有效 且检出退化信号 → 建议交接;只有规模大、无退化 → 本维度不给结论(交给 B)
B 压缩声明窗口(与官方线同坐标)<50% 继续;5065% 轻度提示;6580% 推荐手动 /compact(带成本/收益);≥80%(或进入 80% 线前方 5 个百分点预警带)→ 现在手动 /compact(否则官方即将自动压缩)

覆盖规则:退化信号优先于压缩;压缩后仍 >60% 声明、或已发生 ≥2 次模型摘要 → 建议交接。

为什么质量维度要加「且退化」这个前提(2026-09-16 用扫描矩阵实测出来的): 旧规则里"有效 ≥90% → 立即新开"只看规模,而 90% 有效 = 声明 45% —— 早于官方线(80% 声明)。 结果是声明 45%~100% 全区间都被 drivenBy=quality 的 handoff 顶替, 那条「现在手动 /compact,否则官方即将自动压缩」的建议一次都没出现过(官方线分支永远不可达)。 而"上下文大"本身不是故障信号,真正不可救的是退化(压缩只腾空间、不修退化); 反过来没有退化,也就没必要为了规模换会话。

为什么压缩档位改锚声明窗口:锚在有效窗口(0.5×声明)时,声明 40% 起就全部落进 urgent, 紧急档失去区分度;更要命的是它必须与质量维度共用坐标,质量维度一旦只看规模就会遮蔽整条压缩曲线。

推迟压缩(2026-09-16 用户反馈后新增):档位只说明"风险在升高",该不该现在动手还要看 「回本能否在官方自动压缩线到来之前兑现」—— 因为官方压缩到线时先做零 token 的无损裁剪 (prune),只有不够才调模型做有损摘要;而手动 /compact 是立刻调模型、立刻付全价。 于是满足任一条件就不催压缩,改为「可以继续(暂不必压缩)」:

  • 距官方线还有 ≥30 轮(现在压没意义);
  • 回本轮数 > 距官方线轮数(收益兑现不了);
  • 估不出轮数时,占声明窗口 <60%(保守代理)。

官方线分支排在推迟规则之前:到了官方线就不再谈"值不值"—— 那是控制权问题。 手动压缩与自动压缩都要调模型、都有损,区别只在谁定时机、以及之前能否先用无损 prune 争取空间。 (代理线定为 60 而不是 50:50 正好等于档位起点,会让这条代理判据永远不可达,本轮踩过。)

同时修正两句不实文案:不再出现"有效窗口 ≥80% 但离官方线还远,却说否则官方即将自动压缩"; 推荐档不再说"先压缩再继续"(档位本意是"阶段结束点执行")。

  • 同时展示:声明窗口、有效窗口、当前上下文、距官方压缩线余量(含"还能撑几轮")、缓存命中/未命中
  • 成本模型:两套账并列 ——
    • token 当量(相对量,便于横向比较):A 继续 = 上下文×(命中率×cacheFactor(0.02)+未命中率×1) 每轮; B 先压缩 = 被压缩历史×缓存系数 + 摘要输出×全价 + 缓存重建(1.5 轮),之后 压缩后上下文×同系数 每轮; C 机械交接 = 交接文档实测长度(两遍渲染回填,不是固定值);
    • 真实价格(元)(绝对量,用于决策):DeepSeek flash 空闲价 命中 0.02 / 未命中 1 / 输出 4(元每百万 token)。 新开会话分段计费:首轮 = 跨会话公共前缀(系统提示 + 工具 schema,实测首轮命中率中位数 76%) 加权 + 交接文档按未命中价;第 2 轮起首轮输入已落盘为缓存前缀单元 → 按命中价。 判定 =「交接回本轮数 + 1 轮余量 < 预计剩余轮数」。 (2026-09-18 修正:旧口径"新开会话一律按未命中价、新开总额 × 1.5 < 继续总额" 对任何窗口长度都无解 —— 实测 76 个会话里旧判定命中 0 个,成本维度等于被钉死。)
  • 边界:只提示、不自动压缩、不改官方 compaction 配置;压缩是续命(有损),机械交接是无损事实底座

架构

handoff.mjs            CLI 入口(--list / --health / -o / --stats)
                       ⚠️ 渲染**直接复用** lib/handoff.js 的 buildHandoff
lib/                   ← 2026-09-18 标准包化:仓库根 == npm 包根,全部源码都在这里
  zstd-frames.mjs      Zstandard 多帧扫描与解压
  session-log.mjs      会话定位、代际选择、事件解析
  extract.mjs          【已弃用】早期的第二套折叠/渲染实现,仅供测试交叉验证
  detect.mjs           【离线侧】扫日志后**交给插件的同一套判据与评分**(不再自持实现)

  —— 下面是运行时(host + UI)的两半,也是产物质量的**唯一来源** ——
  index.js             host 半:attentionHealth projection + 交接生成路由
  handoff.js           barrel:只转发下面 6 个模块的导出(CLI 与插件都 import 这个路径)
  handoff-core.js      常量 / 评分 / token 口径(不依赖任何兄弟模块)
  handoff-text.js      文本工具(截断 / 清洗 / 切句 / 决策句抽取)
  handoff-judge.js     退化判据(全项目唯一实现)
  handoff-fold.js      事件流折叠(冷会话日志 → 结构化事实)
  handoff-plan.js      计划推导(评分 → 继续 / 压缩 / 交接)
  handoff-render.js    Markdown 渲染(交接文档)
  history.js           交接历史留痕(`--calibration` 的素材;纯本地、零 token)
  guard.js             实时思考守卫(复读 / 空转即中止本轮)
  guard-log.js         守卫现场落档(尾部 500 字,可切隐私模式)
  prices.js            价格数据与峰谷档位(可被 prices.json 覆盖)
  jsonl.js             JSONL 追加 + 512 KB 轮转
  client.js            UI 半:健康提示条 + 复制交接内容(浏览器 bundle)
cordis.patch.yml       本包作为 bundle 的挂载行(一行同时装入 host 与 UI 两面)

包形态(2026-09-18 标准包化):package.json 声明 main: lib/index.js、 exports["./client"]: ./lib/client.js、dsh.bundle.patch: ./cordis.patch.yml、 dsh.client.platform: web —— 与生态里同为双面的 dsh-context 完全同形, 所以别人可以 dsh plugin --profile web add dsh-attention-health 直接装。 浏览器半的模块 id 必须等于包名(dsh-client-modules 用解析出的包名当模块身份)。 files 白名单只放 lib/ + handoff.mjs + cordis.patch.yml + README.md + LICENSE, 开发档案(DEPLOYMENT-NOTES / HANDOFF.md / work/ / test/ / *.ps1)不进包。

漂移防护(2026-09-15 全量复核 N-1 / N-5):CLI 与插件曾各持一套渲染实现, 第二批修复的 12 项只落到了插件里,于是同一会话会产出两种质量的文档 (表格错位、附录 B 无上限、"最新目标"可能仍是"继续")。 现在 handoff.mjs 直接 import { buildHandoff } from './lib/handoff.js' —— 一处修改、两处生效,漂移在结构上不可能再发生;lib/extract.mjs 的渲染层已标记弃用。 判据与评分同理:唯一实现是 handoff.js 的 replyDegradeReasons() / scanReasoning() / scoreContextHealth(),lib/detect.mjs 只负责扫日志、然后调用它们 (plugin-test 会逐会话比对热/冷两条路径的 token 数与评分)。

2026-09-18 D3:handoff.js 已按「判据 / 折叠 / 计划 / 渲染」拆成 6 个模块, 对外仍是同一个 barrel(导出 17 个名字,一个不多一个不少), 所以上面这些"唯一实现"的说法全部不变 —— 变的只是各自有了独立的文件边界, 改判据时不必再在 3,656 行里找依赖。


关键技术陷阱(都已实测确认)

1. 会话日志是多帧 zstd,不能用 Node 内置 API 直接解

  • ❌ zlib.zstdDecompressSync(buffer) 只解第一帧(435KB 文件只解出 207 字节)
  • ❌ zlib.createZstdDecompress() 流式报 ZSTD_error_prefix_unknown
  • ✅ 必须自己走帧边界再逐帧解压 —— scanZstdFrames() 移植自 DSH 自带的 @deepseek-ai/dsh-session-persistence-jsonl(MIT),保证与官方实现一致

实测:71/71 个会话解析成功,3 秒完成,0 帧失败。

2. usage.inputTokens 是未命中缓存的输入,不是上下文规模

这是最容易踩的坑。实测某个长会话:

inputTokens: 142       ← 只有 142!
cacheReadTokens: 258944
totalTokens: 259196    ← 真实上下文规模

用 inputTokens 会把上下文低估三个数量级。 必须用 totalTokens (= inputTokens + cacheReadTokens + outputTokens,已实测验证)。

3. 模型声明的窗口 ≠ 可靠工作区间

用户环境里 contextWindow 被配成 1,000,000。但 Chroma 的 Context Rot 研究显示, 宣称百万窗口的模型在声明窗口的 50%~60% 处就已明显退化。

因此评分基于「有效窗口 = 声明窗口 × 0.6」,而占声明窗口的百分比仍然如实呈现 —— 事实与判断分开,避免误导。系数的唯一来源是 lib/handoff.js 的 COMPACT_DEFAULTS.effectiveWindowRatio(lib/detect.mjs 那份与它保持同值)。

4. 用户对 ask_user_question 的回答不走 user/message

它作为工具结果返回:{"answers":[{id, selected, custom}]}。 只统计 user/message 会漏掉这些交互(实测某会话 9 次人类输入中有 3 次是这样来的)。

5. user/message 的 source.kind 必须过滤

实测分布:user × 6、agent-message × 3、subagent-settled × 3、 agent-instructions × 1、plugin × 1、goal × 1。只有 kind === 'user' 是人类输入。

6. 磁盘上存在多个代际,必须只读最高版本

session.jsonl.zstd(v0) / session.v2.jsonl.zstd / session.v3.jsonl.zstd。 v0 的词汇表不同(assistant/chunk、reasoning-chunks 等),且用 seq0/time0 字段名。 实测分布:v0 × 38、v2 × 3、v3 × 30。

7. todo 投影会在每轮开始被清空

DSH 的 dsh-tool-todo 在 turn/start 时把投影重置为 null。所以必须自己折叠日志, 取最后一个 turn/start 之后的最后一条 todo/write。


已知限制(诚实声明)

  • 文件修改清单不完整:只覆盖经 DSH 文件工具(str_replace_editor / write / edit) 完成的修改。通过 pwsh / bash 直接写文件的操作不会出现在日志中。
  • "关键决策与未决问题"是启发式抽取(已实现,不是"没做")。日志里没有"决策"事件, 只能按关键词从助手文本与问答回答里抽句。词表刻意偏"取舍 / 结论"语境(宁可少抽也不要噪音), 所以非工程领域可能漏抽;漏掉的内容由「用户请求主线」与附录原文兜底。
  • 压缩过的会话评分有上限:经历 1 次模型摘要 → 最高 79 分、2 次 → 65、≥3 次 → 50。 压缩是"续命"(有损),不该把分数送回满分(那会把"续命"记成"治愈")。
  • 重复工具调用检测过于保守:只做精确匹配(同工具 + 同参数)。 实测 71 个会话中该信号从未触发(因为 DSH 自带的 repeat-tool-reminder 会在 3 次时提醒模型,模型通常会改)。近似重复(改了个路径)检测不到。
  • 退化检测是启发式的,分两类信号(判据的实测依据见下一节):
    • 输出退化:代码块围栏不闭合、同行复读、段落逐字重复、符号堆砌(表格框线字符已排除, 且先剥掉代码、要求命中串以非 ASCII 符号为主 —— 见下文二次复核)。 原「中英严重混杂」判据已删除 —— 全量 25 次命中、25/25 全是误报。
    • 思考退化:行重复率 ≥0.7 且行数 ≥30(打转)、单块 ≥10 万字符(异常长)、 usage.reasoningTokens ≥ 16,000(真实计量,与字符数无关)。
    • 立场不变:宁可漏报也不误报(误报会让用户忽略提示,比不提示更糟)。
  • 评分是启发式指标:阈值与扣分力度是内部校准出来的,没有做回溯验证, 用途是比较同一会话的变化趋势,不是精确测量 —— 交接文档与界面都写明了这一点 (阈值校准留痕见下一节)。
  • contextWindow 依赖日志里的 request/context 事件。若会话从未记录该事件, 无法计算占用百分比。
  • 验证范围是单机、单 DSH 版本(2026-09-20 补):全部阈值与判据是在一台机器、 dsh 0.1.5-rc.2 + cordis 4.0.2 上、用 79 个真实会话标定与回归的。换机器 / 换模型 / 官方改契约后,结论可能不再成立 —— 遇到不符请带着会话日志提 issue。
  • 成本口径是估算,不是账单:单价来自官方公开价目表(可被 prices.json 覆盖), 「每轮」按实测平均请求数换算;官方调价或改缓存规则后需要更新价表。

隐私与威胁模型(2026-09-18 补)

插件全程本地:零模型调用、零 token、不联网、不启动子进程。但它确实会写盘, 位置与上限如下(全部在 $DSH_HOME\attention-health\,可用 DSH_HOME 环境变量整体改道):

文件内容上限
handoff-history.jsonl每次交接的一行统计(评分、建议、成本口径,不含对话原文)512 KB 后轮转为 .1.jsonl,共两份
guard-trips.jsonl守卫命中的种类、理由、detail,以及当时的思考原文尾部同上
prices.json可选的价格覆盖表(没有就用插件内置价)手工维护,无轮转

守卫现场(唯一会落"原始文本"的地方):

  • 默认保留思考原文尾部 500 字 —— 这是为了"误杀要能事后复核": 守卫会中断正在进行的生成,抓错了必须能回看当时到底贴了什么(dsh-node.err.log 每次重启覆盖,靠不住)。实际至今真实命中 0 次、该文件为空;
  • 想完全不落原文:设 DSH_ATTENTION_HEALTH_GUARD_TAIL=0,改为只存哈希 + 总长度 + 首尾各 40 字特征(足够人工认出是哪一次,但不含完整正文);
  • 想更短:设成任意非负整数(如 120)即保留尾部 120 字;非法值回落到 500。

清理入口:直接删掉 $DSH_HOME\attention-health\ 目录即可(没有索引、没有外部副本, 删了就是删了);插件下次用到时会自己重建。交接历史丢了只影响阈值回溯分析,不影响功能。

威胁模型:插件的 HTTP 路由 GET /attention-health/handoff 是本机通道,把守条件为 四层同时成立 —— Host 是 loopback、sec-fetch-site 非 cross-site、Origin(若带)与 Host 同源、 以及连接来源 remoteAddress 是 loopback。

  • 不校验 token:官方 isTrustedApiRequest 同样不校验(token 校验在 browser-auth 层, 值存在模块私有的 PROCESS_LAUNCH_TOKENS WeakMap 里,插件拿不到),所以这里不发明新机制; remoteAddress 是唯一不可伪造的一层(Host 头可以伪造),当前只绑 127.0.0.1 时它无副作用, 将来若开放局域网,它能挡住"外部伪造 Host"这一类;
  • 由此得出的边界:能访问这条路由的本机进程,本来就能直接读 ~/.dsh/sessions (交接文档的全部素材都来自那里),所以这条路不额外扩大暴露面。


退化检测与校准留痕(2026-09-15 复核 N-5 / N-6)

判据按实测数据重做

旧判据在全量 73 会话 / 4,185 条回复 / 3,491 个思考块上的实测表现:

旧判据真实触发处置
中英严重混杂(全篇中文占比 5%~25%)25 次,25/25 全是误报删除
长段落逐字重复(>60 字)1 次阈值降到 25 字(中文一句话常 25~50 字)
围栏不闭合 / 同行复读 / 符号堆砌0 次被全局 length < 80 门槛挡住 → 门槛删除

误报的代价是具体的:规则 5 每条扣 6 分、最多扣 30 分,还会让提示条显示"输出异常 N 条" —— 而误报会让用户忽略提示,比不提示更糟(这是插件自己写下的立场)。

另外「符号堆砌」原来会把 Unicode 边框表格(╔══╦══╗)判成退化,现在排除了表格框线字符; 命中原因现在会带进提示条与文档(如"输出异常 2 条(符号堆砌 ×2)"),用户能自己判断提示可不可信。

二次复核:符号堆砌在思考里 100% 是误报(2026-09-15 深夜)

N-5 排除了框线字符,但漏掉了更大的误报源:代码本身。全量实测 (70 会话 / 3,638 个思考块 + 2,271 个回复块):

判据思考侧命中回复侧命中人工判定
围栏不闭合00从未触发(安全网)
同行复读00从未触发(安全网)
段落复读01真实重复(同一段代码块逐字出现两次)
符号堆砌21122/22 全部落在代码里
思考打转1—真事故
思考异常长1—真事故

命中的串清一色是模型正在思考正则和代码:

/^[{}()[\];,.<>|=+\-*/\\_#`~^&%$@!?:"']+$/
`^\*\*(.+)\*\*$`
"((?:[^"\\]|\\.)*)"
('|---|---|---|---|');

也就是说,旧判据等于因为模型思考正则表达式而扣它 12 分(reasoningFlags 每条 −4、上限 −12)。 修复两条:先 stripCodeBlocks() 剥掉围栏代码块与行内代码;再要求命中串以非 ASCII 符号为主 (真实乱码 / 卡图必然是非 ASCII,而长串 ASCII 标点实测 100% 是正则或代码)。

复验:思考侧 21 → 2、回复侧 1 → 0。残留 2 条的形态是 ◆◇■□▲△▼▽…, 来自一个"正在写符号堆砌测试样本"的会话 —— 文本上无法与真乱码区分,属可接受的止损。 受此修复影响,3 个历史会话曾被错误多扣 12 分、另外 3 个多扣 4 分。

最大盲区:思考过程

旧版只读 type === 'text',思考块一个字都没检查。而实测思考量是最终回复的 7.9 倍 (3,491 块 / 592 万字符 vs 2,284 块 / 75 万字符),用户描述的退化现象恰恰是 "对话长了以后思考过程夹杂一堆无用的东西和符号"。真实事故:

会话      :session-SAMPLE-bfd2b142(本机)
单个思考块:535,355 字符 / 116,152 行 / 只有 56 个唯一行(重复率 0.9995)
最高频行  :「好。」×69,667、「执行。」×23,214、「(执行)」×23,205
usage     :reasoningTokens = 256,000(输出预算被打满)
旧判据    :**正常**(漏检)          新判据:思考打转 ×1 → 评分 30/100

新增两条思考判据(阈值由该事故 + 全量 3,491 个思考块零误报共同定出):

判据条件阈值敏感性(实测)
thinkLoop 思考打转行数 ≥30 且行重复率 ≥0.70.6/0.7/0.8/0.9 都只命中那 1 个事故块;0.5 会多带 2 个正常代码块
thinkHuge 思考异常长单块 ≥10 万字符实测最长正常块 44,249,事故块 535,355
thinkBudget 思考预算打满usage.reasoningTokens ≥16,000(真实计量)3,534 条带该字段的消息 p50=269 / p90=1,648 / p99=4,237,事故那条 256,000 → 语料上零误报

thinkBudget 与 thinkHuge 不是重复:后者只看单个块的字符数, 遇到"多个中等块加起来烧掉预算"就会漏(每块都不到 10 万字符); thinkBudget 按消息级真实 token 判定,且不依赖思考正文是否落盘(该字段 81% 的消息都有)。

思考退化参与评分:打转 −25/次(上限 −50)、其他异常 −4/次(上限 −12)—— 比输出退化(−6/条,上限 −30)重,因为一次打转就是几万 token 的输出预算被烧掉。

校准留痕(N-6)

复核指出:所有阈值(30/50/65/70/80/90、扣分力度、0.6 系数)都是内部校准出来的, 校准目标是"分数与等级自洽",没有做过预测准确性验证。最低成本的改进是留痕:

  • 每次生成交接文档(UI 按钮 / CLI)都会往 $DSH_HOME\attention-health\handoff-history.jsonl 追加一行: 时间 / 会话 / 评分 / 等级 / 有效占用 / 压缩次数 / 退化信号 / 文档字符数;
  • node handoff.mjs --calibration 汇总这些记录(评分分布、有效占用中位数、最近 10 次);
  • 零 token、纯本地、只追加,写失败绝不影响交接生成。

攒够样本后就能回答复核提的那个问题 —— "评分 55 分时交接,新会话实际能撑多久" —— 用真实数据反推阈值。


现状与后续

✅ 阶段 1:离线工具(已完成)

  • zstd 多帧解析、会话定位、代际选择
  • 交接文档机械提炼(8 个章节)
  • 健康度检测(上下文占用 / 重复调用 / 输出退化)
  • CLI 三模式(--list / --health / 生成文档)
  • 验证:71/71 会话解析成功,3 秒,0 错误

✅ 阶段 2:host 插件(已部署并生效)

实现:lib/index.js。2026-09-20 门 3 起,线上形态是标准插件包: $DSH_HOME\profiles\web\node_modules\dsh-attention-health(由 dsh plugin add 装入, 层栈记在 profile 的 dsh.profile.bundles)。旧形态(profiles\web\attention-health\ + profiles\node_modules\attention-health-ui\ 两个手拷目录 + patch 里两条 insert 行) 已退役但保留在磁盘上,作为回退线(快照见 <TOOLS>\DSH\留档\attention-health-发布前快照-20260920\)。

⚠️ 开发时的关键差别:改 lib/ 不会自动同步到线上。改完要 .\install-package.ps1(打包 + 安装)再重启;自测用 .\test-all.ps1(自动指向线上那份)。 这两个脚本随仓库公开;deploy.ps1 / restart-attention-health.ps1 是作者本机的开发工具 (内含本机 DSH 安装布局),不随仓库公开。

  • 函数形态插件:export { apply, inject, name },inject: ['sessionProjections']
  • 注册 attentionHealth projection(ctx.sessionProjections.register(def)),客户端零代码自动同步
  • apply(state, event) 纯同步折叠,无关事件返回同一引用(严格遵守投影契约)
  • 检测数据全部来自已入日志的事件,零 token
  • 算法与 lib/detect.mjs 刻意保持一致,用测试保证不漂移

验证(五套自测合计 486 项全绿,2026-09-16):

测试规模覆盖
test/plugin-test.mjs84 项加载 / 注册 / schema 契约(含 wire 键集合逐键比对);折叠契约(无关事件返回同一引用、null 安全);6 个真实会话与离线版 token 数 / 评分 / 等级 / 退化计数逐一致;边界(缺 usage 不崩、正确使用 totalTokens、100% 有效占用判为 critical);畸形输入加固回归(null 事件、contextWindow 0/负/NaN、usage NaN/Infinity 均不得产出非法 state);推迟压缩规则;有损压缩的评分封顶;热/冷两条路径的退化判据逐项一致(含思考打转)
test/ui-build-test.mjs100 项客户端包契约、各 health 取值的渲染、"整条提示条只允许一个行动建议"、展开区排版(每项只出现一次、不出现未渲染的 Markdown)、思考异常行与命中原因、"为什么这么建议"与三方案成本对照、缺数据时不许编造结论、host 未重启(缺新字段)时的向后兼容、结论说"别压"时不得出现省钱暗示、回本偏慢的语境、启发式标注
test/handoff-test.mjs278 项提炼正确性、渲染完整性、边界健壮性、性能、两个输出档位、压缩不该"治愈"评分、退化判据的正/反例(含真实事故形态、旧误报样本、代码里的正则不算退化)、思考预算真实计量判据、上下文规模单一实现(缺 totalTokens 时三项相加、忽略 NaN/Infinity)、缺数据时"继续"与"无法判断"分开 + 界面依据 FINAL_NOTES(无 Markdown、不复述结论)、推迟压缩(回本 vs 官方线)、压缩次数覆盖规则的成本闸门(摘要 ≥2 但钱不支持新开时不得再劝交接;缓存真崩时仍须劝)、边界可达性守卫(网格扫描:四个档位 / 官方线分支 / 结构不变量 / 常量间结构关系)、文档侧不得给出"每轮省"暗示、分级词表、校准留痕、输出体积(附录上限不变式 + 确定性 fixture)
test/guard-test.mjs13 项实时思考守卫(正反例并重):正常推理 / 100 行各不相同 / 短行反复 / 同关键词多句 —— 均不得触发;同行连续 8 次 / 行内片段复读 / 窗口空转 / 真实事故形态 —— 必须抓住;enabled=false 与畸形输入安全;性能(20000 delta ≈ 80 万字符 < 2 秒)
test/crosscheck-test.mjs11 项多方面求证:74 个会话在热路径投影 / 冷路径日志折叠 / 离线 CLI 三条实现上逐项一致;全量不变量(score 区间、level 与阈值一致、计数非负、封顶生效、schema 与 view 可序列化);27 例畸形输入不得抛异常;性能(无 O(n²) 迹象、最大真实会话折叠 < 2 秒)。加载已部署副本,所以顺带验证"源码改了是否真的部署了"

测试过程中抓到并修复了两个真实 bug:

  1. wire.view 直接返回 state 会被 viewSchema 的 .strict() 拒绝 (ZodError: unrecognized_keys ["lastCallKey","callStreak"])—— 若直接挂载,DSH 每次发布都会报错
  2. 评分与等级阈值不匹配:100% 有效占用只判到 warn。已校准扣分力度 (≥90% → −75 → critical),并同步修正 lib/detect.mjs

✅ 已加载生效:新增/更新插件行不会热加载,必须重启 DSH(HANDOFF.md 第 2.1 节实测)。 本机已验证:dsh-node.err.log 出现 [attention-health] projection "attentionHealth" registered。 重启与自动验证用 restart-attention-health.ps1(14 项检查,结果写 <TOOLS>\DSH\临时\restart-result.txt)。 日志按 [RUN <id>] BEGIN / [RUN <id>] END 配对读:只有 BEGIN 没有 END = 这次没跑完, 紧挨着的最后一条日志就是中断位置(不再需要猜"是卡死了还是没输出")。 -VerifyOnly 可只验证当前状态、不重启(同步执行,不会掐断会话); 完整说明见 DEPLOYMENT-NOTES-20260915.md 第 21 节。

✅ 阶段 3:Web UI 提示(已实现并注册进页面)

采用了原「出路 1」:手写浏览器半产物(lib\client.js),不依赖 tsdown 构建链。 本机无源码 checkout,但宿主加载客户端 bundle 的契约很简单(window.__ModuleLoader__.load), 且所需依赖(react、@deepseek-ai/dsh-client-ui-primitives 等)全在页面基座 seed 内, 所以手写可行 —— 已实测进页面插件批次并正常渲染。

  • 位置:conversation.composer.dock(composer 卡下方常驻条目),id: attention-health, 与既有 ui-chat 的 StatsPills(id: stats)并存互不覆盖
  • 渲染条件:shouldNotify(level !== 'ok' 或最终建议不是"继续")。 早期只看 level,于是"score 92/ok 但建议交接"时整条提示条连同复制按钮一起消失
  • 健康时常驻一个极轻入口(一行淡字「复制交接内容」),不再是"正常时完全不占空间" —— 用户可能在任何时候想主动交接,而不是只在被提示时
  • 形态:彩色圆点 + 唯一行动建议(双维度最终结论)+ 评分(有损压缩过则附「有损压缩 N 次」, 检出思考退化时附橙色「思考异常 N 次」); 点击展开:上下文规模 / 维度A 质量(有效窗口)/ 维度B 上下文占用(声明窗口分档)/ 为什么这么建议 / 三方案每轮成本 / 评分依据 / 启发式说明; 退化原因带次数(如"输出异常 2 条(符号堆砌 ×2)"、"思考异常 1 次(思考打转)")
  • 为什么这么建议(2026-09-16 新增):显示 host 给出的一句话决策依据 (如"已发生 3 次有损摘要,反复压缩收益递减""退化信号优先于压缩")—— 它解释的是决策逻辑,不复述占用率,避免同一信息渲染两遍
  • 三方案每轮成本(2026-09-16 新增):继续 X / 先压缩 Y(有损) / 机械交接 Z(无损), 附「压缩每轮省 S,约 N 轮回本」与缓存命中率(成本折扣的依据)。 三个数都是相对 token 当量,不是钱 —— 让"权衡"本身可见,而不是替用户下结论
  • 缺数据时如实说"无法判断"(2026-09-16 边界修复):没有窗口或没有用量时, 规模显示"未知"、两个维度显示"无法判断(缺窗口或用量数据)"、成本显示"未知"。 旧版会渲染成 "0 token · 有效占用 未知" 并给出 "可继续 / 继续" 这种编造出来的结论
  • 展开区底部两个按钮:复制交接内容(精简档,默认)与复制完整版
  • 等级文案只描述规模(留意上下文 / 上下文偏大 / 上下文过大),不含任何行动建议; 行动建议的唯一来源是 host 的双维度结论(compactAdviceText)
  • 安装方式(2026-09-20 起):与 host 半同一个包 —— $DSH_HOME\profiles\web\node_modules\dsh-attention-health\lib\client.js, 由包内 cordis.patch.yml 的一行 insert 同时挂载(dsh.client.platform: web + exports["./client"] 让外壳自己把它变成浏览器 bundle;模块 id 必须等于包名)。 旧形态(单独的 attention-health-ui 包 + patch 里第二行)已退役,见「阶段 2」的说明。

另注:DSH 自带的 ContextMeter 已经在 composer 旁显示占用率,但它不在任何 slot 中、 也未从包导出,外部插件无法替换它,且它只有中性色、没有健康语义 —— 这正是本插件提示条的价值空间。


相关文档

  • DEPLOYMENT-NOTES-20260915.md —— 部署与排障:一键部署/重启脚本用法(第 8 节)、 两个历史故障的根因与修复、四个真实坑的记录与修法(第 9 节)
  • HANDOFF.md —— 完整的框架调研结论(需求、环境、技术结论、坑与红线、待验证清单)