简洁高效,让模型自主工作。问题分级输出,帮用户区分轻重缓急。
> ⚠️ 【部分章节检查提示】
> - 如果用户只要求检查部分章节/页面,请先提醒用户:建议将该部分内容另存为新文档后再提交校对
> - 因为模型需要阅读整篇文档才能判断上下文一致性,单独检查部分章节的调用量与全文检查相近,效果不一定更好
> - 如果用户坚持只检查部分章节,可以只提取指定章节内容进行检查,但需在报告中说明"仅检查了指定章节,全文一致性未覆盖"
根据文件类型选择读取方式:
| 文件格式 | 工具 | 方法 |
|---------|------|------|
| .docx | python-docx | 提取段落和表格 |
| .pdf | pymupdf + pdfplumber | 文本 + 表格双轨提取 |
| .doc | win32com / 转换为 docx | COM 接口 |
| .xlsx | openpyxl | 读取 Excel |
| .txt/.md | 直接读取 | 纯文本 |
# DOCX 提取(推荐方式)
import sys, glob
sys.stdout.reconfigure(encoding='utf-8')
from docx import Document
files = glob.glob('path/*报告*.docx')
doc = Document(files[0])
paragraphs = []
for i, para in enumerate(doc.paragraphs):
text = para.text.strip()
if text:
paragraphs.append({'index': i, 'text': text})
tables = []
for t_idx, table in enumerate(doc.tables):
rows = [[cell.text.strip() for cell in row.cells] for row in table.rows]
tables.append({'index': t_idx, 'rows': rows})
print(f'段落数: {len(paragraphs)}, 表格数: {len(tables)}')
模型直接阅读文档内容,自主发现所有类型的不一致问题。
A类:数据矛盾(严重)
B类:逻辑异常(严重)
C类:编号与结构问题(需核实) → 统一判定为P1
D类:术语与格式问题(建议修正)
E类:规范引用问题(需核实/建议修正)
> ⚠️ 静态库提示:standards_library.json 已内置 141条 常见水利规范数据(截止 2026-04-09),建议每季度手动更新一次。完整库(800+)访问:http://gjkj.mwr.gov.cn/jsjd1/bzcx/
核查分三步走:
第一步:从文档中提取规范编号
常见格式:
GB/T 50123-2019(国标推荐性)
GB 50201-2014(国标全文强制)
SL 252-2017(水利行业标准)
SL/T 618-2021(水利行业推荐标准)
GB 50487-2008(2022年版)(局部修订版)
第二步:查静态库(standards_library.json)
> ⚠️ 编号归一化:静态库的 key 格式为 GB/T 50363-2019(/后有空格),
> 但文档引用常写为 GB/T50363-2018(无空格)。查表前需去掉空格归一化。
> ```python
> def normalize(std_id): return std_id.replace(' ', '').replace('\u3000', '')
> lib_norm = {normalize(k): k for k in stds.keys()}
> ```
库中每条标准字段:
| 字段 | 说明 |
|------|------|
| status | 现行 / 已修订 / 废止 |
| latest | 当前最新版本编号 |
| replaced_by | 被哪个标准替代(已修订时) |
| superseded | 本标准替代了哪些旧版 |
| category | 国标/水利行业标准等 |
命中静态库 → 直接判定,输出结论
> 重要:E类需同时检查标准号和标准名称
> - 标准号检查:标准号是否存在、是否已废止、是否有更新版本
> - 标准名称检查:文档引用的标准名称是否与标准号匹配(防止"SL303-2017《错误名称》"这类错误)
> - E类分级标准(严格对齐,不降级不升级):
> - P0:已废止且有明确替代版本的标准(如SL303-2014→SL303-2017、SL166-2010→SL/T291.3-2025)
> - P1:未收录但可推断最新版本的标准(需标注"建议核实年份")
> - P1:标准名称与标准号不匹配(如"GB/T50662-2011名称错误")
> - P2:格式问题(缺书名号、HJ/T格式不规范、数字空格不统一等)
> - 建议输出完整引用:发现问题时同时输出新标准的完整引用(标准号 + 《标准名称》)
> - 例如:GB/T50363-2006 废止 → 建议改为 GB/T50363-2018《节水灌溉工程技术标准》
> - latest 字段已在静态库中记录此信息,审核时应直接使用。
> - 常见废止标准及替代版本(需熟记):
> - SL 303-2014 → 已废止,被 SL 303-2017 替代
> - SL 166-2010 → 已废止,被 SL/T 291.3-2025 替代(2025-09-29发布)
> - GB 50013-2006 → 已废止,应为 GB 50013-2018
第三步:规范核查(查库 + 未收录时联网入库)
> ⚠️ 【强制要求】E类检测必须执行! 每篇报告都要提取并核查所有规范引用,不得遗漏。
E类规范引用核查遵循以下流程:
① 查静态库
import re, json
# 库加载(归一化key)
lib = json.load(open('standards_library.json', encoding='utf-8'))
stds = lib['standards']
def norm_key(key):
"""库key归一化:去空格 + 全角→半角 + HJ/T数字→HJ数字"""
key = key.replace(' ', '').replace('-', '-').replace('—', '-')
return re.sub(r'^HJ/T(\d)', r'HJ\1', key)
# 归一化key → 原key 映射(同归一化key可能对应多个原key,取第一个)
lib_norm_map = {}
for k in stds:
n = norm_key(k)
if n not in lib_norm_map:
lib_norm_map[n] = k
def clean_std(raw):
"""从文档原文提取标准号"""
raw = raw.strip().rstrip(' )))):::。.,,')
patterns = [
(r'GB/T\s*(\d+(?:\.\d+)?[--]?\d*)', 'GB/T'),
(r'GB\s*(\d+(?:\.\d+)?[--]?\d*)', 'GB'),
(r'SL/T\s*(\d+(?:\.\d+)?[--]?\d*)', 'SL/T'),
(r'SL/Z\s*(\d+(?:\.\d+)?[--]?\d*)', 'SL/Z'),
(r'SL\s*(\d+(?:\.\d+)?[--]?\d*)', 'SL'),
(r'HJ/T\s*(\d+(?:\.\d+)?[--]?\d*)', 'HJ/T'),
(r'HJ\s*(\d+(?:\.\d+)?[--]?\d*)', 'HJ'),
(r'DL\s*(\d+(?:\.\d+)?[--]?\d*)', 'DL'),
]
for pat, prefix in patterns:
m = re.match(pat, raw, re.I)
if m:
num = m.group(1).replace('-', '-').replace('—', '-').replace(' ', '')
return prefix + num
return None
def lookup(std_key):
"""
查询标准状态。
策略:
1. 归一化精确匹配(HJ/T2.3-2018 → HJ2.3-2018)
2. 原值精确匹配
3. 前缀兜底(GB50201 → 找 GB50201-* 取最新年份)
"""
n = norm_key(std_key)
# ① 归一化匹配
if n in lib_norm_map:
return stds[lib_norm_map[n]], lib_norm_map[n]
# ② 原值匹配
if std_key in stds:
return stds[std_key], std_key
# ③ 前缀兜底(文档缺少年份时)
has_year = bool(re.search(r'-\d{4}', std_key))
if not has_year:
matches = [(k, stds[k]) for k in stds if k.startswith(std_key)]
if matches:
def year_of(k):
m = re.search(r'-(\d{4})', k)
return int(m.group(1)) if m else 0
matches.sort(key=lambda x: year_of(x[0]), reverse=True)
best_key, best_val = matches[0]
inferred = dict(best_val)
inferred['_inferred'] = True
return inferred, best_key
return None, None
命中 → 直接判定(现行有效 / 已废止 / 已修订)
未命中(含前缀推断结果)→ 进入②
前缀推断结果标注 _inferred: True,输出时提示"建议核实年份"
② 未收录时询问用户
对所有未命中静态库的标准,汇总后一次性询问用户:
以下 N 个标准未收录于本地库,请确认是否联网核查现行状态?
• GB/T50625-2023 @[第3段]
• SL xxx-xxxx @[第7段]
• ...
是否联网核查?(Y/N)
③ 联网核查(用户同意后)
调用 web_search,逐条查询:
查询词:【标准号】+ 关键词 + 现行标准 / 最新版
示例:GB/T 50625-2023 规范名称 现行标准 最新版
根据搜索结果判断:
| 搜索结果 | 判定 |
|---------|------|
| 全国标准信息公共服务平台 / 水利部 / 生态环境部有记录 | 现行有效 |
| 明确标注"已废止""已被替代" | 已废止 |
| 有更新版本号 | 已修订(含 latest 和 replaced_by)|
④ 自动入库
联网核查完成后,将结果追加写入静态库:
lib = json.load(open('standards_library.json', encoding='utf-8'))
stds = lib['standards']
stds['GB/T50625-2023'] = {
'name': '规范名称',
'status': '现行有效', # 或 '已废止' / '已修订'
'category': 'GB/T',
'替代': '被替代的标准号(已修订时)',
}
lib['standards'] = stds
with open('standards_library.json', 'w', encoding='utf-8') as f:
json.dump(lib, f, ensure_ascii=False, indent=2)
> ⚠️ 重要:入库后所有 key 必须归一化(去空格、-→-、—→-),以确保后续查询命中。
⑤ 格式规范检查
| 引用场景 | 正确格式 | 错误示例 | 级别 |
|---------|---------|---------|------|
| 中文规范名 | 《规范名称》GB/T XXXXX-XXXX | 仅写GB/T XXXXX无书名号 | P2 |
| 中文规范名 | 《SL 640-2013》 | SL 640-2013 缺书名号 | P2 |
| HJ/T格式 | HJ 2.3-2018(《环境空气质量标准》 HJ 2.3-2012) | HJ/T 2.3-2012(T应去掉) | P2 |
| HJ/T保留 | HJ/T 88-2003(排放许可,不归一化) | 特殊标准号按原样引用 | - |
| 英文规范名 | | ISO 9001:2015 无包裹 | P2 |
| 同时出现 | 《规范名称》<英文缩写> | 混用书名号和尖括号 | P2 |
| 局部修订版 | GB 50487-2008(2022年版) | GB 50487-2008 不注版本 | P2 |
| 标准号+名称 | GB/T50363-2018《节水灌溉工程技术标准》 | 仅写标准号无名称,或名称与号不匹配 | P1 |
每个问题必须标注优先级:
| 级别 | 标准 | 处理建议 |
|------|------|---------|
| P0 严重 | 数据直接矛盾、数量级错误、影响文件可信度 | 必须修正后才能提交 |
| | 包括:同一指标多处数值矛盾、分项合计与总和不符、完工/验收超出工期、多年平均降雨量等核心数据前后不一致、已废止标准有明确替代版本 | |
| P1 需核实 | 数据存疑、编号错误、逻辑不清晰、未收录但可推断最新版本的标准 | 建议核实后修正 |
| P2 建议 | 格式不统一、术语轻微差异、表述瑕疵、缺书名号等 | 酌情修改 |
> ⚠️ 【分级红线】以下情况不得降级:
> - 多年平均降雨量、工程投资等核心指标前后矛盾 → P0
> - 已废止标准(SL303-2014、SL166-2010、GB50013-2006等)→ P0(有明确替代版本)
> - 章节编号跳段、目录正文不一致 → P1
> - 术语混用、单位不统一、格式问题 → P2
> ⚠️ 【强制要求】完成分析后必须执行此步骤,不得跳过!
输出到用户原文档位置(不在工作目录)
【必须同时输出以下两个文件,缺一不可】:
> ⚠️ 提示:生成的 _annotated.docx 文件请用 Microsoft Office Word 打开(不要用 WPS,两者渲染效果不同)
位置标注格式:
位置:第4页第163段 ↔ 第4页第180段
标注方式:
highlight_color=4)
highlight_color=7) + 粗体
"""
DOCX 标注工具 - 在原文处黄色高亮 + 段落末尾橙色说明
健壮版:含异常处理,不因单个问题失败而中断整个流程
"""
import sys, glob, shutil
sys.stdout.reconfigure(encoding='utf-8')
from docx import Document
from docx.oxml.ns import qn
from docx.shared import RGBColor
def highlight_run(run):
"""为 run 添加黄色背景高亮"""
try:
rPr = run._r.makeelement(qn("w:rPr"), {})
hl = run._r.makeelement(qn("w:highlight"), {})
hl.set(qn("w:val"), "yellow")
rPr.append(hl)
run._r.insert(0, rPr)
return True
except Exception:
return False
def annotate_simple(input_path, output_path, issues):
"""
标注 DOCX - 在原文处黄色高亮 + 段落末尾橙色说明
健壮实现:单个问题失败不影响其余问题处理
issues 格式:
[{
"text": "问题关键词(用于定位)",
"type": "数据矛盾/逻辑异常/编号错误/术语不统一/规范引用错误",
"priority": "P0/P1/P2",
"para_index": 段落索引(从0起),
"estimated_page": 估算页码,
"problem": "问题描述",
"suggestion": "修正建议",
"compare_location": "对比位置(第X页第Y段 ↔ 第X页第Y段,确保批注与校对报告一致)"
}, ...]
> ⚠️ **【定位精度要求】**
> - para_index:段落在文档中的顺序索引(从0起),需与校对报告中"第X段"对应
> - estimated_page:估算页码,需与校对报告中"第X页"对应
> - compare_location:格式为"第X页第Y段 ↔ 第X页第Y段",确保校对报告与批注文档位置一致,无偏差
> - 避免段落索引偏差:同一问题在校对报告和批注文档中的段落编号应完全一致
"""
# Step 1: 复制原文件(失败则退出)
try:
shutil.copy2(input_path, output_path)
except Exception as e:
print(f"[错误] 无法复制文件:{e}")
return 0
# Step 2: 打开文档(失败则退出)
try:
doc = Document(output_path)
except Exception as e:
print(f"[错误] 无法打开文档:{e}")
return 0
processed = 0
failed = 0
for issue in issues:
target = issue.get("text", "").strip()
para_index = issue.get("para_index", 0)
estimated_page = issue.get("estimated_page", 1)
priority = issue.get("priority", "P1")
if not target or para_index < 0:
continue
if para_index >= len(doc.paragraphs):
failed += 1
continue
para = doc.paragraphs[para_index]
# Step 3: 在原文中查找并高亮问题文字(黄色)
for run in para.runs:
if target in run.text:
try:
highlight_run(run)
except Exception:
pass
# Step 4: 在段落末尾添加橙色粗体标注
location = f"第{estimated_page}页第{para_index}段"
compare_loc = issue.get("compare_location", "")
compare_hint = f" ↔ {compare_loc}" if compare_loc else ""
annotation = (
f"\n\n"
f">> 【{priority}·{issue.get('type', '')}】{location}{compare_hint}\n"
f"问题:{issue.get('problem', '')}\n"
f"建议:{issue.get('suggestion', '')}"
)
try:
run = para.add_run(annotation)
run.font.highlight_color = 7
run.font.bold = True
run.font.color.rgb = RGBColor(0xFF, 0x80, 0x00)
processed += 1
except Exception:
failed += 1
# Step 5: 保存
try:
doc.save(output_path)
print(f"标注完成:成功 {processed} 条,失败 {failed} 条")
return processed
except Exception as e:
print(f"[错误] 保存失败:{e}")
return 0
# issues 格式示例:
# [{
# "text": "428.24",
# "type": "数据矛盾",
# "priority": "P0",
# "para_index": 163,
# "estimated_page": 4,
# "problem": "渠道总长428.24km,但分项相加只有398.54km",
# "suggestion": "核实总长数据或检查分项统计",
# "compare_location": "第4页第180段"
# }, ...]
> ⚠️ 【强制要求】生成的报告必须包含以下所有章节,不得遗漏!
# {文档名称} 校对报告
---
### 📋 文档信息
| 项目 | 内容 |
|:-----|:-----|
| 文档名称 | {文档名称} |
| 发现问题 | **X 处**(P0: X个 / P1: X个 / P2: X个) |
| 检测时间 | {YYYY-MM-DD} |
---
### 🔴 严重问题(P0 - 必须修正)
---
#### 问题 1:{问题简述}
> **位置**:第 X 页第 Y 段 ↔ {对比位置}
**原文摘录**:
{原文内容}
**发现问题**:{详细描述}
**修正建议**:{修正建议}
---
### 🟡 需核实问题(P1 - 建议核实)
(同上格式)
---
### 🔵 建议优化(P2 - 格式/术语)
(同上格式)
---
### ✅ 一致性校对好的部分
- {一致性良好的数据/章节说明}
- {另一个良好的部分}
---
### 📊 修改建议优先级汇总
| 优先级 | 问题 | 建议 |
|:------:|------|------|
| 🔴 P0 | {问题简述} | {修正建议} |
| 🟡 P1 | {问题简述} | {修正建议} |
| 🔵 P2 | {问题简述} | {修正建议} |
---
### ⚠️ 免责声明
本报告由 AI 自动生成,**仅供辅助参考**。报告中的问题标注基于文档文本的一致性核查,不代表对文档内容真实性的完全验证。
- 本报告**不能替代人工审核**,尤其涉及数据、工程量、投资额等关键信息时,请务必对照原始批复文件、设计图纸等进行核实
- 规范引用问题基于内置标准库(141条,截止2026-04-09)及网络核查结果,标准状态可能随政策更新而变化,建议以水利部等官方平台最新公布为准
- 报告生成方不对因使用本报告而产生的任何直接或间接损失负责,使用前请自行判断
---
*报告由文档一致性校对助手 v2.4.6 自动生成*
v2.4.2 - 2026-04-09
priority 字段
v2.4.3 - 2026-04-09
replace(' ', '') 归一化(静态库格式 GB/T 50363 vs 文档 GB/T50363)
v2.4.8 - 2026-04-09
'SL 379-2007', 'SL/T 386-2025' 等),导致 clean_std() 提取的 'SL379-2007'(无空格)查不到 → P1已废止仅检出10个(应为15个)
norm(k) = k.replace(' ','').replace('-','-').replace('—','-') 归一化所有key
clean_std():从匹配文本中正确提取数字部分
灌溉[的]?保证率 不覆盖"灌溉设计保证率" → 75%漏检 → 修改为 灌溉(?:设计)?[的]?保证率
\d+(?:[~\-至]\d+)? 捕获"5~10",但 float("5~10") 报错 → 取第一个数字 re.search(r'\d+', m.group(1)).group()
T\d{2,3}[--]\d{4} 只收录T+年份格式(如T01-2007),过滤T450等噪声
v2.4.9 - 2026-04-09
v2.4.6 - 2026-04-10
v2.4.5 - 2026-04-10
v2.4.4 - 2026-04-09
standards_library.json(归一化key入库)
HJ/T2.3-2018 → HJ2.3-2018),同时保留 HJ/T88-2003 等例外原值匹配
GB50201、SL274),自动在库中查找同前缀标准,取最新年份;标注 _inferred: True 提示"建议核实年份"
SL/T 251-2015(天然建筑材料勘察规程,现行)、SL 303-2014(已废止,被SL 303-2017替代)
共 10 个版本