4.2 过滤与聚合搜索


只按相似度排序,把三年前的旧文档顶到最前面

在检索层的第二站,我们加一道「约束」:相似不等于合适。元数据过滤就是让业务规则参与排序的入口。

下面用一段带过滤的检索,演示如何在近似召回后,用时间、状态等字段收窄结果。

import numpy as np def search_filtered(index, q, top_k=5, filt=None): # 先近似召回一大批,再用过滤函数收窄 cands = index.search(q, top_k * 4) if filt: cands = [c for c in cands if filt(c)] return cands[:top_k] docs = [ {'text': 'A', 'year': 2024, 'status': 'online'}, {'text': 'B', 'year': 2021, 'status': 'offline'}, {'text': 'C', 'year': 2023, 'status': 'online'}, ] def only_recent_online(d): return d['year'] >= 2023 and d['status'] == 'online' # index.search 用伪实现返回全部 class FakeIdx: def search(self, q, k): return docs idx = FakeIdx() print('过滤后:', [d['text'] for d in search_filtered(idx, None, filt=only_recent_online)])

要点:过滤在「召回之后、截断之前」做,否则会先把不合规则的项占掉 top 位。我们强调过滤是「缩减候选池」,不是「事后删几条」。

聚合检索则是另一种玩法:把多个字段的命中做逻辑组合。下面给出 AND/OR 的聚合评分。

def aggregate(hit_map, op='or'): # hit_map: {字段: [命中文档]} if op == 'or': return set().union(*hit_map.values()) return set.intersection(*[set(v) for v in hit_map.values()]) hits = {'tag': ['A', 'B'], 'author': ['B', 'C']} print('OR 聚合:', aggregate(hits, 'or')) print('AND 聚合:', aggregate(hits, 'and'))

案例:过期下架商品排第一

  • 背景:电商搜索只按相似度,已下架商品因文本匹配高被顶到前排。
  • 操作:接入 only_online 过滤,召回后先剔除下架项再截断。
  • 结果:前排点击率提升 23%,客诉「搜到买不到」明显下降。
  • 解读:过滤把业务规则前置进检索,避免靠相似度掩盖约束违规。
  • 变式:可把过滤做成可配置表达式,运营自助调整而无需改代码。

过滤还能嵌套。真实业务里条件往往是「状态在线且年份新,或置顶标签」。下面给出一段组合过滤函数,用结构化条件代替散落的人肉判断。

def combo_filter(doc, rules): # rules 是形如 {'and':[...], 'or':[...]} 的嵌套结构 if 'and' in rules: if not all(combo_filter(doc, r) for r in rules['and']): return False if 'or' in rules: if not any(combo_filter(doc, r) for r in rules['or']): return False for k, v in rules.items(): if k in ('and', 'or'): continue if doc.get(k) != v: return False return True doc = {'status': 'online', 'year': 2024, 'pin': True} print('命中:', combo_filter(doc, {'and': [{'status': 'online'}, {'year': 2024}], 'or': [{'pin': True}]}))

结构化的好处是可序列化:运营在后台勾选条件,前端生成这段规则,后端原样执行,无需为每种组合写代码。这正是过滤从「写死」走向「配置」的关键一步,呼应 3.4 的配置化取向。

过滤还有一层语义要讲清:它是「硬约束」而非「软偏好」。硬约束意味着不满足就绝对不能出现,例如已下线、无权限。软偏好则是排序上的微调,更适合用重排分数表达。把硬约束误当成软偏好去「降权」而不是「剔除」,是常见的合规事故来源,我们建议凡是涉及权限与上下架的,一律走硬过滤。

聚合检索则常用于「多路召回后合并」。比如先按标签召一批、再按作者召一批,两路取并,再统一重排。它和过滤的区别在于:过滤是往窄了收,聚合是往宽了并。两者可以组合——先聚合多路,再做硬过滤,最后重排,这条顺序我们在 4.3 的混合检索里也用到过,是同一套思路在不同尺度的体现。

操作 方向 典型用途
过滤 收窄 权限、上下架
聚合 拓宽 多路召回合并

过滤条件写错,比不写更危险,因为它给你一种「我已经处理了」的错觉。我们建议在涉及权限的过滤上线前,专门用越权用例做反向测试:构造一个本不该看到的查询,确认它确实被挡下,而不是只测「正常查询能返回」。安全相关的逻辑,验证的重点永远是「坏的输入被挡住」,而非「好的输入能通」。

另外,过滤表达式本身也可能成为攻击面。如果过滤规则来自用户输入且未做校验,攻击者可以构造畸形规则触发异常或绕过。所以过滤规则要先解析校验再执行,和配置校验是同一套纪律。在实践中,我们把过滤规则的白名单字段写死在服务端,用户只能选字段和值,不能自己拼表达式。这样即使前端被绕过,后端也只认合法字段,攻击面收到最小。

把过滤想成门的安检而非货架的排序,很多设计纠结会瞬间清晰:安检只负责拦人,排面留给后面的重排,各司其职系统才不拧巴。

本节可考核点:能解释「过滤应在截断前做」的原因,并区分过滤与聚合各自的适用场景。


作者与出处
原作者: 灏天文库
来源:灏天文库
整理: 灏天文库整理
由灏天文库平台收录,内容或由平台用户上传,仅供学习交流
发布者: 作者: 灏天文库 转发
评论区 (0)
U