零人工的房贷文档抽取——拿不准就自动找客户确认补全
一家房贷中介把每一份工资单、银行流水、税单都靠人手敲进核保。我们做了一套 OCR + LLM 抽取流水线,只自动填它扛得住的字段——两个独立来源、归一化后一致、置信度够高——填不了的就自动回到客户那边确认或补传。全程零人工录入,也没有任何靠猜的值进入信贷档案。
更新于 2026-06-20
成果
- 彻底取消
- 人工录入
- 自动找客户确认补全
- 不确定或缺失字段
- 零
- 靠猜填的值
服务
- 定制企业系统
- 数据检索与企业获客
技术栈
项目记录
没人想干、可人人都在为它付钱的活
做房贷运营的都清楚时间花在哪。工资单、银行流水、税单——每一单申请都是一摞 PDF 和手机拍的扫描件,然后有人坐在那里,一个字段一个字段地把数字重敲进系统。吞吐被人头卡死:招人快过培训速度,质量就掉;慢慢培训,队列又堆起来。
要命的是哪些字段最容易被敲错。驱动核保和合规的那些数——收入、余额、监管哪天可能要你拿出来交代的数字——恰恰是最容易敲错的。工资字段里调错一位数,它不会自己跳出来报警,而是直接流进信贷决策,等到事后再纠,代价远不止当初敲错的那一分钟。
所以这个需求看着简单,其实不是。把录单从人手里彻底拿走,但绝不能以一个错的数字进入信贷档案为代价。字段必须对;而且只要有东西进了系统,你得说得出它凭什么进去。
定下整套设计的那条约束
我们把这种不对称当成设计主轴,因为在这个行业里,两种”错”完全不是一回事。系统拒绝去填的字段,代价不过是给客户自动发一条确认消息;系统填错的字段,换来的是一份被重新打开的档案、一个合规质询,外加签字那个人对你的信任崩掉。一个是一次往返,另一个是把你拖去审计的东西。
这就排除了那个看着最顺的做法——跑 OCR、把结果灌进表单、让复核员去抓错。它长得像自动化,其实只是把校对往下游推;而校对恰恰是人最不擅长的环节:复核员盯着已经填好的表单,会被那个现成的值锚住,漏掉的偏偏是那些看着合理的错读——而看着合理的错读,正是最危险的。我们反着来——系统只在扛得住的时候才填一个字段,填不了的时候,它不会把缺口丢给人,而是自己把它补上。这条不对称写进了系统被调校的那个指标,于是每一个阈值、每一次平手时的取舍都朝同一个方向落地:拿不准就问,别写。
抽取和决策是两个独立环节
第一个结构性决定,是把抽取和决策分开,因为把两者揉在一起,正是”自信地填错”产生的方式。OCR 和 LLM 产出候选值,但它们不往系统里写任何东西。每个候选都带上”是哪种方法抽到的”标签——哪个引擎、哪一遍抽到的——和一个置信度信号,一个字段可以同时挂着来自几种方法的多个候选,并排留着,而不是一上来就压成一个”答案”。这正是后面那些校验得以成立的前提:如果你在抽取阶段就把三个读数压成一个值,你就把”分歧”扔掉了——而分歧恰恰是你手里最有用的东西,两种方法说一个数、第三种说另一个数,这不是要被平均掉的噪声,而是一个”这个字段需要确认”的信号。所以抽取只负责把带标签的证据摆出来,而只有一道独立的门才有权把证据变成写入的字段。把环节拆开在运营上也划算:我们可以只动抽取这一侧,而完全不碰”什么才算可以安全写入”那条规则,让决策逻辑保持得小、能单独测——对一个职责就是”要保守”的组件来说,这点很重要。
决策门:两个一致来源、归一化、置信度高
这道门对每个字段问两件事:多个独立来源是否一致?置信度是否高到可以动手?一个字段只有在至少两个独立抽取一致、且每个支撑它的信号都是 HIGH 时,才会被自动写入。凑不齐这条的,一律不写——转去走客户确认那条回路。系统绝不靠猜去填空格,也绝不把一个孤证读数提拔进信贷档案。
真正起作用的词是”独立”。同一个模型对同一张图跑两遍,不算两个来源;它们一起失败、为同样错误的理由达成一致。真正的相互印证,来自抵达同一事实的、真正不同的路径——不同的抽取方法、文档的不同区域、把一个结构化字段和从正文里读出来的数字交叉核对。把”非独立的读数”当成一致来数,制造出的假把握看上去和真把握一模一样,这正是我们专门防的那个隐蔽失败。
而比较要有意义,前提是先把值规范化。日期、金额、姓名各自按类型先归一化,再去比,所以”一致”是两个来源真的在说同一件事,而不是作为原始字符串碰巧相同、或碰巧不同。读成 01/02/2026 和 2026-02-01,一旦都归一成规范日期就是一致,只有底层那一天确实不同时才算分歧;金额归一成规范数字,于是 $4,500.00 和 4500 收成同一个值;姓名按大小写、空格、顺序折叠,于是工资单抬头和银行流水之间的格式差异,不会伪装成一个冲突。少了这一步,你会在格式噪声上得到假一致——这比没有一致更糟,因为它穿着”匹配”的外衣过了门,而一个真正的不匹配从底下溜了过去。
拿不准时,它去问客户,而不是塞给人工
凡是过不了门的——不足两个一致的高置信读数,或者文档里干脆就没有的值——都会触发那条取代复核队列的回路。系统不会把这个字段停在那儿等人去追,而是给客户生成一条精准的请求:确认这一个值,或者补传还缺的那一份文档。请求是对着具体那个缺口来的,不是一句笼统的”你的申请不完整”,所以客户回答的是一个关于自己资料的精确问题,而不是再去对一遍清单。
这里有两个不同的触发,生成的是不同的请求。一个有值但不确定的字段——读到了,但没印证到那条线——产出一条确认请求:这是我们读到的,对吗。一个根本没有来源的字段——整份文档缺了——产出一条补件请求:还需要你最近一期的工资单。把这两件事当成一件,会发出糟糕的消息:那个已经把工资单传上来的客户,不该因为单子上某个数读得发虚,就被再喊一遍”请上传工资单”。回复一回来,流水线就用它把字段补上、继续往下走——一个被确认的值能过门,因为对自己的资料而言客户本人按定义就是权威来源,一份新传上来的文档则重新进入抽取、和其它所有东西一样再走一遍同一道门。结果是不确定靠自己解决、环里没有运营人员,唯一碰一个不确定字段的人就是确认自己那个数的客户本人。这就是我们敢说”零重敲”并且当真的那条底线:门的下游没有复核队列,因为门溢出的去处是客户,不是某张办公桌。
来源出处跟着每个字段走
推断出来的值绝不当成事实呈现。每个字段都带着它从哪来——哪种方法读的、被什么印证的、是否经客户确认——所以任何一个数都能追回到那份文档和产生它的那条路径。我们把”把推断值当成已知事实来呈现”单列成一类缺陷,因为在信贷里,这恰恰是那种能一路潜伏到审计才暴露的安静错误。这不是做台账,而是让那条不对称在事后还能被兑现:当有人问某个字段为什么是这个值,答案就在记录里——两种方法一致、它们一致的归一化值在这儿,或者客户在某月某日确认过它。这也意味着模型或 prompt 改动时,我们能把门对着历史输入重跑一遍、看清楚究竟哪些字段会判得不一样,把”这次改动有没有弄坏什么”从一次猜测变成一次查询。
精确率在 CI 里设门
抽取对着一套带标注的评测集来量——真实文档配已知正确的输出——而给构建设门的指标是”自动填那一片的精确率”,不是对所有东西笼统算的准确率。准确率会因为把简单字段答对而给系统加分,它根本不说明系统挑去动手的那些字段值不值得动手。自动填那一片上的精确率,量的才是这里唯一要紧的事:在没问就写下的那些值里,有多少是对的。我们刻意守住这条线,还是那个贯穿整套设计的不对称理由——宁可多问客户一句,也不要写错一个数。
评测集是作为回归门接进来的,不是一次性的跑分。一次对模型、prompt 或解析层的改动,都会触发整套重跑,而一次让设门那一片精确率掉到线下的提交,门就不让过——它上不了线。Prompt 改动就是代码改动,会用同样的方式弄坏东西;团队之所以察觉不到,只是因为他们不量。我们在开自动化之前先把基线立好——一套真实案例被逐字段打分,自动填那一片必须先干净才放任何东西上线——这条基线也是此后每一次改动的契约:你别想悄悄把它降下去。同一套评测也裁决模型选型:有些读数需要一个能看到页面图像的多模态模型,而更朴素的结构化抽取用一个更便宜的纯文本模型就够了,所以由设门那一片量到的精确率、而不是口味,去决定哪个模型该接哪份活,而不是默认所有页面都上最大的模型、然后为每一页买单。
PII 和访问全程锁死
采集到的文档满是 PII——姓名、账号、收入——所以它们绝不离开处理边界,按规定也不进版本库;我们用来测的样本是脱敏过的或被 gitignore 掉的,让真实客户数据绝不和代码一起被提交。这一条塑造测试搭法的程度,不亚于它塑造流水线本身:评测基线得在真实文档上证明精确率,又不能让这些文档变成一份躺在仓库里的敏感数据副本。
字段级访问遵循和 OWASP Broken Access Control 一样的逻辑:一个字段只对拥有它的请求可见,而这个归属校验放在服务端,不放在界面层。在前端把一个值藏起来,不是访问控制;一个绑在某个客户申请上的字段,必须对一个属于另一个客户的请求不可读,这要在服务端、每一次读取时都校验。自动化流水线在这上面拿不到任何豁免——一个会写、会读字段的自治组件,仍然是个特权客户,得过和任何用户一样的校验,因为”系统有把握”和”系统被允许”,根本是两个问题。
结果
重复录单没了——不是挪给了复核员,是真的没了。系统自动填的那些字段,都是它扛得住的:两个一致的独立来源、归一化让一致是真的、高置信、带着出处。其余的,由客户自动确认补全——少一份工资单、读得发虚的一个值,都不用人插手就自己解决了。自动填那一片的精确率,由 CI 里的一道门守着,于是这条线不会在一次次模型和 prompt 改动里悄悄被磨掉。凡是管线拿不出两个一致来源的,一律不进档案——这一条,才是让运营能睡安稳的部分,也是审计来问”这个数打哪儿来”时扛得住的那一条。