在构建基于大语言模型的智能应用时信息检索 Agent 的能力直接决定了系统回答的准确性和时效性。开发者常常面临一个核心选择是使用 Serper 这类专门聚合 Google 搜索结果的 API 服务还是集成像豆包搜索这样国内厂商提供的搜索工具这个选择并非简单的功能对比而是涉及到数据源质量、访问稳定性、成本控制以及是否符合本地化需求等多维度权衡。本文将通过一个实际的对比测试项目深入剖析 Serper 与豆包搜索作为 Agent 信息检索组件的表现差异。我们将从环境配置、API 调用、结果解析到实际应用场景完整还原测试流程并提供具体的代码示例和排查指南帮助你在自己的项目中做出更明智的技术选型。1. 理解信息检索 Agent 的核心工作机制信息检索 Agent 并非一个单一模块而是一个由搜索、解析、评估和整合等多个环节构成的系统。它的核心任务是根据用户查询从互联网获取最新、最相关的信息并提炼成结构化的答案。1.1 为什么需要外部搜索能力即使是最先进的大语言模型其知识也存在截止日期无法获取最新事件、实时股价或特定网站的最新内容。外部搜索能力的引入就是为了突破模型训练数据的时空限制让 AI 应用能够“呼吸”到实时信息。例如询问“今天北京的空气质量指数”或“某科技公司最新财报”都必须依赖实时搜索。1.2 典型的信息检索流程一个完整的信息检索 Agent 通常遵循以下流程查询理解与优化Agent 首先分析用户原始问题可能会将其重写为更符合搜索引擎习惯的关键词组合。执行搜索向搜索 API 发送请求获取原始搜索结果。结果解析与过滤从返回的 HTML 或结构化数据中提取标题、链接、摘要等核心信息并根据相关性进行初步排序。内容获取与摘要针对高优先级的链接可能进一步抓取页面正文内容并由大模型进行关键信息摘要。答案合成最后将摘要后的信息与模型已有知识结合生成最终回答。在本对比中我们主要聚焦于流程中的第 2 和第 3 步即搜索 API 返回结果的质量和可用性。1.3 Serper 与豆包搜索的定位差异Serper一个专门针对 LLM 应用优化的搜索 API 服务。它代理了用户的 Google 搜索请求返回清洗后的结构化 JSON 数据省去了开发者解析 HTML 的麻烦。其优势在于数据源是 Google 搜索覆盖范围广结果质量相对较高。豆包搜索作为国内厂商推出的搜索工具其数据源和排序算法更侧重于中文互联网环境在访问速度和对中文内容的理解上可能有天然优势。对于主要服务国内用户、查询内容高度本地化的应用来说这是一个重要的考量点。2. 测试环境搭建与依赖配置为了进行公平对比我们需要构建一个统一的测试框架确保两个搜索服务在相同的条件下被调用和评估。2.1 项目初始化与依赖管理创建一个新的 Python 项目目录并初始化虚拟环境是第一步。这能有效隔离依赖避免版本冲突。# 创建项目目录 mkdir search-agent-comparison cd search-agent-comparison # 创建并激活虚拟环境以 Linux/macOS 为例 python -m venv venv source venv/bin/activate # 创建 requirements.txt 文件并安装核心依赖在requirements.txt文件中我们需要定义以下依赖requests2.28.0 # 用于发送 HTTP 请求到 Serper 和豆包搜索 API pydantic1.10.0 # 用于定义数据模型验证 API 返回的数据结构 python-dotenv0.19.0 # 用于管理环境变量安全地存储 API Keys安装依赖pip install -r requirements.txt2.2 安全地管理 API 密钥绝对不要将 API 密钥硬编码在代码中。使用.env文件来管理它们是行业最佳实践。在项目根目录创建.env文件SERPER_API_KEYyour_serper_api_key_here DOUBAN_API_KEYyour_douban_api_key_here # 假设豆包搜索的密钥变量名创建.gitignore文件确保.env不会被意外提交到代码仓库venv/ .env __pycache__/ *.pyc2.3 构建统一的测试接口为了公平对比我们设计一个统一的SearchTool基类然后让SerperTool和DoubanSearchTool分别实现它。这样上层的测试逻辑可以完全一致。首先定义搜索结果的统一数据模型。这有助于标准化评估。# models.py from pydantic import BaseModel from typing import List, Optional class SearchResult(BaseModel): title: str link: str snippet: Optional[str] None # 搜索结果摘要 position: int # 排名位置 class SearchResponse(BaseModel): query: str results: List[SearchResult] search_engine: str # 标识是哪个搜索引擎返回的结果接下来创建抽象基类和具体的工具类。# search_tools.py import os from abc import ABC, abstractmethod from typing import List import requests from dotenv import load_dotenv from models import SearchResponse, SearchResult # 加载环境变量 load_dotenv() class BaseSearchTool(ABC): 搜索工具抽象基类 def __init__(self, name: str): self.name name self.api_key os.getenv(self._get_api_key_name()) if not self.api_key: raise ValueError(f请检查环境变量 {self._get_api_key_name()} 是否已正确设置。) abstractmethod def _get_api_key_name(self) - str: 返回环境变量中对应 API Key 的名称 pass abstractmethod def search(self, query: str, num_results: int 10) - SearchResponse: 执行搜索返回统一格式的结果 pass class SerperTool(BaseSearchTool): Serper API 封装 def __init__(self): super().__init__(Serper) self.base_url https://google.serper.dev/search def _get_api_key_name(self) - str: return SERPER_API_KEY def search(self, query: str, num_results: int 10) - SearchResponse: headers { X-API-KEY: self.api_key, Content-Type: application/json } payload { q: query, num: num_results } response requests.post(self.base_url, headersheaders, jsonpayload) response.raise_for_status() # 如果请求失败则抛出异常 data response.json() # 解析 Serper 返回的特定结构 results [] if organic in data: for idx, item in enumerate(data[organic]): results.append(SearchResult( titleitem.get(title, ), linkitem.get(link, ), snippetitem.get(snippet, ), positionidx 1 )) return SearchResponse(queryquery, resultsresults, search_engineself.name) class DoubanSearchTool(BaseSearchTool): 豆包搜索 API 封装示例结构需根据官方文档调整 def __init__(self): super().__init__(豆包搜索) # 注意豆包搜索的 API 端点需要查阅其官方文档确认 self.base_url https://api.douban.com/v2/search # 此为示例 URL非真实地址 def _get_api_key_name(self) - str: return DOUBAN_API_KEY def search(self, query: str, num_results: int 10) - SearchResponse: headers { Authorization: fBearer {self.api_key} } params { q: query, count: num_results } response requests.get(self.base_url, headersheaders, paramsparams) response.raise_for_status() data response.json() # 解析豆包搜索返回的特定结构此处为示例需按实际 API 响应调整 results [] # 假设返回数据在 data[books] 或类似字段中需要根据真实文档修改 items data.get(items, []) for idx, item in enumerate(items): results.append(SearchResult( titleitem.get(title, ), linkitem.get(alt, ), # 或 url, link snippetitem.get(summary, ), positionidx 1 )) return SearchResponse(queryquery, resultsresults, search_engineself.name)重要提示豆包搜索的工具类实现是示例性的。在实际使用中你必须查阅其官方 API 文档确认正确的端点 URL、认证方式、请求参数和响应结构并对解析逻辑进行相应调整。3. 设计并执行对比测试用例测试用例的设计应覆盖不同的查询类型以全面评估搜索能力。3.1 定义测试查询集一个好的测试集应包含以下几类查询# test_cases.py TEST_QUERIES [ # 1. 事实性查询有明确答案 {query: 珠穆朗玛峰的最新精确高度, type: factual}, # 2. 技术性查询偏向开发者和文档 {query: Python asyncio 如何实现异步上下文管理器, type: technical}, # 3. 新闻时事查询考验时效性 {query: 上周召开的全球人工智能大会主要发布了哪些新产品, type: news}, # 4. 本地化查询考验中文理解 {query: 北京海淀区最好的编程培训班推荐, type: local}, # 5. 开放性/比较性查询 {query: 比较 React 和 Vue 在大型项目中的优缺点, type: comparative} ]3.2 实现对比测试脚本测试脚本的核心是使用相同的查询并行或顺序地调用两个搜索工具并收集结果。# run_comparison.py import asyncio # 如需并行可改用异步 import json from datetime import datetime from search_tools import SerperTool, DoubanSearchTool from test_cases import TEST_QUERIES def run_single_test(search_tool, test_query): 对单个搜索工具运行单个测试查询 try: print(f正在使用 {search_tool.name} 搜索: {test_query[query]}) response search_tool.search(test_query[query]) print(f {search_tool.name} 返回了 {len(response.results)} 条结果) return response except Exception as e: print(f {search_tool.name} 搜索失败: {e}) # 返回一个空的响应对象以示失败 from models import SearchResponse, SearchResult return SearchResponse(querytest_query[query], results[], search_enginesearch_tool.name) def main(): serper_tool SerperTool() douban_tool DoubanSearchTool() all_results {} for test_case in TEST_QUERIES: query test_case[query] print(f\n 测试查询: {query} ) serper_result run_single_test(serper_tool, test_case) douban_result run_single_test(douban_tool, test_case) all_results[query] { serper: serper_result.dict(), douban: douban_result.dict() } # 将结果保存为 JSON 文件便于后续分析 timestamp datetime.now().strftime(%Y%m%d_%H%M%S) filename fsearch_comparison_results_{timestamp}.json with open(filename, w, encodingutf-8) as f: json.dump(all_results, f, indent2, ensure_asciiFalse) print(f\n测试完成结果已保存至: {filename}) if __name__ __main__: main()运行此脚本后你会得到一个包含所有测试结果的 JSON 文件这是进行详细分析的基础。4. 结果评估与关键指标分析评估搜索质量不能仅凭感觉需要定义可量化的指标。以下是几个核心评估维度4.1 量化评估指标结果数量返回的有效结果总数。数量过少可能意味着覆盖率不足。首条结果相关性排名第一的结果是否直接、准确地回答了问题。这对于需要快速答案的 Agent 至关重要。前三条结果平均相关性手动评估前三条结果用户最常点击的范围与查询的匹配程度可以用 1-5 分打分。摘要信息量snippet字段是否包含了足够的关键信息让 Agent 或用户无需点击链接即可了解大意。链接可访问性返回的链接是否有效是否指向权威或高质量的来源。响应时间从发送请求到收到完整响应的时间。这对于交互式应用很重要。4.2 制作结果对比分析表根据 JSON 结果文件可以人工或编写脚本进行评分并汇总成表格。查询类型查询内容搜索服务结果数量首条相关性 (1-5)摘要质量 (1-5)来源权威性 (1-5)备注事实性珠峰高度Serper10545直接来自地理权威网站数据准确事实性珠峰高度豆包搜索8434结果正确但摘要略模糊来源为百科类技术性Python asyncioSerper10555首条即为官方文档摘要清晰技术性Python asyncio豆包搜索9444首条为技术博客质量高但非官方本地化北京编程培训Serper10332多为国际或通用信息本地化结果少本地化北京编程培训豆包搜索10544精准返回本地培训机构信息和评价初步结论分析 从示例数据看Serper 在技术性、事实性查询上表现稳定链接来源权威性强。而豆包搜索在涉及中文本地化、生活服务类查询上优势明显结果更“接地气”。这表明选型强烈依赖于你的目标用户和主要查询类型。4.3 处理 API 限制和错误在实际测试中你可能会遇到各种 API 限制或错误。问题现象可能原因检查与解决思路401 UnauthorizedAPI 密钥错误或未设置检查.env文件变量名和值是否正确确认密钥有效429 Too Many Requests达到速率限制或每日配额查看服务商文档了解限制策略考虑增加间隔或升级计划返回结果为空或很少查询词过于生僻或 API 数据源覆盖不足尝试更通用的关键词确认该服务是否支持此类查询解析错误 (KeyError)API 响应结构发生变化或与示例不符打印出完整的 API 响应 (print(data))根据实际结构调整解析代码5. 集成到 AI Agent 框架的实战建议对比测试完成后下一步是如何将优胜的搜索工具集成到 LangChain、LlamaIndex 等主流 AI Agent 框架中。5.1 创建 LangChain Tool以 LangChain 为例你可以将自定义的搜索工具包装成标准的Tool对象以便被 Agent 无缝调用。# langchain_integration.py from langchain.tools import BaseTool from typing import Type from pydantic import BaseModel, Field from search_tools import SerperTool # 假设 Serper 胜出 class SearchInput(BaseModel): query: str Field(description要搜索的查询词) class CustomSearchTool(BaseTool): name web_search description 当你需要查找最新的、模型知识库之外的信息时使用此工具进行网页搜索。 args_schema: Type[BaseModel] SearchInput def _run(self, query: str) - str: 执行搜索并返回一个对 LLM 友好的字符串摘要 search_tool SerperTool() response search_tool.search(query, num_results3) # 为节省 token取前3条 if not response.results: return 未找到相关结果。 # 将结果格式化为一个连贯的段落 results_summary [] for result in response.results: results_summary.append(f[{result.position}] {result.title}: {result.snippet} (来源: {result.link})) return \n\n.join(results_summary) async def _arun(self, query: str) - str: 异步版本可选 raise NotImplementedError(此工具暂不支持异步调用) # 现在你可以将这个 tool 添加到 LangChain Agent 的 tools 列表中5.2 设计有效的 Agent 提示词搜索工具返回的是原始信息Agent 如何利用这些信息至关重要。需要在系统提示词中给出明确指令。# 一个示例性的系统提示词 SYSTEM_PROMPT 你是一个有帮助的AI助手可以访问网络搜索功能来获取最新信息。 请遵循以下规则 1. 当用户的问题涉及近期事件、非常具体的实时数据、或你不确定的知识时请务必使用搜索工具web_search。 2. 仔细阅读搜索返回的结果并基于这些最权威、最相关的结果来回答问题。 3. 在回答中如果引用了搜索结果请注明来源或说明信息是刚刚检索到的。 4. 如果搜索结果与你的内部知识有冲突以搜索到的最新信息为准。 5. 如果搜索没有返回有用结果诚实地告知用户并尝试基于已有知识提供一般性建议。 6. 生产环境部署的考量与排错指南将搜索 Agent 投入生产环境还需要考虑更多因素。6.1 生产环境清单[ ]错误处理与降级当搜索 API 不可用时Agent 应优雅降级告知用户并尝试仅用模型知识回答而不是直接崩溃。[ ]速率限制与重试实现带有退避策略的重试机制处理短暂的 API 故障或限流。[ ]缓存对相同的查询进行短期缓存例如 5-10 分钟避免重复请求节省成本和提升响应速度。[ ]日志与监控记录所有搜索请求和结果数量监控 API 的延迟和错误率便于排查问题。[ ]成本控制设置每月或每日的搜索次数预算防止意外消耗。6.2 常见问题排查路径当 Agent 返回的信息不准或搜索失败时可以按以下顺序排查检查查询词Agent 生成的搜索查询是否准确反映了用户意图有时需要优化提示词让 Agent 学会生成更好的搜索词。验证 API 状态直接使用 curl 或 Postman 测试搜索 API 是否正常工作排除网络或账户问题。# 测试 Serper API curl -X POST https://google.serper.dev/search \ -H X-API-KEY: $SERPER_API_KEY \ -H Content-Type: application/json \ -d {q:测试查询, num: 3}审查原始结果在日志中打印出搜索 API 返回的完整原始响应检查数据结构是否如预期解析逻辑是否正确。评估结果质量手动执行相同的搜索对比返回的链接和摘要判断是 API 数据源的问题还是集成方式的问题。信息检索是增强 AI Agent 能力的关键一环。Serper 凭借其稳定的 Google 数据源在通用性和技术性搜索上往往表现优异而豆包搜索等本土化服务在特定中文场景下可能更具优势。最佳的选型策略是根据你的应用场景、目标用户和预算进行实际的对比测试。本文提供的测试框架和方法论可以为你自己的技术选型提供扎实的依据。在生产环境中务必做好错误处理、监控和成本控制确保搜索功能的稳定和高效。