跳到主要内容
极客日志极客日志面向AI+效率的开发者社区
首页博客GitHub 精选镜像工具UI配色美学隐私政策关于联系
搜索内容 / 工具 / 仓库 / 镜像...⌘K搜索
注册
博客列表
PythonAI

Web Search 技能:构建具备实时联网能力的 AI Agent

综述由AI生成Web Search 技能让 AI Agent 突破知识时效限制,通过集成搜索引擎 API 获取实时信息。了从架构设计到 LangChain 代码实现的完整流程,涵盖查询优化、缓存策略、错误处理及安全控制。结合财经问答与多语言新闻聚合案例,展示了如何构建高可用、低成本的联网智能体,为生产环境落地提供实践参考。

王初壹发布于 2026/4/7更新于 2026/5/2110 浏览

Web Search 技能:构建具备实时联网能力的 AI Agent

在 AI Agent 的开发实战中,我们常面临大模型知识时效性的瓶颈。训练数据截止于过去某个时间点,难以应对动态世界中的最新事件、股价波动或突发新闻。Web Search 技能正是解决这一痛点的关键模块,它通过集成搜索引擎 API(如 SerpAPI、Google Programmable Search Engine 等),赋予 Agent 实时获取互联网公开信息的能力。

本模块广泛应用于智能客服、市场情报分析、科研辅助及金融舆情监控等场景,能显著提升 Agent 的信息鲜度与决策质量。

技能概述

Web Search 技能的核心在于:Agent 接收用户查询后,自动调用外部搜索引擎接口,获取网页摘要、链接及结构化信息,经清洗、去重、排序和语义压缩后返回给大模型。

功能边界

  • 输入:自然语言查询(例如'2024 年苹果 WWDC 发布了哪些新功能?')
  • 输出:结构化搜索结果(标题、URL、摘要) + 聚合后的文本摘要
  • 不包含:网页全文抓取(需配合 Document Parser)、登录态访问、付费内容获取

核心能力

  1. 关键词提取与查询优化:识别实体与意图,生成高效搜索 Query
  2. 多源结果聚合:支持融合多个搜索引擎的结果
  3. 结果过滤与可信度评估:排除低质量、广告或虚假信息
  4. 上下文感知排序:结合对话历史调整结果相关性

架构设计

采用分层架构确保高内聚低耦合,支持插件式扩展(例如替换 Google 为 DuckDuckGo 只需实现新的 SearchEngineAdapter)。

+---------------------+
| User Query          |
+----------+----------+
           v
+---------------------+
| Query Preprocessor  |
| - 实体识别          |
| - 意图分类          |
| - 查询重写          |
+----------+----------+
           v
+---------------------+
| Search API Gateway  |
  多引擎适配器      
  请求限流重试     
  缓存代理          

           v

  Postprocessor
  去重              
  摘要提取          
  可信度打分        
  结果截断 (TopK)  

           v

 Output Formatter    
  JSON  Markdown   
  引用标注          

|
-
|
|
-
/
|
|
-
|
+
----------+----------+
+
---------------------+
|
Result
|
|
-
|
|
-
|
|
-
|
|
-
-
|
+
----------+----------+
+
---------------------+
|
|
|
-
/
|
|
-
|
+
---------------------+

接口设计

遵循 MCP(Model Context Protocol)标准,定义统一接口:

class WebSearchSkill:
    def __init__(self, api_key: str, engine: str = "google", max_results: int = 5):
        self.api_key = api_key
        self.engine = engine
        self.max_results = max_results

    def search(self, query: str, region: str = "us", lang: str = "en") -> dict:
        """
        执行网络搜索并返回结构化结果
        Args:
            query (str): 搜索关键词
            region (str): 搜索区域(如"cn", "us")
            lang (str): 返回语言
        Returns:
            dict: {
                "query": str,
                "results": [
                    { "title": str, "url": str, "snippet": str, "position": int },
                    ...
                ],
                "summary": str # 聚合摘要
            }
        """
        pass

返回格式示例

{
  "query": "2024 年苹果 WWDC 新功能",
  "results": [
    {
      "title": "Apple WWDC 2024: iOS 18, macOS Sequoia...",
      "url": "https://www.apple.com/newsroom/2024/wwdc/",
      "snippet": "Apple announced iOS 18 with Apple Intelligence...",
      "position": 1
    }
  ],
  "summary": "在 2024 年 WWDC 上,苹果发布了 iOS 18、macOS Sequoia,并首次推出 Apple Intelligence..."
}

代码实现(Python + LangChain)

这里基于 LangChain 实现完整 Web Search 技能,集成 SerpAPI 并支持本地缓存,以减少 API 调用成本。

import os
import hashlib
from typing import List, Dict, Optional
from langchain_community.utilities import GoogleSerperAPIWrapper
from langchain_core.tools import Tool
from langchain_core.callbacks import CallbackManagerForToolRun
from langchain_core.pydantic_v1 import BaseModel, Field
import tiktoken

# 安装依赖:pip install langchain-community serpapi tiktoken

class WebSearchInput(BaseModel):
    query: str = Field(description="The search query in natural language")
    max_results: int = Field(default=5, ge=1, le=10)

class WebSearchSkill:
    def __init__(self, serper_api_key: str, cache_dir: str = "./search_cache", max_tokens: int = 2000):
        self.search = GoogleSerperAPIWrapper(
            serper_api_key=serper_api_key, gl="us", hl="en"
        )
        self.cache_dir = cache_dir
        self.max_tokens = max_tokens
        os.makedirs(cache_dir, exist_ok=True)
        self.encoder = tiktoken.encoding_for_model("gpt-3.5-turbo")

    def _get_cache_key(self, query: str) -> str:
        return hashlib.md5(query.encode()).hexdigest()

    def _read_cache(self, key: str) -> Optional[Dict]:
        cache_file = os.path.join(self.cache_dir, f"{key}.json")
        if os.path.exists(cache_file):
            import json
            with open(cache_file, 'r', encoding='utf-8') as f:
                return json.load(f)
        return None

    def _write_cache(self, key: str, data: Dict):
        cache_file = os.path.join(self.cache_dir, f"{key}.json")
        import json
        with open(cache_file, 'w', encoding='utf-8') as f:
            json.dump(data, f, ensure_ascii=False, indent=2)

    def _summarize_results(self, results: List[Dict]) -> str:
        snippets = [r.get("snippet", "") for r in results if r.get("snippet")]
        combined = "\n\n".join(snippets[:5])
        tokens = self.encoder.encode(combined)
        if len(tokens) > self.max_tokens:
            combined = self.encoder.decode(tokens[:self.max_tokens])
        return combined

    def search(self, query: str, max_results: int = 5) -> Dict:
        cache_key = self._get_cache_key(query)
        cached = self._read_cache(cache_key)
        if cached:
            print(f"[CACHE HIT] {query}")
            return cached

        try:
            raw_results = self.search.results(query, num_results=max_results)
            organic = raw_results.get("organic", [])
            formatted_results = []
            for i, res in enumerate(organic):
                formatted_results.append({
                    "title": res.get("title", ""),
                    "url": res.get("link", ""),
                    "snippet": res.get("snippet", ""),
                    "position": i + 1
                })
            summary = self._summarize_results(formatted_results)
            output = {
                "query": query,
                "results": formatted_results,
                "summary": summary
            }
            self._write_cache(cache_key, output)
            return output
        except Exception as e:
            print(f"[SEARCH ERROR] {e}")
            return {"query": query, "results": [], "summary": "搜索服务暂时不可用,请稍后再试。"}

# 封装为 LangChain Tool
def create_web_search_tool(serper_api_key: str) -> Tool:
    skill = WebSearchSkill(serper_api_key=serper_api_key)

    def _run(query: str, run_manager: Optional[CallbackManagerForToolRun] = None) -> str:
        result = skill.search(query, max_results=5)
        return result["summary"]

    return Tool(
        name="web_search",
        description="Useful for when you need to answer questions about current events, latest news, or real-time information. Input should be a search query.",
        func=_run,
        args_schema=WebSearchInput
    )

# 使用示例
if __name__ == "__main__":
    SERPER_API_KEY = os.getenv("SERPER_API_KEY")
    if not SERPER_API_KEY:
        raise ValueError("Please set SERPER_API_KEY environment variable")
    tool = create_web_search_tool(SERPER_API_KEY)
    result = tool.run("What is the latest stock price of NVIDIA?")
    print(result)

环境配置:请确保已安装 langchain-community, serpapi, tiktoken 等依赖,并设置环境变量 SERPER_API_KEY。

实战案例

案例 1:实时财经问答 Agent

业务背景:构建一个能回答股票价格、财报日期、行业新闻的金融助手。

技术选型:

  • 搜索引擎:SerpAPI(支持 Google Finance)
  • LLM:OpenAI GPT-4
  • 框架:LangChain AgentExecutor

完整实现:

from langchain_openai import ChatOpenAI
from langchain.agents import AgentExecutor, create_openai_tools_agent
from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder

# 初始化 LLM 和工具
llm = ChatOpenAI(model="gpt-4-turbo", temperature=0)
search_tool = create_web_search_tool(os.getenv("SERPER_API_KEY"))

# 定义 Agent 提示词
prompt = ChatPromptTemplate.from_messages([
    ("system", "You are a financial assistant. Use web_search to get real-time data."),
    MessagesPlaceholder("chat_history"),
    ("human", "{input}"),
    MessagesPlaceholder("agent_scratchpad")
])

# 创建 Agent
agent = create_openai_tools_agent(llm, [search_tool], prompt)
agent_executor = AgentExecutor(agent=agent, tools=[search_tool], verbose=True)

# 测试
response = agent_executor.invoke({"input": "What is Tesla's current stock price and market cap?"})
print(response["output"])

运行结果:

Tesla Inc. (TSLA) is currently trading at $178.50 per share with a market capitalization of approximately $568 billion as of June 2024.

问题与解决:

  • 问题:搜索结果包含广告链接。
  • 解决:在 _summarize_results 中过滤 url 包含 /ads/ 或 googleads 的结果。

案例 2:多语言新闻聚合器

业务背景:为跨国企业提供当日全球新闻摘要,支持中英双语。

关键技术点:

  • 动态设置 hl(host language)和 gl(geolocation)
  • 结果翻译(调用翻译 API 或 LLM)

代码片段:

def multilingual_news_summary(topic: str, languages: List[str] = ["en", "zh"]):
    all_snippets = []
    skill = WebSearchSkill(serper_api_key=os.getenv("SERPER_API_KEY"))
    for lang in languages:
        region = "cn" if lang == "zh" else "us"
        results = skill.search(f"{topic} site:news.google.com", max_results=3)
        for r in results["results"]:
            all_snippets.append(r["snippet"])
    
    # 调用 LLM 生成多语言摘要
    llm = ChatOpenAI(model="gpt-4")
    summary_prompt = f"Summarize the following news snippets about '{topic}' in both English and Chinese:\n\n" + "\n".join(all_snippets)
    return llm.invoke(summary_prompt).content

错误处理

常见异常及处理策略如下表所示:

异常类型原因处理机制
API Rate Limit超出 QPS 限制指数退避重试(最多 3 次)
Invalid Query查询含非法字符自动清理(移除特殊符号)
Empty Results无相关结果返回友好提示 + 建议改写查询
Network Timeout网络波动设置 10 秒超时,失败后降级缓存

在代码中通过 try-except 捕获异常,并记录结构化日志:

import logging
logger = logging.getLogger(__name__)
try:
    # 搜索逻辑
except requests.exceptions.Timeout:
    logger.warning("Search timeout", extra={"query": query})
    return fallback_response()

性能优化

  1. 缓存策略:
    • 使用 LRU 缓存(内存) + 文件缓存(持久化)
    • TTL 设置:新闻类 1 小时,静态事实 24 小时
  2. 结果截断:
    • 限制返回结果数(默认 5 条)
    • 摘要 Token 数控制(避免 LLM 上下文溢出)

并发处理:

from concurrent.futures import ThreadPoolExecutor

def batch_search(queries: List[str]) -> List[Dict]:
    with ThreadPoolExecutor(max_workers=5) as executor:
        return list(executor.map(self.search, queries))

安全考量

  1. 输入校验:
    • 过滤 SQL 注入字符(如 ;, --)
    • 限制查询长度(≤200 字符)
  2. 权限控制:
    • 敏感查询拦截(如'how to hack')
    • 企业环境中按角色控制搜索范围
  3. 沙箱隔离:
    • 搜索结果不直接执行 JavaScript
    • URL 白名单机制(仅允许可信域名)

测试方案

单元测试示例(pytest):

def test_web_search_skill():
    skill = WebSearchSkill(serper_api_key="fake_key")
    # Mock Serper API
    with patch.object(skill.search, 'results') as mock_search:
        mock_search.return_value = {"organic": [{"title": "Test", "link": "http://test.com", "snippet": "Test snippet"}]}
        result = skill.search("test query")
        assert len(result["results"]) == 1
        assert "Test snippet" in result["summary"]

集成测试:

  • 验证端到端流程:用户提问 → 搜索 → LLM 生成答案
  • 使用真实 API Key 在 CI 中运行(每日限额内)

端到端测试指标:

  • 成功率 ≥ 95%
  • P95 延迟 ≤ 2s
  • 缓存命中率 ≥ 30%

最佳实践

  1. 查询重写优于原始输入:使用 LLM 先将模糊问题转为精准关键词
  2. 结果必须标注来源:避免幻觉,增强可信度
  3. 避免过度搜索:非时效性问题优先查本地知识库
  4. 成本意识:监控 API 调用量,设置预算告警
  5. 用户隐私:不在查询中泄露 PII(个人身份信息)

扩展方向

  1. 多引擎融合:同时调用 Google、Bing、DuckDuckGo,加权融合结果
  2. 垂直搜索:集成 arXiv(学术)、GitHub(代码)、Patent(专利)等专业引擎
  3. 语义搜索增强:将搜索结果向量化,与用户问题做相似度匹配
  4. MCP 标准化:实现符合 Model Context Protocol 的通用接口,便于跨平台复用

总结

Web Search 技能是 AI Agent 连接动态世界的桥梁。通过合理设计查询预处理、结果后处理和缓存机制,可显著提升信息获取的准确性与时效性。本文提供了基于 LangChain 的完整实现,涵盖错误处理、安全控制和性能优化,适用于生产环境。

在后续开发中,我们将深入 Knowledge Graph 技能,探讨如何利用结构化知识进行复杂推理。

关键要点回顾:

  • 搜索查询需经过意图识别与关键词提取,避免直接透传用户原始输入
  • 必须实现缓存机制以降低 API 成本并提升响应速度
  • 结果摘要应保留关键事实并标注信息来源,防止幻觉
  • 对搜索结果进行可信度过滤,排除低质量或广告内容
  • 严格控制输入长度与敏感词,保障系统安全
  • 在非必要场景下优先使用本地知识库,减少对外部依赖
  • 监控 API 调用量与延迟,设置熔断与降级策略
  • 遵循 MCP 协议设计接口,确保技能可移植性

参考文档:

  • LangChain 官方文档 - Tools and Agents: https://python.langchain.com/docs/modules/agents/
  • SerpAPI 开发者指南:https://serpapi.com/documentation
  • Google Programmable Search Engine: https://developers.google.com/custom-search
  • MCP (Model Context Protocol) 规范草案:https://github.com/modelcontextprotocol/spec
  • LlamaIndex Web Search Integration: https://docs.llamaindex.ai/en/stable/examples/query_engine/web_search_query_engine/
  • 'Rethinking Web Search for LLMs' - arXiv:2305.14282
  • DuckDuckGo Search API (开源替代): https://pypi.org/project/duckduckgo-search/
  • Open Source Search Aggregator: https://github.com/emptycrown/llama-hub/tree/main/llama_hub/tools

目录

  1. Web Search 技能:构建具备实时联网能力的 AI Agent
  2. 技能概述
  3. 架构设计
  4. 接口设计
  5. 代码实现(Python + LangChain)
  6. 安装依赖:pip install langchain-community serpapi tiktoken
  7. 封装为 LangChain Tool
  8. 使用示例
  9. 实战案例
  10. 案例 1:实时财经问答 Agent
  11. 初始化 LLM 和工具
  12. 定义 Agent 提示词
  13. 创建 Agent
  14. 测试
  15. 案例 2:多语言新闻聚合器
  16. 错误处理
  17. 性能优化
  18. 安全考量
  19. 测试方案
  20. 最佳实践
  21. 扩展方向
  22. 总结
  • 💰 8折买阿里云服务器限时8折了解详情
  • Magick API 一键接入全球大模型注册送1000万token查看
  • 🤖 一键搭建Deepseek满血版了解详情
  • 一键打造专属AI 智能体了解详情
极客日志微信公众号二维码

微信扫一扫,关注极客日志

微信公众号「极客日志V2」,在微信中扫描左侧二维码关注。展示文案:极客日志V2 zeeklog

更多推荐文章

查看全部
  • 异构数据迁移工具:DataX 与 DataX-Web 使用指南
  • 前端监控最佳实践:错误捕获与性能分析
  • 归并排序实战:计算右侧小于当前元素个数与翻转对
  • Selenium 与 Python 实现基础自动化测试
  • 单链表实战:合并、分割与约瑟夫环算法详解
  • Clawdbot 部署实战:反向代理与 WebAuth 安全配置
  • 基于 Vivado 平台的 FPGA 程序在线更新方案
  • 前端地图开发:标记点(Marker)的添加、删除、拖拽及事件绑定
  • 基于 SpringBoot 与 Vue 的旅游数据分析管理系统设计
  • C++ std::list 容器接口详解与使用指南
  • Python 实现 Coze API 集成:OAuth 授权与消息交互
  • 与模型对话:理解并预防 ChatGPT 中的常见误解
  • Apache IoTDB 基于 Kubernetes 的部署与运维指南
  • C++11 核心新特性详解:初始化、声明与移动语义
  • Apache Maven 3.9.9 详细安装配置教程
  • ChatGPT 变懒:GPT-4 拒绝执行任务频发,OpenAI 确认未更新模型
  • 利用 Claw Cloud Run 免费部署前端网页
  • Python 基础语法与数据结构算法实战示例
  • ChatGPT-4o 发布:AI 大模型迈入感知时代
  • Spring AI 快速入门与实战指南

相关免费在线工具

  • RSA密钥对生成器

    生成新的随机RSA私钥和公钥pem证书。 在线工具,RSA密钥对生成器在线工具,online

  • Mermaid 预览与可视化编辑

    基于 Mermaid.js 实时预览流程图、时序图等图表,支持源码编辑与即时渲染。 在线工具,Mermaid 预览与可视化编辑在线工具,online

  • 随机西班牙地址生成器

    随机生成西班牙地址(支持马德里、加泰罗尼亚、安达卢西亚、瓦伦西亚筛选),支持数量快捷选择、显示全部与下载。 在线工具,随机西班牙地址生成器在线工具,online

  • curl 转代码

    解析常见 curl 参数并生成 fetch、axios、PHP curl 或 Python requests 示例代码。 在线工具,curl 转代码在线工具,online

  • Base64 字符串编码/解码

    将字符串编码和解码为其 Base64 格式表示形式即可。 在线工具,Base64 字符串编码/解码在线工具,online

  • Base64 文件转换器

    将字符串、文件或图像转换为其 Base64 表示形式。 在线工具,Base64 文件转换器在线工具,online