# GrayPrint · dsh 思考文字双指纹面板 中文 | [English](README.en.md) [![Awesome dsh-plugin](https://awesome-dsh-plugin.com/badge.svg)](https://github.com/awesome-dsh-plugin/awesome-dsh-plugin) [DeepSeek Harness](https://github.com/deepseek-ai/deepseek-harness)(dsh)网页端插件,默认从会话页右下角打开两条彼此独立的实时指纹:① 当前思考文字更像灰度样本还是当前版样本;② 段落开头更偏 `Let me` 逐步试探,还是 `We need` 规划执行。两条读数都只描述文字形态,不直接证明模型版本或能力。 | 极点 | 写法 | 证据来源 | |---|---|---| | 🟢 **灰度指纹** | 第一人称叙述:`I'm planning out…` `I'll set up…` `I've got…` | opncd.ai 分享的 dsv4 灰度 opencode 会话 **41 个 / 1749 个 raw reasoning block / 7870 个内部段落** | | 🔵 **当前版指纹** | 集体人称速记:`We need answer likely…` `Let's inspect…`,不成句 | 本机 DSH 导出的当前版 **standard preset** 会话 **41 个 / 3021 个 raw reasoning block / 53994 个内部段落** | ## 为什么是这两极 起因是检验 [NoLetMe](https://github.com/Yuer6327/NoLetMe) 的判据在这批语料上是否成立。结论是**极性相反**:`let me` 的总命中并不能稳定区分两极,因此不参与灰度得分;只有每个内部段落的段首 `Let me` / `We need` 会进入另一条独立的组织方式指纹。GrayPrint v0.4 将所有判据统一到同一个口径:**先取 reasoning block,按 Markdown 空行拆出实际段落,再只检查每段开头**。 ## 对照组的一次重大修正 **v0.1 的对照组是错的。** 当时的"当前极"取自 `anchored-standard` preset —— 而那个 preset 的设计目的本就是把模型锚回 minimal 的电报体轨迹,等于把结论写进前提。用 169 个真实会话分层复测后发现:**同一个当前模型,只换 preset,得分从 10% 跳到 86%**,8 个当前版会话被误判成「灰度指纹」(其中一个还是本插件的开发会话自己)。 改用 `standard` preset 作对照,并把每个 reasoning block 拆成内部段落后,真实分离度是这样: | 判别轴(每个内部段落归一) | 灰度 | 当前版 standard | 单轴准确率 | 权重 | |---|---:|---:|---:|---:| | 段首第一人称(`I'm / I'll / I've / I`) | **22.15%** | 3.76% | **93.9%** | **49%** | | 段首 `I'm` / `I'll` | **14.94%** | 1.28% | **96.3%** | **51%** | | 段首 `we` / `let's`(仅保留计数) | 0.0% | 1.13% | **54.9%**(≈随机) | — | | 段首 `We` / `Let's` / `Need`(仅保留计数) | 0.0% | 1.35% | **54.9%**(≈随机) | — | | 各会话内部段落中位长均值(只展示) | 342 字 | **198 字** | **95.1%** | — | 两条计分轴都只看段首,每个内部段落最多贡献 1 次;不会把同一段正文里的重复词累计进去。`we / let's` 弱轴接近随机,已移除其进度、灰度方向评分和权重,只保留按段落开头统计的数量;更宽泛的 `We / Let's / Need` 段首计数与段落长度也只展示、不计分。`Let me` 只在独立组织指纹里按段首计数。 ### 段落口径 `reasoning block` 是传输容器,不是统计段落。插件先把其中的 CRLF 统一为 LF,再按一个或多个空行拆分非空 Markdown 段落并去掉首尾空白。`raw reasoning block` 数量仍会在详情里单独显示;两条灰度轴、`Let me ↔ We need` 指纹、风格信号守卫和样本门槛都按内部段落的开头计算。运行时不再统计或展示正文任意位置的词频;“原始统计”只保留段首计数及非词频元数据。 ## 判别式 两条段首比例轴加权(第一人称轴线性,低频的 `I'm / I'll` 段首轴用 `log10(x+0.01)` 拉开低值区,再在两极值间夹逼到 `[0,1]`)。详情里的“相对轴位置”表示当前比例在当前版参考与灰度参考之间的归一化位置,不是段首命中率或模型概率;实际命中率另列在“当前会话”。权重运行时重新归一。得分 **≥55%** 判「灰度指纹」,**≤25%** 判「当前版指纹」,其间为「两者之间」。 **非对称证据门**:判「灰度指纹」额外要求 **≥48 个内部段落**。灰度是"意外主张",而小样本下段首比率极不稳;不足 48 段时报「样本偏少 · 倾向灰度」。普通样本少于 **5 个内部段落**时直接拒判。 ## 段首写法倾向(独立指标,v0.4) 第二条进度与灰度得分完全独立,只统计每个 reasoning block 内部段落的开头: - `Let me…` / `Now, let me…` → 试探型一侧 - `We need…` / `Now, we need…` → 规划型一侧 进度为 `We need 开头数 ÷(Let me 开头数 + We need 开头数)`。两类合计少于 3 个内部段落时显示“指纹信号不足”,不会硬塞一个 50%。面板里的 `Let me 开头`、`We need 开头` 和 `Let me 开头比例` 都是段首口径;raw block 数单独展示。 ## 验证 **留出验证 94.2%**(200 次 70/30 随机划分,阈值在训练折内重选;每次先拆 reasoning block 内部段落,再只提取段首特征)。这才是诚实的数字 —— v0.1 宣称的 96.3% 是在同一批数据上自证的。 发货代码在全部语料上的实测: | 语料 | n | 灰度 | 两者之间 | 当前版 | 守卫拒判 | |---|---:|---:|---:|---:|---:| | 灰度 opencode(41 会话 / 1749 raw block / 7870 段落) | 41 | **38** | 1 | 1 | 1 | | 当前版 standard(41 会话 / 3021 raw block / 53994 段落) | 41 | **1** | 4 | 29 | 7 | | 当前版其他 preset(**标定外** 49 个) | 49 | 0 | 2 | 41 | 6 | 上方数字来自同一份备份语料快照;`raw block` 和内部段落是两种不同计数口径,不能混作同一个样本数。 得分分位:灰度 中位 96%(p25 79%);当前版 standard 中位 5%(p75 14%);当前版其他 preset 中位 0%。 灰度侧的 `Pybm06QA` 得分最低(13%,当前版指纹);`6AWmBTvh` 虽然得分很高,但只有 47 个内部段落,因此按非对称证据门显示“样本偏少 · 倾向灰度”。当前版 standard 中有 1 个会话落入灰度档,说明这仍是文字风格相似度,不是模型身份判定。 ## 六道守卫(拒绝给假读数) 1. **中文思考** — 中文占比 >15% 时拒判并标注。 2. **其他非英文** — 拉丁字母占全部字母 <40% 时拒判,避免日语、韩语、俄语等被自动判成当前版。 3. **英文风格信号不足** — 即使是拉丁字母语言,`I'm / I'll / we / let's` 等信号过少也拒判,避免法语、西语等落到当前版低分区。 4. **样本太少** — 内部思考段落 <5 拒判。 5. **非对称证据门** — 判「灰度指纹」需 ≥48 个内部段落,否则降级为「样本偏少 · 倾向灰度」。 6. **没有思考文字** — 只有可见回复时报告异常,不从回复文本编造轨迹。 ## 诚实边界 - 两极样本**来自不同渲染通道**:灰度侧是 opencode 分享页的叙述体呈现,当前侧是 DSH 原生 `reasoning` 块。**部分差异可能来自通道而非模型版本 —— 这一点至今没有被排除。** 要排除它,需要"当前模型跑在 opencode 上"的样本,而那批仓库是灰度期存档,没有。 - 灰度侧**只有正例**(社区精选后发布,幸存者偏差),两侧**都没有任务评分或 rubric**。 - **preset 的影响大于版本的影响**:同一模型换 preset,得分可以从 10% 到 86%。所以一个「灰度指纹」读数**不能**推断模型版本。 - 因此本面板测的是推理的**叙述人称形态**,**不是**能力、后端、路由或 checkpoint 判定。 ## 安装 **前置条件**:dsh CLI ≥ `0.1.0-rc.7`,并已建好目标 profile。 **方式一 · 从 GitHub 直装(推荐)** —— **不需要任何构建授权**。本插件的 `lib/` 是手写的、已随仓库提交,没有 `prepare` 脚本,所以 pnpm 没有需要你 `allowBuilds` 批准的东西: ```sh dsh plugin --profile web add github:HongzhongL/dsh-grayprint ``` 想钉死版本就加 commit:`github:HongzhongL/dsh-grayprint#`。 **方式二 · 本地 tgz**(自行 `npm pack`,或使用已有 Release 提供的包): ```sh dsh plugin --profile web add ./dsh-grayprint-.tgz ``` **方式三 · 本地 clone**: ```sh git clone https://github.com/HongzhongL/dsh-grayprint.git dsh plugin --profile web add ./dsh-grayprint ``` 装好后**重启 dsh web 宿主**,再刷新页面。卸载:`dsh plugin --profile web remove dsh-grayprint`。 ## 使用 面板初始位于右下角,展开态和折叠胶囊都可拖动;两种形态共享一个位置锚点,并在展开时自动向窗口内避让以保证完整可见。短按吸顶标题栏收回,长按或拖动不会误触;开合带缓动动画。默认只显示灰度样本指纹与独立的 `Let me ↔ We need` 段首写法倾向,两条计分判据和段首原始统计收在可展开详情中。界面跟随 dsh 的中英文语言设置,开合与位置状态记在 `localStorage`,并常驻提示“只比较文字风格,不代表实际模型版本”。 ## 数据口径与隐私 - 只统计当前浏览器会话快照里的**推理块**(`kind === 'reasoning'`);可见回复文本仅用于"没有思考文字"的异常诊断,不参与任何风格统计。 - 推理块先按 Markdown 空行拆成内部段落;所有判据、组织指纹、风格守卫和样本门槛只看每段开头。运行时不保留全文词频,raw block 数、段落长度与段首计数分别展示。 - 每个推理块的段落计数缓存在 `WeakMap`;命中缓存时同时校验当前文本,宿主即使原地更新同一个块对象也会重新统计,避免流式结束后沿用旧分母和旧段首计数。 - 数据不离开你的浏览器。 ## 架构 ``` lib/index.js # Node(宿主)半边 —— 空操作,满足 Loader lib/client.js # 浏览器包(手写闭包工厂,无构建步骤) # 计数引擎 / 判别式 / 守卫 / 实时会话源 / store / 面板 evidence/profile.json # 标定档:两极参照值、单轴判别力、验证结果、preset 敏感性 test.mjs # 零依赖回归:计数、语言守卫、双指纹、订阅销毁 ``` 浏览器包是 `window.__ModuleLoader__.load({id, factory})` 闭包工厂产物,externals 走注入的 `require`(此插件只用 `react`),通过官方 `shell.overlay` 插槽挂载,并接入官方 `locale` 服务。会话订阅、统计 store 和自注入样式都挂在插件生命周期上,热重载/卸载时主动清理。不改动、不补丁任何既有 UI。 **为什么没有构建步骤**:`lib/client.js` 直接手写成符合 dsh 客户端契约的闭包工厂产物,不经 tsdown。代价是没有 TypeScript 类型检查,收益是 git 直装即可用、用户无需授予任何安装期代码执行权限。 ## 致谢 判据的起点来自 [NoLetMe](https://github.com/Yuer6327/NoLetMe)(Yuer6327)与 [xiaobright/modeltest](https://github.com/xiaobright/modeltest) 的轨迹调研;灰度语料来自 [YunhaoFu/dsv4ga-news-gather](https://github.com/YunhaoFu/dsv4ga-news-gather) 收录的 opncd.ai 分享链接。本插件的结论与它们**不一致**,理由见上文。 ## 许可证 [MIT](LICENSE)