如果你在 Amazon S3 Vectors 上跑 RAG(检索增强生成,就是先从一个向量库里捞出相关文档片段,再喂给大模型生成答案),并且带元数据过滤条件,那你可能一直在悄悄丢结果。比如某个租户有 100 条记录,你请求返回最相近的 10 个 chunk,结果可能只回来 3 个,或者回来 10 个但根本不是这个租户数据里真正最相近的。没有报错,没有告警,模型只是拿比预期更少的上下文去回答问题。2026 年 9 月 30 日,AWS 发布了 S3 Vectors 的元数据预过滤(metadata pre-filtering)功能。索引现在有两种模式,新模式在相似度搜索之前先求过滤条件,而不是在搜索过程中才过滤。AWS 官方称,当过滤条件选择性较强时,返回的匹配向量数量最多能提升 5 倍。同时还新增了 $startsWith 前缀操作符,每个查询最多 100 个过滤条件,而且不额外收费。
先解释下背景。S3 Vectors 是 AWS 在 S3 上推出的向量存储服务,你可以把文档切片后用 embedding 模型(把文本变成一串数字向量的模型)转成向量存进去,然后用近似最近邻搜索找最相似的片段。多租户场景下,每个租户的数据都存在同一个索引里,但查询时必须用租户 ID 做过滤,否则会把别的租户的数据搜出来。问题就出在这个过滤的时机上。
向量搜索在大规模下是近似的:引擎不会把你的查询向量和库里的每一个向量都算一遍距离,而是先在一个有限的“候选池”里探索一批可能相近的结果。CLASSIC 模式(旧模式)在探索过程中同时检查过滤条件。如果过滤条件能匹配一半的数据,那么大部分候选都能通过,结果基本没问题。但如果过滤条件很苛刻(比如某个租户只占全部数据的 0.005%),那么几乎每个候选都会被丢弃,探索预算很快耗尽,你最终拿到的结果要么不足 K 个,要么这 K 个并不是过滤集合内真正的最近邻。AWS 文档也直接承认:在 CLASSIC 模式下,当匹配的向量很少时,带过滤的查询可能返回少于 K 个结果。
有人用 100 万个合成向量做了测试:recall@10(前 10 个结果中正确命中的比例)从无过滤时的 0.86,降到选择性 10% 时的 0.83,1% 时只有 0.42,0.01% 时只有 0.15。之前常见的规避办法是把数据按低基数字段(比如租户、语言、Region)拆分成多个独立索引,能把召回率拉回 15~31 个百分点,代价是要管理更多索引,运维复杂度直线上升。
新模式 ENHANCED 把顺序反过来:先解析过滤条件,缩小搜索范围,再在匹配的子集上做相似度搜索。AWS 给的例子是 800 万条支持工单,某个客户拥有其中 400 条。带预过滤时,相似度搜索只在这 400 条里跑,而不是从全部 800 万条里抽候选。这个顺序翻转带来的效果是本质性的。有个值得重复一遍的结论:返回结果数量并不能代表质量。CLASSIC 模式下即使你拿到了 10 条结果,也不能保证这 10 条是最优的。
具体怎么用?新的索引模式分两种:CLASSIC 和 ENHANCED。2026 年 9 月 30 日之后创建的向量桶默认是 ENHANCED,且无法切回 CLASSIC;之前创建的桶默认还是 CLASSIC,你可以通过 UpdateIndexMode 命令(或 CLI/SDK/API)切换成 ENHANCED,也可以切回 CLASSIC,但控制台只能开启增强模式,无法回退。你还可以在单个查询上用 queryMode=ENHANCED 先测试效果,再决定是否全局切换。新加的 $startsWith 操作符只能用在 ENHANCED 索引上,适合按路径组织的集合,比如 {"s3_path": {"$startsWith": "matter-4417/exhibits/"}}。
注意一个硬限制:ENHANCED 索引的过滤条件上限是 100 个,而且每个 $in 列表里的值都单独计数。比如 {"region": {"$in": ["us-east-1","us-west-2","eu-west-1"]}} 算 3 个条件。如果你动态生成带长 $in 列表的过滤器(比如允许用户访问的所有项目列表),迁移前一定要审计,超过 100 个会直接返回校验错误。元数据本身的限制没变:每个向量最多 40 KB 元数据,其中最多 2 KB 可过滤;最多 50 个键;每个索引最多 10 个不可过滤键,且创建时固定,后面不能改。所以要把过滤字段尽量精简,把 chunk 文本放在不可过滤的元数据里(Bedrock Knowledge Bases 就是用 AMAZON_BEDROCK_TEXT 和 AMAZON_BEDROCK_METADATA 这么干的)。
怎么验证自己索引的收益?文章给了一套完整的测量方法。第一步查索引当前模式:aws s3vectors get-index --query 'index.indexMode'。第二步在迁移前测召回率:挑一个小租户,暴力计算它在全量数据里的真实 top-K(也就是用余弦相似度把所有向量排序取前 K 个),再分别用 CLASSIC 和 ENHANCED 模式查询,对比召回率。代码里用到 ListVectors 读全索引,建议在测试副本上做。第三步用 update-index-mode 切换,或用 put-vector-bucket-default-index-mode 改桶默认模式。
多租户检索服务部署在 Amazon EKS 上时,架构有个关键点:租户 ID 必须从用户 token 里解析,绝不能从前端参数里拿。API 层构建过滤器,Pod 用限定到单个索引角色的权限去查询 S3 Vectors。有个权限坑:s3vectors:QueryVectors 单独使用时,只在无过滤且不返回元数据的情况下有效。只要带过滤或返回元数据,就必须同时有 s3vectors:GetVectors 权限,否则会 403。
最让我意外的是,这个改动没有额外成本,所有提供 S3 Vectors 的商业区域都能用,包括圣保罗和中国区。对于已经在用分索引来规避低召回率的团队来说,现在完全可以合并回单索引,省去索引管理的开销。对于还没切 ENHANCED 的现有桶,建议先在少量查询上对比一下 recall,看到实际差距后再批量切换。这个思路也能套用到其他向量数据库上:当你的过滤条件非常选择性时,先过滤再搜索几乎是必须的,否则近似搜索的候选池很容易被不相关的数据占满。以后设计 RAG 系统时,记得把“过滤时机”当成一个一等公民参数来评估,而不是默认搜索能自动处理。

内容与图片版权归原作者所有 · 原文: https://dev.to/matias_martinez_185d9e0ee/s3-vectors-now-filters-before-it-searches-metadata-pre-filtering-for-multi-tenant-rag-5eaj