LLM在贷前材料审核中的落地
LLM 在贷前材料审核中的落地
贷前材料审核是信贷流程中最依赖人工的环节之一。借款人上传身份证、营业执照、银行流水、经营合同等材料后,审核员要逐份核对信息一致性、识别 PS 痕迹、提取关键字段、判断材料是否合规。一套中型信贷机构的日均进件量在几百到上千单,每单平均涉及 5-10 份材料,纯人工审核的成本和时效都很难持续。
大模型(LLM)在这个场景里能做三件事:信息抽取、一致性校验、合规判断。这篇文章不讲"LLM 能不能用"的论证,直接讲工程落地的四个关键决策点。
一、整体架构:LLM 不是替代 OCR,是 OCR 的下游
一个常见的误解是把 LLM 直接怼到图片上做端到端审核。实际工程中,LLM 处理的是 OCR 产出的结构化/半结构化文本,而不是原始图片。链路如下:
用户上传材料(图片/PDF)
│
▼
┌─────────┐
│ OCR 引擎 │ ← 传统的文档 OCR(阿里云/腾讯云/自建)
└────┬────┘
│ 输出:结构化文本(姓名、身份证号、金额、日期等字段)
▼
┌─────────┐
│ LLM 层 │ ← 信息抽取 + 交叉校验 + 合规判断
└────┬────┘
│ 输出:审核结果(通过/驳回/转人工)+ 驳回原因
▼
┌─────────────┐
│ 规则引擎兜底 │ ← 硬规则(金额上限、黑名单等)最终决策
└─────────────┘
OCR 和 LLM 的分工很清晰:OCR 负责"看清楚字",LLM 负责"理解意思"。不要把 LLM 放在 OCR 前面——LLM 的多模态能力虽然能看图,但在高精度文字识别上远不如专用 OCR 引擎,且延迟和成本高出几个数量级。
二、Prompt 设计:结构化输出是关键
贷前审核的 LLM 调用不是聊天场景,必须返回结构化数据,供下游的规则引擎和决策系统消费。JSON 格式的强制输出是基本要求。
一个营业执照信息抽取的 Prompt 模板:
EXTRACT_BIZ_LICENSE_PROMPT = """
你是一个信贷材料审核助手。从以下 OCR 识别结果中提取营业执照的关键字段。
## OCR 文本
{ocr_text}
## 输出要求
严格按以下 JSON 格式输出,不要包含任何其他文字:
{{
"company_name": "企业全称",
"unified_code": "统一社会信用代码(18位)",
"legal_person": "法定代表人",
"registered_capital": "注册资本(含单位,如'500万元人民币')",
"establish_date": "成立日期(YYYY-MM-DD格式)",
"business_scope": "经营范围(取前200字)",
"registration_authority": "登记机关",
"extraction_confidence": "high|medium|low",
"missing_fields": ["未提取到的字段名列表"],
"ocr_quality_issues": ["OCR质量问题描述,如'文字模糊''水印遮挡'等"]
}}
## 注意事项
1. unified_code 必须是18位,如果识别结果不足18位,标注为缺失
2. establish_date 统一转为 YYYY-MM-DD 格式,无法转换的保持原文
3. extraction_confidence 按以下标准判断:
- high: 所有关键字段清晰可读,无歧义
- medium: 部分字段模糊或不确定
- low: 多数字段无法识别或 OCR 质量差
"""
def extract_biz_license(llm_client, ocr_text: str) -> dict:
"""调用 LLM 提取营业执照信息"""
prompt = EXTRACT_BIZ_LICENSE_PROMPT.format(ocr_text=ocr_text)
response = llm_client.chat(
messages=[{"role": "user", "content": prompt}],
temperature=0.0, # 零温度,要求确定性输出
response_format="json" # 强制 JSON 模式
)
return json.loads(response.content)
关键设计点:
- temperature=0:信息抽取任务不需要创造性,零温度保证每次相同输入得到相同输出。
- response_format="json":用 API 原生的 JSON 模式约束,比在 prompt 里写"请输出 JSON"可靠得多。
- extraction_confidence 字段:让 LLM 自己对抽取质量打分,下游可以据此决定走自动审核还是转人工。
三、交叉校验:多份材料之间的信息比对
单独抽取每份材料的信息只是第一步。真正有价值的是多份材料之间的交叉校验——比如营业执照上的企业名称和银行流水上的户名是否一致、身份证号和申请表上的身份证号是否匹配。
CROSS_VALIDATION_PROMPT = """
你是一个信贷材料一致性审核助手。以下是同一笔贷款申请的多份材料提取结果,
请逐项比对并判断是否存在不一致。
## 材料提取结果
{extracted_fields_json}
## 校验规则
1. 姓名一致性:申请表姓名 == 身份证姓名 == 银行流水户名
2. 证件号一致性:申请表证件号 == 身份证号
3. 企业信息一致性:营业执照公司名 == 银行流水户名 == 经营合同甲方/乙方
4. 金额合理性:申请金额 ≤ 银行流水月均入账 × {income_multiplier}
5. 日期逻辑:营业执照成立日期 < 申请日期,材料日期在合理范围内
## 输出格式
{{
"overall_result": "pass|reject|manual_review",
"checks": [
{{
"check_name": "校验项名称",
"result": "pass|fail|uncertain",
"source_a": {{"document": "材料A", "field": "字段名", "value": "值"}},
"source_b": {{"document": "材料B", "field": "字段名", "value": "值"}},
"detail": "不一致的具体描述",
"severity": "critical|warning|info"
}}
],
"reject_reasons": ["驳回原因列表(仅 overall_result=reject 时填写)"],
"manual_review_reasons": ["转人工原因(仅 overall_result=manual_review 时填写)"]
}}
"""
def cross_validate(llm_client, extracted_data: dict) -> dict:
"""多材料交叉校验"""
prompt = CROSS_VALIDATION_PROMPT.format(
extracted_fields_json=json.dumps(extracted_data, ensure_ascii=False, indent=2),
income_multiplier=3.0
)
response = llm_client.chat(
messages=[{"role": "user", "content": prompt}],
temperature=0.0,
response_format="json"
)
result = json.loads(response.content)
# 硬规则兜底:LLM 的判断结果可以被人为覆盖
# 比如 LLM 判 pass 但金额超过硬上限,直接 reject
if extracted_data.get("apply_amount", 0) > HARD_AMOUNT_LIMIT:
result["overall_result"] = "reject"
result["checks"].append({
"check_name": "硬规则_金额上限",
"result": "fail",
"detail": f"申请金额 {extracted_data['apply_amount']} 超过系统上限 {HARD_AMOUNT_LIMIT}",
"severity": "critical"
})
return result
注意 severity 字段的分级设计:
- critical:一旦触发直接拒绝(姓名不一致、证件号对不上、金额超限)
- warning:标记但不阻断(成立日期与申请日期间隔 < 6 个月、流水月度波动 > 50%)
- info:仅记录供人工参考(经营范围含敏感行业关键词)
四、驳回原因的生成:不只是"通过/不通过"
审核系统如果只返回"驳回",审核员不知道原因、借款人也看不懂,客服电话会被打爆。LLM 在这个场景里的另一个价值是生成可读的驳回原因。
REJECT_REASON_PROMPT = """
你是信贷审核系统的客户沟通助手。根据以下审核结果,生成一段驳回原因说明。
要求:面向借款人,语言礼貌但清晰,明确指出需要补充或修正的材料,不超过150字。
## 审核结果
{reject_checks_json}
## 输出格式
{{
"reason_summary": "一句话总结驳回原因",
"detail_to_customer": "面向借款人的详细说明",
"action_required": ["借款人需要执行的具体操作列表"]
}}
"""
这里有一个实践要点:驳回原因的 Prompt 和审核 Prompt 必须分开。审核 Prompt 要求严谨、技术化,驳回 Prompt 要求面向借款人、通俗易懂。混在一个 Prompt 里,LLM 会倾向于折中输出,两头不讨好。
五、三个工程踩坑实录
5.1 OCR 噪声是最大的不确定性来源
OCR 输出不是干净的文本。常见问题:身份证号里的 "0" 被识别成 "O"、"1" 被识别成 "l"、水印文字混入正文、表格错行导致字段错位。
解法:在 OCR 和 LLM 之间加一层预处理。不是做复杂的 NLP,而是做简单的规则清洗:
def preprocess_ocr_text(text: str, doc_type: str) -> str:
"""OCR 文本预处理"""
if doc_type == "id_card":
# 身份证号常见纠错
text = text.replace("O", "0").replace("o", "0")
text = text.replace("I", "1").replace("l", "1")
text = text.replace("S", "5").replace("s", "5")
# 去除非数字字符(仅对身份证号行)
import re
text = re.sub(r'(\d{6})\s+(\d{8})\s+(\d{4})', r'\1\2\3', text)
elif doc_type == "bank_statement":
# 银行流水常见:去除水印行(通常全是英文大写 + 重复模式)
lines = text.split('\n')
text = '\n'.join([
l for l in lines
if not (l.isupper() and len(l) > 30 and l.count(l[0]) / len(l) > 0.3)
])
return text
5.2 LLM 输出格式不稳定
即使设了 response_format="json",LLM 偶尔还是会输出带 markdown 代码块的 JSON(json ... ),或者 JSON 中包含非法的控制字符。
def safe_parse_llm_json(raw_response: str) -> dict:
"""鲁棒的 LLM JSON 解析"""
import re
# 去掉可能的 markdown 代码块包裹
cleaned = re.sub(r'^```(?:json)?\s*', '', raw_response.strip())
cleaned = re.sub(r'\s*```$', '', cleaned)
# 去掉非法控制字符(ASCII 0-31 除了 \t \n \r)
cleaned = re.sub(r'[\x00-\x08\x0b\x0c\x0e-\x1f]', '', cleaned)
try:
return json.loads(cleaned)
except json.JSONDecodeError:
# 兜底:尝试用正则提取最外层的 {}
match = re.search(r'\{.*\}', cleaned, re.DOTALL)
if match:
return json.loads(match.group())
raise
5.3 延迟和成本的平衡
一笔贷款申请的完整审核链路通常涉及 3-5 次 LLM 调用(每份材料一次抽取 + 一次交叉校验 + 可选的驳回原因生成),如果每次都用 GPT-4 级别的模型,单笔成本可能超过 0.1 元,日处理 1000 单就是 100 元,还不算 OCR 费用。
降本策略:
| 环节 | 模型选择 | 理由 |
|---|---|---|
| 信息抽取 | GPT-4o-mini / Qwen-2.5-7B | 抽取任务相对简单,小模型即可 |
| 交叉校验 | GPT-4o / Qwen-2.5-72B | 需要多材料联合推理,大模型更可靠 |
| 驳回原因生成 | GPT-4o-mini | 生成任务对逻辑要求低,小模型足够 |
| OCR 置信度低时 | 全链路升级到 GPT-4o | 输入质量差时用小模型容易出错 |
另外,交叉校验可以加一级缓存:同一用户短期内多次申请时,历史校验结果可复用。
六、什么时候不该用 LLM
不是所有审核场景都适合上 LLM。以下场景应该继续用传统方法:
- 精确数值比对(身份证号、金额、日期):正则 + 规则引擎比 LLM 更可靠、更快、更便宜。LLM 做数值比对偶尔会出错(比如把 "18位" 数成 17 位)。
- 黑名单匹配:ElasticSearch 或 Redis Set 的 O(1) 查询,不需要 LLM。
- 材料真伪鉴定(PS 检测、水印验证):这是图像处理领域的专长,不是 LLM 的能力边界。
- 监管合规的硬性规则:这些规则必须 100% 准确执行,不允许 LLM 的概率性判断。
LLM 最适合的是模糊语义判断——"这份经营合同的业务描述和营业执照的经营范围是否一致?""银行流水的交易摘要中是否有可疑关键词?"这类没有精确规则、依赖语境理解的任务。
贷前材料审核是 LLM 在风控领域落地最成熟的场景之一。信息抽取 + 交叉校验 + 驳回原因生成的三段式架构,配合 OCR 预处理和硬规则兜底,能把人工审核量压缩到 20%-30%,同时保证不放过关键的不一致。上线前重点关注两点:OCR 噪声的容错能力和 LLM 输出的格式稳定性——这两个问题不解决,自动化率再高也是假象。