接手过诉讼材料整理的人,应该都有过这样的体验:上千页扫描件堆在桌上,几十段录音要逐字听写,视频里关键画面要一帧一帧找。传统的做法是靠人海战术,几个实习生或者助理连续干一周,眼睛看花、耳朵听麻,还可能漏掉关键证据。
最近我在处理一个案子的大批量证据材料时,发现用 AI 工具链可以把这个流程从“两周”压缩到“两天”。核心思路不是用一个大模型解决所有问题,而是手写转文本、录音转文字、视频分析、本地模型与云端 API 拆开来,各用各的专用工具。这篇文章会把整套方案、成本结构和关键代码完整写清楚。
真正让我决定把这套流程写下来的原因,是材料里有大量手写证人证言和早年间的录音。手写部分用通用 OCR 识别率很差,录音转文字用云端大模型虽然效果不错,但几十小时的音频费用不低。换成本地模型做初筛,再让云端模型做精校,识别效果甚至更高,费用却大幅下降。
这篇文章适合正在做法律合规、尽调、仲裁或诉讼支持的开发者和技术负责人,也适合后端工程师想要快速搭建“AI 文档预处理管线”的人。读完后,你能掌握一条:怎么把手写材料、录音、视频三类证据,用开源模型加少量 API 调用,变成结构化的时间线文本。
1. 这篇文章真正要解决的问题
先把这个需求拆开看。法律场景的“证据整理”,本质上是一个多模态信息抽取任务:把纸上的字、声音里的词、视频里的画面,统一转成可检索、可引用、可交叉验证的文本时间线。
过去的标准做法分三步:
- 文件复印、扫描、命名归档。
- 人工阅读纸质件,按时间顺序做证据清单。
- 录音逐句听写,视频截图存证,统一编入证据册。
这套流程有两个致命问题。第一是慢,一千页材料,一个人逐页编号、摘录、录入系统至少需要 3 到 5 个工作日。第二是漏,人工听写录音听久了注意力下降,关键内容经常漏掉;手写材料里的改动、批注、不同颜色的笔迹也容易被忽略。
AI 能解决的真正痛点是“自动化和筛选前置”。整套管线可以做到:手写材料扫完自动变成文本,录音讲完自动生成带时间戳的会谈纪要初稿,视频不用人看,模型先把字幕抽出来,再把关键画面提帧。人只负责复核,工作量和漏检概率都下来了。
这里要泼一盆冷水:别指望一个模型做完所有事。市面上确实有综合能力很强的多模态大模型,能看图、能听话、能读视频。但在批量证据处理场景里,直接调多模态大模型成本高、速度慢,而且每一页图片都要传上去,数据隐私也难控制。更好的方式是“专用小模型初处理 + 大模型精加工”,这正是本地模型与云端 API 组合的核心价值。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 技术选型:本地模型与 API 按需省钱
聊方案之前,先把“本地模型 + API”这套混合架构讲透。它看起来像是一个成本策略,本质上是一种工程分工。
本地模型跑在自有机器上,数据不出内网,适合处理敏感材料。法律材料、商业合同、证人证言都涉及隐私,能不上传就不要上传。本地模型的缺点也很现实:识别准确率普遍低于云端大模型,尤其是复杂手写体和带口音的语音。它更适合做“粗筛”和“批量初转”,也就是先把能识别的轮廓转出来,把问题样本挑出来。
云端 API 的强项是理解力。它能把上下文、口音、专有名词处理得更自然,但按 token 或按次计费,对几十小时音频、上千页扫描件来说,全部走云端 API 的费用并不低。这类 API 适合做“精校”和“语义理解”,也就是只处理本地模型转出来质量差的部分,以及需要对内容做摘要、时间线归纳的部分。
结合法律场景,我的选型思路如下:
| 处理任务 | 首选方案 | 备选方案 | 选型理由 |
|---|---|---|---|
| 印刷体扫描件转文本 | 本地 OCR(PaddleOCR) | 云端 OCR API | 印刷体识别本地模型已足够,支持批量并行 |
| 手写材料转文本 | 手写专用 OCR(PaddleOCR 手写模型) | 云端多模态 API | 手写体先本地过一遍,识别率低于阈值再走云端 |
| 录音转文字 | 本地 Whisper 模型 | 云端语音转写 API | 录音可能涉密,本地 Whisper 成本为零,速度可控 |
| 视频抽取字幕 | 本地 Whisper + FFmpeg 抽音轨 | 云端音视频 API | 绝大多数视频分析只需要音轨,不用逐帧看画面 |
| 时间线归纳与摘要 | 云端大模型 API | 本地大模型(如 Llama 系列量化版) | 归纳总结需要强推理,云端效果明显更好,但控制调用量 |
| 向量检索与证据交叉比对 | 本地向量模型(例如 bge-m3) | 云端向量库 | 证据本地建索引,检索不出内网 |
从这个表能得出一个清晰结论:能本地干的活不要上云,云端的钱只花在“本地干不了的理解工作”上。这套架构下,几十小时录音的成本可能只有纯云端方案的二十分之一。
需要提前提醒的是,开源模型的版本更新很快,本文举例用的模型只是当前阶段的参考,版本请以实际项目为准,重点演示的是通用处理思路和调用方式。
3. 环境准备与前置条件
整套管线我建议部署在一台带 GPU 的工作站上。没有 GPU 也可以用 CPU 跑,只是速度会慢,尤其是 Whisper 转录音频时。如果你想先把流程跑通,CPU 也能接受,只是材料特别多时会比较急人。
推荐的环境配置如下:
- 操作系统:Ubuntu 22.04 LTS 或 Windows 11 + WSL2。
- Python:3.10 或 3.11,建议用 conda 创建独立环境。
- GPU:NVIDIA 显卡,显存 8GB 以上,可以跑 Whisper 的 small/base 模型。
- 内存:16GB 起步,处理大 PDF 时 OCR 会比较吃内存。
- 磁盘:准备 100GB 以上可用空间,模型文件和中间产物都不小。
- 模型管理工具:Ollama 或者 LM Studio,用于管理本地大模型。
Python 依赖建议用以下命令安装:
bash复制conda create -n legal-ai python=3.11
conda activate legal-ai
pip install paddlepaddle-gpu paddleocr openai-whisper ffmpeg-python pymupdf
pip install openai langchain-openai
安装完成后先检查关键组件:
bash复制python -c "from paddleocr import PaddleOCR; print('PaddleOCR OK')"
python -c "import whisper; print('Whisper OK')"
ffmpeg -version
有一个比较常见的坑:PaddleOCR 第一次运行会下载检测模型和识别模型,网络环境不好的话会卡很久。建议提前把模型下载好,或者配置 PaddleOCR(use_doc_orientation_classify=False) 关掉不必要的分类模型,加 use_doc_unwarping=False 关掉文档矫正,减少下载量。
如果打算用 Ollama 管理本地大模型,只需执行:
bash复制ollama pull qwen2.5:7b-instruct
这条命令会下载一个量化后的 7B 中文模型,适合做本地文本清洗、初步规则抽取。注意这只是备用方案,主流程里精加工仍建议选择云端 API。
4. 核心流程拆解:从材料到证据时间线
整套证据处理流程可以拆成四个阶段。我用法律项目第一批材料来具体说明,这批材料包含一百二十页扫描件、十五段录音和八段视频。
第一阶段是材料接入与格式标准化。扫描件可能是 PDF 也可能是单张图片,需要全部统一转成 PNG 图片,方便 OCR。录音格式五花八门,手机录的 MP3、会议系统导出的 WAV、微信语音转存过来的 AMR,都要先转成 16kHz 单声道 WAV,Whisper 处理起来会稳定很多。视频则先把音轨抽出来转成音频文件,再把视频文件另存备用。
第二阶段是批量 OCR 和语音初转。印刷体扫描件和手写材料分开处理。印刷体用普通 OCR 模型就能拿到不错的效果;手写体需要单独配置手写识别模型。录音统一交给本地 Whisper 做初转,输出带时间戳的文本。这个阶段跑完后,材料集中区会出现一个文本目录,文件名与原始材料一一对应。
第三阶段是质量分流。OCR 的结果不是每页都好,手写体的识别置信度尤其不稳定。此时需要做一次“置信度筛选”:置信度高的文本直接进入成品库,置信度低的、有大量空白的页面,被标记为“待精校材料”。精校阶段再调用云端大模型,把这些页面重新读一遍,补充上下文推导,得到更完整的文本。录音同理,如果某段 Whisper 输出的识别时间戳密集但文字是乱码,说明这段音频口音太重或者背景噪音太强,需要走云端二次转写。
第四阶段是语义加工与检索入库。精校后的文本是大模型理解的基础,它不只是做摘要,还要标注证据中涉及的时间点、人物、金额、事件关键词,并把整批材料串联成一条时间线。这一步还要把文本向量化后存入本地向量数据库,做完这一步,就可以执行“找出所有提到某年某月某日某地点以及相关人物”这样的交叉检索。这也是整套管线价值最集中的阶段,因为人工核对材料时最耗时的就是这份交叉检索。
5. 完整示例与代码实现
这一部分给出每个阶段的真实可运行代码。代码中用到的模型和 API,均以当前普遍可用的版本为例,文件名和路径请按自己的项目调整。
5.1 扫描件转图片,并批量 OCR
扫描件转图片我用 PyMuPDF 来做,它的速度和稳定性都很好。转换时统一把 PDF 每页渲染成 300 DPI 的 PNG,300 DPI 是 OCR 的推荐精度,太低看不清小字,太高文件过大且识别率不一定更好。
python复制# 文件路径:scripts/pdf_to_images.py
import fitz # PyMuPDF
import pathlib
input_pdf = pathlib.Path("cases/contract_scan.pdf")
output_dir = pathlib.Path("cases/images")
output_dir.mkdir(parents=True, exist_ok=True)
doc = fitz.open(str(input_pdf))
for page_number in range(len(doc)):
page = doc.load_page(page_number)
mat = fitz.Matrix(300 / 72, 300 / 72) # 300 DPI
pix = page.get_pixmap(matrix=mat)
pix.save(str(output_dir / f"page_{page_number:04d}.png"))
print(f"Total pages: {len(doc)}")
印刷体 OCR 用 PaddleOCR,它自带文本检测和识别,适配中英文混排材料效果比较好。第一次运行时模型会自动下载,之后每次都会走缓存,速度会快很多。
python复制# 文件路径:scripts/ocr_print.py
from paddleocr import PaddleOCR
import pathlib
ocr = PaddleOCR(
lang="ch",
use_doc_orientation_classify=False,
use_doc_unwarping=False,
use_textline_orientation=False,
)
image_dir = pathlib.Path("cases/images")
output_file = pathlib.Path("cases/ocr_print_result.txt")
results = []
for image_path in sorted(image_dir.glob("*.png")):
result = ocr.predict(str(image_path))
page_text = []
for line_group in result:
for line in line_group:
if line and "rec_texts" in line:
page_text.extend(line["rec_texts"])
results.append(f"=== {image_path.name} ===\n" + "\n".join(page_text))
output_file.write_text("\n".join(results), encoding="utf-8")
print("OCR done:", output_file)
这个阶段要注意,PaddleOCR 返回格式在不同版本里会有差异。如果你的版本返回的是 res[0]["rec_texts"] 而不是 line.get("rec_texts"),按实际输出结构调整即可。写代码时建议打印一段原始输出看看结构,别直接照抄。
5.2 手写材料识别与置信度过滤
手写体识别的关键不在于模型有多强,而在于要保留“识别置信度”。置信度高的行直接收下,置信度低的行单独归档,留给云端模型精校。这样既不浪费每一页云端 API 的调用,也不会因为手写识别结果太差而彻底丢掉有效证据。
python复制# 文件路径:scripts/ocr_handwritten.py
from paddleocr import PaddleOCR
import json
import pathlib
ocr = PaddleOCR(lang="ch", use_doc_orientation_classify=False)
image_dir = pathlib.Path("cases/handwritten_images")
output_json = pathlib.Path("cases/handwritten_result.json")
low_confidence_dir = pathlib.Path("cases/low_confidence")
low_confidence_dir.mkdir(parents=True, exist_ok=True)
all_pages = []
for image_path in sorted(image_dir.glob("*.png")):
result = ocr.predict(str(image_path))
page_entries = []
for line_group in result:
for line in line_group:
if not line or "rec_texts" not in line:
continue
for text, score in zip(line["rec_texts"], line["rec_scores"]):
page_entries.append({
"text": text,
"score": float(score),
"image": image_path.name,
})
all_pages.extend(page_entries)
# 置信度低于阈值的行标记为待精校
for entry in all_pages:
if entry["score"] < 0.85:
entry["need_refine"] = True
with open(low_confidence_dir / f"low_{entry['image']}.txt", "a", encoding="utf-8") as f:
f.write(entry["text"] + "\n")
output_json.write_text(
json.dumps(all_pages, ensure_ascii=False, indent=2),
encoding="utf-8"
)
print(f"Total lines: {len(all_pages)}")
对于置信度低的手写页面,这一步的重要性就能看出来了。它相当于给后期精校阶段画了一个重点范围,避免大量的无效 API 调用。不要试图在本地 OCR 阶段把识别率调到完美,那样投入的调优时间往往比云端精校的成本更高,工程上不合理。
5.3 录音转文字与视频抽字幕
录音转文字我用 OpenAI 开源的 Whisper 本地模型。它识别中文的效果在开源方案里属于第一梯队,特别适合带时间戳的逐字稿生成。为了平衡速度和精度,我选择了 small 模型。如果你的材料里口音很重,考虑换用 medium,代价是推理时间明显变长。
python复制# 文件路径:scripts/audio_to_text.py
import whisper
import pathlib
model = whisper.load_model("small") # 可选 tiny/base/small/medium/large
audio_dir = pathlib.Path("cases/audio")
output_dir = pathlib.Path("cases/transcripts")
output_dir.mkdir(parents=True, exist_ok=True)
for audio_path in sorted(audio_dir.glob("*.wav")):
result = model.transcribe(
str(audio_path),
language="zh",
verbose=False,
initial_prompt="这是一段中文法律会谈录音,可能涉及合同谈判、证人陈述。"
)
output_file = output_dir / f"{audio_path.stem}.txt"
with open(output_file, "w", encoding="utf-8") as f:
for segment in result["segments"]:
start = segment["start"]
end = segment["end"]
text = segment["text"].strip()
f.write(f"[{start:.1f}s - {end:.1f}s] {text}\n")
print("Done:", output_file)
视频分析的思路本质上和录音相同,因为视频关键信息绝大多数在音频里。先用 FFmpeg 抽取视频音轨,再调用 Whisper 转写。需要保存画面证据的时候,再用 FFmpeg 按指定的时间点截帧,生成 JPG 图片。
bash复制ffmpeg -i video_evidence_01.mp4 -vn -acodec pcm_s16le -ar 16000 -ac 1 video_evidence_01.wav
python复制# 文件路径:scripts/extract_keyframes.py
import subprocess
import pathlib
import json
frames_dir = pathlib.Path("cases/frames")
frames_dir.mkdir(parents=True, exist_ok=True)
# 时间点建议从 Whisper 转写结果里找:包含关键事件描述的时间段
key_times = [
{"video": "video_evidence_01.mp4", "time": "00:12:35", "name": "contract_sign"},
{"video": "video_evidence_01.mp4", "time": "00:48:22", "name": "cash_count"},
]
for item in key_times:
output_path = frames_dir / f"{item['name']}.jpg"
cmd = [
"ffmpeg", "-i", item["video"], "-ss", item["time"],
"-frames:v", "1", str(output_path), "-y"
]
subprocess.run(cmd, check=True)
print("Saved:", output_path)
在实际项目中,截帧这个动作往往是律师最需要的。录音转文字只能给出“说了什么”,画面截帧才能作为证据展示“做了什么”。建议把 Whisper 生成的带时间戳文本和截帧的图片命名关联起来,方便后续挂接进证据时间线。
5.4 本地模型清洗与云端 API 精校
本地 Whisper 和 OCR 初步转写完的材料,可以先用本地大模型做一遍快速清洗。清洗的内容包括:把明显的空行、乱码、重复片段去掉,统一标点符号。这一步用 Ollama 调用本地 7B 模型就够了,不用花钱。
python复制# 文件路径:scripts/local_clean.py
import requests
import json
import pathlib
OLLAMA_URL = "http://localhost:11434/api/generate"
def ollama_clean(text: str) -> str:
prompt = (
"请清洗以下法律材料文本。要求:\n"
"1. 删除乱码、重复行、无意义的空行。\n"
"2. 统一中英文标点。\n"
"3. 保留所有数字、金额、日期、人名和地点。\n"
"4. 直接输出清洗后的文本,不要解释。\n\n"
f"---\n{text}\n---"
)
resp = requests.post(OLLAMA_URL, json={
"model": "qwen2.5:7b-instruct",
"prompt": prompt,
"stream": False
}, timeout=120)
data = resp.json()
return data.get("response", "").strip()
transcript_dir = pathlib.Path("cases/transcripts")
clean_dir = pathlib.Path("cases/cleaned")
clean_dir.mkdir(parents=True, exist_ok=True)
for txt_path in sorted(transcript_dir.glob("*.txt")):
raw_text = txt_path.read_text(encoding="utf-8")
if len(raw_text) < 50:
continue
cleaned = ollama_clean(raw_text)
output_path = clean_dir / f"{txt_path.stem}_cleaned.txt"
output_path.write_text(cleaned, encoding="utf-8")
print("Cleaned:", output_path)
清洗后,真正需要云端的只是“低置信度材料 + 时间线归纳”两个子任务。低置信度页面适合用带视觉理解能力的大模型 API,把图片重新读一遍,结合上下文补全内容。时间线归纳则把前面所有初转文本汇总起来,请求大模型输出结构化 JSON。下面这段代码演示了如何用 OpenAI 兼容接口做精校,多数大模型 API 服务都提供类似格式的接口。
python复制# 文件路径:scripts/refine_and_timeline.py
import base64
import json
import pathlib
from openai import OpenAI
# 注意:不同服务商的基础地址和密钥配置方式不同,请以官方文档为准
client = OpenAI(
api_key="sk-your-api-key",
base_url="https://api.example.com/v1"
)
def refine_handwriting(image_path: str) -> str:
with open(image_path, "rb") as f:
b64 = base64.b64encode(f.read()).decode("utf-8")
resp = client.chat.completions.create(
model="gpt-4o-mini", # 或按服务商提供的视觉模型名填写
messages=[{
"role": "user",
"content": [
{"type": "text", "text": "请转写这张手写证据图片中的全部文字。保持原意,不要润色。"},
{"type": "image_url", "image_url": {"url": f"data:image/png;base64,{b64}"}}
]
}]
)
return resp.choices[0].message.content
# 对之前标记的低置信度图片逐张精校
low_dir = pathlib.Path("cases/low_confidence")
for txt_path in sorted(low_dir.glob("low_*.png.txt")):
# 这里的文件名保留了原图名,实际按自己的命名习惯关联
image_path = txt_path.stem.replace("low_", "cases/handwritten_images/", 1)
refined = refine_handwriting(image_path)
output_path = pathlib.Path("cases/refined") / f"{txt_path.stem}_refined.txt"
output_path.parent.mkdir(parents=True, exist_ok=True)
output_path.write_text(refined, encoding="utf-8")
print("Refined:", output_path)
def build_timeline() -> None:
all_materials = []
clean_dir = pathlib.Path("cases/cleaned")
refined_dir = pathlib.Path("cases/refined")
for txt_path in sorted(clean_dir.glob("*.txt")):
all_materials.append(f"### {txt_path.name}\n" + txt_path.read_text(encoding="utf-8"))
for txt_path in sorted(refined_dir.glob("*.txt")):
all_materials.append(f"### {txt_path.name}\n" + txt_path.read_text(encoding="utf-8"))
combined = "\n".join(all_materials)
prompt = (
"你是资深法律助理。请根据以下证据材料文本,生成证据时间线。\n"
"输出格式为 JSON 数组,每个元素包含:id, date_time, event, evidence_source, people。\n"
"事件按时间先后排列,尽量保留原文中的金额、日期、地址等细节。\n"
"不要编造没有出现的事实。\n\n"
f"材料如下:\n{combined[:30000]}"
)
resp = client.chat.completions.create(
model="gpt-4o-mini",
messages=[{"role": "user", "content": prompt}],
response_format={"type": "json_object"}
)
timeline = resp.choices[0].message.content
pathlib.Path("cases/timeline.json").write_text(timeline, encoding="utf-8")
print("Timeline saved:", timeline)
build_timeline()
用 API 做精校时,建议给每个请求设置较长的超时时间。视觉模型对图片的编码和推理确实需要更长时间,短的超时会导致请求直接中断,而大模型 API 是按成功请求计费的,失败重试反而多花时间。
这段代码里把上下文长度限制在 30000 字符左右,主要是为了避免超出模型的上下文窗口。如果材料特别多,建议分批给模型,最后再汇总成文档。
6. 运行结果与效果验证
整套管线跑完后,需要做一次质量验证,不验证就交付给业务方是不负责任的。我建议按照以下三个维度抽查结果:
第一,OCR 识别率抽查。随便抽取十页印刷体材料,人工与 OCR 结果比对,统计正确识别率。正常来说,清晰印刷体配合 PaddleOCR,识别率应该在 95% 以上。如果出现大面积低位,优先检查原始扫描件清晰度,看是否存在歪斜、模糊或反光。
第二,录音转写时间戳对齐。Whisper 输出的每句话都有时间范围。抽查三处关键时间点,播放录音对应位置,核对是否与转写内容一致。时间戳偏移如果超过 10 秒,说明音轨处理有延迟或模型采样出了问题,需要重新预处理音频。
第三,时间线事件交叉验证。拿到 timeline.json 后,随机挑两个关键日期,去对应原始材料里找依据。这项验证做得好不好,直接决定这个系统有没有被业务方信任。如果时间线里的事件在原始材料里找不到出处,必须标记为“待核验”,不能直接当证据用。
一个比较推荐的做法是保存全流程中间产物,别只输出最终结果。法律材料的特点是需要可追溯性,如果你拿着最终时间线去找律师,律师问你“这个金额是哪里来的”,你要能回溯到原始页和原始录音。中间产物就是这套系统的审计日志。
✅ 我判断成功的标准很简单:能不能在半小时内回答“某一页证据里有没有提到某个金额或某个人名”。如果能做到,这套管线就产生了实际价值。
7. 常见问题与排查思路
法律场景下使用 AI 处理材料,问题往往不在模型效果,而在工程链路和稳定性。以下是我实际踩过的坑,按出现频率排列。
| 问题现象 | 可能原因 | 排查方式 | 解决方案 |
|---|---|---|---|
| PaddleOCR 首次运行卡住 | 模型下载失败或网络受限 | 查看运行日志中是否卡在 Downloading | 提前手动下载模型文件,配置到本地缓存目录 |
| OCR 识别结果乱码 | 扫描件方向错乱或 DPI 过低 | 检查原始图片清晰度 | 确保 300 DPI,开启方向矫正或手动旋转 |
| Whisper 转写字符串大量重复 | 音频噪音大或采样率不统一 | 检查 WAV 文件采样率是否为 16kHz 单声道 | 统一用 FFmpeg 重采样,加降噪预处理 |
| Whisper 识别人名、地名不准 | 模型不认识案件实体专名 | 查看初始提示词是否包含上下文 | 在 initial_prompt 里加入当事人、地名、公司名 |
| API 返回 401 Unauthorized | API Key 无效或权限不足 | 对照服务商后台核对密钥 | 重新生成密钥并检查环境变量配置 |
| API 返回 400 超出上下文长度 | 单次请求内容过长 | 查看报错中的 token 数 | 按批次拆分文本,每次不超过上下文窗口的一半 |
| 向量检索结果不相关 | 切分粒度太大或太小 | 检查切分后的文本块长度 | 法律材料建议按段落切分,保留时间戳和证据来源字段 |
| 本地大模型回答不稳定 | 量化模型能力衰减 | 对比不同量化级别输出差异 | 关键任务使用较高精度模型,或者交给云端 API 精校 |
每个问题都不复杂,但如果没想过,出问题时真的会手忙脚乱。我的建议是先拿一杯茶,把原始材料格式从头检查一遍,再查模型输出。很多时候问题出在输入而非模型。
另外,如果你在精校环节使用了云端 API,并且报错中出现了明显的 key 或权限问题,第一时间去服务商后台查看密钥状态,不要反复重试。而且最好不要在前端页面或代码仓库里明文存 API Key,环境变量就是底线。
8. 最佳实践与工程建议
把这套流程真正落地到项目里,有一些工程层面的经验值得写下来。它们比模型调参更影响最终效果。
第一,给每个材料设计完整的命名规范。 文件名不要叫 page_0001.png 或 audio_01.wav,而是 ID-日期-类型-序号 的形式。一个可行的命名模式是 案件号_证据类型_日期_序号_页码。比如 SH2025-008_contract_20241120_003_002.png。这样是做证据交叉比对的基础。
第二,中间产物和临时文件不要清理太快。 本地 OCR 结果、初转文本、精校文本、时间线 JSON,全部保留在中间目录里。这样既方便审计,也方便调整后续阶段的提示词。就算项目结束了,这些中间产物也值得按件归档。
第三,API 调用要做总量控制和失败重试。 云端模型调用虽然强,但费用是真实存在的。建议每次运行前统计待精校材料的页数和音频时长,估算一下预期费用。如果以前一周调用五千次,这周直接翻到两万次,大概率是某个环节的筛选条件失效了。查询类的调用可以设置重试 3 次、退避时间为 1 秒、2 秒、4 秒,超过 3 次直接记录失败文件,等人工介入。
第四,不要为了让材料“生成”得更完整而强迫模型编造细节。 这是这条路径上最容易犯的错。我们处理的是法律证据,不是文案生成,如果手写材料某处确实无法辨识,宁可标记为“无法辨识”,也不能让模型凭猜测补全。
第五,把人工复核当成流水线的一环去设计,而不是事后补救。 我建议时间线 JSON 生成后,直接导出成 Excel 表格,让律师助理在表格里逐行标注“确认/存疑/需补充”。这个反馈表反过来又可以作为下一轮优化的训练参考。
第六,选择模型时不要盲目追求最大参数。 Whisper 的 large 模型确实更准,但在普通 GPU 上处理几十小时录音可能要跑几天,那就不划算了。先用 small 或 medium 跑一遍,把“问题音频”筛出来,再针对问题音频用 large 精校,效果和成本会平衡得多。
这个原则同样适用于本地大模型。如果能用 7B 解决的问题就不上 14B 或更大模型。推理时间、内存占用、热量散热,在批量任务里都是成本。
9. 总结与后续学习方向
这条“AI 法律助手”管线,本质上是一次多模态工程组合的实践。它没有用到最前沿的模型推理技术,也没有复杂算法,但真实解决了实际痛点。如果你把每个步骤拆开看,会发现全是现成的开源工具和公开 API:PaddleOCR、Whisper、Ollama,再加一个视觉大模型 API。真正花心思的是把这些工具按成本、隐私、准确率三个维度排列组合,形成一条合理的流水线。
从更广泛的角度看,任何“AI 处理非结构化文档”的场景,都可以套用这条思路:先识别,再筛选,然后精校,最后语义归纳。不管是处理合同、病例、技术文档,还是历史档案,底层逻辑都一样。
后续值得深入研究的方向有四个。一个是向量检索与 RAG,把全部证据文本入库后,通过语义检索回答“某份合同里有没有提到过特定条款”这类问题;第二个是 Agent 编排,用自动化流程批量处理新增材料,自动生成补证清单,而不是每批新材料都手动跑一遍;第三个是更细的实体识别,专门抽取法律材料中的人物、金额、日期、公司、案号等结构化信息;第四个是多轮校验,用两个模型交叉验证关键事件,降低单模型“幻觉”带来的风险。
如果你正打算处理一批积压的材料,建议先拿一个小的卷宗或合同包跑通流程,认真核对一遍结果,再逐步扩展到全量材料,不要一上来就追求自动化率。多模态 AI 工具真正改变的是材料和人的协作方式。麻烦的初筛、转写、检索可以交给机器,但真正需要判断、权衡的部分,仍然值得保留人的把关。
