feat: expand deai writing checks
GoodBuddy Agents / Validate Agent source (push) Canceled after 0s
GoodBuddy Agents / Build Linux arm64 Agent (push) Canceled after 0s
GoodBuddy Agents / Build Linux x64 Agent (push) Canceled after 0s
Cross-platform packages / Validate source (push) Canceled after 0s
Cross-platform packages / Authorize desktop packaging (push) Canceled after 0s
Cross-platform packages / linux arm64 (push) Canceled after 0s
Cross-platform packages / macos arm64 (push) Canceled after 0s
Cross-platform packages / windows arm64 (push) Canceled after 0s
Cross-platform packages / linux x64 (push) Canceled after 0s
Cross-platform packages / macos x64 (push) Canceled after 0s
Cross-platform packages / windows x64 (push) Canceled after 0s
Cross-platform packages / Publish GitHub and OSS release (push) Canceled after 0s

The bundled deai-writing skill now detects additional template patterns,
unsupported attribution, role-slot prose, engineering metaphors, and absolute
claims. Its JSON report also summarizes findings by level and category while
preserving GoodBuddy's cross-platform Python guidance.

Release note: 中文“去 AI 味”审校新增分析套话、模糊归因、角色槽位、工程黑话和绝对化承诺等检查,并提供按分类汇总的扫描结果。
This commit is contained in:
mesalogo
2026-09-01 20:48:58 +08:00
parent 2f1881a414
commit 2fca39ddb2
4 changed files with 217 additions and 12 deletions
+110 -12
View File
@@ -1,10 +1,10 @@
---
name: deai-writing
version: 1.1.0
version: 1.3.0
description: |
中文正式文档「去 AI 味」审校。用于任何需要产出不露 AI 痕迹的正式中文文本:
中文正式文档「去 AI 味」审校。用于降低模板化、空泛和过度工整的机器生成痕迹:
投标方案、技术方案、公司官网文案、研究文章、汇报材料、说明文档、商务邮件。
在生成或润色中文正式文档之后调用,也可在评审阶段单独调用做质量门禁。
在生成或润色中文正式文档之后调用,也可在评审阶段单独调用做质量检查。
提供可执行的病症词典扫描脚本,把「凭感觉找 AI 味」变成「按清单定位并改写」。
触发词:去 AI 味、AI 腔、AI 味、文案审校、润色中文文档、官网文案评审。
allowed-tools:
@@ -17,13 +17,14 @@ compatibility: Python 3.9+,不依赖第三方 Python 包
# 中文正式文档「去 AI 味」审校
AI 味不是玄学,而是一批可枚举、可正则命中、可批量修改的固定套路。所以这件事
能做成脚本 + 清单反复调用,不用每次靠人肉感觉。
所谓 AI 味,通常是模板化连接词、空心形容词、强迫式排比、元话语和低信息密度
等可观察特征。扫描器负责找候选,Agent 结合语境改写,人工负责事实与语气验收。
检测结果不是作者身份鉴定,也不能证明一段文字由 AI 生成。
## 什么时候用
- 刚用大模型生成或润色完一份中文正式文档,交付前。
- 长文方案编制流程里,接在关键词核验之后、人工通读之前,作为固定质量门禁
- 长文方案编制流程里,接在关键词核验之后、人工通读之前,作为固定检查步骤
(见 `longdoc-docx` 技能)。
- 官网/产品文案评审,被人挑出「像 AI 写的」但说不清哪里像。
@@ -46,7 +47,8 @@ AI 味不是玄学,而是一批可枚举、可正则命中、可批量修改
输出分两级:
- **阻断项**:命中即应改写,目标压到接近 0。
- **阻断项**:高置信度套路表达,应逐条核对并改写,目标压到接近 0;法律原文、
客户原话、引用内容和固定术语可以保留,但要记录理由。
- **复核项**:只是候选,结合页面类型、事实边界和专业语境逐条判断,**不追求
机械清零**。研究文章里的「闭环」如果确有定义就该留着。
@@ -56,7 +58,9 @@ AI 味不是玄学,而是一批可枚举、可正则命中、可批量修改
2. 扫描源文件,将 JSON 命中清单与原文一起交给 Agent 定向改写。
3. 逐条核对改写没有编造数字、删除限制条件或改变责任主体。
4. 复扫,直到阻断项收敛;逐条处理复核项,不机械清零。
5. 通读全文,检查关键词扫描无法发现的前后矛盾和主体错位。
5. 做一次结构复核:检查段落是否等长、标题是否同构、列表是否总凑三点、句子
是否反复使用同一种节奏。
6. 通读全文,检查关键词扫描无法发现的前后矛盾、主体错位和语气漂移。
扫描器只负责定位,不提供自动替换。语义改写必须由 Agent 结合上下文完成,
避免把专业术语、法定提示和事实边界误删。
@@ -140,8 +144,9 @@ AI 味不是玄学,而是一批可枚举、可正则命中、可批量修改
按事实保留。**
### 11. 对照句式、问答式标题与人为凑数
句式:`不是 A,而是 B`、`并非 A,而是 B`、`A,而不是 B`、`不先谈 A,先看 B`。
英文的 `not A but B`、`rather than`、`instead of` 同属一类。
句式:`不是 A,而是 B`、`并非 A,而是 B`、`不只是 A,还要 B`、
`A,而不是 B`、`不先谈 A,先看 B`。英文的 `not A but B`、`rather than`、
`instead of` 同属一类。
标题:`结果回答了三个具体问题`、`以下四点值得关注`、`三个发现`、
`我们需要回答什么`。这类标题只描述文章结构,没有说明本节内容。
@@ -190,9 +195,89 @@ CTA 应指向项目咨询、合作沟通或联系团队,避免 `按清单准
等内部审查语言,改写为客户可理解的适用范围、接入条件和分阶段交付边界;规划
能力仍须用将来时或设计阶段表述。
### 14. 万能分析框架与标签化收束
`从本质上看`、`从某种意义上说`、`从多个维度`、`核心在于`、`关键在于`、
`归根结底`、`可以看出`、`由此可见`、`具体而言`、`从……角度来看`。
这些表达不一定错误,但连续出现时常在替代真实推理。删掉框架词后检查句子:
如果仍没有对象、动作、条件或证据,就补充具体信息;如果信息已经完整,直接
陈述即可。
### 15. 假引用、假共识与来源模糊
`业内普遍认为`、`研究表明`、`数据显示`、`专家指出`、`实践证明`、
`有观点认为`,但没有来源、样本、日期或适用范围。
改法:能追溯就补来源和口径;不能追溯就改成本文可承担的判断,或删除。不要用
模糊权威给观点加重量,也不要为了显得具体而编造论文、机构、链接和数字。
### 16. 句式和段落节奏过度整齐
命中信号:连续段落以同一短语开头;每段都是「结论 + 三个分点 + 小结」;
多个标题使用同一种问句或冒号结构;句长和段长近乎一致。
改法:按信息关系组织,不按模板配平。合并重复段,拆开超载句;事实、解释、
限制和动作可以使用不同长度,不必每节都做总结。扫描器只能发现少量局部信号,
这项必须人工复核。
### 17. 角色槽位式三段法
`系统做什么、学生做什么、教师看到什么`、`用户需要什么、平台提供什么、
最终获得什么`、`输入是什么、处理什么、输出什么`。这类句子先盘点角色或槽位,
再把内容填进去,形式完整但没有描述真实流程。
角色主语本身没有问题,问题在于反复使用 `谁做什么`、`谁看到什么` 等元问题,
或把每个角色机械配成长度相近的一段。改写时按事件顺序写清触发条件、系统动作
和可见结果,不必给每个角色单独设栏。
- 「系统做什么、学生做什么、教师看到什么」
→ 「学生提交答案时,系统保存答题步骤和引用依据;教师可在评分页展开查看」
- 「教师看到的不只是最终分数」
→ 「评分页同时展示最终得分、答题步骤和引用依据」
需求矩阵、职责表和泳道图本来就按角色组织,可以保留;叙事正文和宣传标题应
优先描述具体流程或结果。
### 18. 闸门、门禁、硬约束等工程黑话
`闸门`、`门禁`、`硬约束`、`卡点`、`护栏` 在工程语境中可能是准确术语,也可能
只是给普通规则套上的比喻。单独出现只作复核,不机械删除。
检查它是否说明了四件事:对象、条件、执行动作和结果。例如:
- 「设置质量门禁」→「测试失败时禁止合并」
- 「形成发布闸门」→「延迟超过 200 ms 时停止发布」
- 「建立安全门禁」→「未通过权限校验的请求直接拒绝」
- 「施加硬约束」→「显存占用不得超过 80 GB」
`CI 门禁`、优化问题中的 `硬约束` 等团队已有明确定义的术语可以保留。若删掉
黑话后仍看不出谁在什么条件下阻止什么,就应改成可执行规则。
### 19. 拟人化数量词与连续工程隐喻
`每一次思考都有证据`、`一颗权重`、`数十亿颗权重`、`数据喂不够快`、
`存储到计算的路越来越繁忙`。拟人化或比喻偶尔能辅助解释,但多个比喻连续出现,
会让技术说明像自动生成的科普文案,并掩盖可验证的技术关系。
改写时回到实际对象、单位和瓶颈:
- 「每一次思考都有证据」→「系统保存答题步骤及其引用依据」
- 「数十亿颗权重都要被反复读取」→「每生成一个 Token,都需要访问模型各层的
大量权重」
- 「从算得不够快变成数据喂不够快」→「Decode 阶段通常受显存带宽而非计算吞吐
限制」
- 「这条路越繁忙」→「权重读取总量上升,显存带宽压力随之增加」
不要为了去掉比喻而引入未经测量的结论。`通常受显存带宽限制` 仍需结合模型、
批量大小、硬件和推理实现确认。
### 20. 绝对化承诺与全称判断
`每一次……都……`、`所有……均……`、`任何情况下`、`始终确保`、`完全避免`。
这类表达容易把局部能力写成无条件保证,也常与口号、拟人化表达组合出现。
先核对产品机制和证据范围。能够保证时写清条件,例如「开启过程记录后,评分页
保存每次提交的步骤与引用」;不能保证时改成实际覆盖范围。法规、数学命题和测试
验收标准中的全称表达按原意保留。
## 改写纪律
去 AI 味不是把文本改得干瘪,而是去套路、留信息。四条底线:
去 AI 味不是把文本改得干瘪,更不是伪装作者身份,而是去套路、留信息。六条
底线:
1. **只删套路,不删事实。** 形容词换成数字/动作是「换」不是「删信息」;
连接词、铺垫、升华才是直接删。
@@ -203,21 +288,32 @@ CTA 应指向项目咨询、合作沟通或联系团队,避免 `按清单准
规划能力写成已经实现。
4. **改完复读一遍出声。** AI 味的本质是结构过于工整、信息密度偏低,出声读
最容易发现。
5. **不编造“人味”。** 不故意加入错别字、口头禅、虚构经历、情绪或未经证实的
个人判断。自然表达来自具体信息和真实语境,不来自人为瑕疵。
6. **尊重引用与固定文本。** 法规、合同条款、客户原话、品牌标语和准确引文不
因命中词典而改写;必要时从扫描范围排除,或在评审记录中说明保留原因。
## 一分钟自查清单(不跑脚本时)
- 开头有没有「随着/在……的今天」?删。
- 有没有「不仅……而且/综上所述/值得注意的是」?删。
- 有没有「不是 A,而是 B」或「回答三个问题」式标题?直接写 B 或具体结果。
- 有没有「不只是 A,还要 B」?删掉对照架子,直接并列实际信息。
- 公司首页是否写成了实施步骤?改成服务领域、产品能力和公司定位。
- CTA 是否像需求填报表?改成项目咨询或合作沟通。
- 官网是否出现「当前基线」「已知缺口」等内部审校语言?改成适用范围和交付条件。
- 标题是否以 `把/让/先/再` 或 `Bring/Start/Let/First` 发出操作口令?改名词性。
- 产品首屏是否塞入接口字段、回退链、调试过程?下沉到技术说明。
- 有没有「研究表明/数据显示/业内普遍认为」却不给来源?补证据或删除归因。
- 是否反复用「从本质上看/核心在于/具体而言」代替实际分析?直接写对象和结论。
- 是否在盘点「系统/用户/教师做什么」?按真实事件顺序描述触发、动作和结果。
- `闸门/门禁/硬约束/护栏` 是否说明了执行条件和后果?否则改成具体规则。
- 是否使用「一颗权重/数据喂不够快/道路繁忙」等连续比喻?改回技术对象和指标。
- `每一次/所有/始终/完全` 是否超出了证据范围?补条件或缩小范围。
- 限制是否在同一页面重复出现?合并为一处正向范围说明,同时保留必要的研究、
安全和人工复核边界。
- 形容词能不能换成数字或具体动作?能就换。
- 是不是每段都凑三点、每句都加粗?打破它。
- 是不是每段都凑三点、每句都加粗、相邻段落等长同构?按信息关系重组。
- 有没有破折号、Emoji、客服式结尾?清掉。
- 出声读一遍:像人说话,还是像念 PPT?
@@ -267,6 +363,8 @@ deai-writing/
## 已知边界
- 结构性问题(排比、三段式、每段凑三点)正则只能给候选,最终要人读。
- 角色槽位、工程黑话、拟人化比喻和绝对化表达都有合理语境,只能作为复核线索。
- 「不仅……而且」等词单独出现误报率高,词典里已收敛成句式匹配;仍会有误报,
阻断项要逐条看过再改,不能盲目全局替换。
- 扫描器不判断事实正确性。改写时新引入的数字必须有证据支撑。
- 扫描结果只描述文本特征,不判断作者身份,也不应用于学术诚信或人员考核结论。
@@ -25,6 +25,8 @@ AI_SMELL = {
],
"对照模板": [
r"不是.{0,40}而是", r"并非.{0,40}而是", "而不是",
r"不只是.{0,40}(而是|还|也|更)", r"不止是.{0,40}(而是|还|也|更)",
r"不只是[^,。;!?\n]{1,30}(?:[。;!?\n]|$)",
r"不先.{0,30}先", r"下一步是.{0,40}而不是",
],
"问答式标题": [
@@ -67,6 +69,13 @@ AI_SMELL = {
"过度对冲": [
"在某种程度上", "总体而言", "一般来说",
],
"万能收束": [
"从本质上看", "从某种意义上说", "归根结底", "可以看出",
"由此可见",
],
"来源模糊": [
"业内普遍认为", "专家指出", "有观点认为",
],
"格式痕迹": [
"——", "✅", "❌", "🚀", "💡", "🎯", "✨", "🔥", "📌", "⚡",
],
@@ -93,6 +102,38 @@ REVIEW_ONLY = {
"否定句堆叠": [
"不代表", "不包含", "尚未", "不能",
],
"分析框架": [
"从多个维度", "核心在于", "关键在于", "具体而言",
r"从.{0,20}角度来看",
],
"无来源归因": [
"研究表明", "数据显示", "实践证明",
],
"角色槽位": [
r"(系统|平台).{0,20}(做什么|负责什么).{0,40}"
r"(学生|教师|用户|客户).{0,20}(做什么|看到什么|获得什么)",
r"(学生|教师|用户|客户).{0,20}(做什么|负责什么).{0,40}"
r"(系统|平台|教师|用户|客户).{0,20}(做什么|看到什么|获得什么)",
r"输入是什么.{0,30}(处理什么|怎么处理).{0,30}输出什么",
r"用户需要什么.{0,30}平台提供什么.{0,30}(最终)?获得什么",
],
"工程黑话": [
"闸门", "门禁", "硬约束", "卡点", "护栏",
],
"拟人化技术表达": [
r"[一两二三四五六七八九十百千万亿0-9]+颗(权重|参数)",
"数据喂不够快", r"(存储|内存|显存).{0,20}(道路|路).{0,20}(繁忙|拥堵)",
r"(模型|系统).{0,10}(思考|理解|读懂|看懂)",
],
"绝对化承诺": [
r"每一(次|个|项|条).{0,30}(都|均)", r"所有.{0,30}均",
"任何情况下", "始终确保", "完全避免",
],
"标签化三联": [
r"(看得见|可见).{0,20}(查得到|可查).{0,20}(可追溯|能追溯)",
r"事前.{0,20}事中.{0,20}事后",
r"业务侧.{0,20}技术侧.{0,20}管理侧",
],
}
# ---------------------------------------------------------------------------
@@ -219,6 +219,19 @@ def collect_files(targets, exts):
return list(dict.fromkeys(out))
def summarize_findings(findings):
"""Count findings by level and category for CI and agent consumers."""
counts = {LEVEL_BLOCK: {}, LEVEL_REVIEW: {}}
for finding in findings:
level_counts = counts[finding["level"]]
category = finding["category"]
level_counts[category] = level_counts.get(category, 0) + 1
return {
level: dict(sorted(category_counts.items()))
for level, category_counts in counts.items()
}
def main():
ap = argparse.ArgumentParser(description="中文正式文档 AI 味扫描")
ap.add_argument("targets", nargs="+", help="待扫描的文件或目录")
@@ -271,6 +284,7 @@ def main():
n_block = sum(1 for f in all_findings if f["level"] == LEVEL_BLOCK)
n_review = len(all_findings) - n_block
by_category = summarize_findings(all_findings)
if args.json:
print(json.dumps({
@@ -278,6 +292,7 @@ def main():
"requested_files": len(files),
"block": n_block,
"review": n_review,
"by_category": by_category,
"errors": errors,
"findings": all_findings,
}, ensure_ascii=False, indent=2))
@@ -31,6 +31,55 @@ class DeaiScanTests(unittest.TestCase):
levels = {finding["level"] for finding in findings}
self.assertEqual(levels, {deai_scan.LEVEL_BLOCK, deai_scan.LEVEL_REVIEW})
def test_reports_new_reasoning_and_attribution_patterns(self):
findings = self.scan(
"从本质上看,业内普遍认为该方案有效。\n"
"研究表明,核心在于形成闭环。"
)
categories = {finding["category"] for finding in findings}
self.assertTrue({"万能收束", "来源模糊"}.issubset(categories))
self.assertTrue({"无来源归因", "分析框架"}.issubset(categories))
def test_summarizes_findings_by_level_and_category(self):
findings = self.scan("综上所述,归根结底,需要复核当前基线。")
summary = deai_scan.summarize_findings(findings)
self.assertEqual(summary[deai_scan.LEVEL_BLOCK]["套路连接词"], 1)
self.assertEqual(summary[deai_scan.LEVEL_BLOCK]["万能收束"], 1)
self.assertEqual(summary[deai_scan.LEVEL_REVIEW]["内部审校话语"], 1)
def test_reports_user_identified_templates_and_metaphors(self):
findings = self.scan(
"每一次思考都有证据,教师看到的不只是最终分数。\n"
"系统做什么、学生做什么、教师看到什么。\n"
"设置质量门禁和硬约束,让数据不再喂不够快。\n"
"Decode 时需要读取数十亿颗权重。"
)
categories = {finding["category"] for finding in findings}
self.assertIn("对照模板", categories)
self.assertTrue(
{
"角色槽位",
"工程黑话",
"拟人化技术表达",
"绝对化承诺",
}.issubset(categories)
)
def test_keeps_concrete_engineering_rules_clean(self):
findings = self.scan(
"测试失败时禁止合并。延迟超过 200 ms 时停止发布。"
"显存占用不得超过 80 GB。"
)
watched = {
"角色槽位",
"工程黑话",
"拟人化技术表达",
"绝对化承诺",
}
self.assertFalse(
any(finding["category"] in watched for finding in findings)
)
def test_masks_markdown_code(self):
findings = self.scan("正文没有问题。\n```text\n综上所述,打造闭环。\n```\n")
self.assertEqual(findings, [])
@@ -80,6 +129,8 @@ class DeaiScanTests(unittest.TestCase):
result = subprocess.run(command, check=True, capture_output=True, text=True)
report = json.loads(result.stdout)
self.assertGreater(report["block"], 0)
self.assertIn("阻断", report["by_category"])
self.assertIn("复核", report["by_category"])
failed = subprocess.run(
command + ["--fail-on-block"],