Serper与豆包搜索API对比:LLM信息检索Agent技术选型指南
在构建基于大语言模型的智能应用时,信息检索 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文件中,我们需要定义以下依赖:
requests>=2.28.0 # 用于发送 HTTP 请求到 Serper 和豆包搜索 API pydantic>=1.10.0 # 用于定义数据模型,验证 API 返回的数据结构 python-dotenv>=0.19.0 # 用于管理环境变量,安全地存储 API Keys安装依赖:
pip install -r requirements.txt2.2 安全地管理 API 密钥
绝对不要将 API 密钥硬编码在代码中。使用.env文件来管理它们是行业最佳实践。
在项目根目录创建
.env文件:SERPER_API_KEY=your_serper_api_key_here DOUBAN_API_KEY=your_douban_api_key_here # 假设豆包搜索的密钥变量名创建
.gitignore文件,确保.env不会被意外提交到代码仓库:venv/ .env __pycache__/ *.pyc
2.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, headers=headers, json=payload) response.raise_for_status() # 如果请求失败则抛出异常 data = response.json() # 解析 Serper 返回的特定结构 results = [] if 'organic' in data: for idx, item in enumerate(data['organic']): results.append(SearchResult( title=item.get('title', ''), link=item.get('link', ''), snippet=item.get('snippet', ''), position=idx + 1 )) return SearchResponse(query=query, results=results, search_engine=self.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': f'Bearer {self.api_key}' } params = { 'q': query, 'count': num_results } response = requests.get(self.base_url, headers=headers, params=params) response.raise_for_status() data = response.json() # 解析豆包搜索返回的特定结构(此处为示例,需按实际 API 响应调整) results = [] # 假设返回数据在 data['books'] 或类似字段中,需要根据真实文档修改 items = data.get('items', []) for idx, item in enumerate(items): results.append(SearchResult( title=item.get('title', ''), link=item.get('alt', ''), # 或 'url', 'link' snippet=item.get('summary', ''), position=idx + 1 )) return SearchResponse(query=query, results=results, search_engine=self.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(query=test_query['query'], results=[], search_engine=search_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 = f"search_comparison_results_{timestamp}.json" with open(filename, 'w', encoding='utf-8') as f: json.dump(all_results, f, indent=2, ensure_ascii=False) 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) | 备注 |
|---|---|---|---|---|---|---|---|
| 事实性 | 珠峰高度 | Serper | 10 | 5 | 4 | 5 | 直接来自地理权威网站,数据准确 |
| 事实性 | 珠峰高度 | 豆包搜索 | 8 | 4 | 3 | 4 | 结果正确,但摘要略模糊,来源为百科类 |
| 技术性 | Python asyncio | Serper | 10 | 5 | 5 | 5 | 首条即为官方文档,摘要清晰 |
| 技术性 | Python asyncio | 豆包搜索 | 9 | 4 | 4 | 4 | 首条为技术博客,质量高但非官方 |
| 本地化 | 北京编程培训 | Serper | 10 | 3 | 3 | 2 | 多为国际或通用信息,本地化结果少 |
| 本地化 | 北京编程培训 | 豆包搜索 | 10 | 5 | 4 | 4 | 精准返回本地培训机构信息和评价 |
初步结论分析: 从示例数据看,Serper 在技术性、事实性查询上表现稳定,链接来源权威性强。而豆包搜索在涉及中文本地化、生活服务类查询上优势明显,结果更“接地气”。这表明选型强烈依赖于你的目标用户和主要查询类型。
4.3 处理 API 限制和错误
在实际测试中,你可能会遇到各种 API 限制或错误。
| 问题现象 | 可能原因 | 检查与解决思路 |
|---|---|---|
401 Unauthorized | API 密钥错误或未设置 | 检查.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_results=3) # 为节省 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 数据源,在通用性和技术性搜索上往往表现优异;而豆包搜索等本土化服务在特定中文场景下可能更具优势。最佳的选型策略是根据你的应用场景、目标用户和预算进行实际的对比测试。本文提供的测试框架和方法论,可以为你自己的技术选型提供扎实的依据。在生产环境中,务必做好错误处理、监控和成本控制,确保搜索功能的稳定和高效。
