花拾录
← 返回知识库

全文检索搜不到文档的前半段:为特定格式写的启发式如何误伤正规文档

软件工程 / 工具导入2026/09/220 阅读0 评论

你把一份带子标题的结构化文档归档进知识库,之后想搜其中一个关键词,怎么都搜不到。翻回原文一看——那段话明明白白在文档的开头部分。

现象

一份分节的结构化文档归档后,全文检索搜不到它前面的章节。实测下来,大约丢掉了整篇的前 28%:凡是靠前半部分的词,全都检索不到;后半部分则正常。这也让问题更迷惑——文档没报错、入库"成功"了,只有搜不到时才暴露。

更反直觉的是它的"选择性":搜后半段的词一切正常,搜前半段的词石沉大海。如果不是恰好要搜的词在前半段,这个 bug 可能永远发现不了——这正是静默数据丢失最麻烦的地方。

根因:一段为"某类摘要格式"写的启发式

检索脚本里有一段启发式剥离逻辑。它当初是为了处理某一类会话摘要文件而写的:那种文件的格式是"前面一段进程输出,最后一段才是正文",所以脚本无条件按某个分隔符截断,只保留最后一段。

问题在于,"无条件"三个字。当输入是正规文档、并不匹配那种摘要标记时,这段逻辑依然照砍不误:找不到分隔符就砍掉前半段,只留后半段。于是文档越结构化、分节越规整,反而越容易被误伤。

一句话概括:为特定格式写的启发式,遇到别的格式就是 bug。

这里还藏着一层更普遍的教训:这段逻辑当年在"目标格式"上多半是验证通过的——毕竟它就是为那种格式写的。问题恰恰出在它从没被拿去测过"别的格式"。一个只在特定输入上验证过的处理,本质上就是一个未经验证的猜想。

解决:让剥离逻辑"条件化"

  1. 加前置判断——只有确实匹配到那类摘要标记时才剥离,否则全文检索:

    text = raw
    marker = "=== 摘要 ==="
    if marker in raw:            # 只有真的匹配到才剥离
        text = raw.split(marker, 1)[1]
    else:
        text = raw               # 否则原样全文入库
    

    核心改动是把"无条件截断"变成"条件成立才截断",让默认行为退回到最安全的"全量保留"。

  2. 回归验证全部存档。 对归档目录做一次全量比对:原文的每段都应该能被检索到,确保零丢失。不能只测"新格式能搜到",还要测"老格式不受影响"。

  3. 专项排查技巧: 某份文档搜不到时,全文一次、截断一次各测一遍。如果全文能搜到、截断后搜不到,就能直接定位到这段截断逻辑,不必大海捞针。

延伸与预防

这个坑有两层教训。

第一层是技术性的:任何"启发式"处理(按分隔符切、按正则抽、按行号截)都应该默认保留全部内容,只在特征明确匹配时才做删减。删减是危险动作,要让它需要"证据"才能发生。

第二层是流程性的:这类 bug 属于"静默数据丢失",不会报错,只有在检索落空时才暴露。所以对归档 / 入库这类"只写不读"的管道,必须有独立的一步:从库里面往回搜,验证写进去的东西真的能读出来。只验证"写成功"是不够的——"写成功"和"写对了"是两回事。

再补一个通用的判断题:任何"按特征截断 / 抽取"的代码,都该问一句"特征不匹配时它会做什么"。如果答案是"照样截、只是截出来是错的",那就是隐患;正确的不匹配行为应该是"什么也不做,原样返回"。

一句话教训

为特定格式写的启发式,遇到别的格式就是 bug。

评论(0)

  • 还没有评论,来抢沙发~

相关文章