查错工具的第一版,在真书稿上报了 147 条,其中 143 条是它自己错
接着上一篇《长文档的成本不在写,在保一致》。上一篇讲的是为什么该有这么个东西,这一篇讲我把它做出来之后发生了什么。
一、工具做出来了
1235 行 JavaScript,零依赖——连读 .docx 的 ZIP 解包和 OOXML 解析都是自己写的,只用了 Node 自带的 fs / path / zlib。不联网、不上传、不调任何模型。
最后一条是刻意的。这类工具面对的稿子是什么?未出版的书稿、还没开标的投标文件、客户的合同。你跟人家说"传到我服务器上我帮你查",对方凭什么答应。所以我把它做成纯本地确定性算法:编号连续性、交叉引用、术语写法、章节骨架、目录对账——这些本来就不需要模型,一行都不需要。
然后我拿手上真在做的一本教材去跑:《工程制图与计算机绘图》初稿,689 段。
二、它报了 147 条。其中 143 条是它自己错了。
这一段是这篇文章唯一值得写的部分。
如果我没拿真书稿跑,只拿自己造的样例跑,这 143 条假红点会一直躺在里面,直到第一个用户打开报告、看到满屏胡说八道、然后把它删了。而且不会告诉我为什么。
假阳性一:26 条"重号",全是书前面的图目录
报告说"图1-1-1 出现了 2 次题注",我去翻,第 41 段和第 99 段。第 41 段是图1-1-1.异形孔垫片零件图 4。
结尾那个孤零零的 4,是页码。这是书前面的图目录——它把全书每一条图题连同页码再列一遍。于是正文里每一张图,都被我的工具当成"第二次出现"。
这个错误很有意思的地方在于:它不是漏了一个 if,它是我脑子里那本书和真实那本书不一样。我写规则的时候想的是"一份 Markdown 稿子",真实的书是有前言、有目录、有图目录、有表目录的。
修法不是简单地把目录扔掉。目录被单独摘出来之后,反而多了一条真检查:目录列了但正文里没有(读者按目录翻会翻空),和正文有但目录漏了。一个假阳性变成了一条新能力。
假阳性二:每章都有"学习目标",被报成标题重复
教材当然每章都有"学习目标""教学内容""课后作业"。这是体例,是对的,正是编辑要求的样子。
我的规则说"同级标题重名 → 目录里会并成两条一样的"。这条规则本身没错,错在它不知道什么叫"模板栏目"。
修法:骨架规则本来就要算"多数章都有的栏目"(用来查某一章漏了哪个栏目),把这个集合顺手传给重复标题规则,模板栏目直接跳过。两条规则本来就该互相知道对方的存在。
假阳性三:"1. 掌握投影法的概念及分类;"被当成标题
这是列表项。我的标题识别规则里有一条"以数字编号开头的短行 = 标题"。
修了两处:一是裸的"1."要求至少两段编号("1.2 标题"才算,"1. 什么什么"不算);二是标题不以句末标点收尾——没有哪个章节标题是以分号结尾的。
第二条几乎零成本,但它一次干掉了这一整类。这种规则我称之为"便宜的常识":写代码的人容易去想复杂的判据,但真正管用的往往是"标题不带句号"这种一句话就说完的东西。
三、修完剩 4 条,全是真的
147 → 4。剩下的这几条,我列出来给你看它值不值:
- 图 2-2-* 序列缺 2-2-2、2-2-11、2-2-13、2-2-15、2-2-17、2-2-20(现有 1–29)。六个断号。这个如果靠人查,得把全章图号抄下来排一遍。
M14X1和M14x1混用。螺纹标注,一个大写 X 一个小写 x。这是我最得意的一条——一本工程制图教材里,人工校对几乎不可能发现这个。
另外三条不是"错误",是下一节要说的那件事。
四、我做的最重要的一个决定:查不了就说查不了
剩下三条长这样:
正文引用了 193 个不同的「图」,但文字层只找到 26 条图题注(目录里列了 158 条)。
题注多半嵌在图片里,或用了 Word 自动题注域(域不落在正文文字里,本工具读不到)。这一项本工具查不了,不代表没问题——需要把题注落到文字层后重跑,或走人工核对。
这本教材的图题,要么是做图的时候直接写进图片里了,要么用了 Word 的自动题注(那是"域",域的结果不落在正文文字流里)。我的工具读不到。
这里有三个选择:
- 假装没这回事,报"未发现问题"——用户拿到一份干净报告,以为查过了;
- 把 109 处引用全报成"悬空"——刷屏 109 行,用户看两眼就关了;
- 合成一条,明说"这一项查不了,而且这不等于没问题"。
我选三。
这不是谦虚,是这类工具唯一能立住的地方。一个体检工具最大的危害不是漏检,是让人以为体检过了。如果它敢在读不到的地方给你一份干净报告,那它给你的任何一份干净报告都不能信。
五、发出去的时候又栽了一次
顺手记一笔,给同样要发 skill 的人省半小时。
发完 GitHub,我用 npx skills add 装回来验一遍——直接被拒:No skills found. Skills require a SKILL.md with name and description.
可我的 SKILL.md 明明有 name 和 description。
找了半天,拿一个已知能装的包做对照才看出来:它的 frontmatter 里 description 是单行,我的是 YAML 块标量(description: | 然后换行缩进)。skills.sh 的 frontmatter 解析器不支持块标量,读出来是空的。
改成单行就过了。顺手加了 .gitattributes 锁 LF——简易解析器碰上 CRLF 检出也会挂。
教训:发布之后一定要从零目录装回来跑一遍。不然这个包会静默地谁都装不上,而你的 GitHub 页面看起来一切正常。
六、关于白嫖,我的想法变了
发之前我纠结了一下许可证。1235 行代码开源出去,别人拿去改个名字做成产品收钱,我图什么?
想通了一件事:这 1235 行不是护城河。一个熟练的人一周能重写。真正值钱的是三样——分发触点、线索,和人工交付的那一段(改好的文件、招标符合性矩阵、跨全书的语义级术语归并,这些工具都做不了)。
用许可证去卡代码,等于为了守一个守不住的东西,砸掉唯一在起作用的获客管道。
但我确实需要署名。想明白的第二件事是:真正的署名不在 LICENSE 文件里,没人读 LICENSE。真正的署名在每份体检报告的页脚——那是甲方(出版社编辑、投标经理)唯一会看到的地方。
所以最后是这样:
- 许可证 Apache-2.0。商用随便用,fork 随便 fork。§4(d) 让 NOTICE 跟着衍生版走,§6 明确不授予商标权——代码 fork 走是你的,「U-King」这个名字不是。
- NOTICE 里只写一件事:报告页脚请留着。
- 想去掉页脚?那是白标授权,可以谈,不贵,也不会为难你,发个邮件说一下你在做什么就行。
最后这条是关键。别想着防白嫖,去给白嫖标价。愿意付钱买白标的人,本来就是商业用户——他自己举手表明了身份,从"损失"变成了"线索"。而不愿意付的那批人,你本来也拦不住,何必为他们把门槛砌在所有人面前。
七、怎么用
npx skills add dongsheng123132/doc-consistency
# 或
clawhub install doc-consistency
# 或直接
git clone https://github.com/dongsheng123132/doc-consistency
node scripts/check.mjs 你的稿子.docx
支持 .docx / .md / .txt。--fail-on high 会在还有高危项时返回非 0,可以直接塞进 CI,或者写进合同验收条款——这是我觉得它跟"我帮你看了一遍"最大的区别:
不是「我检查过了没问题」,是「你自己跑一遍,红点从 137 条降到 0」。验收标准是机器能判定的,省掉扯皮。
工具是免费的,永远免费。上一篇末尾说的那八类活——教材、标书、网文、合同、翻译质检——还是那句话:先免费试跑一章,一条真问题都没查出来就不收钱,那这单本来就不该花钱。
邮箱 HEFANGSHENG@gmail.com,代码在 github.com/dongsheng123132/doc-consistency。