检索基础与原理
Q:多维度的查询改写是什么?改写遇到需要用户补充信息时怎么设计?
来源:抖音基础架构 Agent 一面
新手答:“用大模型把用户的查询扩展一下。”
高手答:
多维度查询改写是指同时从多个角度对原始 query 做变换,提升召回的全面性:
原始 query:"北京周末带娃去哪玩"
维度1-意图展开:亲子活动 / 儿童乐园 / 博物馆 / 户外游
维度2-实体补全:北京市 → 朝阳区/海淀区/...
维度3-同义改写:"带娃" → "亲子" / "儿童" / "适合小朋友"
维度4-约束提取:时间=周末,地点=北京,人群=有孩子的家庭需要用户补充信息时,用槽位填充 + 主动澄清机制:
- 意图识别后,检查必要槽位是否完整(比如“订酒店”需要日期、城市、人数)
- 缺失槽位时,不是笼统地问“你能说得更清楚吗”,而是生成具体的选择题:“你想去哪个城市?”或“入住日期是什么时候?”
- 多个槽位都缺时,按优先级排序,先问最关键的
技术实现上,查询改写用模型生成 + 规则约束混合:模型负责语义理解和改写,规则负责实体标准化和格式校验。槽位检测用一个轻量分类器判断缺失情况,避免每次都调大模型。
差距在哪:新手的”扩展一下”是单维度思考。高手的多维度改写覆盖了意图、实体、同义、约束四个角度,且有用户交互的槽位填充机制。面试官考的是你对查询理解链路的完整认知。
追问:Query 改写为什么能提升召回精准度?底层原理是什么?
改写不是”让 query 变长”,而是解决 query 和文档之间的三类鸿沟:
| 鸿沟类型 | 问题 | 改写如何解决 | 示例 |
|---|---|---|---|
| 词汇鸿沟 | 用户用词 ≠ 文档用词 | 同义扩展弥合词汇差异 | “手机卡” → “SIM 卡 / 流量卡 / 电话卡” |
| 意图鸿沟 | 短 query 意图模糊 | 意图展开消除歧义 | “苹果” → “Apple 公司 / 苹果水果”(结合上下文选择) |
| 粒度鸿沟 | query 太宽泛或太细碎 | 拆分/合并调整检索粒度 | “怎么部署” → “Docker 部署 + K8s 部署 + 物理机部署” |
为什么多维度改写比单次改写效果好:单次改写只能沿一个方向扩展,可能强化了某个意图但遗漏了其他。多维度改写用多条路径并行检索再合并,Recall 提升显著(实测 +15~25%),但 Precision 可能下降——所以改写后必须接 Rerank 精排过滤噪声。
Q:RAG 的检索如何实现?
来源:阿里 AI Agent 开发一面
新手答:“用向量数据库做相似度搜索。”
高手答:
RAG 检索分离线和在线两部分。
离线侧负责文档清洗、切片、去重、embedding 计算和向量入库。在线侧的流程是:
- 用 query 编码成向量,去向量库做召回
- 结合 BM25 或关键词检索做混合召回
- 用 rerank 模型重排
- 把最相关的证据拼接给大模型生成答案
# 简化版 RAG 检索流程
query_vec = embed(query)
candidates = vector_db.search(query_vec)
bm25_docs = bm25.search(query)
merged = merge_and_dedup(candidates, bm25_docs)
reranked = reranker.rank(query, merged)
context = "\n".join(doc.text for doc in reranked[:top_k])
answer = llm.generate(query, context)切片策略直接影响效果:块太大召回不准,块太小上下文断裂。通常用滑窗切割 + overlap,对长文档还会给每个 chunk 带上标题、章节路径、来源元信息,提升召回相关性。
差距在哪:新手只说了“向量数据库”——这是一个组件,不是方案。高手的回答覆盖了离线和在线两条链路,且点出了 chunk 设计这个影响效果的关键因素。面试官考的是你对 RAG 工程的完整认知。
Q:并行化意图识别是什么?为什么要并行化?如何实现的?
来源:抖音基础架构 Agent 一面
新手答:“用多线程跑意图分类。”
高手答:
并行化意图识别是指同时执行多个意图维度的判断,而不是串行地先判大类再判小类。
串行方案的问题是链路长、延迟高。比如先判“是不是搜索意图” → 再判“搜索什么品类” → 再判“有没有比较意图”,三级串行加起来可能 300ms+。而且前一级判断错了,后面全错——错误级联放大。
并行化把多个维度同时发出去:
┌→ 意图大类分类器(搜索/导航/交易)
user query ──┼→ 品类识别器(美食/旅游/购物/...)
├→ 行为意图检测器(比较/推荐/查询/...)
└→ 情感倾向检测器(正面/负面/中性)每个分类器独立运行,结果汇总后做融合决策。实现关键点:
- 各分类器用异步并发执行(
asyncio.gather或线程池),总延迟 = 最慢的那个 - 每个分类器可以用不同规模的模型——简单维度用小模型,复杂维度用大模型
- 设置超时兜底:某个分类器超时不影响其他维度,用默认值填充
差距在哪:新手的“多线程”只答了实现手段,没答为什么要并行化。高手先解释了串行方案的问题(延迟高 + 错误级联),再给出并行化的架构和融合策略。面试官考的是性能优化思维。
Q:讲一下项目里召回的流程
来源:抖音基础架构 Agent 一面
新手答:“用向量搜索召回相关文档。”
高手答:
召回是一个多路召回 → 合并去重 → 精排的三阶段流程:
第一阶段:多路召回
┌→ 向量召回(语义相似度,覆盖同义表达)
改写后 query ──┼→ 关键词召回(BM25,覆盖精确匹配)
├→ 标签召回(实体/品类标签匹配)
└→ 图召回(知识图谱关联路径)每路召回各取 top-K,各有优势:向量召回抓语义,关键词召回抓精确词,标签召回抓结构化属性,图召回抓实体关系。
第二阶段:合并去重
多路结果合并,同一文档被多路召回的加分。去重用文档 ID 做精确去重 + 内容指纹做近重复去重。
第三阶段:精排
用 Cross-Encoder 或精排模型对候选集做细粒度相关性打分。精排模型能看到 query 和文档的交互特征,精度远高于召回阶段的双塔模型。关键工程细节:每路召回的 K 值怎么定——K 太小漏结果,K 太大精排压力大,通常根据各路历史准确率动态调整。
差距在哪:新手只知道向量检索一路。高手的多路召回 + 精排是搜索系统的标准范式,面试官考的是你对召回-精排两阶段架构的完整理解。
Q:如果 RAG 召回了很多相互矛盾的文档,Agent 应该怎么处理,而不是直接让模型自己总结?
来源:腾讯大模型应用开发二面
新手答:“让模型自己综合判断。”
高手答:
不能直接把矛盾文档一股脑丢给模型让它自己“综合”,那样很容易生成一个看起来圆滑但实际上没有依据的答案。
更合理的做法是先做证据归一化和冲突检测:
- 分组:按来源、时间、可信度对召回文档分组
- 冲突检测:抽取同一字段的不同取值,判断冲突类型——是时间差异导致的,还是来源本身互相打架
- 冲突消解:
- 时间敏感信息 → 新版本优先
- 来源权威性不同 → 官方文档优先
- 无法判断 → 明确告诉用户存在冲突,说明目前最可信的依据
Agent 在这里更像证据调解器,而不是万能总结器。核心是在模型生成前就把冲突处理好,而不是让模型在矛盾信息中自己编一个“看起来合理”的答案。
差距在哪:新手直接把矛盾丢给模型——这会生成看似合理但无依据的答案。高手在模型生成前加了冲突检测和消解层,按时间、权威性、可判断性三个维度做处理。面试官考的是你有没有意识到 RAG 不只是“召回 + 生成”,中间还需要证据治理。