Agent 的 RAG 不只是向量搜索:混合检索架构实践
Skill 检索本身就是一个 RAG 系统——用检索到的 skill 增强 agent 的生成能力。本文拆解双路混合检索的实现与取舍。
Skill 检索本身就是一个 RAG 系统——用检索到的 skill 增强 agent 的生成能力。
1. 问题
在我们 56 个 skill 的技能系统中,每次任务开始前需要从技能库里找到最相关的 skill 加载到 prompt 里。这本质上就是一个 RAG 问题:
Query = 当前任务描述
Corpus = 56 条 SKILL.md
Goal = 召回最相关的 3-5 个 skill
大多数 RAG 教程只讲向量搜索。但在生产环境中,纯向量搜索不够。
2. 双路检索架构
我们不走纯向量路线,而是用结构化召回 + 向量召回的混合方案:
Query
│
├─→ Structural Recall (结构化召回)
│ ├─ 实体匹配:query 中的关键词 vs skill 的 provides/requires/tags
│ ├─ 实体扩展:seed entity → cosine 近邻 → 候选集
│ └─ 评分:base_score + overlap_weight × min(entity_overlap, cap)
│
└─→ Vector Recall (向量召回)
├─ Query embedding (384d) vs 所有 skill embeddings
├─ Cosine similarity > threshold → 候选集
└─ 评分:similarity × vector_weight
│
└─→ Fusion
├─ Both paths hit → structural + vector + both_boost
├─ Only structural → structural score
└─ Only vector → vector score
└─ Sort by score → top-k
3. Embedding 方案
# 模型:paraphrase-multilingual-MiniLM-L12-v2 (384维,CPU 可跑)
# 文本构建:name + description + tags + triggers + body_preview
text = f"{skill['name']} {skill['description']} {' '.join(skill['tags'])} ..."
embedding = model.encode(text, normalize_embeddings=True)
# 降级策略:sentence-transformers 不可用 → hash-based embedding
# MD5 分桶 + 符号位 → 384 维稀疏向量 → cosine 仍可用为什么选这个模型?
- 384 维,CPU 推理 2ms/条
- 多语言支持(技能名称有中英文混杂)
- sentence-transformers 生态成熟,pip install 即用
降级策略是生产环境必备——如果 embed 模型加载失败(OOM、版本冲突),系统不能挂。hash-based embedding 虽然精度差,但保证检索不中断。
4. 为什么是"混合"而非"纯向量"
| 场景 | 纯向量 | 混合 |
|---|---|---|
| "部署服务器" → server-operations | ✅ 语义匹配 | ✅ + tag 精确匹配 |
| "code-review" → code-review | ✅ 但可能匹配 code-style | ✅ provides 精确命中 |
| "查错 debug" → debugging-toolkit | ✅ | ✅ + when_to_apply 匹配 |
| 拼写错误 "dubug" → debugging | ❌ embedding 偏移 | ✅ 结构化召回兜底 |
结论:结构化召回处理精确匹配和容错,向量召回处理语义泛化,两者互补。
5. 融合策略
双路召回后需要融合排序。我们试过三种策略:
| 策略 | 效果 | 问题 |
|---|---|---|
| 加权平均 | 简单可控 | 阈值难调,一路高分可能盖过另一路 |
| RRF (Reciprocal Rank Fusion) | 对排名敏感,鲁棒性好 | 忽略分数绝对值 |
| 条件融合 (目前在用) | 双命中加分,单路降权 | 需要调 both_boost 系数 |
最终选条件融合,因为它允许我们把"双路都命中的 skill"推到顶部——经验上这些往往是最相关的。
6. 生产级别的考量
- 缓存:embedding 构建时预计算所有 skill,运行时只有 query 需要 encode
- 延迟:一次检索 ≈ 5ms (embed) + 1ms (cosine) + 0.5ms (结构化)
- 更新:skill 新增/修改 → 增量更新 embedding 索引,不需要重建全部
- 降级链:正常 → 退化到纯向量 → 退化到关键词 LIKE 查询 → 退化到随机返回
7. 与通用 RAG 的区别
我们这里 RAG 的"G"不是外部文档,而是技能库。这意味着:
- 召回的不是"信息",而是"行为模式"
- 相关性不是语义相似度,而是"这个 skill 能不能处理当前任务"
- 召回错误的影响不是"答非所问",而是"Agent 用错了方法"
所以召回质量比通用 RAG 更关键——召回一个不相关的 skill,可能让整个任务方向跑偏。
结论:混合检索 + 条件融合 + 三阶降级,是我们在生产环境验证过的可靠方案。