Consistency guard for long-form writing: scans conversation prose for character, timeline and numeric setting conflicts across chapters, then jumps straight to the conflicting message.
Install
# from npm (prebuilt)
dsh plugin --profile web add dsh-consistency-guard
# from GitHub (first run asks for allowBuilds approval — follow the hint, retry)
dsh plugin --profile web add github:bycall/dsh-consistency-guard
Any plugin you install runs third-party code with your own permissions — it can read your files, use your credentials, and reach the network, and tool approvals don’t sandbox it. GitHub-sourced plugins also run build scripts at install time — pnpm blocks those until you allow them, so an install can stop with ERR_PNPM_GIT_DEP_PREPARE_NOT_ALLOWED or ERR_PNPM_IGNORED_BUILDS; dsh prints the exact key to add under allowBuilds in your profile’s pnpm-workspace.yaml, and the install works on the next run. Allowing a build is a trust decision: only install sources you trust, and pin a commit (github:owner/repo#sha).
README
This plugin publishes its README in Chinese only.
DeepSeek Harness(dsh)长文创作一致性守卫插件:扫描会话正文中的人物设定、时间线、数值型设定,自动发现跨章节矛盾,并点击直接跳转到冲突原文位置。
背景:百万字长篇创作中,人物年龄/等级/境界、时间线、数值设定在数十次对话迭代后极易失控。dsh 内置没有跨章节内容审计能力,现有写作类插件做的是编辑与导出,本插件补上「一致性守卫」这一环。
功能
| 能力 | 说明 |
|---|---|
| 实体提取引擎 | 纯前端规则引擎,从会话正文中提取属性设定:年龄(25 岁 / 二十七岁)、年份(1998 年 / 星历 412 年)、身高(180cm / 身高 180)、等级、境界。中英文数字统一归一化,25岁 与 二十七岁 可正确比对。 |
| 实体归属 | 优先匹配设定字典中的已知人名,其次按常见姓氏识别,最后用「动词/属性词邻接」启发式回退;内置停用词拦截,避免把「境界三层的修士」误判成人名。 |
| 设定字典 | settings.section 插槽提供编辑器:维护 实体 / 属性 / 标准值 条目,保存在浏览器本地存储(localStorage),编辑后 dock 面板即时生效,无需重载。 |
| 两级冲突检测 | 数值矛盾(numeric):同一实体同一属性出现两个不同数值——硬性矛盾,如林默第 1 轮 25 岁、第 7 轮 27 岁。表述差异(wording):非数值属性的表述漂移,低置信度提示。无归属的 mention 归入 (unattributed) 桶,只给提示、不误报硬冲突。 |
| 冲突 dock 面板 | 输入框上方( conversation.input.dock 插槽)显示冲突数胶囊:无冲突为绿色、有冲突为红色;点击展开冲突列表(portal 到页面根部,不受布局容器裁剪),按「数值矛盾优先 + 轮次」排序。 |
| 点击定位跳转 | 点击任意冲突项 → 切回「对话」视图 → 平滑滚动到第二个冲突值所在消息并红色高亮。若目标在未加载的更早记录中,自动调用 loadOlder(最多 3 次)后重试。 |
安装
dsh plugin --profile web add file:/path/to/dsh-consistency-guard
然后在 ~/.dsh/profiles/web/package.json 的 dsh.profile.bundles 数组中加入 "dsh-consistency-guard",重启 DSH.app(或硬刷新浏览器窗口)生效。
卸载
dsh plugin --profile web remove dsh-consistency-guard
工作原理
- 插槽注册(客户端):通过
ctx.slots注册两个服务——conversation.input.dock(idconsistency-guard-dock):输入框上方的冲突胶囊 + 展开列表。settings.section(idconsistency-guard-settings):设定字典编辑区。
- 数据来源:插槽组件拿到会话快照,读取
snapshot.chat.order/snapshot.chat.nodes,取assistant的data.blocks[kind=text]与user/steering的文本作为正文来源;工具调用与代码输出刻意跳过,避免把代码里的数字当成设定。 - 扫描流水线:正文 → 按句切分 → 句内属性模式匹配(
年龄/岁/身高/cm/等级/境界/年份)→ 值归一化 → 实体归属 → 按(实体, 属性)分组 → 组内出现两个及以上不同值即判定冲突。 - 属性轴区分:
境界/修为与等级/级别是中文类型小说中两个独立维度,刻意不合并,否则「境界三层,等级 9」会被误判为矛盾。 - 跳转机制:复用 dsh 聊天视图的内置语义锚点
data-chat-anchor-key="<key>",跳转 = 找到该行 →scrollIntoView+ 红色高亮;点击胶囊先按 label 点击[role="tab"]切回「对话」(中文「对话」/ 英文「Chat」,回退首个 Tab),再轮询等待目标行挂载。 - 字典联动:字典保存在 localStorage,
apply内维护一个极简订阅器(subscribe/getDict/setDict),设置区保存后即时广播给 dock 面板。
目录结构
dsh-consistency-guard/
├── package.json # dsh.bundle.patch + dsh.client + exports["./client"]
├── cordis.patch.yml # 挂载插件到 profile 层叠
├── lib/
│ ├── index.js # 服务端挂载入口(UI-only,最小实现)
│ └── client.js # 客户端 bundle(window.__ModuleLoader__.load 格式)
└── test/
└── smoke.cjs # 冒烟测试(mock ModuleLoader + ctx + 纯函数断言)
开发说明
- 纯手写客户端 bundle(仅依赖
react,零构建步骤),格式遵循 dsh web 的 lazy-CJS 模块表契约:window.__ModuleLoader__.load({ id, factory })。 dsh.client.inject声明了插件可用的客户端服务:slots/sessions/locale。- 所有核心逻辑(实体提取、归一化、人名校验、冲突检测、字典读写)都是不依赖 react 的纯函数,通过
exports.__test暴露给冒烟测试。 - 校验:
node --check lib/client.js;冒烟测试node test/smoke.cjs(覆盖工厂物化、两个插槽注册、中文数字归一、实体归属、去重、人名校验、端到端冲突检测)。
冒烟测试覆盖
factory materialized; exports: [ 'apply', 'inject', '__test' ]
registered slots: ["conversation.input.dock[consistency-guard-dock]","settings.section[consistency-guard-settings]"]
cnNumToInt: ok
normalizeValue: ok
extractMentions attribution + dedup: ok
plausibleName: ok
mentions: 8 conflicts: 2
detectConflicts end-to-end: ok
loadDict without localStorage: ok
ALL CHECKS PASSED
端到端用例模拟《归墟》修订场景:林默第 1 轮 25 岁 / 第 7 轮 27 岁(报数值矛盾)、沈砚青等级 9 → 12(报数值矛盾)、苏晚晴两处均为 27 岁(不误报)、境界三层与等级 9 不合并(不误报)。
已知限制
- 规则引擎覆盖常见中文小说设定的属性词,未收录的属性不会参与比对;设定字典用于补强专有名词。
- 人名识别基于常见姓氏表 + 启发式,生僻姓名建议加入设定字典以确保归属准确。
- 设定字典保存在浏览器 localStorage,换浏览器或清理站点数据后需要重新录入。
路线图(二期)
- 伏笔追踪:标记「伏笔-回收」对,扫描未回收伏笔
- 时间线校验:事件先后顺序矛盾检测
- 语料库统计:章节字数、出场角色频次、设定引用热图
- 一致性审计报告(Markdown)一键导出
License
MIT
Links
More in this category
tt-a1i/archify#integrations/deepseek-harness★ 92724
Generate validated, self-contained interactive architecture, workflow, sequence, data-flow, and lifecycle diagrams from repositories or system descriptions.
dream-num/dsh-univer-office★ 657
Give DeepSeek Harness a real office environment. Univer Office Plugin brings spreadsheets, docs, slides, canvases, relational tables, and more into one runtime — with connected data, validation, versioned changes, and isolated worktrees for multi-agent collaboration.
ECNU/EduWork#dsh-knowledge-studio★ 275
A workspace-based Studio for source-grounded reports, slides, tables, mind maps, quizzes, flashcards, audio and video, using DSH models and shared artifact services. Office generation requires Python 3.12+ with its dependencies and DSH_OFFICE_PYTHON configured; media rendering requires Chromium, and local ASR additionally requires whisper.cpp and a model.
PerryLink/dsh-industry-research★ 215
Deterministic industry research reports for DeepSeek Harness — company and industry research flows produce structured, verifiable reports from staged evidence.
HuanLinOTO/dsh-plugin-mineru★ 51
Expose MineRU document parsing tools to the model.
PolinniZhong/dsh-knit★ 48
Puts the Markdown documents, source files, images and video that already exist anywhere in the session workspace into the DSH sidebar, ranked by relevance to the current conversation. The default lexical mode matches your recent messages against title, summary and body locally with IDF weighting (BM25) — no model calls, no network, nothing leaves the machine. The list comes from scanning the workspace rather than from a recent-files list, so restarting DSH or opening a new session does not empty it. Results are organised into a three-tier reading order (primary / supporting / related) with a verifiable reason on every row — which field matched, which document cites this one — and one click opens an in-place preview: Markdown as rendered text, relative-path images actually drawn, source code with the host's own syntax highlighting, video streamed over HTTP Range. The same Context Pack is exposed to the agent as a knit_docs tool. Later versions added read evidence and lifecycle tracking (attribution is frozen at the moment of reading), pin / exclude (exclusion applies before retrieval, and is undoable), relation evidence (references / imports / tests, with line numbers) and an incremental index reused across restarts. Since v0.23 there is also an optional hybrid mode: it is off by default and only runs once you configure an Ollama or OpenAI-compatible embedding provider yourself. When it runs, what leaves the machine is your query text plus each candidate file's title and summary — a Markdown summary is an excerpt of the body's first paragraph (at most 60 characters), a code file's summary is its workspace-relative directory, and an image or video contributes its file name; full bodies and absolute paths are not sent, and a loopback provider keeps everything on this machine. Since v0.24 it also associates uncommitted changes: a read-only git status (fixed argv, no shell, whitelisted child-process environment) combined with the same reference / import / test / mention evidence shows which files a change is likely to drag along, and it is off by default. The benefit of semantic retrieval still awaits a real-provider run, so no recall improvement is claimed.
Community comments
Comments are public GitHub Discussions. Loading them connects to GitHub and Giscus; a GitHub account is required to post.