使用免费的AI大模型处理单据
朋友新店进货有不少打印单据、手写单据需要录入记账软件,起初使用图像识别软件先将单据图片转换为Excel,人工核对数据正确性并提取所需的数据,再按照记账软件模板创建符合导入格式的Excel。这种方式虽比纯人工录入表格效率要高得多,但单据太多也很花时间。
硅基流动上线免费的PaddleOCR-VL-1.5模型后,就有了用大模型API来批量处理单据的想法。
创建 OpenAI 客户端:
from openai import OpenAI
SILICONFLOW_API_KEY = "硅基流动API Key"
SILICONFLOW_BASE_URL = "https://api.siliconflow.cn/v1"
client = OpenAI(api_key=SILICONFLOW_API_KEY, base_url=SILICONFLOW_BASE_URL)
将图片编码为Data URL:
import base64
def encode_image_to_base64_url(self, image_path):
"""
编码图片并格式化为 Data URL
"""
try:
with open(image_path, "rb") as image_file:
base64_image = base64.b64encode(image_file.read()).decode('utf-8')
mime_type = Image.open(image_path).get_format_mimetype()
return f"data:{mime_type};base64,{base64_image}"
except FileNotFoundError:
print(f"错误:图片文件未找到 '{image_path}'")
return None
调用PaddleOCR-VL-1.5模型提取图片中的文本:
import re
def get_raw_text_from_image(self, image_url):
"""
使用OCR模型提取文本
"""
response = client.chat.completions.create(
model="PaddlePaddle/PaddleOCR-VL-1.5",
messages=[{
"role": "user",
"content": [
{
"type": "image_url",
"image_url": {
"url": image_url,
"detail": "high"
}
}
]
}],
max_tokens=8192,
timeout=120
)
raw_text = response.choices[0].message.content
pattern = r'<\|LOC_\d+\|>'
cleaned_text = re.sub(pattern, ' ', raw_text)
cleaned_text = re.sub(r'\s+', ' ', cleaned_text).strip()
return cleaned_text
使用语言模型Qwen/Qwen3-8B将图片中提取到的原始文本转换为结构化数据:
import re
import json
def structure_text_with_llm(self, raw_text):
"""
使用语言模型提取原始文本为JSON数据
"""
system_prompt = "你是一个专业的单据分析师,擅长从非结构化文本中提取关键信息并将其转换为结构化数据。"
user_prompt = f"""
**待处理文本:**
---
{raw_text}
---
**JSON字段说明:**
1. **counterparty**: 智能提取交易对方名称,优先使用页眉中的名称。
2. **product**: 智能提取产品名称、规格型号,将它们合并,并用空格隔开 (例如, "公牛插座 GN-901")。
3. **quantity**: 智能提取数量(例如, 2)。
4. **unit**: 智能提取单位(例如, 个、件)。
5. **price**: 智能提取产品的单价。
6. **amount**: 智能提取产品的金额。
7. **date**: 智能提取日期,格式为 "YYYY-MM-DD"。
**注意事项:**
1. 如果某个字段无法找到,使用 `null` 作为值。
2. 最终输出应为包含多个项目的JSON数组,每个项目对应一个产品。
**示例输出:**
{{
items:[{{
"counterparty": "张三",
"product": "公牛插座 GN-901",
"quantity": 10,
"unit": "个",
"price": 12.50,
"amount": 125.00,
"date": "2026-05-02"
}}]
}}
"""
response = client.chat.completions.create(
model="Qwen/Qwen3-8B",
messages=[
{"role": "system", "content": system_prompt},
{"role": "user", "content": user_prompt}
],
max_tokens=8192,
temperature=0.0,
response_format={"type": "json_object"},
timeout=120
)
content = response.choices[0].message.content
content = content.strip()
json_str = re.search(r'{.*}', content, re.DOTALL)
return json.loads(json_str.group(0)) if json_str else None
PaddleOCR-VL-1.5的识别正确率还是不错的,但API返回了位置标记,这里只是简单的过滤了位置标记内容。可能是我处理不够好,最终使用语言模型结构化数据不太理想。
于是,我又尝试了硅基流动的一款免费视觉模型:THUDM/GLM-4.1V-9B-Thinking
import re
import json
def get_json_from_image(self, image_url):
"""
使用多模态模型从图片获取结构化JSON数据
"""
user_prompt = f"""
从单据图片中提取关键信息,并将其转换为结构化JSON数据。
**JSON字段说明:**
1. **counterparty**: 智能提取交易对方名称,优先使用页眉中的名称。
2. **product**: 智能提取产品名称、规格型号,将它们合并,并用空格隔开 (例如, "公牛插座 GN-901")。
3. **quantity**: 智能提取数量(例如, 2)。
4. **unit**: 智能提取单位(例如, 个、件)。
5. **price**: 智能提取产品的单价。
6. **amount**: 智能提取产品的金额。
7. **date**: 智能提取日期,格式为 "YYYY-MM-DD"。
**注意事项:**
1. 如果某个字段无法找到,使用 `null` 作为值。
2. 如果为手写单据图片,只提取手写内容,忽略打印内容。
3. 部分单据会有类似如下格式的内容:数量x单价=金额(例如,6个x2=12),需从中智能提取数量、单位、单价和金额。
4. 最终输出应为包含多个项目的JSON数组,每个项目对应一个产品。
**示例输出:**
{{
items:[{{
"counterparty": "张三",
"product": "公牛插座 GN-901",
"quantity": 10,
"unit": "个",
"price": 12.5,
"amount": 125,
"date": "2026-05-02"
}}]
}}
"""
response = client.chat.completions.create(
model="THUDM/GLM-4.1V-9B-Thinking",
messages=[{
"role": "user",
"content": [{
"type": "image_url",
"image_url": {
"url": image_url,
"detail": "high"
}
}]
}, {
"role": "user",
"content": user_prompt
}],
max_tokens=8192,
timeout=120
)
content = response.choices[0].message.content
content = content.strip()
json_str = re.search(r'{.*}', content, re.DOTALL)
return json.loads(json_str.group(0)) if json_str else None
测试效果很不错,部分AI不认识的手写体,基本我也认不出了。相比使用OCR先提取文本再使用语言模型结构化数据,视觉模型一步到位更简单。