本文记录了借助 AI 编程助手整理 2000+ 份政策制度文件的真实全过程。涵盖文件摸底、分类体系设计、自动归类、OCR 批处理、双库架构搭建等完整环节,并详细标注了 5 个容易返工的坑 和 3 个技术踩坑点,供有类似需求的同行直接参考。
机关事业单位通常会积累大量政策制度文件——上级发文、内部规章、历史档案……年复一年堆在硬盘里,文件夹嵌套五六层,命名混乱,格式混杂(PDF / DOC / DOCX / HTM / XLS),其中还有大量扫描件。
笔者的实际情况:
维度 | 数据 |
|---|---|
文件总数 | 2068 个 |
总大小 | 2.18 GB |
目录数 | 256 个 |
1159 个(含大量扫描件) | |
DOC | 529 个 |
DOCX | 120 个 |
HTM | 114 个 |
XLS | 76 个 |
原有文件夹结构是多年手工累积的,存在以下典型问题:
目标:把 2000+ 文件整理成按事项分类、可检索、可导入 LLM 的结构化知识库。
工具 | 用途 | 选择理由 |
|---|---|---|
AI 编程助手 | 全程规划+脚本编写 | 能操作本地文件、写 Python 脚本、后台长任务 |
RapidOCR | OCR 文字识别 | PaddleOCR 模型 + ONNX 引擎,纯 CPU 可跑,Windows 兼容 |
Python | 批量脚本 | 分类、重命名、索引表生成、OCR 调度 |
Ollama + AnythingLLM | 本地 LLM(备选) | 敏感文件不联网,本地部署 DeepSeek R1 7B |
⚠️ 硬件提示:OCR 和本地 LLM 都吃算力。笔者的电脑没有 NVIDIA 显卡(AMD 显卡不支持 CUDA),OCR 只能跑 CPU 模式,速度约 2 分钟/页。如果有 NVIDIA 显卡(6GB+ 显存),OCR 速度可提升 5-10 倍,还能跑本地 13B 大模型。
最初的想法是"在原有 13 个文件夹基础上微调"。结果发现:
教训:不要在旧体系上修补,直接推倒重来。 旧分类的惯性会干扰你的判断。
最终确定了 30 个大类,核心原则有三条:
分类示例(脱敏后):
01_人事管理 02_工程建设与采购 03_财务管理
04_党建 05_安全管理 06_纪检与处分
07_机构编制 08_公文与档案 09_议事规则
10_审计 11_合同管理 12_巡视巡察
13_固定资产 14_公务用车 15_会议管理
16_政治建设 17_培训管理 18_出国境管理
19_休假考勤 20_宣传管理 21_保密与信息安全
22_工会群团 23_督查督办 24_信息化管理
25_信访与政务公开 26_外事合作 27_规划与科技
28_印章与机关事务 29_行业业务管理 30_法律事务💡 第 29 类"行业业务管理"是预留的行业专属分类,不同单位根据自身业务替换即可。
第二版定稿后,又发现 11 组重复分类需要合并(比如"人员培训管理"和"培训管理"重复、"干部选拔任用"应该归入"人事管理"而不是独立成类)。
合并后又引发子文件夹序号重复的问题(详见第五节踩坑清单)。
总结:分类设计至少要经过 3 轮迭代,不要指望一次到位。
分类脚本的核心逻辑是 三轮关键词匹配:
第一轮:精确匹配
第二轮:模糊匹配
第三轮:兜底分配
分类前必须先清洗文件名,否则关键词匹配会漏掉大量文件:
import re
def clean_filename(name):
# 去除 WPS 只读尾巴(如"(只读)" "[只读]"等)
name = re.sub(r'[\((]\s*只读\s*[\))]', '', name)
name = re.sub(r'[\[\【]\s*只读\s*[\]\】]', '', name)
# 去除日期前缀(如"20230101_" "2023-01-01 ")
name = re.sub(r'^\d{4}[-_]?\d{2}[-_]?\d{2}[\s_\-]*', '', name)
# 去除序号前缀(如"01_" "001-" "(1)")
name = re.sub(r'^\d{1,3}[\s_\-).\)]+', '', name)
# 去除多余空格
name = name.strip()
return name⚠️ 踩坑预警:笔者有 216 个 DOC 文件带着 WPS 只读尾巴,如果不清洗,关键词匹配会漏掉 10% 以上的文件。
经过三轮匹配 + 人工审核:
在第二轮分类合并后,发现 27 个大类存在子文件夹序号重复。例如:
01_人事管理/
01_人员培训管理/ ← 旧的序号
01_干部选拔任用/ ← 合并后新增,序号重复!
02_薪酬福利/每次调整分类后,新增子文件夹时没有重新排列序号,导致同一层级出现多个"01_"。
写了批量重命名脚本,扫描所有大类的子文件夹,按名称排序后重新编号:
import os
def fix_subfolder_numbering(base_dir):
for category in os.listdir(base_dir):
cat_path = os.path.join(base_dir, category)
if not os.path.isdir(cat_path):
continue
subfolders = [f for f in os.listdir(cat_path)
if os.path.isdir(os.path.join(cat_path, f))]
# 提取子文件夹名(去掉序号前缀)
subfolders_clean = []
for sf in subfolders:
clean = re.sub(r'^\d{2}_', '', sf)
subfolders_clean.append(clean)
# 按名称排序后重新编号
subfolders_clean.sort()
for i, name in enumerate(subfolders_clean, 1):
new_name = f"{i:02d}_{name}"
old_path = os.path.join(cat_path, subfolders[i-1])
new_path = os.path.join(cat_path, new_name)
if old_path != new_path:
os.rename(old_path, new_path)共执行了 52 次重命名/合并操作。
💡 教训:每次调整分类结构后,都要跑一遍序号检查脚本。 不要指望手动维护。
整理完成后,发现 836 个大于 500KB 的 PDF 文件。检查后发现:
类别 | 数量 | 处理方式 |
|---|---|---|
有文本层的 PDF | 338 个 | 直接提取文本,无需 OCR |
需要 OCR 的扫描件 | 471 个 | 用 RapidOCR 识别 |
已有 OCR 结果 | 27 个 | 跳过 |
第一版脚本用 pdf2image 将 PDF 页面转为临时 PNG 文件,再传给 RapidOCR 识别:
# ❌ 错误做法
images = pdf2image.convert_from_path(pdf_path)
for i, img in enumerate(images):
temp_path = f"_temp_page_{i}.png"
img.save(temp_path) # ← 这里报错!
result = ocr(temp_path)273 个文件全部失败,报错 OSError: [Errno 22] Invalid argument。
临时文件路径中包含中文(文件名本身是中文政策标题),Windows + Python 在某些路径长度/编码组合下,创建临时文件会触发 Errno 22。
第二版脚本改用 cv2.imdecode 将 PDF 页面解码为 numpy 数组,直接传给 RapidOCR,完全不创建临时文件:
import fitz # PyMuPDF
import cv2
import numpy as np
from rapidocr_onnxruntime import RapidOCR
ocr = RapidOCR()
def ocr_pdf(pdf_path):
doc = fitz.open(pdf_path)
full_text = []
for page in doc:
# 将 PDF 页面渲染为图片,存入内存
pix = page.get_pixmap(dpi=200)
img_bytes = pix.tobytes("png")
# 解码为 numpy 数组(不写磁盘!)
img_array = np.frombuffer(img_bytes, dtype=np.uint8)
img_np = cv2.imdecode(img_array, cv2.IMREAD_COLOR)
# 直接传 numpy 数组给 OCR
result, elapse = ocr(img_np)
if result:
text = "\n".join([line[1] for line in result])
full_text.append(text)
doc.close()
return "\n\n".join(full_text)3/3 测试通过,471 个文件批量处理正式启动。
⚠️ 这是整个项目中最大的技术坑。 如果你也在中文 Windows 环境下做 OCR,务必避免临时文件,用内存中的 numpy 数组直接传递。
OCR 结果保存为 Markdown 格式,而非纯文本或 JSON:
# 文件标题
## 第一章 总则
**第一条** 为了规范……,制定本办法。
**第二条** 本办法适用于……
## 第二章 适用范围
**第三条** ……选择 Markdown 的理由:
471 个文件跑 OCR,中途可能断电、崩溃、被手动终止。脚本支持断点续传:
def batch_ocr(file_list, output_dir):
for pdf_path in file_list:
# 检查是否已有输出文件(断点续传)
md_name = Path(pdf_path).stem + "_OCR.md"
md_path = os.path.join(output_dir, md_name)
if os.path.exists(md_path):
print(f"跳过已完成: {pdf_path}")
continue
# 执行 OCR
text = ocr_pdf(pdf_path)
with open(md_path, "w", encoding="utf-8") as f:
f.write(text)同时还写了一个 进度查看脚本,可以双击运行,显示:
💡 建议:长任务一定要有进度查看机制,否则你永远不知道它是在跑还是挂了。
整理完成后,需要两个输出目标:
政策文件_整理后/
├── 01_人事管理/
│ ├── 01_人员培训管理/
│ │ ├── 培训管理办法.pdf
│ │ ├── 培训考核制度.doc
│ │ └── ...
│ ├── 02_干部选拔任用/
│ └── ...
├── 02_工程建设与采购/
└── ...特点:
政策文件_LLM导入/
├── 01_人事管理_培训管理办法.txt
├── 01_人事管理_培训考核制度.txt
├── 02_工程建设与采购_招标管理办法.txt
└── ...特点:
大类编号_大类名称_文件名.txt最初 LLM 导入库也按照正式库的两级目录结构来组织。后来发现:
修复:将 LLM 导入库从两级目录拍平为一级目录,分类信息编码到文件名前缀中。
💡 教训:人工查阅库和机器读取库的结构需求不同。人工需要层级导航,机器需要扁平结构。不要用同一套目录结构。
整理完成后,生成一份 Excel 索引表,记录每个文件的元信息:
序号 | 文件名 | 大类 | 子类 | 格式 | 大小 | 是否扫描件 | OCR状态 | 发文日期 | 发文单位 |
|---|---|---|---|---|---|---|---|---|---|
1 | 培训管理办法 | 01_人事管理 | 01_人员培训管理 | 256KB | 否 | 有文本层 | 2023-05-12 | XX部门 | |
2 | 财务报销规定 | 03_财务管理 | - | DOC | 128KB | 否 | - | 2022-03-08 | XX单位 |
索引表的用途:
后续计划:从 OCR 文本中自动提取发文日期,回填到索引表中(很多旧文件的发文日期只在正文里,文件名中没有)。
整理完成后,日常还会有新文件需要入库。为此写了一个 add_new_file.py 脚本,一键完成:
新文件 → [1]文件名清洗 → [2]关键词分类 → [3]OCR(如需)→ [4]放入正式库 → [5]同步到LLM导入库 → [6]更新索引表这个脚本可以在本地独立运行,不需要每次都依赖 AI 助手。
返工点 | 问题描述 | 代价 | 预防建议 |
|---|---|---|---|
1. 分类体系推倒重来 | 第一版在旧分类上修补,后发现体系不统一,全部推倒 | 重新分类 2000+ 文件 | 开工前先定好分类原则,不要在旧体系上修补 |
2. 分类合并引发连锁反应 | 合并 11 组重复分类后,子文件夹序号全部乱掉 | 52 次重命名 | 每次合并后立即跑序号检查脚本 |
3. 未分类文件堆积 | 105 个文件因关键词没覆盖到,留在根目录 | 三轮额外匹配 | 第一轮分类时就把兜底规则写全 |
4. OCR 全量返工 | v1 因临时文件问题 273 个全失败 | 重写整个 OCR 脚本 | 中文路径环境下避免临时文件 |
5. LLM 导入库重构 | 两级目录导致导入工具出错 | 重新拍平 30 个大类 | 机器读取库用一级目录,信息编码到文件名 |
OSError: [Errno 22] Invalid argumentcv2.imdecode 解码为 numpy 数组,直接传给 OCR,不创建临时文件deep_clean 函数,对文件名做深度清洗后再匹配(去除扩展名、特殊字符、空格、日期前缀等),匹配不上的再走关键词 fallback环节 | 工具 | 备注 |
|---|---|---|
AI 助手 | 任意支持代码执行的 AI 编程助手 | 全程规划+脚本编写 |
OCR 引擎 | RapidOCR (ONNX Runtime) | PaddleOCR 模型,纯 CPU 可跑 |
PDF 处理 | PyMuPDF (fitz) | PDF 转图片、提取文本 |
图像处理 | OpenCV (cv2) | 图片解码、numpy 数组操作 |
批量脚本 | Python 3.13 | 分类/重命名/索引/OCR调度 |
本地 LLM(备选) | Ollama + AnythingLLM | 敏感文件本地处理 |
文档检索 | AnyTXT | 全文检索已整理文件 |
本文基于真实项目经验整理,已脱敏处理。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。