
如果你正在做基于大模型的问答产品大概率遇到过这种场景同一个问题用户拆开问和整段问回答质量差别很大。比如“Python 和 Go 在云原生开发中各自有什么优势”如果不做任何处理直接交给模型它通常会输出一篇泛泛而谈的对比如果让系统先搜索一次资料再回答效果会好一些但总觉得信息不够密。这时候很多人会想是不是我用的搜索引擎不够强换一个更“智能”的搜索 API效果是不是就能上去一类专门研究 LLM 联网搜索效果的基准测试Web Search Benchmarks给出了一个看起来反直觉的结论More Searches Beat a Better Search Engine——在 LLM 结合网页搜索的任务里允许模型多搜索几次往往比换一个“更好的搜索引擎”更能提升最终效果。这个结论不是说搜索引擎不重要而是在提醒开发者你优化搜索效果的第一杠杆可能不在引擎端而在检索策略端。这篇文章会帮你搞清楚三件事为什么“搜索次数”在很多场景下能战胜“搜索质量”怎样用最小代码实现一个多轮搜索的 RAG 流程以及如何用一个小型基准测试验证这个结论同时避开延迟、成本、上下文膨胀这些工程坑。无论你是做 RAG 应用、智能问答还是 Agent 工具这个思路都能直接用在效果优化上。1. 这篇文章真正要解决的问题先说清楚为什么这个结论值得关注。过去一年里做 LLM 应用的人几乎都会遇到同样的困惑模型知识有截止日期内部知识不够用必须接搜索。接完搜索之后效果却千差万别。有的人接上之后回答质量明显提升有的人接上之后回答反而变“碎”甚至比不接搜索还差。于是大家开始怀疑搜索引擎的质量。有人从免费搜索源换成商业搜索 API有人自建搜索服务有人在排序模型上投入大量精力。这些做法有没有用有但通常没有想象中那么大。因为问题往往不在“能不能搜到”而在“有没有从多个角度去搜”。LLM 需要的不是一个“最佳链接”而是一组能够互相补充的材料。传统搜索引擎优化的是“最相关结果”的排序而 LLM 回答复杂问题时往往需要覆盖时间、地域、对比维度、不同观点等多个信息面。单次搜索天然有主题聚集性搜一次可能只覆盖其中一个面如果针对不同子问题分别搜索再把结果合并信息覆盖面会大很多。这篇文章要解决的就是“优化顺序”的问题。换搜索引擎是成本最高、周期最长的优化手段之一而增加搜索次数、优化查询分解往往只要改一段代码就能看到效果。先把便宜的杠杆用起来再谈引擎调优才是更稳妥的技术路线。适合读这篇文章的人有三类做 RAG 或智能问答系统的开发者正在被“搜索效果不好”困扰。做 Agent 工具、AI 搜索产品的人需要在成本和效果之间做取舍。负责评测 LLM 应用效果的同学想把“搜索次数”“查询规划”纳入评测变量。如果你只是写一个简单的 API 包装不需要复杂检索这篇文章的判断不一定适用凡是涉及复杂问题、事实查证、对比分析、时效性内容的场景下面这套思路对你的收益会非常明显。2. 核心判断为什么“多搜几次”赢过“更好的搜索引擎”2.1 搜索引擎的优化目标和 LLM 的信息需求不一样传统搜索引擎优化的核心指标是“用户点了哪个结果”。它追求的是把最相关、最权威的链接放在前面。这套体系服务的是“通过浏览网页自己找答案”的人而不是“需要把多段证据揉成一段回答”的大模型。LLM 做搜索增强时需要的不是“最相关的那一个结果”而是“一组覆盖不同侧面的结果”。你可以把这两种需求对比着看维度传统搜索引擎优化目标LLM 联网搜索需求核心目标提高点击率、降低跳出率提高答案覆盖度、可追溯性结果使用方式用户自己筛选模型把结果聚合成回答信息丰富度希望前几个结果一击命中希望结果之间能互补错误容忍度用户可以自行判断模型可能把错误信息写进回答这解释了为什么很多团队换了“更好”的搜索引擎后效果提升很有限。搜索引擎的排序能力再强单次搜索返回的信息面依然是窄的。它很难知道你正在做的是一个需要比较两岸资讯、多方立场、多时间点的复杂问题。2.2 单次搜索的信息覆盖是有限且偏科的搜索引擎返回的结果有很强的“主题聚集性”。搜“Python 云原生开发”前十页结果里大概率是框架、教程、最佳实践但如果你还想了解 Python 在云原生里的性能短板、社区生态、与大厂落地案例的对比这些内容不会在同一页出现。多轮搜索的本质是通过多个查询词去“撬开”不同的信息面。同一个引擎搜索“Python 云原生开发”和搜索“Python 云原生 性能瓶颈 对比 Go”返回的内容差异可能比换一个引擎还大。这就是查询多样性的价值它不依赖引擎本身多聪明而是通过扩大采样范围来覆盖更多信息。在实际评测中单次搜索往往能覆盖答案中 40% 到 60% 的得分点如果拆成 3 个查询去搜覆盖度通常能到 80% 以上。这个差距不是引擎质量差异能填上的而单纯是“采样数量”带来的统计优势。2.3 多轮搜索提升了整体鲁棒性搜索引擎的排序结果并不稳定。同一个查询词隔几天搜一次结果可能变化很大SEO 污染严重的领域前排结果甚至可能都是低质量聚合页。如果只依赖单次搜索LLM 很容易把排名靠前但不可靠的内容当成事实。多轮搜索天然带有交叉验证的效果。同一个信息如果出现在多个不同来源的结果里它大概率是可靠的如果一个信息只出现在某一个冷门页面里模型可以选择更谨慎地报告。多轮搜索不是说每次新增的查询词都能找到高质量内容而是它给模型提供了“多个来源比对”的机会降低了单点失败风险。2.4 从基准测试的角度看为什么这个规律如此明显Web Search Benchmark 的评测集通常会包含大量需要多知识点的复杂问题。比如“某某公司在 2024 年发布了什么产品这对它的竞争对手有什么影响”这道题至少需要三个信息点公司新闻、产品细节、竞争格局。单次搜索很难把这三个点一次搜齐多轮搜索则能针对性地补充。评测还特别看重“答案稳定性”。同一道题跑十次如果搜索一次受排序波动影响可能五次答得好、五次答得差如果搜索三次即使某一次结果跑偏另外两次仍有概率补充关键信息。从评测角度来看多轮搜索的优势体现在平均分和方差两个维度上后者往往容易被忽视。所以核心判断可以概括为一句话搜索引擎是起点检索策略才是决定 LLM 回答质量的主导因素。“多搜几次”不是万能的但它是最容易上手的工程杠杆。3. 关键概念与适用场景3.1 什么是 Web Search BenchmarkWeb Search Benchmark 是专门用来评估“LLM 通过联网搜索获取信息并生成回答”能力的评测任务集合。它不是单一数据集而是指一类评测方法给定一组问题允许模型调用搜索工具最后根据模型回答的准确性、覆盖度、来源可信度等维度打分。这类基准测试和传统问答基准不同。传统问答假设所有知识都在训练数据里而 Web Search Benchmark 会刻意选择时效性强、信息面广、需要交叉验证的问题逼迫模型真正去搜索而不是凭内部记忆硬答。因此它更适合用来指导搜索增强类产品的迭代方向。3.2 LLM 与搜索结合的主要形态目前 LLM 与搜索结合的形态大致有三类它们的优化重点各不相同形态典型产品/场景搜索次数的作用传统 RAG文档问答、知识库问答中等通常一次检索即可Agent 搜索自动规划、多步推理高查询规划直接影响任务成功率AI 搜索产品对话式搜索、报告生成高多次搜索是内容密度和可信度的基础如果你做的是 Agent搜索次数本质上就是 Agent 的“行动步数”如果你做的是 AI 搜索多次搜索意味着每个子问题都有独立的信息源。这两类产品里“More Searches”策略的收益远大于传统 RAG。3.3 什么样的场景适合“多搜索”策略适合多搜索策略的问题通常具备以下特征多维问题问题里包含“对比”“影响因素”“优缺点”等词说明答案需要多个角度。时效性问题需要追踪最新动态单次搜索容易错过关键新闻。事实核查类需要多方交叉验证降低单一信源误导风险。长流程任务比如写报告、做竞品分析天然需要多个搜索步骤。不适合的场景也很明确用户只是问一个简单事实比如“今天星期几”“某个函数怎么调用”多搜索只会增加延迟和成本高频实时接口也对多次搜索不友好因为每次额外搜索都意味着更多时间和费用。4. 最小可运行的“多轮搜索 RAG”示例下面用 Python 写一个最小可运行的多轮搜索 RAG 流程。这里不依赖重型框架只实现四个模块搜索封装、LLM 调用、查询规划、结果聚合。4.1 环境准备建议使用 Python 3.10 以上版本安装依赖pip install requests项目结构如下multi_search_demo/ ├── search_web.py # 搜索 API 封装 ├── llm_helper.py # LLM 调用封装 ├── planner.py # 查询规划器 ├── rag.py # 主流程 └── questions.json # 小型评测集搜索 API 的选择请依据你的工程环境和合规要求决定。示例代码以某搜索服务为例实际项目里你大概率会换成企业内部的搜索网关或商业搜索 API只需修改search_web()一个函数即可其他逻辑不用动。4.2 代码实现搜索封装# search_web.py 搜索 API 封装不同搜索服务只需要实现在这个接口里。 import requests def search_web(query: str, api_key: str, top_k: int 5) - list[dict]: 统一搜索入口。 以 SerpAPI 为例实际项目请换成你方符合条件的搜索服务。 url https://serpapi.com/search params { q: query, api_key: api_key, num: top_k, } resp requests.get(url, paramsparams, timeout10) resp.raise_for_status() data resp.json() results [] for item in data.get(organic_results, []): results.append({ title: item.get(title, ), link: item.get(link, ), snippet: item.get(snippet, ), }) if len(results) top_k: break return results这段代码的核心价值是把“搜索”收敛成一个函数。无论你后续接的是 Bing Web Search API、Brave Search API还是内部搜索服务只需要保证返回列表里包含title、link、snippet三个字段即可。多轮搜索主流程根本不需要关心底层引擎是谁。4.3 代码实现LLM 调用封装# llm_helper.py LLM 调用封装兼容 OpenAI Chat Completions 格式。 import requests def call_llm(messages: list[dict], api_key: str, base_url: str, model: str) - str: resp requests.post( f{base_url}/chat/completions, headers{Authorization: fBearer {api_key}}, json{ model: model, messages: messages, temperature: 0.2, }, timeout30, ) resp.raise_for_status() return resp.json()[choices][0][message][content]这里采用了兼容 OpenAI 规范的调用方式。国内很多大模型服务也提供同样的接口格式你把base_url和model换成自己的服务即可。重点是把 LLM 调用收敛成一个函数后面所有环节都通过这个函数完成。4.4 代码实现查询规划器# planner.py 查询规划把一个大问题分解为多个可独立搜索的查询。 import json import re from llm_helper import call_llm def rule_based_split(question: str) - list[str]: 规则版适合只做演示不消耗 LLM 调用。 parts re.split(r[。、?]|还有|并且|然后|同时, question) queries [p.strip() for p in parts if len(p.strip()) 2] if len(queries) 2: return queries[:4] return [question] def llm_plan_queries(question: str, api_key: str, base_url: str, model: str) - list[str]: LLM 版按用户问题生成 2 到 4 个覆盖不同方面的搜索查询。 prompt f你是一个搜索查询规划器。给定用户问题生成 2 到 4 个独立的搜索查询。 要求每个查询聚焦问题的某个方面用于多轮网页搜索查询词不能重复只返回 JSON 数组。 问题{question} content call_llm( [{role: user, content: prompt}], api_key, base_url, model, ) try: queries json.loads(content) if isinstance(queries, list) and queries: return [str(q) for q in queries] except json.JSONDecodeError: pass return rule_based_split(question)查询规划器是“More Searches”策略的核心。规则版适合调试LLM 版适合生产环境。实际项目里我建议先用规则版跑通流程验证效果确实有明显提升后再升级到 LLM 规划器这样能先排除成本因素。4.5 代码实现多轮搜索 RAG 主流程# rag.py 多轮搜索 RAG 主流程。 from search_web import search_web from llm_helper import call_llm from planner import llm_plan_queries, rule_based_split def deduplicate(results: list[dict]) - list[dict]: seen_links set() unique [] for item in results: link item.get(link, ) if link and link not in seen_links: seen_links.add(link) unique.append(item) return unique def build_context(results: list[dict], max_len: int 3500) - str: blocks [] total 0 for item in deduplicate(results): block f[{item.get(title)}]({item.get(link)})\n{item.get(snippet, )} if total len(block) max_len: break blocks.append(block) total len(block) return \n\n.join(blocks) def multi_search_rag(question: str, config: dict) - str: api_key config[search_api_key] top_k config.get(top_k, 5) # 1. 查询规划 if config.get(use_llm_planner): queries llm_plan_queries( question, config[llm_api_key], config[llm_base_url], config[llm_model], ) else: queries rule_based_split(question) # 2. 多查询多轮搜索 all_results [] for q in queries: try: results search_web(q, api_key, top_ktop_k) all_results.extend(results) print(f[搜索] {q} - {len(results)} 条结果) except Exception as exc: print(f[警告] 查询 {q} 失败: {exc}) # 3. 结果聚合 context build_context(all_results) # 4. 生成最终回答 system_prompt ( 你是信息整合助手。请基于搜索结果回答问题必须标注信息来源 不要编造搜索结果中没有的信息。如果信息不足请明确说明。 ) user_prompt f问题{question}\n\n搜索结果\n{context} answer call_llm( [ {role: system, content: system_prompt}, {role: user, content: user_prompt}, ], config[llm_api_key], config[llm_base_url], config[llm_model], ) return answer if __name__ __main__: config { search_api_key: YOUR_SEARCH_API_KEY, llm_api_key: YOUR_LLM_API_KEY, llm_base_url: https://api.openai.com/v1, llm_model: gpt-4o-mini, use_llm_planner: True, top_k: 5, } question 对比一下 Python 和 Go 在云原生开发中的适用场景 answer multi_search_rag(question, config) print(\n最终回答\n answer)这段代码的工程结构值得注意查询规划、搜索、聚合、生成四个环节完全解耦。后续你想换引擎、加缓存、改上下文策略只需要改对应函数不会牵一发动全身。4.6 如何运行与验证把代码保存后运行python rag.py预期输出分两部分。第一部分是每一步搜索的执行日志例如[搜索] Python 云原生开发优势 - 5 条结果 [搜索] Go 云原生开发优势 - 5 条结果 [搜索] Python Go 云原生 性能对比 - 5 条结果第二部分是最终回答。成功标志是最终回答里引用了多个来源并且信息密度明显高于不搜索、单次搜索的结果。如果只输出一个来源的信息大概率是查询规划失败或者build_context里的max_len限制太小。5. 用简单基准测试验证“搜索次数 vs 搜索质量”光看示例跑一次还不够要验证“多搜索次数胜过更好的搜索引擎”这个判断需要设计一个可复现的小型对比实验。5.1 构造一个小评测集先准备questions.json包含 10 到 20 道题覆盖事实查询、对比分析、时效追踪、因果推理四类问题[ { id: q01, question: Python 和 Go 在云原生开发中各自的优势是什么, keywords: [Python, Go, 云原生, 性能, 生态] }, { id: q02, question: 对比 Redis 和 Memcached 在缓存场景下的适用性, keywords: [Redis, Memcached, 缓存, 持久化, 数据结构] }, { id: q03, question: 某主流云厂商最近一次大规模故障的主要原因是什么, keywords: [云厂商, 故障, 原因, 恢复] } ]keywords字段是给简单评分器用的。正式评测中建议人工标注参考答案这里为了演示先用关键词覆盖度。5.2 对照组实验设计实验需要两个对照组变量只保留“搜索次数不同”搜索引擎保持不变组别查询策略搜索结果数目标组 A单次搜索直接用原始问题5 条模拟“只搜一次”组 B查询规划器拆成 2-4 个查询每组 5 条共 10-20 条模拟“多搜几次”运行脚本# benchmark_evaluate.py 一个极简评测脚本比较“单次搜索”和“多次搜索”在同一个问题集上的表现。 import json from rag import multi_search_rag, build_context from llm_helper import call_llm from search_web import search_web def single_search_answer(question: str, config: dict) - str: 对照组 A只搜索一次不分解查询。 top_k config.get(top_k, 5) results search_web(question, config[search_api_key], top_ktop_k) context build_context(results) return call_llm( [ {role: system, content: 你是信息整合助手。请基于搜索结果回答问题并标注来源。}, {role: user, content: f问题{question}\n\n搜索结果\n{context}}, ], config[llm_api_key], config[llm_base_url], config[llm_model], ) def keyword_grader(item: dict, answer: str) - float: 简单评分器答案中出现的关键词覆盖比例。正式环境建议用 LLM-as-Judge。 keywords item[keywords] hit sum(1 for kw in keywords if kw in answer) return hit / len(keywords) def run_evaluation(dataset_path: str, config: dict) - dict: with open(dataset_path, r, encodingutf-8) as f: dataset json.load(f) single_scores [] multi_scores [] for item in dataset: q item[question] single_answer single_search_answer(q, config) single_scores.append(keyword_grader(item, single_answer)) multi_answer multi_search_rag(q, config) multi_scores.append(keyword_grader(item, multi_answer)) print(f[{item[id]}] single{single_scores[-1]:.2f} multi{multi_scores[-1]:.2f}) return { single_avg: sum(single_scores) / len(single_scores), multi_avg: sum(multi_scores) / len(multi_scores), single_scores: single_scores, multi_scores: multi_scores, } if __name__ __main__: config { search_api_key: YOUR_SEARCH_API_KEY, llm_api_key: YOUR_LLM_API_KEY, llm_base_url: https://api.openai.com/v1, llm_model: gpt-4o-mini, use_llm_planner: True, top_k: 5, } result run_evaluation(questions.json, config) print(json.dumps(result, ensure_asciiFalse, indent2))5.3 结果解读跑完实验后重点看两个数平均分和方差。从经验来看你会观察到这几个典型现象在简单事实题上组 A 和组 B 的差距不大有时甚至组 A 略好因为查询拆散后可能引入噪音。在对比分析、时效追踪类问题上组 B 的覆盖度明显更高答案里能同时看到多个来源的观点。在多次重复测试中组 B 的得分方差更小这说明多轮搜索不仅提升效果还提升了稳定性。不过要特别说明上面的关键词评分器只是演示用的。它测的是“答案是否包含关键词”而不是“答案是否真正解决了问题”。生产环境里更推荐用 LLM-as-Judge 做多维评分判断维度包括信息覆盖度、来源可信度、是否存在无依据推断等。如果你用 LLM-as-Judge评分 prompt 可以这样写你是一个评测助手。请根据参考答案和任务要求对模型回答进行评分。 评分维度 1. 信息覆盖度是否涵盖所有关键得分点0-5 分 2. 事实准确性是否有来源支撑是否出现无依据信息0-5 分 3. 可追溯性是否明确标注信息来源0-5 分 只输出 JSON 格式{coverage: 分数, accuracy: 分数, traceability: 分数}6. 多搜索策略的常见问题与排查多轮搜索不是免费的午餐它会引入新的工程问题。下面按出现频率排序整理一份排查思路。问题现象可能原因排查方式解决方案响应延迟明显变高查询串行执行每轮搜索都在等待网络返回打印每个查询的耗时看是搜索慢还是 LLM 慢改为并发搜索把 top_k 调小对超时查询做降级成本上升查询次数增加LLM 调用次数也增加统计单次请求消耗的 token 数和搜索 API 调用量增加结果缓存设置单问题搜索次数上限上下文太长模型抓不住重点多次搜索结果拼接后超过模型上下文窗口查看build_context中max_len是否合理按相关性截断对片段做摘要只保留含关键实体的句子多查询返回大量重复结果多个查询词命中同一批页面统计去重前后结果数量在deduplicate里增加标题相似度去重模型把不同来源信息混在一起出现事实冲突搜索词太相关结果来自同一信源检查最终回答的引文来源分布在提示词中要求“分别标注不同来源的观点”搜索 API 返回限流错误并发搜索触发服务方限流查看响应状态码和错误信息增加退避重试控制并发数接入本地缓存关键词评分反映不出真实质量评分器只看关键词不看语义人工抽检几道题的答案改用 LLM-as-Judge或人工评分后与自动评分做相关性分析这里最容易被忽视的是“上下文膨胀”。很多开发者在增加搜索次数后直接把所有结果拼在一起扔给模型结果模型反而被大量冗余信息带偏。正确的做法是先对多轮搜索结果做一次筛选或摘要再进入最终生成环节。7. 工程化最佳实践7.1 先给问题分类再决定搜索次数不是所有问题都需要搜索三次。一个负责任的系统应该先判断问题类型。简单事实类、工具使用类问题搜一次甚至不搜也能答对比分析、时效追踪、深度报告类问题才需要多轮搜索。你可以在查询规划器之前加一个分类器把问题分成“轻检索”和“重检索”两类分别走不同的策略。7.2 查询规划要控制粒度查询规划的粒度很讲究。拆得太粗两个查询词覆盖的是同一个信息面拆得太细会引入大量与问题无关的噪音。比较好的做法是给规划器加约束例如“两个查询之间的语义相似度不能高于某个阈值”“每个查询必须包含问题中的一个核心实体”。如果用的是 LLM 规划可以在提示词里显式加上这些约束。7.3 结果聚合要做三层处理多轮搜索返回的结果通常有噪音我的经验是做三层处理第一层是链接去重去掉完全相同的 URL。第二层是内容去噪将 snippet 过短、标题与查询无关、来自明显聚合页的结果过滤掉。第三层是上下文裁剪结合用户问题的关键词只保留和问题强相关的片段而不是整段堆进上下文。7.4 缓存与成本控制多轮搜索的成本控制压力主要来自高频场景。可以在两个位置加缓存搜索 API 层缓存相同查询词的结果LLM 生成层缓存相同问题相同上下文的最终回答。这样同一个问题被反复询问时几乎不消耗额外成本。缓存时要注意设定过期时间时效性内容建议缓存几分钟常识性内容可以缓存更久。7.5 维护一份自己的评测集网上公开的 Web Search Benchmark 数据可以作为参考但真正能指导你迭代的是结合自身业务场景构造的私有评测集。建议每周更新 10 到 20 道题尽量覆盖你的核心用户会问的问题。版本管理、人工标注、回归对比这三点必须坚持否则无法判断一次改动到底是优化还是回退。7.6 安全与合规搜索增强涉及外部信息需要关注几个边界。第一使用搜索 API 时要确认服务条款允许你的调用方式和数据用途生产环境接入前先经过合规评估。第二不要把用户敏感信息直接拼接进搜索查询词必要时做脱敏处理。第三LLM 基于搜索结果生成回答时必须在提示词中明确“不得编造来源”同时保留源 URL 作为可追溯证明。涉及生产环境配置变更时遵循最小权限原则先在测试环境验证、再灰度发布并准备好回滚开关。7.7 灰度与回滚多轮搜索改动会直接影响用户体验和成本。上线时建议做成动态配置例如初始化时只对 5% 用户开启多查询规划观察延迟、成本、用户满意度这几项指标确认没问题再逐步放大。一旦发现效果劣化或成本超预算可以直接通过配置中心关闭新策略不需要重新发版。8. 总结与后续学习方向回到最初的问题在 LLM 联网搜索场景里为什么“多搜几次”经常赢过“更好的搜索引擎”因为它解决的是信息覆盖面问题而搜索引擎本身解决的是排序质量问题。LLM 回答复杂问题需要多维度信息多轮搜索比任何单次精确搜索都更接近这种需求。这篇文章最重要的一个结论是先优化检索策略再优化搜索引擎。用查询规划器把问题拆成多个搜索查询再用多轮搜索聚合材料是成本最低、见效最快的路线。你可以用上面的代码模板先跑通一个最小流程再用小型评测集验证你的业务场景是否适用千万不用盲目迷信“换一个更厉害的搜索 API”。下一步值得深入的方向有三个。一是查询规划器本身如何通过 few-shot 让模型拆出更高质量的子查询二是结果聚合策略如何让模型从多个来源中提取关键事实而不是简单拼接三是评测体系如何用 LLM-as-Judge 替代关键词这种粗粒度评分。搜索引擎的接入只是起点真正决定应用体验的是语境设计、策略迭代和评测闭环。