首页 / 资讯中心 / 文章详情

企业知识库的文档解析与切分,为什么直接决定召回效果?

企业知识库的文档解析与切分,为什么直接决定召回效果? ★ FEATURED ARTICLE
一、一份一百二十八页的材料问不出正确答案上个月一个做建材的客户把他的企业资料打包发过来三百一十二个文件格式很杂一百六十多个 Word八十多个 PDF剩下的散着 Markdown 和纯文本。其中最长的一份是产品手册一百二十八页翻页导出之后还带着页眉页脚和水印。客户提了三个问题其中一个是「三号线用的密封胶型号和固化温度分别是多少」这条信息确实写在手册第十二页的表格里可我们的问答接口给出的答案牛头不对马嘴把一段讲包装运输的文字抄了回去。我们把整条链路拆开看了一遍从文件上传到答案输出一共经过解析、切分、入库、召回、拼装五个环节。模型用的是同一个参数也没动过检索日志里那一句的问法甚至已经改成了和原文几乎一致的措辞可命中的块里就是没有那行表格。问题最后落在切分上那一页的表格在入库时被切成了三块型号留在第二块固化温度掉进了第三块模型拿到的上下文里两个信息再也没碰过面。这类问题的规模不小。我们扫了一遍这三百多个文件抽取出来的纯文本合计两千一百多万个汉字最长的单篇十一万字最短的两百来字。按最早那版切法总共切出了四万七千多个块平均每块四百五十字可其中有一万一千多个块的正文是从一句话中间断开的。召回接口一次只能塞进去六块这意味着大量有价值的块根本没有机会被送进提示词链路后面的功夫全都白做。二、问题出在切分而不是模型很多人遇到答不准第一反应是换模型或者调提示词。我们也试过把提示词从八百字加到一千八百字把温度从零点三降到零点一答案的措辞变得规整了一些但那句密封胶的固化温度依然答不出来。原因很简单提示词里从来没有出现过那个数字模型没有能力凭空把它编出来除非它愿意瞎猜而这比答错更危险。真正的根因是切分破坏了语义单元。企业在写文档时一个完整的说明往往跨着标题、正文和表格三样东西标题给出主题正文给出条件表格给出数据三者落在同一页上才成立。按固定字数硬切切点落在哪里完全由字符数决定它不认识标题也不认识表格于是把一页完整的说明打散成互不相干的碎片块与块之间那点上下文联系在入库时就丢掉了。另一个被忽略的细节是块的大小分布。太小的块信息量不足召回时容易命中一堆只能说半句话的碎片太大的块又会让提示词被单个来源占满其他来源进不来。我们统计过那四万七千个块短的只有十二个字长的到三千四百字中位数在四百字左右但这种分布是硬切出来的长的那些并不是因为内容真的那么多只是因为恰好没碰到句子边界。三、三种切分策略的取舍第一种是最省事的做法整篇文档直接塞进提示词不做切分。这条路实现成本几乎为零只要文档不超过模型上下文就行。代价也很明显一份一百二十八页的材料折合二十多万个汉字远超任何常见模型能吃下的窗口就算截断到前面几万字命中的信息也大概率落在被截掉的部分里等于把检索交给运气。第二种是按固定长度硬切外加一点重叠。常见参数是每块五百字前后重叠五十字用滑动窗口顺着往下走。这条路稳定、可预测重叠也确实能缓解切断语义的问题代价是重叠会把存储放大将近一成而且切点依然不认识标题一页里的表格和它的说明文字仍然可能被劈开需要靠重叠的五十个字去赌那点运气赌不中的时候就回到原点。第三种是按文档自身的结构来切。先解析出标题层级把正文挂到最近的上级标题下再在段落边界处按目标字数收口块与块之间天然带着标题路径作为上下文。这条路要多写不少解析代码还得处理格式不统一带来的各种边角情况但切出来的块语义完整度明显更高同一个来源的相邻块共享同一条标题路径模型看到的不是碎片而是带位置的一段说明。四、先统一成中间结构再切块我们把解析拆成了两步第一步只做一件事把 Word、PDF、Markdown、纯文本四种输入抽成同一种中间结构每条记录带三个字段层级 level、文本 text 和页码 page。docx 走 python-docx按段落的样式名判断层级Heading 1 记一级Heading 2 记二级正文段落记零级Markdown 直接数行首井号的个数纯文本没有显式层级就用空行和缩进去推断推断不出来的一律按零级处理。PDF 是最麻烦的一种。pdfplumber 能按字符坐标抽文本也能把表格还原成二维数组我们先用它把页面里的文字和表格分别取出来再按纵坐标给所有元素排一次序让阅读顺序尽量贴近人眼看的样子。扫描出来的 PDF 没有文本层抽取结果是一串空字符串这一种我们单独标记出来不参与切分。这套解析与切分是 AI智能媒体助理里让知识库真正起作用的那一步切错了后面全白搭我们把切分参数反复调了三轮才定下来。目标块大小四百个汉字允许上下浮动一百二十字切点一律找最近的段落边界遇到单个段落超过六百字的情况才强制在句子之间切开。同一份文档里块的顺序严格按原文顺序排编号从零开始连续递增。五、块表的字段与注入上限块表叫 kb_chunk字段一共八个chunk_id、doc_id、seq、level、title_path、content、char_len 和 page_no。title_path 存的是从一级标题拼到当前层级的路径用斜杠分隔比如「产品手册/技术参数/密封材料」它同时充当召回结果的展示标题和注入提示词时的上下文前缀一个字段两用省掉了一次额外的关联查询。注入策略也做了限制。一次问答最多注入六块按相似度从高到低取六块的正文加前缀合计不超过两千四百个汉字超出的部分按相似度从尾部裁掉。这个上限是压着模型上下文的两成定的剩下八成留给系统提示、对话历史和用户问题避免因为检索结果太长把模型的注意力挤没了。入库时还对块做了一次去重。同一份文档里正文完全相同的块只保留 seq 值靠前的那个标题路径相同的相邻小块在总长度没超过六百字的前提下合并成一块。这一步做完四万七千多个块收敛到三万九千多去掉的多数是页眉页脚和重复的表头它们本身没有信息量却会在召回时占据本该属于正文的名额。六、踩过的三个坑第一个坑是 PDF 里的表格被抽成一行乱码。现象是某份设备清单里的参数表在抽取结果里变成了一长串没有分隔的汉字和数字读起来像「型号A-120.5MPa-30℃」切块时被当成正文切成了两半。根因是表格的单元格在 PDF 里本来就是独立定位的文字对象按纵坐标排序之后它们被顺序串了起来行列关系全丢了。改法是先用坐标还原成二维数组列数不超过六列的表格转成「字段名 冒号 字段值」的多行文本超过六列的按行展开写成条目让每一行自身都是可读的。第二个坑是扫描件根本没有文本层。现象是客户上传的一批盖章文件问答时怎么问都答不出来检索日志里连一个候选块都没有。根因是这类 PDF 是图片扫描的抽取出来只有空白字符解析这一步没报错块表里也没有它的数据链路一路安静地走完了。改法是在解析结束时做一次字符数校验单页抽取字符数低于五十且整篇低于两百就把这份文档标记为疑似扫描件在前端给出待识别的提示同时不阻塞其他文档入库。第三个坑是超大文档切出上千块带来召回噪声。现象是那份一百二十八页的手册切出了一千零四十个块问任何一个参数问题返回的六块里有三块来自这份手册而且经常命中目录页和修订记录。根因是块越多相似度分数挤在同一区间的概率越高目录页因为它覆盖了所有章节名反而成了高分常客。改法有三个一是把目录页识别出来单独标记并在召回时降权二是同一文档在一次问答里最多贡献三块三是给命中块按标题路径做一次聚类同一路径下只留分数靠前的一块。七、这层解决不了什么切分解决不了信息本身没写清楚的问题。文档里如果只写了「按标准执行」却没写是哪个标准切得再漂亮也答不出来这种情况我们只能靠召回结果里的原文片段提醒用户去补资料而不能指望模型推导。同义词和口语化提问也不在这层用户问「封口胶」而文档里写的是「密封胶」靠字符串相似度匹配不上得在检索侧补同义词典或者用向量召回兜底。跨文档的推理同样超出这层的能力。一个问题的答案如果需要把采购合同里的报价条款和产品手册里的技术参数放在一起才能得到我们的召回能分别捞到两块但让模型把两个来源自行拼起来结果并不稳定偶尔会把两家供应商的数据混着用。这类场景我们目前的处理方式是让用户在提问时带上文档名把召回范围先收窄再让模型在有限来源里作答。解析的准确率也有代价。四种输入格式的抽取逻辑各自维护每接一种新格式就要补一套分支Word 里嵌的文本框、PDF 里的分栏排版、Markdown 里的代码围栏都会影响层级推断的结果。我们现在的做法是解析完让用户在页面上预览前二十块确认无误再入库这一步看着多余但它把返工的成本从重新切一遍整份文档降到了重新上传一次文件。八、小结回头看这件事我们改过的东西其实只有一处就是把切分从按字符数硬切换成了按文档结构切。参数层面老老实实定了三个数目标块四百字、浮动一百二十字、单次注入六块两千四百字剩下的精力都花在了四种格式的解析和三个坑的修补上。切换之后同一批问题的答准率从六成上下提到了九成出头用的还是同一个模型。AI智能媒体助理的知识库现在按这套方式切块一份几十页材料也只注入命中的那几块提示词长度从最早的一万四千字压到了两千七百字上下单次问答的响应时间跟着从十一秒降到四秒以内。这些数字里没有一项是靠换模型换来的全部来自解析和切分这两步它们藏在链路最前面出错的时候却总是最后由答案来背锅。
阅读完成 · 觉得有帮助?
咨询建站