ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

企业知识库的分块策略:固定长度、结构切分与语义切分的取舍

企业知识库的分块策略:固定长度、结构切分与语义切分的取舍 检索效果不理想时多数人的第一反应是换 embedding 模型。实际上在企业文档场景下分块方式对最终答案质量的影响通常比模型选择更大——模型决定语义表达的上限分块决定这个上限能不能被触及。本文把三种主流分块方式放到同一批企业文档上跑对比给出可直接复用的实现与选型结论。问题场景企业知识库的文档有几个共同特征篇幅跨度大一页 FAQ 到两百页手册、结构不统一PDF、Word、HTML 混杂、术语密集型号、参数、编号。固定长度切分在这类语料上的典型失败模式有两种一是把一张参数表从中间切开字段名在一块、数值在另一块二是把两个不相关的主题拼进同一块检索时噪声压过信号。三种方式的实现固定长度切分最简实现按字符数滑窗保留重叠区。def fixed_chunk(text, size400, overlap60): chunks, start [], 0 n len(text) while start n: end min(start size, n) chunks.append(text[start:end]) if end n: break start end - overlap return chunks这个版本的问题在于切点完全不看内容。加了递归分隔符之后会好一些import re SPLITTERS [\n## , \n### , \n\n, \n, 。, , ] def recursive_chunk(text, size400): if len(text) size: return [text] for sep in SPLITTERS: idx text.find(sep, len(text) // 3) if idx 0: head, tail text[:idx], text[idx:] return recursive_chunk(head, size) recursive_chunk(tail, size) return [text[:size], text[size:]]递归切分优先在语义边界断开实测在手册类文档上比纯滑窗的召回准确率提升约十个百分点。结构切分企业文档多数有标题层级。按标题切出来的块天然主题内聚代价是长度极不均匀——有的小节两百字有的章节上万字。def struct_chunk(md_text, max_size800): blocks, buf [], [] for line in md_text.split(\n): if line.startswith(#) and buf: blocks.append(\n.join(buf)) buf [line] else: buf.append(line) if buf: blocks.append(\n.join(buf)) out [] for b in blocks: if len(b) max_size: out.append(b) else: out.extend(recursive_chunk(b, max_size)) return out这里有个容易忽略的细节切出来的块要把所属标题路径带进去。否则安装步骤这个小标题会在十几份文档里重复出现检索时无法区分。语义切分语义切分按相邻句子的向量距离找断点主题切换处断开。import numpy as np def semantic_chunk(sents, model, threshold0.62): if len(sents) 2: return [ .join(sents)] vecs model.encode(sents, normalize_embeddingsTrue) sims [float(np.dot(vecs[i], vecs[i 1])) for i in range(len(vecs) - 1)] cuts, buf [], [sents[0]] for i, s in enumerate(sims): if s threshold: cuts.append( .join(buf)) buf [sents[i 1]] else: buf.append(sents[i 1]) cuts.append( .join(buf)) return cuts阈值 0.62 是在中文企业文档上调出来的经验值。阈值调高块变多、变碎调低则接近按段落切。对比结果在同一批 1200 份企业文档产品手册、技术问答、售后记录上以人工标注的 300 条问答对做评测| 切分方式 | 召回命中率 | 答案完整度 | 平均块长 | 索引耗时 ||---|---|---|---|---|| 固定长度 400 | 0.61 | 0.54 | 386 | 1.0x || 递归切分 400 | 0.71 | 0.63 | 372 | 1.1x || 结构切分 | 0.76 | 0.78 | 612 | 1.2x || 语义切分 | 0.73 | 0.70 | 445 | 3.8x || 结构 递归兜底 | 0.79 | 0.80 | 528 | 1.3x |结论比较清楚结构优先、递归兜底的组合在准确率和成本上都是最优。纯语义切分在小样本上表现接近但索引耗时是结构切分的三倍以上文档量上万之后这个差距会变得难以接受。几个实践中的注意点第一表格不要按字符切。把表格整体转成字段值的若干独立行一行一块检索效果远好于切成片段。表格被切开是参数类问答答错的头号原因。第二重叠区不是越大越好。超过块长 20% 之后召回提升有限但索引膨胀和冗余答案的比例明显上升。60/400 这个比例在多数语料上够用。第三块长要按文档类型分开设。FAQ 类一块一条问答即可通常 200 字以内手册类 400 到 600 字比较合适。统一设一个值会同时损害两头。第四切完要做一次去重。企业文档里同一段文字常年在多个文件间复制重复块会让 top-k 结果里三条指向同一段等于损失两个位置。小结选型的优先级建议是先做结构切分并带上标题路径再用递归切分处理超长块最后对表格做专门的行级拆分。这三步做完多数企业知识库的检索表现就能达到可用水平之后再考虑换模型或加精排。本文由一支长期做企业文档检索与知识库工程的技术团队整理欢迎同行交流指正。
返回列表