dsh-novel-craft
English: docs/README.en.md(只放在仓库里,不进 npm 包——否则 npm 页面会显示英文版)
目的:让 AI 写出你觉得对味的文字。
方法:你只点赞、点踩,模型自己总结规律。
读候选稿时顺手点一下——好段落 👍、坏段落 👎。然后让模型把这批点选总结成几条能照着做的写法,写进一份档案。下一轮写稿带着这份档案,写完再点、再总结。它总结出来的东西长这样(我自己的档案):
## 已验证偏好(作者喜欢什么)
- 让在场群像先静默再爆响
- 危险降临先写器物异动,再写人的闷哼
## 避免的写法(作者不喜欢什么)
- 不要用比喻堆砌来写人群的恐惧
- 不要在施法后补旁白解释动机
这些不是我写的,是我点出来的。每条后面能点「🔍 证据」,看它当年是从哪几段原文里总结的;不认同的直接删掉。
**档案里只有规律,没有原文。**这是这个方法能成立的前提:原文一轮就几千字,带两轮上下文就满了,更别说一本书——带不动的东西,谈不上"持续"。规律是几百字节(我的是 714 字节),每一轮写稿都带得动;而且模型记住的是"怎么写",不是"这几句长什么样",前者能迁移到新章节,后者只会被抄。所以它能一轮轮跑下去,越用越对味。
为什么要做这个
因为原来的两条路都太累。
自己写提示词。"别写那么用力"它听不懂,你得反复调;调好这一次,下次开个新对话又得重来。
**逐段给它写评语。**一轮摊开八到十篇候选,每篇两三千字,读完还要说"哪里好"——说不出来,只会觉得"这篇有感觉"。勉强写下的那些"更细腻""节奏不错""有点刻意",翻译回写作等于没说,第三轮它还是同一个毛病,只是换了个说法。
说到底:**判断很好下,表达很难写。**点一下是一秒钟的事;让你把"我想要什么"讲清楚,你写得动十次,写不动一百次。所以别让你表达,让模型总结。
装
dsh plugin --profile web add dsh-novel-craft
装完重启一次 dsh(宿主半区在启动时装载)。侧栏底部会出现「🎴 抽卡工作台」。
不想装也能先看:把仓库里的 docs/preview.html 用浏览器打开,那是真实组件渲染出来的界面快照,七个页签都能点。
怎么用
一、点它一轮,它就学到一点
选一个放着候选稿的目录(选择器会推荐、记最近、也能浏览,不用记路径),然后读,按键盘:
j/k 换段 · G 标好 · B 标坏 · 空格 取消 · n/p 换篇
读完点「⚗️ 提炼规律」。它把这一批点选总结成条目,你逐条勾选,采纳的才进档案。不勾的不会进去——所以你永远能拦下它总结错的部分。
就这样,一轮一次。你不需要写一句话。
二、写新的一章(候选稿也让模型写)
「🗂 章节」页右上角有个「✍️ 开新章」,四步。
**第一步,说清这一章要干什么。**章号、本章设定(目标、必须发生、禁止发生),要几个方向(默认 10 个)。
第二步,挑方向。让它先给一批"场景决策"方向——注意不是换十种形容词,是换怎么讲这个故事:
A 保守精修:以最小改动保留现有骨架,只在细节处收紧
B 配角识货:让同行配角先认出货色,主角的算计藏在他的沉默里
C 双层信息差:让掌柜也在算计,读者比主角先看出一层
方向不顺眼的改名、改说明、取消勾选,剩下的才写。这一步很重要:让它"写十个版本",你只会拿到十篇形容词不同、决策相同的稿子,挑等于没挑。
**第三步,一篇篇写。**每写完一篇立刻落盘(第13章-A-保守精修.txt,沿用你的命名习惯)。看得见"第 3/10 篇",能中途停,单篇失败只影响那一篇。写完点「🎴 去抽卡选段」,它会顺手把当前卡池切成刚写出来的那个目录——接着就是回到第一步:读、点赞、点踩。
**第四步,合并定稿。**你标了赞的段落按稿件顺序列出来,带上你写的那句"为什么用这一段",成为一张取用段落表。跨篇接不上的地方,它写成 〔此处需过渡〕 标记——不替你补写。那些位置正是"文不文、白不白"的来源,得你自己过。写入正文时原稿自动备份。
三、改稿:它只能改你标过的段落
读到哪儿不对,点那一段按 A 留一条批注(AI 腔 / 啰嗦 / 情绪直给 / 平 / 人物失真 / 逻辑账目 / 信息差 / 其他,外加一句话——注意这里可以写字,因为一处一处指点比逐段写总评省力得多,而且你是在说"这里不对",不是在解释"我要什么")。
点「按批注微调」,它只改被批注的那几段。两道防线:
- 交给模型时只有那几段,它碰不到别的段落;
- 改完逐字核对,没批注的段落差一个字、或者段落数变了,直接拒绝写回。
采纳前给你逐段前后对照,采纳时原稿先备份。以前那种"点三处、它重写一整章、语感全变了"的事,现在被堵住了。
四、看全局:它在本地算,不喂正文给模型
写到十几万字,"哪里塌了"靠记忆算不出来。「📈 情节」页给你:
- 张力曲线 —— 可以自己点 1–5 标(实线),没标的才用正文估(虚线,每个点都写了估计依据)
- 大纲偏移 —— 这一章该发生的事,在剧情总结里对上了吗
- 伏笔欠账 —— 埋了几条、收了几条、哪条欠太久了
- 字数失衡 / 事件密度 / 评分下滑
还有「🧭 全书」页:立项 → 设定 → 人物 → 大纲 → 逐章正文 → 修订 → 完本,每步该有什么产物、缺了哪样,一目了然,也能一键起草。
那条不能破的线:进写稿上下文的只有规律
这条线是上面"持续学习"的地基,不是洁癖:
- 写稿、写候选、要方向的时候,模型只拿到一个写作包,包里除了上一章结尾(≤900 字,接续必须用,包内注明)之外没有任何正文。
- 你标过的段落、证据摘录、批注,全都留在本地,你随时能回查——但写稿时不读。
- 所以上下文里只有几百字节的规律 + 该有的前情与设定。这就是它能一轮轮带着你的口味跑下去的原因。
但我不想把话说大。有三处你亲手点下的按钮会触发一次辅助调用、带上原文,各有硬上限:
| 按钮 | 带进去的 | 上限 |
|---|---|---|
| 提炼规律 | 你点过的段落 | ≤40 段 × 240 字,总 ≤20KB |
| 补齐来源 | 证据摘录里的引文 | 每条 160 字 |
| 按批注微调 | 被批注的段落 + 前后段各 80 字 | 每段 800 字,单次 ≤20 条 |
除此之外没有任何路径把正文送进模型。这条我以前写的是"原文不进模型上下文",属于说大了——"补来源"就会带引文,文案里没写。现在照实写,并把上限都列出来,因为承诺应该能被代码逐行验证。
写作包本身是写稿会话唯一该读的文件,十节:
① 本章任务(你写的)② 作者偏好档案(只有规律)③ 上一章结尾 ④ 前情提要(来自各章剧情总结,不重读正文) ⑤ 本章人物 ⑥ 道具与增益台账 ⑦ 情节节点 ⑧ 未兑现伏笔 ⑨ 禁 AI 腔清单 ⑩ 写作要求
每节都有上限,整包默认 9000 字预算。超了就按"先削补得回来的、后削没它写不了的"顺序削,削了谁、削了多少都写在预算表里。包尾固定列一段「不要读进上下文的东西」,把证据摘录、标注文件、批注、全书合并稿点名列为禁区。
它不做什么
- **不替你写正文。**它写候选和初稿,选和改是你的——这不是谦虚,是设计前提。
- **不做"AI 通读全书"。**正文不进模型是上面那条线,所以情节体检是本地字符串统计,识别不了"语义上的重复"。这是代价,我认。
- 不保证平台过审,也没有封面、排版、发布、数据抓取。
- **不替你决定节奏。**张力曲线可以手工标,估计值只是虚线参考。
这东西靠谱吗
775 条断言 / 7 个测试文件,每个能力都有真数据用例(不需要浏览器、不需要起 dsh)。测的是"作者会怎么用它":微调用例会断言"只有被批注的那一段变了,其余段落逐字相同",开新章用例会验"写入正文前先备份原稿"。
发版前我请了三个独立视角,把宿主半区、浏览器半区、还有上面那句承诺各查了一遍。四个高危都先复现再修,复现脚本固化成了回归测试:
- 分片请求会把中文正文里的字悄悄变成替换符(实测 4 万字每份 2 个坏字)
- 按住
G连标时,标注会互相覆盖(实测并发标 5 段只活下来 1 段) - 手写的偏好档案会被换成空骨架,规律全丢
- 保存路径没校验,
../../…能写到作品目录外面
前两个都不报错,只是悄悄丢东西——这类最该怕,所以现在每个都有测试守着。
更多细节在 DEVELOPMENT.md(改这个仓库前先读)和 Commit 记录里。
兼容性
dsh 是一组各自独立发版的包——同一个公开版本里,CLI 可能是 0.1.5-rc.2 而客户端运行时还是 0.1.1-rc.2。所以本插件不锁版本号,peer 依赖声明为 *,运行时从你的 profile 解析。
实测跑通的:0.1.0-rc.7(作者日常)、0.1.2-rc.1、0.1.5-rc.1(latest)、0.1.5-rc.2(next)、0.1.6-alpha.1。
一条命令自证:
node scripts/check-dsh-compat.mjs next # 或 latest / alpha / 具体版本号
它会在临时目录里真装一份那个版本的 dsh、按 profile 的方式挂上插件、用独立 DSH_HOME 起服务,断言宿主路由可用、客户端半区进了启动清单、半区能正确下发。跑完自动清理,不影响你正在用的实例。
dsh ≥ 0.1.5 需要 Node 22。用 Node 20 起
dsh web会静默退出(不打印、不监听端口),看起来特别像"插件不兼容"。兼容脚本会自己找 Node 22,找不到就把启动失败判成环境问题、跳过,而不是报兼容性失败。
两个写给插件作者的坑:行的 apply 可能早于服务挂载(同步 apply 里 ctx.get('webServer') 会是 undefined,本插件因此改成 ctx.inject 等就绪 + 超时兜底);客户端 bundle 的下发地址变了(新版是组合脚本 /plugins/??a/client.js,b/client.js&rev=…)。
出处与致谢
- 禁 AI 腔六维度清单(
skills/novel-writing)改编自 dsh-novel-solo(MIT, Copyright (c) 2026 Tkingxiao) - 运行平台 DeepSeek Harness(MIT):用它公开的插槽、客户端服务与 LLM 服务,未复制其源码;辅助模型调用的写法参考了平台内
dsh-session-title-llm的公开实现 - **"推理档位要关掉"**这条经验来自官方 Discussion #6857——我们在真机上踩过同一个坑:模型想了一堆、正文一个字都没留下
- 包布局约定参考社区集合仓库 linxiecoder/deepseek-harness-plugins,让
dsh plugin add直接可用
完整条目与许可证原文见 THIRD_PARTY_NOTICES.md。
给想深入的人:文件落点、接口、目录结构(点开)
文件都落在哪
| 文件 | 是什么 | 进上下文吗 |
|---|---|---|
写作包.md(在轮次目录里,没有就退到 .dsh-novel-craft/写作包/) |
写稿会话唯一该读的文件 | ✅ 就该它进 |
.dsh-novel-craft/作者偏好档案.md |
只有规律 | ✅ |
.dsh-novel-craft/证据摘录.md |
你标过的原文 | ❌ 只有点「提炼」「补来源」时进那一次调用 |
.dsh-novel-craft/marks.json |
机器可读的好/坏 | ❌ 同上 |
.dsh-novel-craft/批注/第N章.json |
你的改稿批注 | ❌ 只有点「微调」时取被批注的那几段 |
.dsh-novel-craft/章节设定/第N章.md |
本章目标 / 禁止发生 / 出场人物 | ✅ 作为写作包第 ① 节 |
.dsh-novel-craft/规则来源.json |
规律 → 证据的对应表 | ❌ 只给界面反查 |
.dsh-novel-craft/微调/ |
前后对照、待采纳改写、原稿备份 | ❌ 给人看的 |
.dsh-novel-craft/取用理由.json |
好段的"为什么用这一段" | ❌ |
.dsh-novel-craft/workspace.json |
阶段进度、手工张力、章节状态 | ❌ |
候选稿/、定稿候选/、筛选与合并记录.md |
开新章写出来的候选、合并稿、取用记录 | ❌(只有写候选时喂写作包) |
标注随作品走(存在作品自己的 .dsh-novel-craft/),不写全局配置。候选目录存在 dsh settings 的 dsh-novel-craft 命名空间里。
规律是怎么来的
作者点好/坏 → 落 marks.json → 「更新证据摘录」把原文收进 证据摘录.md → 「提炼规律」把待提炼的原文(≤40 段 × 240 字、总 ≤20KB)交给一次辅助模型调用,系统提示强制它只输出 喜欢: / 避免: 两节、禁止抄原文 → 结果不直接进档案,列出来让你逐条勾选,采纳的才写进规律区,提炼水位推到这批标注(下次不再送一遍)。
- 用哪条模型路由:默认取 dsh 当前的默认模型;想省钱就在设置里填
dsh-novel-craft的distillProvider/distillModel - 不想让任何模型碰原文?跳过「提炼」这一步,把
证据摘录.md交给你的 agent 也行——反正进档案的只有规律 - 自动块(
auto:begin/auto:end之间)每次重写,那是账目;规律区是你的,永远不动 - 你手写的内容会被完整渲染,不会被隐藏或覆盖
「抽卡」这条线里一些刻意的选择
- 阅读优先,不是表格:候选稿按连续正文排版(字号 15.5 / 行高 1.95),标注状态用左侧 3px 色条 + 淡底色。大部分段落本来就不用标,所以标记按钮只在光标所在段或鼠标悬停的那段浮出
- 手不用离开键盘:
G/B标完自动前进;键盘标记是幂等的(不会把刚标好的又切掉);在输入框里打字时快捷键自动让位 - 一眼知道读到哪:底部写「第 7 / 28 段」+ 快捷键;候选条给的是短标签 + 字数 + 本篇标注数,同批目录共有的前缀(
第9章-)会被剥掉,所以是A · 1.2k字 · 标 3 - 规律可以一条一条否决:每条后面有个 ✕,点了就删(自动块里的账目行不给删)
- 第一次打开给一句三段式引导,标过任何一段之后就不再出现
宿主半区 HTTP API(只服务 127.0.0.1)
全部在 /novel-craft/api/ 下,enabled: false 时整体 503。共 23 条,挑几条主要的:
| 方法 | 路径 | 作用 |
|---|---|---|
| GET | state |
候选稿段落 + 标注 + 档案 + 证据摘录情况 + 待提炼数 + 模型路由 |
| POST | marks / evidence / distill / rules / profile |
抽卡这条主线的写入 |
| GET | discover |
推荐目录(含篇数,5 秒短缓存) |
| GET/POST | project |
认出 / 确认作品根(detect / set / clear) |
| GET | workspace |
章节看板(不带正文) |
| POST | chapter / setup |
单章详情、本章设定 |
| POST | annotation |
批注加/改/删(同段同类型重复提交=更新) |
| POST | pack |
生成写作包(save:false 只预览) |
| POST | revise / revise-apply |
按批注生成微调稿 / 采纳写回(先备份、核对不过就拒) |
| POST | tension / check |
手工标张力 / 账目+情节体检 |
| POST | stage |
阶段状态 / 起草 / 写入产物 |
| POST | newchapter |
开新章:status / directions / draft / merge / finalize |
| GET/POST | rule-evidence |
规律来源表;action:'backfill' 补齐老档案 |
改代码后怎么生效
- 浏览器半区:dsh 按请求现读文件下发(
no-cache),刷新页面即生效 - 宿主半区:dsh 启动时装载,必须重启 dsh
测试
npm install
npm test # 7 个文件 775 条断言
跑单个:node test/workbench.test.mjs(0.2 的能力)、node test/ledger.test.mjs、node test/plot.test.mjs、node test/pipeline.test.mjs。
测试不依赖任何本机路径:作品目录由 test/fixtures.mjs 在临时目录里造,写操作全在临时目录,跑完就删。刚 clone 下来没装依赖时会打印「跳过」,不会甩一堆看不懂的红。
目录结构
dsh-novel-craft-plugin/
├── package.json # dsh.bundle.patch + dsh.client.platform=web
├── cordis.patch.yml # 安装时把插件行插进 profile 组合
├── lib/index.js # 宿主半区:settings + 23 条回环路由 + 模型调用编排
├── lib/client.js # 浏览器半区:工作台本体(七个页签,无 JSX)
├── lib/core/ # 十个模块,彼此只共享 text.js
│ ├── text.js # 分段/字数/安全读写/章号解析 + 写队列与原子写
│ ├── workspace.js # 作品根识别、章节看板、人物卡、轮次目录
│ ├── pack.js # 写作包 + 上下文预算
│ ├── annotate.js # 批注读写 + 微调拼装与逐字核对
│ ├── ledger.js # 台账解析 + 账目体检
│ ├── plot.js # 张力曲线 + 伏笔欠账 + 情节诊断
│ ├── pipeline.js # 七阶段定义、产物检查、门禁、起草提示词
│ ├── provenance.js # 规律 → 证据
│ ├── draft.js # 开新章:方向、候选、合并、定稿归一化
│ └── llm.js # 一次性模型调用(六个调用点共用)
├── test/ # 7 个测试文件
├── DEVELOPMENT.md # 改这个仓库前先读(不进 npm 包)
└── LICENSE, THIRD_PARTY_NOTICES.md
No comments yet. Be the first to write one.