测评 AI 文本校对工具,不能只看它改了多少字。先建立人工核对的标准答案,分别检查应改的错字有没有改对、错句是否原样漏过,以及本来没错的句子是否被改坏。把“看起来更流畅”与“严格按要求纠错”分开,才能比较工具是否适合你的任务。
下面给出一套句级测试表和可运行的统计脚本。所有原句、标准答案与候选输出都是人为构造的教学材料;脚本的本地结果只验证统计逻辑,没有实际测试某个 AI 校对工具,也不构成产品排名。

先明确你测试的是哪一种校对
本例只测试中文错别字修正:保持其他文字与事实不变,不接受额外润色。语法修正、长段落改写和专业事实核验应另设测试项目,否则“应该保留原句”和“应该改善表达”会相互冲突。
pycorrector 官方项目区分了拼写纠错与更广的文本纠错,并列出不同模型和任务路径。这说明即使都叫“纠错”,可处理的错误类型也不同。本文不运行这些模型,也不引用它们的公开成绩来代表你自己的文稿表现。
测试前由人确认每条输入是否有错,并写出唯一允许的纠正稿;若存在多种同样正确的修正,先制定可接受答案及人工裁决规则。下方脚本使用单一标准答案,只适合本例这种只改特定错字的严格任务。
六条样例:有错句和无错句都要保留
| 编号 | 原句 | 人工标准答案 | 人工构造的候选输出 | 示例判定 |
|---|---|---|---|---|
| e1 | 请尽块回复。 | 请尽快回复。 | 请尽快回复。 | 修正符合标准答案 |
| e2 | 资料已近发出。 | 资料已经发出。 | 资料已近发出。 | 错句原样保留,漏改 |
| e3 | 欢迎参于活动。 | 欢迎参与活动。 | 欢迎参与本次活动。 | 改了错字,但增加了文字,不符合本例严格要求 |
| c1 | 订单周三发出。 | 订单周三发出。 | 订单周四发出。 | 无错句被改动,且日期事实发生变化 |
| c2 | 开会前请备份。 | 开会前请备份。 | 开会前请备份。 | 正确保留 |
| c3 | 项目名称为 LightFlow。 | 项目名称为 LightFlow。 | 项目名称为 LightFlow。 | 正确保留;名称是虚构的教学专名 |
无错句不是多余样本。一个把所有句子都改写一遍的工具,可能显得很积极,却会让专名、事实和原意更难控制。真实测试应加入已核对的专名、数字、型号和行业用语,并按来源保留原稿。
四个句级口径,避免把“改过”写成“改对”
- 严格纠正比例:有错句中,输出完全等于标准答案的条数 ÷ 有错句条数。
- 原样漏改比例:有错句中,输出完全等于原句的条数 ÷ 有错句条数。
- 改动但未达标准比例:有错句中,既不等于原句也不等于标准答案的条数 ÷ 有错句条数。
- 无错句改动比例:无错句中,输出不同于原句的条数 ÷ 无错句条数。
这是本文约定的句级口径,不是字符级精确率、召回率或某论文的官方评分。前三项在这个分类规则下划分有错句;“原样漏改”不包括改了部分但仍有错的句子,那些进入“改动但未达标准”。没有某类样本时,该类比例记为 null,不用 0 冒充测过且没有错误。
无错句改动也要回看原因。本例只允许纠错,因此任何改动都需复核;若你另做润色测评,合法的风格改写不能简单算作事实错误,要换标注规范和指标。
运行脚本,导出逐条报告
下面只用 Python 标准库,在 Windows 的 Python 3.11.15 运行通过。将完整代码保存为 proofreading_demo.py,在空的工作目录运行 python proofreading_demo.py。脚本保留编号,遇到缺失、额外或重复结果立即停止统计,输出 proofreading_report.csv。
先用内置演示数据检查流程;正式测评时替换 cases 和 predictions。cases 必须来自事先人工审定的测试集,predictions 才是待测工具的真实结果,不能让工具同时制作自己的标准答案。
import csv
import json
from pathlib import Path
# All input and candidate outputs below are constructed teaching examples.
cases = [
{'id': 'e1', 'kind': 'error', 'original': '请尽块回复。', 'gold': '请尽快回复。'},
{'id': 'e2', 'kind': 'error', 'original': '资料已近发出。', 'gold': '资料已经发出。'},
{'id': 'e3', 'kind': 'error', 'original': '欢迎参于活动。', 'gold': '欢迎参与活动。'},
{'id': 'c1', 'kind': 'clean', 'original': '订单周三发出。', 'gold': '订单周三发出。'},
{'id': 'c2', 'kind': 'clean', 'original': '开会前请备份。', 'gold': '开会前请备份。'},
{'id': 'c3', 'kind': 'clean', 'original': '项目名称为 LightFlow。', 'gold': '项目名称为 LightFlow。'},
]
predictions = [
{'id': 'e1', 'text': '请尽快回复。'},
{'id': 'e2', 'text': '资料已近发出。'},
{'id': 'e3', 'text': '欢迎参与本次活动。'},
{'id': 'c1', 'text': '订单周四发出。'},
{'id': 'c2', 'text': '开会前请备份。'},
{'id': 'c3', 'text': '项目名称为 LightFlow。'},
]
def evaluate(cases, predictions):
ids = [row['id'] for row in cases]
pred_ids = [row['id'] for row in predictions]
if len(set(ids)) != len(ids) or len(set(pred_ids)) != len(pred_ids):
raise ValueError('编号重复,停止统计')
if set(ids) != set(pred_ids):
raise ValueError('结果缺失或多出编号,停止统计')
pred = {row['id']: row['text'] for row in predictions}
counts = dict(error_total=0, clean_total=0, corrected=0,
unchanged_error=0, changed_not_gold=0, changed_clean=0)
report = []
for row in cases:
kind, before, gold = row['kind'], row['original'], row['gold']
after = pred[row['id']]
if kind not in ('error', 'clean') or not all(
isinstance(t, str) and t for t in (before, gold, after)):
raise ValueError('类型或文本无效,停止统计')
if (kind == 'clean') != (before == gold):
raise ValueError('无错/有错标签与标准答案冲突')
counts[kind + '_total'] += 1
if kind == 'clean':
result = 'changed_clean' if after != before else 'kept_clean'
elif after == gold:
result = 'corrected'
elif after == before:
result = 'unchanged_error'
else:
result = 'changed_not_gold'
if result in counts:
counts[result] += 1
report.append({'id': row['id'], 'kind': kind, 'result': result,
'original': before, 'gold': gold, 'prediction': after})
rates = {}
for key in ('corrected', 'unchanged_error', 'changed_not_gold', 'changed_clean'):
denominator = counts['clean_total' if key == 'changed_clean' else 'error_total']
rates[key + '_rate'] = round(counts[key] / denominator, 4) if denominator else None
return {'counts': counts, 'rates': rates}, report
summary, report = evaluate(cases, predictions)
with Path('proofreading_report.csv').open('w', encoding='utf-8-sig', newline='') as file:
writer = csv.DictWriter(file, fieldnames=list(report[0]))
writer.writeheader()
writer.writerows(report)
print(json.dumps(summary, ensure_ascii=False, indent=2))
print('report_rows', len(report))
本地执行得到:
{
"counts": {
"error_total": 3,
"clean_total": 3,
"corrected": 1,
"unchanged_error": 1,
"changed_not_gold": 1,
"changed_clean": 1
},
"rates": {
"corrected_rate": 0.3333,
"unchanged_error_rate": 0.3333,
"changed_not_gold_rate": 0.3333,
"changed_clean_rate": 0.3333
}
}
report_rows 6
核对 counts 中有错句与无错句各为 3;有错句里改对、原样漏改、改动未达标准各为 1,无错句改动为 1。四个比例都为 0.3333,但分母分为两组,不能把它们加起来当“总错误率”。CSV 应有六条记录,并能直接找到 e2、e3 和 c1 的具体问题。
Python csv 文档用于核对 DictWriter 的表格写入;脚本以 UTF-8 BOM 保存,便于常见表格软件打开中文。Python json 文档用于核对输出摘要的序列化。自动统计只是定位差异,句子是否应改仍由人工标准决定。
换成真实工具时,怎样才算可比较
- 用你获准处理的材料制作测试集,分别标注错别字、无错句和专名等类型;争议句先裁决。工具还没接触这份测试结果时,冻结答案。
- 给所有候选工具相同任务,例如“只改错别字,保持其他内容;无法判断时原样保留”。不要一个允许润色、另一个禁止改写。
- 逐条保存完整输入与真实输出,并保留工具名、模型/版本、提示词、参数和运行日期。工具不返回编号时,在调用端映射编号,不能按答案文字猜对应关系。
- 先确认所有结果到齐,再运行统计。导出失败、空输出或超时另列执行失败清单,不能静默当成没错。
- 回看“改动未达标准”与“无错句改动”。确认是真错误、任务越界还是标准答案需要经独立人审修订;修改答案后保留版本并对所有候选重算。
若工具输出有随机性,可以对同一组样本重复运行,分别保存每轮结果并观察变化;不要从多轮里只挑最好的一轮。样本类型的比例也会改变最终数值,应报告各类型结果,不能把六条演示的比例推广为工具的真实可靠性。
下一步怎样用于选型
先用代表性材料完成一轮测试,列出最不能接受的错误类别,例如金额误改、专名误改或错字漏改;再比较候选工具在这些类别的结果与人工复核成本。即使某工具严格纠正比例更高,只要它仍会改坏关键事实,也应保留复核流程。
本文没有真实工具运行结果、准确率排名或速度结论。2026 年 10 月 1 日已核对上述官方来源,并实际运行统计脚本及编号缺失、重复的拒绝检查。它提供的是可复用的测评方法,真实工具是否合适要用你的独立测试集回答。
Ai菜鸟网。发布者:AI小管家,转载请注明出处:https://www.alyyhw.com/32526.html