尧图建网站 尧图建网站 YAOTU WEB BUILD 免费咨询
ARTICLE DETAIL

资讯详情

深耕网站建设与建站编程的一线实战洞察。

本地确定性风格检查:构建AI写作质量评估工具

本地确定性风格检查:构建AI写作质量评估工具 很长时间以来AI 写作辅助工具和内容生成模型已经深入开发者的日常工作。我们既享受大模型带来的效率提升也面对一个现实问题AI 生成的文字往往带着明显的“机器味”包括句式重复、连接词滥用、逻辑跳跃以及高频套话。更麻烦的是很多团队想在校稿阶段统一风格却依赖人工逐条核对效率很低。本文围绕一个很有意思的项目理念展开Writing-eval即针对 AI 生成稿件做本地、确定性deterministic风格检查。不是说教模型怎么写得更好而是用一套可复现、可测试、不依赖云端大模型的规则和指标在代码工程层面自动发现风格问题。如果你是内容平台的工程师、AI 应用开发者、技术写作者或者正在做 AI 稿件审核工具这篇文章会比较实用。文中的思路可以脱离具体仓库落地到自己的项目里整套方案适合本地执行也适合集成进 CI 流水线。1. 背景与核心概念1.1 什么是 Writing-eval 和确定性风格检查先拆开来看这个项目名。Writing-eval 可以理解为一套“写作评估器”。它接收一段文本尤其是 AI 生成的初稿输出一系列质量指标、样式警告和可读性评分。最关键的限定词是local deterministic也就是“本地确定性”。这在当前 AI 写作工具泛滥的背景下非常重要local本地所有检查逻辑在本地机器或自建服务器上执行不需要把文本发送给第三方大模型 API。这对于处理内部文档、未发布产品文案、包含业务敏感信息的稿件尤其重要。deterministic确定性同样的输入文本永远产生同样的输出结果。它不像大模型生成那样带有随机性也不依赖模型版本波动。检查规则是固定的指标算法是明确的阈值是可配置的。换句话说Writing-eval 这一类工具更像传统代码风格检查器例如 ESLint、Pylint而不是 AI 评审系统。它不“感受”文章好坏而是用明确规则判断你是否使用了太多被动语态、是否反复出现同一个开头词、是否每个句子都超过 40 个单词。1.2 它解决什么问题在日常工作里我们经常遇到三类场景产品团队批量生成营销文案。AI 初稿往往有固定套路例如每段都以 “首先”、“其次”、“总的来说” 开头。人工修改没有统一标准团队不同成员对“什么是好风格”理解不一样。技术博客和文档平台。AI 生成的技术文章会有很重的“AI 腔”例如频繁使用 “值得注意的是”、“综上所述” 等连接词句子长度分布失衡并列结构过度重复。平台需要在上线前自动拦截这类内容。AI Agent 应用的后处理环节。Agent 生成的内容需要经过风格规则检查如果不符合预设风格标准就进入重写流程而不是直接返回给用户。这个检查环节必须快速、稳定、可批量执行本地确定性规则比再次调用大模型便宜得多。Writing-eval 的核心价值就是把这套检查从人工经验变成程序逻辑规则容易理解、结果可以追溯、行为永远一致。1.3 和基于 LLM 的评估器有什么区别这是理解和运用 Writing-eval 的关键。业界常见的 AI 内容评估方式是用另一个大模型来打分例如让 GPT 系列模型做“论文评审助手”。它确实能理解语义和上下文但存在几个问题对比维度基于 LLM 的评估本地确定性风格检查结果稳定性不稳定随机采样影响大完全可复现延迟高需要网络请求极低毫秒级成本按 Token 计费零边际成本隐私文本出域本地处理规则透明度不透明难以解释规则即代码可阅读语义理解强弱主要靠统计和模式因此实际项目中更合理的组合是先用确定性规则快速过滤明显问题再按需调用大模型做深层的逻辑连贯性评估。Writing-eval 解决的是第一层也是最频繁、最基础的一层。1.4 适合谁使用AI 应用开发工程师需要在 Agent 或内容生成管线中加入后处理质检。平台内容治理工程师需要批量检测 AI 生成内容的风格统一度。技术文档工程师希望用脚本统一博客、API 文档、教程的语言风格。独立开发者想做一个轻量的本地写作辅助工具不依赖大模型 API。2. 环境准备与版本说明Writing-eval 的实现不限定某种特定语言。你可以用 Python、Node.js、Go 甚至 Shell 脚本完成一部分检查。不过考虑到文本处理生态成熟度本文以 Python 为例。2.1 基础环境操作系统Windows 10/11、macOS、Linux 均可。Python 版本建议 3.9 及以上。输入材料没有指定具体依赖版本下面给出的标准库用法无需额外安装第三方包重点演示思路。如果你的项目已经有虚拟环境直接使用即可。检查 Python 版本python --version建议创建独立虚拟环境避免污染全局环境python -m venv .venv # Windows .venv\Scripts\activate # macOS / Linux source .venv/bin/activate2.2 项目结构设计为了让教程清晰我们规划一个最小可扩展的项目结构writing_eval/ ├── checks/ │ ├── __init__.py │ ├── sentence_length.py │ ├── repetition.py │ ├── transitional_words.py │ ├── passive_voice.py │ └── readability.py ├── readers/ │ ├── __init__.py │ ├── text_reader.py │ └── markdown_reader.py ├── reporters/ │ ├── __init__.py │ ├── json_reporter.py │ └── console_reporter.py ├── config/ │ └── default_rules.yaml ├── main.py └── requirements.txt其中checks目录存放各类检查规则。readers目录负责读取不同格式的文本例如纯文本、Markdown、HTML。reporters目录负责输出检查结果。config目录存放规则配置。2.3 为什么要用目录模块化风格检查规则会越来越多如果全部写在单个脚本里最后会变成一座难以维护的大泥球。模块化之后每个规则是一个独立函数或类输入永远是文本块输出永远是RuleResult对象。这样新增一条规则只需要新增一个文件注册一下即可。3. 核心原理拆解规则、指标与模式在写代码之前先理解 Writing-eval 背后的核心机制。它主要由三部分组成文本预处理、规则引擎、指标聚合。3.1 文本预处理原始文本不能直接进入规则判断。需要先做按段落拆分。段落按句子拆分。句子进行单词化Tokenization。去除代码块、Markdown 标记、HTML 标记等噪声。句子拆分看起来简单实际有很多边界情况缩写中的句号例如 “Dr.”、“e.g.”。小数点例如 “3.14”。引号和括号内的句子结束符。换行符导致的错误拆分。一个保守的做法是先处理缩写表再做规则拆分。下面是简化版示例# readers/text_reader.py import re ABBREVIATIONS { dr., mr., mrs., ms., prof., sr., jr., e.g., i.e., etc., vs., inc., ltd., co., } SENTENCE_END re.compile(r[.!?。]) def split_sentences(text: str) - list[str]: 按句号拆分为句子保留缩写词场景。 # 先保护缩写词中的句号避免误拆 protected text for abbr in ABBREVIATIONS: protected protected.replace(abbr, abbr.replace(., )) # 再按句子结束符拆分 candidates SENTENCE_END.split(protected) # 恢复缩写词句号 result [] for sentence in candidates: sentence sentence.replace(, .) sentence sentence.strip() if sentence: result.append(sentence) return result这里把缩写里的句号临时替换成拆分完成后再换回来。思路很朴素但能处理多数英文文本。3.2 规则引擎设计每一条规则都遵循统一接口# checks/base.py from dataclasses import dataclass, field dataclass class RuleResult: rule_name: str severity: str # info, warning, error message: str line: int 0 start_char: int 0 end_char: int 0 suggestions: list[str] field(default_factorylist) class BaseRule: name base description Base rule def check(self, text: str) - list[RuleResult]: raise NotImplementedError所有检查规则都继承BaseRule实现check方法。返回结果是RuleResult列表。这样规则可组合、可排序、可控制误报。3.3 几类核心检查指标Writing-eval 的风格检查可以归为以下几种模式模式 1统计阈值型例如句子平均长度、单词长度分布、段落长度。这类检查只做统计超过阈值则告警。模式 2正则匹配型例如检测“In conclusion”、“Moreover”这类过渡词在相邻句子中是否高频出现检测是否连续三句都以“The”开头。模式 3语言模型/语法规则型例如启发式被动语态检测用词性标注判断 “be 过去分词” 结构。这一层需要依赖 NLP 库比如spaCy或nltk。模式 4上下文一致性型例如同一个术语在文章中出现多种写法“AI”、“A.I.”、“Artificial Intelligence” 混用。这可以基于简单字符串匹配实现。3.4 为什么选择确定性规则确定性意味着你可以在 CI/CD 管道里断言如果 AI 生成的文章违反了超过 5 条 warning 级别规则构建失败。这个断言是可靠且可测试的。你不会遇到“上次跑通过了这次没通过但是我不知道为什么”的尴尬情况。对于工程化交付确定性是硬要求。4. 完整实战案例构建一个本地风格检查工具接下来我们实现一个精简但能运行的本地风格检查器。它包含句子长度检查过渡词频率检查重复开头词检查被动语态基础检测使用正则近似不引入重型 NLP 依赖可读性指标计算Flesch Reading Ease 简化版4.1 创建项目结构与虚拟环境mkdir writing_eval cd writing_eval python -m venv .venv source .venv/bin/activate # Windows 用户运行 .venv\Scripts\activate创建上述目录结构然后在项目根目录创建main.py和配置文件。4.2 实现句子与段落读取器# readers/markdown_reader.py import re from .text_reader import split_sentences class MarkdownReader: 从 Markdown 文本中提取段落和句子忽略代码块。 def __init__(self): self.code_block_pattern re.compile(r.*?, re.DOTALL) def clean_markdown(self, text: str) - str: # 去掉代码块 text self.code_block_pattern.sub( , text) # 去掉行内代码 text re.sub(r[^]*, code, text) # 去掉图片和链接语法保留链接文字 text re.sub(r!\[([^\]]*)\]\([^)]*\), r\1, text) text re.sub(r\[([^\]]*)\]\([^)]*\), r\1, text) # 去掉 Markdown 标题符号 text re.sub(r^#\s*, , text, flagsre.MULTILINE) # 去掉列表符号 text re.sub(r^\s*[-*]\s, , text, flagsre.MULTILINE) return text def read(self, text: str) - dict: cleaned self.clean_markdown(text) paragraphs [p.strip() for p in cleaned.split(\n\n) if p.strip()] return { paragraphs: paragraphs, sentences: [s for p in paragraphs for s in split_sentences(p)], }这里需要注意clean_markdown会尽量保留正文文字去掉标记符号因为风格检查主要针对自然语言内容。4.3 实现句子长度检查规则句子长度是 AI 写作风格失衡的最直观指标。大模型生成内容经常出现两种极端要么全是短句读起来碎片化要么全是 50 词以上的长句信息密度过高但难读。# checks/sentence_length.py from .base import BaseRule, RuleResult class SentenceLengthRule(BaseRule): name sentence_length description 检查是否存在过长或过短的句子 def __init__(self, max_words40, min_words3): self.max_words max_words self.min_words min_words def check(self, text: str) - list[RuleResult]: # 这里简化处理直接用空白分割计算单词数 # 实际项目中可复用 readers 模块的句子拆分结果 import re sentences re.split(r(?[.!?。])\s, text) results [] for idx, sentence in enumerate(sentences, start1): words sentence.split() if not words: continue word_count len(words) if word_count self.max_words: results.append(RuleResult( rule_nameself.name, severitywarning, messagef第 {idx} 个句子过长{word_count} 词建议不超过 {self.max_words} 词, lineidx, suggestions[拆分长句为 2-3 个逻辑短句, 检查是否有冗余修饰语], )) elif word_count self.min_words and len(sentence.strip()) 2: results.append(RuleResult( rule_nameself.name, severityinfo, messagef第 {idx} 个句子过短{word_count} 词, lineidx, )) return results4.4 实现重复开头词检查AI 内容容易出现连续多句使用相同主语或开头词的现象。例如The system provides...The system supports...The system enables...这种重复读起来非常机械。我们可以通过统计开头词的出现次数来发现。# checks/repetition.py from collections import Counter from .base import BaseRule, RuleResult class RepetitiveOpeningRule(BaseRule): name repetitive_opening description 检查相邻句子是否重复使用相同开头词 def __init__(self, min_repetition3): self.min_repetition min_repetition def check(self, text: str) - list[RuleResult]: import re sentences re.split(r(?[.!?。])\s, text) first_words [] for sentence in sentences: words sentence.split() if not words: first_words.append() else: # 取第一个实际单词并转为小写 first_words.append(words[0].strip(\).lower()) # 滑动窗口检查最近 min_repetition 个句子中是否存在大量重复开头 results [] counter Counter() for i, word in enumerate(first_words): if not word: continue counter[word] 1 # 控制窗口 if i self.min_repetition: old_word first_words[i - self.min_repetition] if old_word: counter[old_word] - 1 if counter[word] self.min_repetition: results.append(RuleResult( rule_nameself.name, severitywarning, messagef最近句子重复使用开头词 “{word}” {counter[word]} 次, linei 1, suggestions[f尝试替换部分 “{word}” 为同义表达, 调整句式避免千篇一律的主谓宾结构], )) return results4.5 实现过渡词检测过渡词不是不能用而是 AI 生成的文本特别喜欢在每段开头堆砌固定过渡词。常见有HoweverThereforeMoreoverFurthermoreIn conclusionAdditionallyFinally连续段落使用同一类过渡词会显得模板化。# checks/transitional_words.py import re from .base import BaseRule, RuleResult class TransitionalWordRule(BaseRule): name transitional_words description 检查过渡词是否滥用 DEFAULT_WORDS [ however, therefore, moreover, furthermore, in conclusion, additionally, finally, consequently, nevertheless, meanwhile, ] def __init__(self, max_per_1000_words3, wordsNone): self.words words or self.DEFAULT_WORDS self.max_per_1000_words max_per_1000_words def check(self, text: str) - list[RuleResult]: lower_text text.lower() total_words len(text.split()) if total_words 0: return [] # 统计每个过渡词出现次数 counts {} for phrase in self.words: # 使用正则匹配完整词组避免匹配到单词中间 pattern r\b re.escape(phrase) r\b counts[phrase] len(re.findall(pattern, lower_text)) total_transitions sum(counts.values()) # 归一化到每 1000 词 normalized total_transitions / total_words * 1000 results [] if normalized self.max_per_1000_words: top sorted(counts.items(), keylambda x: x[1], reverseTrue)[:3] detail , .join([f{word}{count} 次 for word, count in top if count 0]) results.append(RuleResult( rule_nameself.name, severitywarning, messagef过渡词使用频率偏高每 1000 词 {normalized:.1f} 次阈值 {self.max_per_1000_words}高频项{detail}, suggestions[删除冗余过渡词, 用具体承接句代替模板化连接词], )) return results4.6 实现基础被动语态检测被动语态不一定是错误但过度使用会让文章冗长、权威感弱。完整的被动语态检测需要词性标注这里我们用正则近似实现一个轻量版本识别be 过去分词结构。# checks/passive_voice.py import re from .base import BaseRule, RuleResult class PassiveVoiceRule(BaseRule): name passive_voice description 检测过度使用的被动语态 # 简化版匹配 be 动词 过去分词 BE_VERBS r\b(am|is|are|was|were|been|be|being)\b PAST_PARTICIPLE r\b(\wed|taken|given|written|built|designed|developed|created|used|implemented)\b PATTERN re.compile(BE_VERBS r\s PAST_PARTICIPLE, re.IGNORECASE) def __init__(self, max_passive_ratio0.15): self.max_passive_ratio max_passive_ratio def check(self, text: str) - list[RuleResult]: matches self.PATTERN.findall(text) total_sentences len(re.findall(r[.!?。], text)) if total_sentences 0: return [] passive_count len(matches) ratio passive_count / total_sentences results [] if ratio self.max_passive_ratio: examples [f{m[0]} {m[1]} for m in matches[:5]] results.append(RuleResult( rule_nameself.name, severitywarning, messagef被动语态比例偏高{ratio:.0%}阈值 {self.max_passive_ratio:.0%}例{, .join(examples)}, suggestions[尽量使用主动语态让动作发出者更清晰, 如果被动语态无法避免保留并确认其必要性], )) return results4.7 实现可读性指标可读性度量最有名的是 Flesch Reading Ease。它考虑句子的平均长度和单词的平均音节数。虽然这个指标诞生于英语文本但它对检测“是否又把文章写得很拗口”依然有效。简化版算法Flesch Reading Ease 206.835 - 1.015 * (总单词数 / 总句子数) - 84.6 * (总音节数 / 总单词数)分数越高文本越容易读。常见分段90-100非常容易读60-70标准英语30-50较难0-30极难# checks/readability.py import re from .base import BaseRule, RuleResult class FleschReadingEaseRule(BaseRule): name flesch_reading_ease description 计算 Flesch Reading Ease 可读性得分 def __init__(self, min_score50, max_score80): self.min_score min_score self.max_score max_score def _count_syllables(self, word: str) - int: 简化音节计数按元音组数估算 word word.lower() # 去掉末尾不发音 e if word.endswith(e) and len(word) 3: word word[:-1] # 计算元音组 vowel_groups re.findall(r[aeiouy], word) count len(vowel_groups) return max(1, count) def check(self, text: str) - list[RuleResult]: sentences re.split(r[.!?。], text) sentences [s for s in sentences if s.strip()] words re.findall(r\b\w\b, text.lower()) if not sentences or not words: return [] total_syllables sum(self._count_syllables(w) for w in words) score 206.835 - 1.015 * (len(words) / len(sentences)) - 84.6 * (total_syllables / len(words)) score round(max(0, min(100, score)), 2) results [] if score self.min_score: results.append(RuleResult( rule_nameself.name, severitywarning, messagef可读性得分偏低{score} / 100阈值 {self.min_score}内容可能过于晦涩, suggestions[拆分复合长句, 替换生僻词为常用词, 补充解释性文字], )) elif score self.max_score: results.append(RuleResult( rule_nameself.name, severityinfo, messagef可读性得分较高{score} / 100内容容易理解但注意不要过于口语化, )) else: results.append(RuleResult( rule_nameself.name, severityinfo, messagef可读性得分 {score} / 100处于合理区间, )) return results4.8 组装规则引擎现在我们把所有规则放入一个检查器统一执行。# main.py import json import sys from readers.markdown_reader import MarkdownReader from checks.sentence_length import SentenceLengthRule from checks.repetition import RepetitiveOpeningRule from checks.transitional_words import TransitionalWordRule from checks.passive_voice import PassiveVoiceRule from checks.readability import FleschReadingEaseRule def run_all_checks(text: str) - list[dict]: reader MarkdownReader() parsed reader.read(text) content .join(parsed[sentences]) rules [ SentenceLengthRule(max_words35, min_words3), RepetitiveOpeningRule(min_repetition3), TransitionalWordRule(max_per_1000_words3), PassiveVoiceRule(max_passive_ratio0.15), FleschReadingEaseRule(min_score50, max_score80), ] all_results [] for rule in rules: results rule.check(content) for r in results: all_results.append({ rule: r.rule_name, severity: r.severity, message: r.message, line: r.line, suggestions: r.suggestions, }) return all_results def report_console(results: list[dict]) - None: if not results: print(✅ 未发现风格问题) return warning_count len([r for r in results if r[severity] warning]) info_count len(results) - warning_count print(f共发现 {len(results)} 条提示其中 {warning_count} 条警告{info_count} 条提示。\n) for r in results: level r[severity].upper() print(f[{level}] {r[rule]}: {r[message]}) if r[suggestions]: for s in r[suggestions]: print(f 建议{s}) print() def main(): if len(sys.argv) 2: print(用法python main.py 文本文件路径) sys.exit(1) filepath sys.argv[1] with open(filepath, r, encodingutf-8) as f: text f.read() results run_all_checks(text) report_console(results) if __name__ __main__: main()代码中run_all_checks函数接收原始文本经过 MarkdownReader 清洗后送给每个规则。最后在控制台输出报告。4.9 准备测试文本并运行创建一份示例 AI 风格的 Markdown 文件sample_draft.md。# Introduction AI technology is used in many industries. It is believed that AI can improve efficiency. However, the implementation of AI systems is complicated. Therefore, organizations need to adopt a careful strategy. # Main Body The system provides a user-friendly interface. The system supports multiple data sources. The system enables real-time collaboration. Moreover, the system is designed with security in mind. The data is processed by the backend server. The results are displayed in a dashboard. In conclusion, the proposed solution is effective. Furthermore, it is expected that the system will be adopted by many teams. Finally, the users are satisfied with the new features.运行python main.py sample_draft.md预期会看到类似输出共发现 4 条提示其中 4 条警告0 条提示。 [WARNING] sentence_length: 第 2 个句子过长43 词建议不超过 35 词 建议拆分长句为 2-3 个逻辑短句 建议检查是否有冗余修饰语 ...你可能注意到上面的输入文本是我演示用的实际检查器会因为分词、句子拆分方式不同而略有差异这很正常。重点是流程能跑通。4.10 集成 JSON 报告输出为了让结果能被 CI 或其他工具消费添加 JSON 导出能力# reporters/json_reporter.py import json def export_json(results: list[dict], output_path: str) - None: with open(output_path, w, encodingutf-8) as f: json.dump(results, f, ensure_asciiFalse, indent2)在main.py中增加参数def main(): if len(sys.argv) 2: print(用法python main.py 文本文件路径 [--json output.json]) sys.exit(1) filepath sys.argv[1] with open(filepath, r, encodingutf-8) as f: text f.read() results run_all_checks(text) report_console(results) if --json in sys.argv: idx sys.argv.index(--json) if idx 1 len(sys.argv): export_json(results, sys.argv[idx 1])这样就完成了从“检查规则”到“结果输出”的闭环。5. 常见问题与排查思路无论工具多简单使用过程中总会遇到问题。下面整理几个高频场景。5.1 检查结果不稳定同一个文件每次运行结果不同如果你的检查器引入了词典、分词模型的随机性建议先做排查。问题现象常见原因解决思路每次运行警告数不同使用了在线 API 或未固定随机种子移除在线依赖固定所有随机种子规则结果依赖环境分词、正则库版本不一致锁定依赖版本使用 lock 文件文件编码导致乱码读取文件时未指定 utf-8统一使用encodingutf-8做好编码降级为了验证确定性建议写一个回归测试python main.py sample_draft.md output1.txt python main.py sample_draft.md output2.txt diff output1.txt output2.txt如果diff没有输出说明结果是确定的。5.2 句子拆分把缩写词的句号当作结尾这是一个很经典的坑。例如 “Dr. Smith is here. He works at MIT.” 如果用简单正则split(.)会把 “Dr” 前面的部分切错。解决思路维护一个常见缩写词表。在拆分前保护缩写词中的句号。也可以引入nltk.sent_tokenize或spaCy的句子边界检测但这会增加依赖。5.3 Markdown 代码块里的内容被误判代码块中经常包含大段英文注释、字符串、甚至文档字符串。如果风格检查器不过滤代码块会被计算进句子长度和可读性指标导致结果严重失真。解决思路在 MarkdownReader 中优先剥离代码块。只对正文自然语言段落执行规则。保留行号映射方便定位问题位置。5.4 阈值太严导致误报太多刚开始设置规则时很容易把阈值调得很严格结果真实有用的警告被淹没在大量误报里。建议先收集一批人工确认过“没有问题”的文本用工具跑一遍把阈值调高到不误报为止。再收集一批人工确认“有问题”的文本验证工具能否发现。规则上线初期设置为info级别观察一段时间再提升为warning。5.5 中文文本处理适配不足Writing-eval 原本针对英文文本设计直接套用到中文会出现分词、句子边界、可读性算法不适用的问题。中文适配点处理思路句子拆分增加。等中文句末标点分词使用 jieba 分词代替空格分割被动语态检测中文的“被、由、给”等词可读性改用基于汉字统计、句子长度的简化指标不要迷信单一指标中文和英文的写作规范差异很大。6. 最佳实践与工程建议6.1 规则配置外部化不要把阈值写死在代码里。实际项目中不同内容线技术博客、产品文案、客服话术要求完全不同。建议把配置抽到 YAML 或 JSON 文件# config/default_rules.yaml sentence_length: enabled: true max_words: 35 min_words: 3 transitional_words: enabled: true max_per_1000_words: 3 words: - however - therefore - moreover passive_voice: enabled: true max_ratio: 0.15 repetitive_opening: enabled: true min_repetition: 3 readability: enabled: true min_score: 50 max_score: 80Python 侧解析import yaml def load_config(path: str) - dict: with open(path, r, encodingutf-8) as f: return yaml.safe_load(f)这样的好处是编辑或内容运营同学也能自己调参数不需要改代码。6.2 规则可组合、可插拔随着业务发展你可能会积累几十条规则。建议实现一个注册表机制# checks/registry.py RULES {} def register(cls): RULES[cls.name] cls return cls register class SentenceLengthRule(BaseRule): name sentence_length ...这样新增规则不需要修改main.py的核心逻辑只需要导入新模块即可。6.3 与 CI/CD 流水线集成本地确定性检查工具最适合放进 CI。例如在 GitHub Actions 中name: style-check on: [pull_request] jobs: writing-eval: runs-on: ubuntu-latest steps: - uses: actions/checkoutv3 - uses: actions/setup-pythonv4 with: python-version: 3.11 - run: pip install -r requirements.txt - run: python main.py docs/draft.md --json report.json - run: python check_report.py report.json --max-warnings 5这里check_report.py可以读 JSON判断错误数是否超过阈值超过则让 CI 失败。因为是确定性的CI 的通过与否可以预期。6.4 控制规则误报建立基线风格检查最怕的是“狼来了”。如果每条规则每天都报 20 条警告但大家知道其中 18 条不用管那么剩下 2 条真问题也会被忽略。工程建议为不同内容类型建不同基线。每周统计各规则触发频率对高频无用规则做降级或下线。在规则配置中为info级别的结果提供豁免列表suppress list只对确实需要整改的文本输出 warning。6.5 安全与隐私边界确定性本地检查的最大卖点之一就是隐私。但要注意不要把文本发送给任何第三方服务除非你检查过其协议。日志中不要记录完整原文只记录文件名、规则名、行号、触发片段摘要。在多租户系统中不同团队的风格库和规则配置需要做权限隔离避免通过自定义规则读取其他团队数据。6.6 与 AI 生成流水线的配合方式在实际 AI 应用开发中Writing-eval 逻辑通常放在生成模型之后、用户展示之前用户请求 - Prompt 组装 - 大模型生成初稿 - 本地确定性风格检查 - 通过则返回 - 不通过则触发特定规则重写局部改写 - 再次检查 - 返回这样做的好处是每一次重写都有明确依据不是无脑重新生成。可控性更强可以只对“过渡词过度使用”这一个问题做定点修复而不是全部重写。成本更低不需要为了修正一个标点问题再调用一次大模型。6.7 日志与可观测性给每条规则加上 ID、版本号和触发器{ rule: sentence_length, rule_version: v1.2.0, severity: warning, trigger: sentence_length 40, message: ... }这样出了问题可以快速判断是规则配置变化导致还是文本变化导致。对于确定性系统可复现性不仅体现在结果上还体现在规则版本的可追溯性上。7. 总结与进一步学习建议Writing-eval 这类本地确定性风格检查工具看起来不如大模型评估器“聪明”但它胜在稳定、便宜、透明、可测试。在 AI 内容生产越来越普及的今天它更像一道路线固定的质检闸门拦截掉那些一眼就能看出的风格问题让真正有价值的深度评审可以聚焦在高层次逻辑和创造性内容上。从一个简单想法到一个可用的工程系统核心路径并不复杂先用统计阈值和正则规则解决最常见的风格痛点例如句子过长、重复开头、过渡词滥用。把规则做成模块化、可配置的插件让不同业务线能调整参数。接入 JSON 输出和 CI 流程保证每次检查都有记录、可追踪。再逐步引入更复杂的 NLP 能力例如词性标注、依赖句法分析提升被动语态和句式多样性的识别准确度。下一步你可以继续探索的方向包括基于词性分布计算句子结构多样性指标。使用 TF-IDF 或关键词密度分析检测主题漂移。把确定性规则的结果作为弱标签训练一个小型的风格分类模型。将同一套检查逻辑封装为 HTTP 服务供多个业务系统调用。如果你正在做 AI 内容平台、Agent 后处理或者团队内部写作规范落地不妨从这套思路开始先跑通最小闭环再逐步丰富规则库。每个规则都是可解释的每次改动都是可测试的这本身就是工程化的最大优势。
返回列表