Imported from UltiKits/UltiTools-Dev-Doc (
AGENTS.md). Install upstream withnpx skills add UltiKits/UltiTools-Dev-Doc. Copyright stays with the author.
文档行文规范
本文件约束 docs/src/ 下所有页面的行文,中英双语同等适用。面向的是修改本仓库的人和 coding agent。
这里只讲怎么写。仓库结构、构建方式、示例代码的防漂移机制等内容不在本文件内。
语域
这个站是技术说明文,不是技术随笔。目标是专业、准确、好读、一遍看懂。
平实的陈述句为主,先给结论再给原因。指导性内容用第二人称「你」,语气是讲解而不是对话。
写之前先读一页现有内容对齐语感,推荐 docs/src/zh/guide/essentials/data-storage.md、docs/src/zh/guide/advanced/transactions.md、docs/src/zh/guide/advanced/ioc-container.md。它们的典型形态是这样:
UltiTools 封装了一套数据储存 API,它支持 MySQL 数据库、SQLite 数据库(6.1.0起)与 JSON 文件储存。数据存储对于开发者来说是透明的,UltiTools 将通过服主的配置判断使用哪种存储方式。
可量化的限制
下面的基线是 2026-08-16 从全站 20 个中文页、4702 行实测得出的,不是估计值。
| 指标 | 全站基线 | 单页上限 |
|---|---|---|
破折号 —— |
每百行 0.2 | 每百行 1 |
粗体 ** |
每百行 4.7 | 每百行 8 |
::: 容器块长度 |
47 个块中 31 个为 1 行 | 3 行 |
破折号在中文技术文档里几乎不需要。转折用「但」「而」,补充用括号或另起一句,解释用冒号。不要用它制造停顿感。
粗体铺满每一段等于没有重点。一段里最多一处,一节里通常一到两处。
英文页同理:em dash 作标点而不作停顿,粗体同样克制。
禁止的写法
比喻
技术文档里的比喻会把可验证的陈述换成不可验证的印象。下面左列都是实际出现过并被退回的:
| 不要写 | 改写成 |
|---|---|
| 旧 pin 只买到上面那一条保证 | 较旧的 pin 只提供上一节所说的那一条保证 |
| 一个自己没改过的模块照样能炸 | 模块自身的代码没有任何改动,仍然可能出现链接错误 |
| 一份清单会把它藏起来 | 一份按 X 组织的清单不会覆盖到这类模块 |
| 一个会乱叫的工具会被关掉 | 误报会降低这项检查的可用性 |
同类词还有「溜过」「狠」「阴」「打脸」等,一律换成直述。
元评论
不要写「这才是重点」「值得单说」「注意下面这条很关键」「说白了」。读者自己会判断轻重,作者反复强调只会稀释它。需要突出就靠位置和结构,不靠强调语。
反问与自问自答
不要写「这意味着什么?意味着……」。直接给结论。
站外带进来的词
「判据」在全站零命中,属于外部用词习惯。用「标准」「依据」「条件」。遇到拿不准的词,先 grep 一下全站有没有人用过。
标题
用名词短语。现有标题的形态是「基本用法」「批量操作」「手动获取」「插件主类」「API 参考」,或者直接是 API 名(BaseDataEntity、@Column 注解)。
不要用口语动词短语:
| 不要 | 改成 |
|---|---|
| 怎么真的验一下 | 检查产物 |
| 它逼你发布什么 | 需要执行的操作 |
| How to actually check | Checking an artifact |
| What that forces you to ship | What has to be released |
英文同理:Basic Usage、Complete Example、Declarative Transactions 这一类。
容器块
::: tip / ::: info / ::: warning / ::: danger 需要带标题,正文一到三句。
容器块不承载论证。需要展开的内容拆成正文小节。此前有一个 ::: warning 长到 125 行、内含六个层次,读者带着一个具体错误进来,要把整个框读完才知道自己属于哪一种情况。
中英对应
英文是根 locale,中文镜像在 docs/src/zh/,两侧结构一一对应,标题数量应当相等。
Java 示例由 <<< 引用同一份 .java 文件,中英共用,不要为中文另写一份。
提交前自检
f=docs/src/zh/guide/advanced/你改的页.md
l=$(wc -l < "$f")
echo "破折号/百行: $(echo "scale=1;$(grep -o '——' "$f" | wc -l)*100/$l" | bc) 上限 1"
echo "粗体/百行: $(echo "scale=1;$(grep -o '\*\*[^*]' "$f" | wc -l)*100/$l" | bc) 上限 8"
# 以下三条应当都没有输出
grep -nE '^#+ (怎么|如何|为什么要|它逼|先说|再说)' "$f"
grep -nE '判据|才是重点|值得单说|说白了' "$f"
bash scripts/check-container-length.sh "$f"
这些命令统计的是字符出现次数,分不清正文和被引用的反例。一个页面如果举了反例、或者写了「不要用破折号」这样的句子,它会把这些也算进去。看每一条命中是什么,不要只看数字。 本文件自身就是例子:跑上面的脚本会报出六处粗体和三处破折号,其中除两处外全部来自反例表格和脚本里的 grep 模式。
改完执行 npm run build,然后从构建产物核对页内锚点,不要在 Markdown 源码里核对。中文标题中的 : 和 , 会被转换成连字符,手写的锚点很容易对不上,而这类链接渲染正常、点击无反应,源码检查看不出来。
python3 - .vitepress/dist/zh/guide/advanced/你改的页.html <<'EOF'
import re, sys, pathlib
s = pathlib.Path(sys.argv[1]).read_text(encoding='utf-8')
ids = set(re.findall(r'<h[2-4][^>]*\bid="([^"]+)"', s))
bad = [l for l in set(re.findall(r'href="#([^"]+)"', s)) - {'VPContent'} if l not in ids]
print("断链:", bad or "无")
EOF
跑这些检查时会骗人的地方
下面每一条都在这个仓库里真实发生过。共同点是失效的时候看起来像通过。
统计命令 | tee 文件 | head -N 会截断那个文件。head 读满 N 行就退出并关闭管道,tee 收到 SIGPIPE 当场死掉,随后 wc -l < 文件 会诚实地报告截断后的数字。统计类命令不要经过 head。
grep -c 数的是行,不是出现次数,一行里出现三次只记 1。要数出现次数用 grep -o 模式 文件 | wc -l。核对构建产物时尤其要注意,压缩后的 HTML 会把很多东西放在同一行。
行尾符按文件而定,中英两侧可能相反。guide/quick-start.md、guide/advanced/external-plugin-api.md、guide/advanced/module-eventbus.md 中英两份是纯 CRLF,其余多为 LF。编辑前当场判定,不要依赖任何静态清单:
t=$(wc -l < "$f"); cr=$(grep -c $'\r' "$f" || echo 0)
[ "$t" = "$cr" ] && echo "$f 纯 CRLF" || echo "$f LF"
用非 CRLF 感知的方式改写会产生整文件 diff,而构建、双语对等与渲染全都照样通过。同理,awk 的范围结束模式 /^:::$/ 在 CRLF 文件上永不匹配,得先 tr -d '\r'。这个坑本仓库踩过三次。
sed -E 会把正文里的 | 当选择符,替换含 catch (Exception | Error) 的行时会把后半截复制一遍。改用逐字字符串替换。
用 grep 验证「某个说法已被清除」,只在它必然以那个字符串出现时才成立。同一处落点常有多种表述,查一个短语返回空不代表概念已消失。改完要正反双向核:反向用多种说法查残留,正向确认新写法真的写进去了。
提交正文里出现 --no-verify 字面量会被 block-no-verify hook 拦下,即使 git 从未收到该参数,因为它扫的是命令文本,heredoc 正文也算。把信息写进文件再用 git commit -F <文件>。
提交正文提到 issue 编号时,别让 fix/close/resolve 紧邻 #<n>,GitHub 会把它当关闭关键字,哪怕那句话只是在陈述事实。本仓库的主追踪 issue 因此被一次合并自动关掉过。
提交正文要写出所处理的 finding 编号,各占一行。后续对账靠从 git 历史里 grep 这些编号回填归宿,只写技术内容不写编号,那一步会落空。
自检的能力边界
上面那些检查通不过,说明一定有问题;全部通过,不说明没问题。 这条是单向的,不要反过来用。
2026-08-16 拿被退回的那一版做过实测。方法是只与全站其余 20 个中文页的语料比对,不预先告诉程序哪些词是坏的:
| 指标 | 语料中位数 | 退回的那版 | 倍数 |
|---|---|---|---|
| 破折号/百行 | 0.0 | 20.1 | 无穷 |
| 粗体/百行 | 3.6 | 48.1 | 13 |
| 标题平均字数 | 7.1 | 13.2 | 2 |
| 句子平均长度 | 101.6 | 52.0 | 1 |
| 括号/百行 | 8.3 | 5.8 | 1 |
前两项是数量级偏离,机器一眼能看出来。但真正被退回的理由是句子本身的语域,例如「旧 pin 只买到上面那一条保证」——而这一页的句子平均长度比全站中位数还短,从结构上看完全正常。
决定性的反证是:只删破折号、只减粗体,把比喻原样留着,所有指标都会变绿,而那样的页面照样不合格。
所以这些数字能做的只有一件事,就是提示「这一页值得回头看一眼」。它们不构成通过标准。新出现的比喻、结构良好但语域不对的句子,只能靠人读,或者靠一个带着上面三个参照页去做对比阅读的 agent 回答「这一页读起来像不像其它页」。那是判断题,不是规则匹配,也会漏。
本规范的由来
2026-08-16,guide/advanced/module-versioning.md 因行文被退回重写。当时它每百行有 12.2 处破折号(全站基线 0.2)、53.7 处粗体(基线 4.7),标题写成口语动词短语,正文用比喻代替直述。重写后事实性内容一处未改,行数从 309 降到 216,减掉的全部是修饰。