长文本审核踩坑实录:从OCR乱码到RAG精召回的实战经验
长文本审核踩坑实录从OCR乱码到RAG精召回的实战经验当法务专家一眼看穿大模型的一本正经胡说八道那种落差比跑20公里还难受写在前面在合同智能审核这条路上我踩过的坑连起来能绕办公室三圈。文档结构复杂、语义偏法律条款化、普通OCR识别后空位错乱、格式崩塌——这些问题堆在一起再强大的大模型也难免产生幻觉。经过长期重复造轮子的折腾我想把这套血泪经验分享给正在做同类项目的同行希望能帮大家少走几段弯路。一、知识库体系与大模型选型格式清理比模型选择更重要1.1 技术栈选型我们的知识库基座采用以下组合向量库pgvector重排模型rerank 系列文本向量化模型BGE-M31.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){constidString(input.contractId||input.documentId||).trim()constfileNameString(input.fileName||).trim()constfileUrlString(input.fileUrl||).trim()return[id,fileName,fileUrl].filter(Boolean).join(__)||contract-${Date.now()}}exportasyncfunctionsaveContractHtmlRecord(record){constkeyrecord.contractIdconstrecordsawaitreadContractHtmlLibrary()constnormalizedTextTypenormalizeTextType(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{PdfOcrResultresultnewPdfOcrResult();Stringcontent;if(fileName.toLowerCase(Locale.ROOT).endsWith(.docx)){try(InputStreaminputStreamnewURL(fileUrl).openStream();XWPFDocumentdocumentnewXWPFDocument(inputStream);XWPFWordExtractorextractornewXWPFWordExtractor(document)){contentextractor.getText();}}else{try(InputStreaminputStreamnewURL(fileUrl).openStream();HWPFDocumentdocumentnewHWPFDocument(inputStream);WordExtractorextractornewWordExtractor(document)){contentextractor.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检索直接过滤这套机制上线后精准度大幅提升幻觉输出显著减少人工介入可控只处理知识库未覆盖的边界案例持续迭代已验证正确的内容会沉淀为知识库资产四、总结技术服务于效率而非替代工作者回顾整个历程有几点核心体会格式处理是地基无论是法律文件还是合同模板投入精力做好结构化清洗比换模型、调参数回报更高。多阶段验证胜过单次大模型推理用ES做事实核查层是抑制幻觉最务实的手段。人工介入不是失败而是闭环标记不确定项、人工验证、沉淀知识库——这是一条可持续优化的路径。技术跟随业务变化不管是RAG增强还是模型微调终极目标都是提升工作者的效率而不是解决工作者本身。AI辅助的终点不是替代专业判断而是让专业人士能更专注于真正需要人类智慧的那部分工作。作者简介多年企业服务SaaS后台开发经验现专注于AI辅助合同智能审核领域。欢迎同行交流讨论。本文首发于CSDN转载请注明出处。

相关新闻