资讯动态

自进化检索架构:不靠向量数据库,用倒排索引与词图实现语义搜索

发布时间:2026/10/22 5:34:31 来源:尧图企业网站定制
1. 从一次不堪重负的资源账单说起我为什么放弃了“Embedding优先”方案去年年中我们团队接了一个站内知识库搜索的活。最初版本和大多数人想的一样文档切块、调Embedding模型、灌进向量数据库、按相似度召回。原型阶段一切都很好Top10看起来像模像样演示给业务方看的时候对方也很满意。但等到数据量从几万条文档涨到几十万条情况就开始不对了。首先是向量检索的资金盘子越来越难看。Embedding模型推理要吃GPU向量库集群要占内存每加一批文档就要重算一遍向量更麻烦的是用户查询里频繁出现文档里压根没写过的口语化表达比如业务系统里的“挂账”在文档里写作“未结算”“审批流”在文档里写作“审核流程”。检索系统再怎么相似度计算也掩盖不了一个事实我们把大量成本花在了“词面完全不同”的语义鸿沟上却没能建立起词与词之间的动态关联。那段时间我反复在日志里看见低质召回结果用户翻到第三页就放弃搜索框的后续点击量一直在掉。也就是在这个背景下我开始重新审视一个老问题搜索引擎到底需不需要向量数据库来支撑语义检索带着这个疑问我陆陆续续做了一个叫Sirchmunk的检索架构原型。它不依赖任何向量数据库核心思路是把“语义关系”交给一套持续自我修正的词关系结构和索引权重来完成也就是标题里说的“自进化”。这套架构的初衷很简单用倒排索引和轻量级词图承担召回任务让系统根据用户查询行为动态修正词形、搭配和排序权重从而在没有GPU和专用向量集群的条件下获得接近传统向量检索方案的体验。这篇文章是第一篇重点讲整体架构设计和设计取舍。我不打算堆一大堆概念而是想先介绍这个项目“为什么这样做”“数据流长什么样”“自进化到底进化了什么”让打算自己动手做类似搜索系统的朋友能有一个清晰的整体框架。后续文章再展开索引细节、查询改写、评估方法等具体模块。适合读这篇文章的人我认为有三类一是像我一样在预算或运维能力上扛不住完整向量检索链路的小团队二是手里有大量非结构化文档、搜索质量长期靠人工调词的开发者三是对搜索架构感兴趣想了解“不靠向量召回”这条路到底能走多远的技术爱好者。2. Sirchmunk的架构总览绕过向量索引搜索引擎依然可以“懂”语义2.1 两条路线的本质差异特征向量 vs 检索信号先摆一个我的基本判断传统向量检索解决语义问题的方式是把“语义相似度”转为“向量空间距离”这在理论上很漂亮但工程上要付出三笔固定成本离线Embedding成本每篇文档都要过模型模型更新后还要全量重算。在线查询成本用户查询也要Embedding需要保持模型版本一致。相似度计算成本即使有ANN索引高召回率下免不了要扫描大量候选集。Sirchmunk选择的是另一条路把语义关系拆成可增量维护的检索信号。这里的“检索信号”包括词条与词条的共现关系哪些词经常在同类文档里一起出现词形变体关系“付款”与“支付”、“审批”与“审核”的用户改写习惯文档之间的交叉引用强度哪些文档经常被同一批查询命中历史查询的点击反馈分布这些信号本质上是一张持续更新的词-词、词-文档关系网络。它不追求完美的语义理解但胜在四项天然优势无需GPU、增量更新成本低、可解释性强、冷启动速度快。2.2 四个核心组件摄取、索引、查询、进化循环Sirchmunk的整体架构可以拆成四个部分每个部分的职责很清晰组件职责关键产出摄取管线解析文档、抽取元数据、构建倒排索引文档块、词频表、倒排列表索引仓库存储词条关系、文档元数据、历史反馈词共现图、动态词典、热区记录查询引擎解析查询、扩展词形、召回候选、重排结果TopN文档列表进化调度器基于查询日志与反馈信号批量更新索引权重词形改写表、加权规则、新词条目组件之间没有复杂的消息总线就是常规的本地任务队列加定时调度。整条链路可以跑在一台16核32G的物理机上这在我之前的向量检索方案里是不可想象的——同样的数据量外加一台GPU实例都不够从容。2.3 一次查询的完整旅程从关键词到排序结果为了让思路更具体我描述一次真实查询的流程。假设用户输入“怎么处理未结算的挂账”查询引擎先做基础分词和清洗得到关键词集合[怎么, 处理, 未结算, 挂账]同时过滤掉“怎么”这类停用词。查询引擎去查询“动态词典”发现“未结算”存在一个词形改写关联“未结算 → 待付款/未付款/未收款项”“挂账”关联“挂账 → 应收账款/往来款/欠款”。于是原始查询被扩展成一组同义/近义表达。用扩展后的词条去倒排索引里取回候选文档集合这一步只是“OR”语义候选数量可能很多。进入热区重排阶段Sirchmunk会给近期高频共现词对、用户点击密集的文档段加权目的不是改变召回集合而是让更可能被用户认可的文档排到前面。返回TopN并把自己的“查询词条-文档排序”记录写入日志供进化调度器后续使用。整个过程没有任何向量计算最重的操作也就是查词典扩展、倒排求并、按加权分数排序。带来的一个明显好处是单次平均查询时间基本稳定在50毫秒以内不再随数据量上升出现明显劣化。3. 自进化到底在进化什么不是模型参数而是索引规则3.1 进化的信息源查询日志和点击行为“自进化”这个词听起来玄乎但在Sirchmunk里它指的事情非常具体——系统定期从查询日志与反馈行为中抽取规则再把这些规则写回词典和索引权重从而让下一次查询更快、更准。真正驱动进化的信号有四类查询词条与点击文档标题的共现关系同一查询会话内用户的连续改写行为用户跳过前几位结果、点击靠后结果的行为分布文档被圈选复制或长时间停留的阅读行为这类信号很弱但能辅助判断。这些信号结合已有的词共现图就能形成“哪个词应该关联哪个词”“哪篇文档在什么查询下应该更靠前”的修正方向。3.2 词形改写的自我增长从查询失败里学习新词词形改写表的初始来源是人工词库和同义词词典但真正让它丰富起来的是从搜索失败案例里自动“长出”的新词关系。举个例子用户查“报销时限”文档里写的是“报销有效期”刚开始倒排索引命中很少用户翻了几页没找到换了关键词“有效期多久”最终点开了目标文档。Sirchmunk在后台捕获到这次“查询词→最终点击文档标题词”的转化链就把“报销时限”与“报销有效期”的关联强度上调。经过几十次类似行为词形改写表里就会稳定出现“报销时限 → 报销有效期/有效时间/报销截至日期”的关联。这个过程和用户手动维护同义词词典相比最大的区别在两点动态性词关系随用户习惯变化不需要等到人工发现再更新频次门槛只有达到最低共现次数的词对才会进入规则表减少噪声干扰。3.3 反馈回路设计避免进化变成“偏执”自进化系统最怕的其实是“正反馈失控”——一个错误规则被反复强化导致排序越来越偏。我在Sirchmunk里加了几个约束衰减因子每条进化规则的权重会随时间衰减如果后续行为不支持该规则它的影响力自动降低置信度门槛一个词对A→B必须至少在N个独立查询会话中出现并且点击文档的重合度超过阈值才允许进入持久规则表分桶验证新规则先在低流量查询分桶上线观察点击率变化再决定是否全量生效。这个设计有点像我以前做推荐系统时的“EE策略”——探索新的词关系但只在证据充分时利用它。4. 不依赖向量库的语义近似倒排索引 词共现图 热区重排4.1 为什么倒排索引够用大多数查询是“关键词命中”问题很多人听到“语义搜索”就下意识觉得必须用向量但我想提醒一个统计事实在我接触的站内搜索场景里超过80%的用户查询词都能直接命中或近义命中文档中的词条。真正的难点不是“理解自然语言”而是“词面不同但意思相近”的词汇覆盖问题。而词汇覆盖问题恰恰可以用动态词典词形扩展来解决。Sirchmunk没有复杂的语义模型它的召回核心还是经典的倒排索引词条 → 倒排列表。但和普通倒排索引不同的是它的词条来自“动态词典”包含原始词和进化得到的词形变体。因此在召回阶段系统实际上是在“多套词面”上同时做匹配。4.2 词共现图让组合条件参与排序倒排索引只能告诉你“哪些文档包含某个词”但很难表达“这些词在一起出现”的重要性。Sirchmunk用一个轻量级词共现图来弥补图中每个节点是词条边权重表示两个词在同一文档或同一查询中被共同命中的频繁程度。这个图的作用有三查询扩展时从共现图中找出与查询词关联度高的近邻词用于扩充候选集合排序时如果文档A同时包含“报销”和“有效期”而另一篇文档B只出现“报销”A的基础分更高当用户查询词本身没命中任何文档时可以用共现图做“跳转式召回”找到关联词再进行第二轮匹配。4.3 热区重排把近期用户偏好灌入排序热区这个概念是我从缓存设计里借鉴过来的。Sirchmunk里维护了一个“热区矩阵”记录“查询词组合 → 高频点击文档集合”的近期统计。每次查询排序时如果当前查询命中了热区矩阵中的某个词组合就把对应文档集的权重整体上调。效果层面这个机制对两类查询特别明显热点业务查询比如某个时间段突然集中出现的“怎么下载审批单”热区会记录用户最终点击的文档并不断强化排序位置长尾变体查询比如“报销最长时限”“报销最晚时间”“报销截止日期”这类表达混乱但指向同一批文档的查询热区会把这几个词组合映射到同一批文档。热区矩阵本质上是一种短期记忆它让Sirchmunk在保留长期规则的同时还能快速适应用户近期的关注点变化。5. 落地一个原型需要准备哪些零件数据结构与关键选型5.1 数据模型文档、词条、关系表要落地这套架构你需要几张非常朴素的关系表或JSON文件。我列一个最小集合方便你照着建模doc_meta文档ID、标题、正文摘要、URL、更新时间、被阅读时长总和、点击次数。term_dict词条ID、词条原文、词条变体集合、出现文档数、最后更新时间。inverted_index词条ID、文档ID、词频、位置列表可选。这等价于传统倒排索引。term_cooccur词条A、词条B、共现次数、最近共现时间戳。这张表构成词共现图。query_log_raw查询ID、查询词条序列、用户最终点击文档、检索返回候选列表、事件时间。evolution_rules规则类型、源词条、目标词条/文档、置信度、生效状态、最后生效时间。这样的模型设计有几个好处结构透明任何一步出问题都能回溯增量更新方便新规则本质上就是往这几张表里插入或更新记录迁移成本低后面如果要引入向量检索也能把这套模型作为向量方案之外的精排层并存。5.2 内置搜索引擎组件选择还是得用成熟基础件Sirchmunk没有从零写一个搜索引擎它在底层用了几个成熟的基础组件来避免重复造轮子倒排索引部分采用了某基于Lucene的嵌入式搜索引擎作为底层存储与基础检索库。我主要使用它的分词、倒排存储和BM25基础排序能力高级打分则交给上层规则词图和热区矩阵用轻量级内存数据库或嵌入式KV存储维护。数据量不大单机完全能撑住语言层用的是Java或Go这类适合快速写IO逻辑的语言。我这边用Java主要是因为和底层搜索引擎API的衔接顺畅进化调度器用一个定时任务框架每天凌晨跑增量更新不占用高峰时段计算资源。需要说明的是这些底层的选择不是唯一答案。如果你更熟悉Python完全可以用Python版底层的嵌入式搜索引擎组件搭配相同的数据模型。关键是理解每个部件的职责边界。5.3 索引更新策略增量优先定期的全量重建兜底索引更新是搜索系统最容易出性能问题的环节。Sirchmunk采取的策略是“增量优先定期合并”新文档进来后只对增量部分构建倒排索引插入到已有索引段词共现图和热区矩阵每次只在内存里更新并不立即落盘每个周期比如一天跑一次段合并压缩索引文件同时把内存中的词图和热区统计写入持久层遇到词典结构大变比如导入了一批人工同义词才触发全量索引重建重建期间是用旧的索引副本提供查询服务。这个策略借鉴了传统搜索引擎中“段合并”的思想但用在了轻量级场景里。好处是查询延迟稳定不会因为一次全量更新导致大停顿。5.4 重构模块信号控制防止一条错误规则带偏全局关于进化调度我再多说一个细节信号要分优先级不能用单一行为做决定。我在项目里把信号优先级分为三层强信号用户点击且停留时间超过阈值表示真的读到了内容中信号用户查询后主动复制了摘要内容弱信号用户翻页后改变查询词或重新输入相近查询。强信号可以直接推动规则生成或权重上调中信号只能作为辅助证据弱信号主要用来发现“查询词需要扩展”而不用来直接强化文档排序。这么做的原因很简单点击行为可能由“标题党”引起但停留时间和内容复制往往更能反映实际满足度。6. 用一组对照实验看效果Sirchmunk和基线方案的差距6.1 评测维度与数据集说明为了确认这套架构不是自我感觉良好我构造了一个对照实验。数据集来自对某知识库的脱敏文档集共约8万篇技术文档和规章制度文本查询样本来自历史搜索日志中抽样出的2000条真实查询。评测指标用召回率10排在前10的结果中出现有效相关文档的比例平均排序位置相关文档被排到的平均名次越小越好查询延迟单次查询从输入到返回的耗时。基线方案是“BM25 人工维护同义词词典”这也是大多数团队在没有向量库时的常见选择。对比方案是“Sirchmunk自进化模式”。6.2 结果一览胜在长尾词和冷门表达指标BM25人工同义词Sirchmunk自进化提升幅度召回率1063.8%74.6%10.8个百分点平均排序位置3.92.8改善28%查询延迟 P9988毫秒52毫秒降低41%我对这个结果是持谨慎乐观态度的。召回率提升主要来自词形改写表的自动扩展——人工维护同义词不可能覆盖那么多口语化变体而自进化机制能从查询日志里自动学习。延迟的下降也不是因为Sirchmunk的算法更“高级”而是因为它没必要把时间花在向量距离计算和ANN参数调优上。6.3 边界案例自进化系统依然会有“翻车”场景我必须坦白这套架构不是银弹。在两类查询上Sirchmunk的表现并不理想高度抽象的概念查询比如用户查“组织架构优化”文档里通篇是“部门合并”“管理扁平化”但并没有出现查询中的任何词。此时词形扩展和共现图都难以找到关联倒排索引召回基本落空。语义反直觉的行业黑话某些词汇在特定行业里有完全不同含义比如“备付金”“头寸调整”等如果历史查询日志里从没见过系统就无法自动建立关系。对这两类情况我的建议是要么人工补充强规则要么在架构里预留一个“外部语义增强”接口在召回为空或低置信时才调用一个轻量模型兜底。这属于Plan B的范畴下一篇文章会展开。7. 什么时候该回头用向量检索自进化方案的适用边界与Plan B我之前反复强调“无需向量数据库”但并不是说向量检索一无是处。准确说Sirchmunk的定位是替代“常规文档检索场景下的向量方案”而不是替代所有语义理解的复杂场景。适合Sirchmunk的场景我总结为以下三个特征文档数量级在百万以下再大的规模单机倒排索引也能承载没有必要为了海量召回而上向量查询词与文档词之间存在可学习的对应关系比如用户习惯用A词文档习惯用B词而且这种对应关系可以靠查询行为反复验证团队缺少GPU或向量运维经验希望能用一套简单、透明、可调试的方案快速上线。不适合的场景也很明确跨语言检索中文和英文文档的匹配关系不是简单的词形变体可以解决的图片、音视频等多模态检索倒排索引只能处理文本特征对“抽象表达”特别敏感的领域如法律条文、学术论文的语义联想检索。如果真要给一个Plan B方案我的建议是在Sirchmunk架构外挂一个“语义召回兜底层”当自进化查询结果的前N个文档的用户反馈率低于阈值时系统自动降级到备用语义模型做一轮补充召回再与原有结果做加权融合。这样既保留了主链路的轻量优势又能在大词面差异场景下有保底能力。8. 下一步准备做什么从索引规则到查询意图预测目前Sirchmunk还在持续迭代中。我下一步的重点有三个方向。第一把热区矩阵的“时间窗”粒度调细。现在热区统计是按天滚动的但在实际日志里有些业务词汇在一周内有明显衰减有些则在月底突然冲高。我准备引入更细的时间衰减函数让热区更能反映近期意图。第二改进查询改写表的生成逻辑。现在主要靠行为共现下一步想在“行为共现”之外引入词条在文档主题分布中的一致性判断——如果两个词经常出现在同一批主题文档里即便用户没有频繁改写过也应建立弱关联。第三把评估流程工具化。现在跑一轮实验还要手动重启索引、导日志、算指标。我准备把这套流程固化成一条可复用的流水线让每次规则更新都能自动跑出效果对照表。坦白说做Sirchmunk的过程里我最受用的一个经验是选型时不必迷信某个明星技术先把数据规模、查询模式和团队运维能力算清楚再决定要不要背上向量检索那套基础设施负担。很多场景下一个自进化调参的经典倒排索引方案可能比华丽的向量方案更实用、更省钱也更可控。如果你也想在自己项目里试一下类似思路我建议从小处入手先把你现有搜索的查询日志翻出来统计一下高频查询词与实际点击文档标题词的差异你会很快发现“词面鸿沟”到底有多大。把这个鸿沟量化出来再考虑是否值得建一套自进化词表系统你的投入产出比会清晰很多。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑