长文本审核踩坑实录:从OCR乱码到RAG精召回的实战经验
2026/6/25 21:06:52 网站建设 项目流程

长文本审核踩坑实录:从OCR乱码到RAG精召回的实战经验

当法务专家一眼看穿大模型的"一本正经胡说八道",那种落差比跑20公里还难受

写在前面

在合同智能审核这条路上,我踩过的坑连起来能绕办公室三圈。文档结构复杂、语义偏法律条款化、普通OCR识别后空位错乱、格式崩塌——这些问题堆在一起,再强大的大模型也难免产生幻觉。经过长期重复造轮子的折腾,我想把这套"血泪经验"分享给正在做同类项目的同行,希望能帮大家少走几段弯路。


一、知识库体系与大模型选型:格式清理比模型选择更重要

1.1 技术栈选型

我们的知识库基座采用以下组合:

  • 向量库:pgvector
  • 重排模型:rerank 系列
  • 文本向量化模型:BGE-M3

1.2 最大的坑:语义分片割裂上下文

起初,我们直接对法律文件进行上传切片,依赖纯语义分片。结果发现:上下段落被强行分开,文本块支离破碎,完整条款被割裂成多个碎片,检索和审核效果大打折扣。

客户要求的是精确结果,而纯语义分片远远达不到。

1.3 解决方案:基于格式标记的结构化清洗

最终我们选择了"笨但有效"的办法——按照指定格式标记统一清理法律法规文件

标记含义
#一级目录
##二级目录
###三级目录
####.三级目录下的条款内容

虽然前期需要投入人力进行格式整理,但整理完成后,向量化切片得到的文本块完整且语义连贯,检索准确率明显提升。

经验总结:不要迷信纯自动化的语义分片,对法律这种强结构化文本,预先做格式标准化处理,性价比远超后期反复调参。


二、合同文本处理:Mammoth.js 拯救格式错乱

2.1 普通OCR的痛点

标准合同文本通常有固定模板,但模板中的空位和横线填写内容才是审核重点,常规条款只需知识库风险验证即可。

问题来了:用POI流转OCR提取文本后,提交给大模型的内容格式完全崩塌——文本堆砌在一起,空格、下划线、填写位置全部丢失。大模型面对一堆"乱码",自然无法给出准确的审核结论。

2.2 破局方案:Mammoth.js 转HTML

经过多轮对比,我们最终采用Mammoth.js.docx文件直接转换为 HTML。这样做的好处立竿见影:

  • 合同格式完整保留
  • 空位和填写区域清晰可辨
  • 提交给大模型的上下文结构完好

审核出来的结果自然更全面、更准确

2.3 核心代码片段

VUE前端(存储与读取HTML格式合同):

exportfunctionbuildContractHtmlKey(input){constid=String(input.contractId||input.documentId||'').trim()constfileName=String(input.fileName||'').trim()constfileUrl=String(input.fileUrl||'').trim()return[id,fileName,fileUrl].filter(Boolean).join('__')||`contract-${Date.now()}`}exportasyncfunctionsaveContractHtmlRecord(record){constkey=record.contractIdconstrecords=awaitreadContractHtmlLibrary()constnormalizedTextType=normalizeTextType(record.isUnifiedText||record.fields?.textType)constsavedRecord={...record,contractType:pickContractType(record),isUnifiedText:normalizedTextType,fields:{...(record.fields||{}),contractType:pickContractType(record),textType:normalizedTextType,isUnifiedText:normalizedTextType},updatedAt:Date.now()}records[key]=savedRecord// 同步存储至chrome.storage.local或localStorage}

Java后端(Word文档内容提取):

privatePdfOcrResultextractWordContent(StringfileUrl,StringfileName)throwsException{PdfOcrResultresult=newPdfOcrResult();Stringcontent;if(fileName.toLowerCase(Locale.ROOT).endsWith(".docx")){try(InputStreaminputStream=newURL(fileUrl).openStream();XWPFDocumentdocument=newXWPFDocument(inputStream);XWPFWordExtractorextractor=newXWPFWordExtractor(document)){content=extractor.getText();}}else{try(InputStreaminputStream=newURL(fileUrl).openStream();HWPFDocumentdocument=newHWPFDocument(inputStream);WordExtractorextractor=newWordExtractor(document)){content=extractor.getText();}}result.setContent(content);result.setCurrentPage(1);result.setTotalPages(1);result.setPdfUrl(fileUrl);result.setFileName(fileName);returnresult;}

三、RAG增强的至暗时刻:从"幻觉狂欢"到精准召回

3.1 多LLM并行抽取策略

我们将提交给大模型的合同文本,通过多个LLM并行处理,每个LLM只负责指定的几项审核任务。这样做的好处:

  • 任务解耦,降低单次处理的复杂度
  • 每个模型专注抽取特定内容,精度更高

审核过程中检测到不合规条款后,输出到融合节点LLM进行语义增强,使其成为可被知识库检索的内容。

3.2 召回策略的两难困境

在知识库检索环节,合同文本的语义整体偏"平仄",我们采取了广检索 + 精召回的策略。但问题随之而来:

策略问题
精确召回要么不输出,要么输出的内容幻觉极重
粗略召回内容不够精准,噪音太多

最让人崩溃的是:大模型输出的内容看似有理有据、引经据典,但拿到客户那边,法务专家一眼就看出全是胡编乱造。初次看到模型输出时的快感不亚于跑完5公里,而被客户当面拆穿时的落差,比跑20公里还难受。

3.3 终极解决方案:ES双验证机制

痛定思痛,我们回归"老路子"——引入Elasticsearch进行二次验证

大模型输出 → 提取关键字 → ES检索知识库 ↓ ┌──────┴──────┐ ↓ ↓ 检索到内容 检索不到 ↓ ↓ 直接输出 标记"需人工验证" ↓ ┌───────┴───────┐ ↓ ↓ 验证正确 验证错误 ↓ ↓ 标记为"已采纳" 标记为"已废弃" 后续可直接引用 后续ES检索直接过滤

这套机制上线后:

  • 精准度大幅提升,幻觉输出显著减少
  • 人工介入可控,只处理知识库未覆盖的边界案例
  • 持续迭代,已验证正确的内容会沉淀为知识库资产

四、总结:技术服务于效率,而非替代工作者

回顾整个历程,有几点核心体会:

  1. 格式处理是地基:无论是法律文件还是合同模板,投入精力做好结构化清洗,比换模型、调参数回报更高。

  2. 多阶段验证胜过单次大模型推理:用ES做"事实核查"层,是抑制幻觉最务实的手段。

  3. 人工介入不是失败,而是闭环:标记不确定项、人工验证、沉淀知识库——这是一条可持续优化的路径。

  4. 技术跟随业务变化:不管是RAG增强还是模型微调,终极目标都是提升工作者的效率,而不是解决工作者本身

AI辅助的终点,不是替代专业判断,而是让专业人士能更专注于真正需要人类智慧的那部分工作。


作者简介:多年企业服务SaaS后台开发经验,现专注于AI辅助合同智能审核领域。欢迎同行交流讨论。


本文首发于CSDN,转载请注明出处。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询