搜索系统这两年似乎走到了一个分岔路口:一边是传统的 BM25 关键词检索,胜在稳定、快、省资源,但对自然语言的语义理解几乎为零;另一边是 RAG 热潮带起来的“向量数据库 + Embedding + 相似度召回”方案,效果好,可是重资产、重运维,尤其当你只是想给个人知识库、内部文档库做一个靠谱的搜索入口时,引 GPU、接向量库、调分块策略、维护 embedding 管道……这一套组合拳下来,比搜索本身还要复杂。
我一直在找一条轻量一点的路,直到看到了 Sirchmunk 这个项目,它的设计思路确实不一样。更为关键的一点是做出来的东西还挺好用。
整篇解析我会拆成一个系列来讲。作为第一篇,这篇的重点放在架构设计的全局拆解上,把它的核心思想、模块划分、检索流程和自进化机制整体掰开揉碎。后面几篇再针对具体的检索策略、更新机制、工程实现做深入分析。
1. 为什么做出这个项目:被两个痛点逼出来的想法
1.1 传统“向量数据库 + 召回”方案的负重感
先说清楚针对什么场景。如果你负责的是一个百万级以上的公开网页搜索或者企业级全量文档检索,向量方案无疑是主流方向,这个没有什么争议。但如果你只是给一个几万条笔记、几十份技术文档、或者某个垂直领域的资料库做语义搜索,向量方案就有点杀鸡用牛刀的感觉了——尤其是跑在普通 CPU 服务器或者个人笔记本上。
我自己在尝试搭建个人知识库搜索时,就走过这条老路。首先是 embedding 的生成和更新,每新增一批文档,要么重新全量向量化一次,要么维护一套增量管道,不然新旧向量的一致性就是问题;其次是向量数据库的存储和索引,HNSW 这类算法索引的构建在数据量小的时候倒也不慢,但引入了一个额外的重依赖;最后是召回与排序的割裂——向量召回后再接重排,这条流水线越长,调试就越麻烦,任何一环的效果波动都会传导到最终结果。
还有一个被很多人忽略的点:向量方案是“把语义理解外包给 embedding 模型”,模型本身是不透明的。哪个查询处理得不好、哪些文档之间的关联识别错了方向,你很难判断是哪层出的问题,只能一层层去翻日志。这种黑盒感在长期维护阶段会放大成一种很大的维护负担。
1.2 检索需求本身在变化:高频、轻量、个性化
与此同时,搜索的使用模式也在发生变化。个人和企业内部场景里,用户对搜索结果的期待不再是“找到相关网页”那么简单,而是要求越来越个性化:同一个查询,张三关注的维度和李四完全不一样;同一个文档,上个月对这个项目还是核心资料,今天可能已经有了新版本,旧答案就得降温。
这种时空动态性,传统的倒排索引很难表达,而向量方案天然也不擅长表达——embedding 是静态的,一个文档向量一旦生成,除非重新做 embedding,否则它不会“记住”上次用户点了什么、没点什么。真正决定搜索质量的反馈信号,其实绝大部分都在检索日志里,但这部分数据在传统方案里基本是浪费掉的,没被回收利用。
所以在设计 Sirchmunk 的时候,我从一开始就把一个原则定死了:系统必须能自己从使用过程中学习,并且把它学到的东西反馈到下一次检索里。自进化这个特性,不是后期加的功能,而是整个架构设计的第一性原则。
2. 核心思路:不在“语义”里内卷,而在“证据”里做文章
2.1 把资源从向量空间转移到文档切片
“无需向量数据库”并不是说 Sirchmunk 不处理语义,它的处理方式完全不同。传统方案是给整篇文档或者分块后的一堆文本算一个高维向量,大致记住“这篇东西讲什么”;而 Sirchmunk 用到的更底层的单元是经过精心切分的文档切片,每一个切片本身是一个自包含的语义块。
这部分跟常见的 RAG 分块可以说是殊途同归,但关键差异在下一步——Sirchmunk 不会为每个切片计算向量,而是把切片当作一个可检索、可评分、可进化的基本实体。系统对切片做的事,是逐渐积累“这个切片对哪些查询有效、哪些无效、有效性有多高”的经验数据,再用这些经验反过来调整后续检索的权重。
它的设计哲学,可以概括成一句话:与其用一个全局的向量空间来表示所有文本的语义距离,不如在局部范围内积累“查询—文档”之间的证据链,让证据变得越来越可靠。本质上,是从“几何空间”的思路转向了“经验累积”的思路。
2.2 自进化的三个层次
Sirchmunk 的自进化,严格来说有三个递进的层次。第一个层次是关键词层面,系统会记录哪些词在我们的语料里区分度高,哪些词经常出现在好结果里,哪些词只是噪音,从而动态调整每个词的检索权重。第二层是文档切片层面,每个切片都有类似“成长值”的属性,被反复验证有用的切片会获得更高的默认权重,被用户排斥的内容会衰退。第三层是查询意图层面,系统不把人每一次检索当场离散孤立事件,而是通过查询上下文和点击模式来猜测查询背后的意图,让检索结果的排序更有针对性。
能够支撑起这三个层次的基础设施不是什么复杂框架,就是一张张结构清晰的数据表,以及一套完整的日志回流机制。后面的章节我会逐一展开,先看一下整体架构是怎么组织起来的。
2.3 简化后的系统结构:从摄取到反馈的闭环
把 Sirchmunk 的架构画成一个大盘子,可以拆成四条并行的链路。内容摄取层负责处理原始文档,分块后写入基础存储;检索与排序层负责接收查询、召回候选、精排并返回结果;反馈捕获层负责把用户行为(点击、忽略、复制、评分等)记录成结构化事件;最后是进化引擎,定期将积累的反馈事件汇总回写,调整检索层的权重和元数据。
这四条链路是解耦的,可以独立扩展。最妙的地方在于,它没有引入任何架构上需要特殊运维的组件:一个文档存储数据库、一个全文索引引擎、一个任务调度器,就齐了。完全可以在 Docker 容器里跑,也可以直接在笔记本上跑,不需要 GPU,不需要专用向量计算服务。
3. Sirchmunk 的架构域划分与各模块职责
3.1 摄取管道的设计:切片、摘要与指纹
摄取管道是整个系统的数据入口,它承担的责任比传统爬虫或文档导入器要多一步:在写入前完成切片标注。Sirchmunk 对切片粒度的设定,参考的是“回答问题所依赖的最小语义单元”——如果一片文本过小,缺乏上下文,没法支撑语义判断;如果过大,则召回时会带入大量噪音。实测下来,在普通技术文档场景下,256 到 512 个 token 是一个合适的区间。
每个切片在写入时还会计算一个语义指纹,这个指纹是从切片的关键词分布和结构特征里提取的,不加向量,这样后续可以判断两个切片是否高度重合。如果一个新切片的指纹与已有的切片重合度超过阈值,系统不会简单跳过,而是会把它标记为“补充证据”,等积累到一定数量再决定是否合并或淘汰。这种设计避免了搜索系统里最常见的尴尬——同一主题的多个近似段落反复出现,把检索结果质量拖垮。
摄取管道的末尾还会生成一个轻量摘要块,用于精排阶段做语义偏好判断。需要强调一下,它不是生成式摘要,而是抽取式的,对长文本切片最有辨识度的一句话或几个关键短语做一个包络,成本很低。
3.2 检索内核的构成:召回、初排、精排三段式
检索内核负责将用户的查询字符串转化成最终结果列表。这一部分我用了一个三段式的设计:召回、初排、精排。
召回阶段,Sirchmunk 使用的是改造过的 BM25。这里的改造点有两个,一是结合了自进化权重字典中的“词级权重”——同一个词,如果它在用户的历史查询中经常与好评结果绑定出现,它的基础权重就上调;反之则下调。二是融合了切片的成长值,一个经验地位较高的切片,在得分计算时会有加成。
初排阶段做的是粗筛,它限制候选集的数量上限,防止精排阶段计算压力过大。初排会根据复杂度预估与启发式规则,把明显不相关的候选踢掉。精排阶段是真正有意思的部分,它会调用一个轻量级打分器——多数情况下是某个 7B 级别的开源模型——对每个候选切片与查询的相关性进行一个逐项打分。打分维度包括信息完整度、直接匹配度、时效性和多样性,综合完后产出最终排序。
我实际测试下来,精排阶段对单条查询的处理耗时大约在 200 到 600 毫秒之间,不是全实时场景也能接受。如果对性能有更高的要求,可以把这个打分环节从同步改为异步——先出召回和初排的结果,再让精排在后台逐步修正。
3.3 反馈捕获层:用户行为的结构化过程
这个模块在传统搜索架构里往往缺失,或者只简单地存了点击率。Sirchmunk 的反馈捕获层把用户和检索结果的每一次交互都转化为结构化事件。事件类型包括:结果点击、结果复制、结果保存、结果举报、翻页跳过、结果上滑阅读时长。
每一种事件还有一个置信加权值。比如点击的置信度高,说明用户认为这个结果值得进入;复制内容通常表示用户在更认真地采信,置信度更高;翻页跳过则意味着当前列表整体上没能命中用户的预期,它反馈给系统的是对这批结果的减分信号。
所有事件都会带一个查询标识、结果标识、上下文标识,如果是在拥有登录体系的环境里,还会带一个伪匿名用户标识。之所以强调这个结构化过程,是因为没有结构化的反馈,自进化就无从谈起——你无法从一堆杂乱的日志里自动学到“什么该升、什么该降”。
3.4 进化引擎:离线任务与实时信号的分配
进化引擎是整个系统的大脑,但其实它的实现并不复杂。它由一个定时调度任务和一组实时回调函数组成。实时回调函数在收到高置信度事件(比如用户复制了某条结果全文)时,会对对应切片的成长值立刻做一个小的增量修改,实现“边用边学”。
定时调度任务则按周期(一般是每天一次)做深度更新。它读取近段时间的事件流水,重新计算每个关键词的区分度权重,重新评估高成长值和低成长值切片的表现,清理那些长期没有有效反馈的冷门切片,并生成一份权重修订快照。
这个引擎正是在架构层面定义了“自进化”这个说法——不需要人工定期调参,权重和排序策略会随使用行为自动漂移。有一点需要说明的是,这个进化是系统层级的,不是模型层级的,它不会去调整任何神经网络参数,它的核心在统计分布与权重调整,可控性要强很多。
4. 检索与排序机制:没有向量索引,靠什么撑起质量
4.1 候选集生成的三条通道
为了确保召回的全面性,Sirchmunk 的召回不只有单一通道,而是三条通道并行。第一条是关键词召回,走 BM25 变体;第二条是近邻偏好召回,即与用户过去点击过的高成长值切片有过关联的切片——通过切片间的共现关系可以建立一个轻量图结构,不需要向量也能找到“相似内容”;第三条是热门度召回,将最近反馈加分较多的切片纳入候选,保证时效性好、活跃度高的内容能冒头。
三条通道产出的候选集会做一个合并去重,然后进入初排。这个设计的价值在于:即使是零反馈的新查询,也能有一条通道兜底;而老查询能凭借反馈信号拿到更准确的结果。
4.2 精排打分模型的取舍原则
在精排阶段,Sirchmunk 对打分模型的选型有一个很清醒的认知:不会硬上大模型。打分器的选择取决于运行环境——有 GPU 的可以挂一个量化过的 7B 模型,纯 CPU 环境可以用更轻量的蒸馏模型,甚至可以用 1.5B 的模型。关键点在于打分器的输出必须稳定、可解释,并且输入输出的格式是严格匹配的。
精排的输出会包括一个相关性分数、一个信息完整度分数和一个置信度。这三个分数会加权合并进最终的总分,结合前两步的分数一起决定结果的序。我实际感受特别深的一点是,精排之后的效果提升,比单纯换更强召回模型带来的提升要直观得多。
4.3 衡量质量的核心指标集
在多轮自进化之后,怎么判断系统是不是真的在变好?Sirchmunk 用了一套组合指标。用户层面的主要观察指标是搜索采纳率——即用户点进某个结果后产生了复制或保存行为的次数占点进次数的比例,这个指标非常直接可信。系统层面的观察指标则是反馈衰减率——即旧权重是否在有效减少用户的负向行为。
还有一个不是靠量化而靠观察的指标:结果多样性。向量方案最容易出现的结果重复推荐问题,在 Sirchmunk 里被切片指纹的差异化机制压制住了,同一主题不会连环出现三个几乎一样的切片。这是我在其他方案里常常忽略,但被它们做得比较好的地方。
5. 自进化闭环详解:权重从哪来、往哪去
5.1 反馈信号的定义与归一化
自进化的前提是有一批定义清晰、可计算的反馈信号。Sirchmunk 把反馈分为三个级别:强正向(复制全文、明确点赞、收藏)、弱正向(点击结果并停留超过阈值)、负向(点击后快速返回并换了其他结果、忽略一个结果多次排在前面仍未点击)。负向信号有一个防误伤机制——只有当一个切片在多个不同查询下都产生负向反馈时,才会真正被降权,否则可能是查询本身的问题。
每个反馈信号都会被归一化处理,最后统一换算成一个标准化事件分。这样做的好处是,不同来源的信号可以直接累加,不需要手工调参来决定“一个收藏相当于几次点击”。归一化之后,切片的成长值计算就有了非常干净的数据源。
5.2 进化调度器的生命周期管理
进化调度器给每个切片定义了一个生命周期状态:新鲜期、成熟期、衰退期、休眠期、淘汰期。新鲜期的切片默认可获得一定的曝光加权,这是为了给新内容一个被看到的机会;成熟期的切片主要倚靠反馈得分;长期低反馈的切片会逐渐从衰退期转入休眠期,检索时会被优先被过滤,而不是彻底删除。
这个生命周期的设计是自进化架构里最值得抄走的地方。它同时兼顾了两件事:一是防止新内容被老内容埋没的“冷启动问题”;二是防止历史表现好的旧内容长期占据头部位置的“马太效应”。系统里不存在一个永生头部,即便是曾经质量很高的切片,如果后来被新版本内容替代,它的成长值也会逐步被摊薄。
5.3 防抖与防污:自进化系统里最隐蔽的坑
这里要专门说一个踩坑经验。自进化系统最害怕的是两类情况,一类是“反馈抖振”,一类是“反馈污染”。反馈抖振指的是某个查询的结果排序在短时间内频繁变动,用户上周搜出的结果和今天搜出的结果差异巨大,这会严重破坏用户对搜索系统的稳定感。解决方法是给权重更新加一个惯性系数,每一次大周期更新时,新权重不能超出旧权重某一个比率范围,真超出幅度,就分几个周期慢慢调整。
反馈污染指的是恶意或误操作产生的失真反馈。比如某个查询反复被点击但复制率为零,可能并不是结果好,而是用户被迫在多个相似结果里找不同。所以系统还加入了反馈归因机制,会把用户在某一个查询下的多次点击当成一个序列来分析,如果连续翻了好几个结果都没停下来,那么前面那些结果的曝光本身就是一种损失,不会被记成正向事件。
5.4 冷启动阶段的表现与过渡策略
一个新的 Sirchmunk 实例在刚起步时,没有反馈数据,自进化引擎暂时处于“空转”状态。这个阶段系统会退化为一个纯 BM25 + 启发式精排的常规搜索引擎,效果不会比传统的搜索差,只是没有语义优化。
冷启动过渡的加速方法也很简单:可以从系统原有的操作日志或统计工具里,导入一批历史点击记录,作为种子反馈。如果没有历史数据,那就设置一个预热期,在此期间的前 200 到 500 个有效反馈事件会被放大标记,用来快速构建初始的权重原型。实际运行中,一般采集到数百条有效反馈后,搜索结果就能出现可感知的提升。这个时间周期比很多人预期的要短,也打破了“学习型系统需要大量数据才能启动”的固有印象。
6. 实测对比、局限与未来演进方向
6.1 与传统方案在效率层面的直观对比
在同样的语料规模下(我测试时用的是大约三万段切片的个人信息库),Sirchmunk 的方案与“全文向量化 + HNSW 索引”的方案做了一轮对比。环境是纯 CPU 运行,无 GPU。
| 项目 | Sirchmunk | 向量方案 |
|---|---|---|
| 索引构建耗时 | 约 2 分钟(含切片处理) | 约 20 分钟(含 embedding 生成) |
| 单查询平均响应 | 约 180ms(不含精排) | 约 90ms(仅向量召回) |
| 依赖组件数量 | 2 个(文档库 + 全文索引) | 4 个(文档库 + 向量库 + embedding 服务 + 调度) |
| 新内容生效速度 | 写入后立即参与检索 | 需要等待向量化管道 |
| 运维难度 | 低,一条命令起服务 | 较高,需要维护向量索引重建 |
可以看到,Sirchmunk 在绝大多数工程指标上更省心,唯一明显劣势是纯召回速度。不过在实际应用里,加上精排之后的总流程其实差别不大,而且对轻量场景来说,200 毫秒的响应本身已经是可用状态。省心、可控、轻装才是这里更重要的因素。
6.2 目前的使用局限与边界定义
Sirchmunk 不是万能的,它的架构设计决定了它有明确的能力边界。首先是不适合超大语料库——如果语料规模达到千万级文档,基于词权重和反馈矩阵的方案在内存占用和更新频率上会变得吃力,此时传统的分部式搜索或向量方案会更适用。其次是不需要提供“开放式生成问答”,它定位为检索系统,不是问答机器人;它给出来的是有依据的结果列表,而不是生成一段话。最后是冷启动效果依赖初始反馈量,如果部署在一个完全没有历史数据的封闭环境,头几天用户可能感受不到它和普通搜索的差异。
但这些局限不会影响它在目标场景里的价值。对个人知识库、中小型团队内部文档库、垂直领域资料库这类场景,它就是最合适的重量级选手——不对,应该是“轻量级但胜率很高的选手”。对能跑在笔记本上、不加 GPU、不招运维的搜索系统,能做到这一步已经相当出色了。
6.3 这个系列后续的拆解计划
架构篇只是整个解析的地基。接下来的系列文章,我计划重点拆两块内容:一块是检索与排序细节,包括 BM25 的改造公式、成长值的具体计算方法、精排模型的 Prompt 设计和输出解析规则,这些是让架构落地的关键零件;另一块是工程实现层面的实操,包括数据表结构设计、调度任务的实现、离线权重更新的批处理脚本,以及如何把系统快速部署到自己的服务器或本地环境。
这应该是目前关于“无需向量数据库、又能自进化”的搜索架构里,能够公开借鉴参考的完整设计之一。也是这个系列写出来的价值所在——不是给你一套 JSON 配置就完事,而是把设计背后的取舍逻辑完整地摊开,让有同样需求的人可以少走弯路。