需求背景
手头有一批电子发票 PDF,想通过自动化工具批量提取格式化信息。比如从发票里把开票日期、货物名称、价税合计这些关键字段抓出来,存成字典格式。
环境准备
1. 安装 Ollama
去官网下载 Ollama 客户端,支持 Windows、Mac 和 Linux。
2. 拉取模型
Ollama 支持多种模型,像阿里的 Qwen、Meta 的 Llama3 都可以。根据自己电脑的显存情况选个合适的版本。如果不确定,可以先试装一个,不行再换。 以 Llama3 为例,在终端执行:
ollama run llama3
等待 llama3:8b 下载完成。
3. 安装 Python 依赖
调用 Ollama 服务需要 Python 包。
pip3 install ollama
4. 启动服务
在 Python 中调用前,先确保本地服务已启动。
ollama serve
看到类似 Listening on 127.0.0.1:11434 的输出,说明服务正常。
实战步骤
1. 读取 PDF 内容
这里用到了 cntext 库来解析 PDF 文本。注意,实际项目中也可以选用 pdfplumber 或 PyMuPDF 等开源库替代。
import cntext as ct
text = ct.read_pdf('data/1.pdf')
print(ct.__version__)
print(text)
输出会包含发票上的原始文本信息,虽然有些乱码,但关键内容都在。
2. 构造 Prompt 提取信息
利用本地的大模型 llama3:8b 进行信息抽取。关键在于设计好提示词,明确告诉模型要返回什么格式。
import ollama
response = ollama.chat(model='llama3:8b', messages=[
{
'role': 'user',
'content': f'提取 TEXT 中的关键信息,返回 DICT_DATA,DICT_DATA 为 dict 数据格式,所含关键词依次为"开票日期", "应税货物 (或服务) 名称", "价税合计 (大写)", "税率", "备注"; 结果只显示 DICT_DATA。TEXT: {text}',
},
])
result = response['message']['content']
print(result)
运行后,模型会返回一段包含字典内容的字符串。有时候它会多带一些解释性文字或者 Markdown 代码块标记,这时候就需要做一下清洗。
3. 清洗与转换
为了确保拿到的是真正的 Python 字典对象,我们需要过滤掉多余的字符。
import re
result = response['message']['content']
# 分割出包含大括号的部分
result = [r for r in re.split('```|DICT_DATA = ', result) if '{' in r][0]
print(type(eval(result)))
print(eval(result))
这样就能得到一个干净的字典对象了。
4. 封装函数
为了方便复用,我们把上面的逻辑封装成一个函数。
def extract_info(text):
response = ollama.chat(model='llama3:8b', stream=False, messages=[
{
'role': 'user',
'content': f'提取 TEXT 中的关键信息,返回 DICT_DATA,DICT_DATA 为 dict 数据格式,所含关键词依次为"开票日期", "应税货物 (或服务) 名称", "价税合计 (大写)", "税率", "备注"; 结果只显示 DICT_DATA。TEXT: {text}',
},
])
result = response['message']['content']
# 清洗逻辑
result = [r for r in re.split('```|DICT_DATA = ', result) if '{' in r][0]
return eval(result)
5. 批量处理
假设 data 文件夹里有几十上百张发票,我们可以遍历它们并导出为 CSV。
import os
import pandas as pd
import cntext as ct
# 获取所有 PDF 文件路径
pdf_files = [f'data/{file}' for file in os.listdir('data') if '.pdf' in file]
dict_datas = []
for pdf_file in pdf_files:
pdf_text = ct.read_pdf(pdf_file)
dict_data = extract_info(pdf_text)
dict_datas.append(dict_data)
df = pd.DataFrame(dict_datas)
print(df)
跑完这个脚本,所有的发票信息就整理好了。
经验与讨论
这次实验只验证了一张发票,实际场景下准确率可能会波动。识别错误通常集中在销售方纳税识别号等细节上,主要是因为 OCR 读入的文本比较杂乱,增加了大模型的语义理解难度。
目前大模型已经支持图文混合输入,未来可以直接上传发票图片,效果会更好。另外,大模型是基于概率生成回答的,存在一定误差风险。为了降低这种风险,可以优先提取特征明显的字段,比如'价税合计 (大写)',因为中文大写数字在文本中非常醒目,模型对这类信息的权重分配通常会更高,准确率也相对更稳。

