跳到主要内容
AI Agent Web Search 技能:互联网搜索与信息聚合 | 极客日志
Python AI 算法
AI Agent Web Search 技能:互联网搜索与信息聚合 AI Agent 中 Web Search 技能的设计与实现。涵盖架构设计、接口规范及基于 LangChain 的 Python 代码示例。通过集成 SerpAPI 等搜索引擎,Agent 可获取实时互联网信息,突破大模型知识时效限制。内容包括缓存策略、错误处理、安全控制及性能优化,并提供财经问答与多语言新闻聚合实战案例,适用于构建企业级智能体。
山野来信 发布于 2026/4/6 更新于 2026/7/25 53 浏览技能概述
Web Search 技能是指 AI Agent 在接收到用户查询后,自动调用外部搜索引擎接口,获取相关网页摘要、链接及结构化信息,并将结果进行清洗、去重、排序和语义压缩后返回给大模型,以支持后续推理或直接回答的能力。
功能边界 :
输入 :自然语言查询(如'2024 年苹果 WWDC 发布了哪些新功能?')
输出 :结构化搜索结果(标题、URL、摘要) + 聚合后的文本摘要
不包含 :网页全文抓取(需配合 Document Parser 技能)、登录态访问、付费内容获取
核心能力 :
关键词提取与查询优化 :从用户问题中识别实体与意图,生成高效搜索 Query
多源结果聚合 :支持多个搜索引擎结果融合(可选)
结果过滤与可信度评估 :排除低质量、广告或虚假信息
上下文感知排序 :结合对话历史调整结果相关性
架构设计
Web Search 技能采用分层架构,确保高内聚低耦合:
+
| User Query |
+
v
+
| Query Preprocessor |
| - 实体识别 |
| - 意图分类 |
| - 查询重写 |
+
v
+
| Search API Gateway |
| - 多引擎适配器 |
| - 请求限流/ 重试 |
| 缓存代理
v
Postprocessor
去重
摘要提取
可信度打分
结果截断(Top K)
v
Output Formatter
JSON Markdown
引用标注
-
|
+
+
|
Result
|
|
-
|
|
-
|
|
-
|
|
-
-
|
+
+
|
|
|
-
/
|
|
-
|
+
该架构支持插件式扩展,例如替换 Google 为 DuckDuckGo 只需实现新的 SearchEngineAdapter。
接口设计 遵循 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 并支持缓存:
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
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" : "搜索服务暂时不可用,请稍后再试。" }
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)
实战案例
案例 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 = ChatOpenAI(model="gpt-4-turbo" , temperature=0 )
search_tool = create_web_search_tool(os.getenv("SERPER_API_KEY" ))
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 = 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 = 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()
性能优化
缓存策略 :
使用 LRU 缓存(内存) + 文件缓存(持久化)
TTL 设置:新闻类 1 小时,静态事实 24 小时
结果截断 :
限制返回结果数(默认 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))
安全考量
输入校验 :
过滤 SQL 注入字符(如;, --)
限制查询长度(≤200 字符)
权限控制 :
敏感查询拦截(如'how to hack')
企业环境中按角色控制搜索范围
沙箱隔离 :
搜索结果不直接执行 JavaScript
URL 白名单机制(仅允许可信域名)
测试方案 def test_web_search_skill ():
skill = WebSearchSkill(serper_api_key="fake_key" )
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%
最佳实践
查询重写优于原始输入 :使用 LLM 先将模糊问题转为精准关键词
结果必须标注来源 :避免幻觉,增强可信度
避免过度搜索 :非时效性问题优先查本地知识库
成本意识 :监控 API 调用量,设置预算告警
用户隐私 :不在查询中泄露 PII(个人身份信息)
扩展方向
多引擎融合 :同时调用 Google、Bing、DuckDuckGo,加权融合结果
垂直搜索 :集成 arXiv(学术)、GitHub(代码)、Patent(专利)等专业引擎
语义搜索增强 :将搜索结果向量化,与用户问题做相似度匹配
MCP 标准化 :实现符合 Model Context Protocol 的通用接口,便于跨平台复用
总结 Web Search 技能是 AI Agent 连接动态世界的桥梁。通过合理设计查询预处理、结果后处理和缓存机制,可显著提升信息获取的准确性与时效性。本文提供了基于 LangChain 的完整实现,涵盖错误处理、安全控制和性能优化,适用于生产环境。
搜索查询需经过意图识别与关键词提取,避免直接透传用户原始输入
必须实现缓存机制以降低 API 成本并提升响应速度
结果摘要应保留关键事实并标注信息来源,防止幻觉
对搜索结果进行可信度过滤,排除低质量或广告内容
严格控制输入长度与敏感词,保障系统安全
在非必要场景下优先使用本地知识库,减少对外部依赖
监控 API 调用量与延迟,设置熔断与降级策略
遵循 MCP 协议设计接口,确保技能可移植性
相关免费在线工具 加密/解密文本 使用加密算法(如AES、TripleDES、Rabbit或RC4)加密和解密文本明文。 在线工具,加密/解密文本在线工具,online
RSA密钥对生成器 生成新的随机RSA私钥和公钥pem证书。 在线工具,RSA密钥对生成器在线工具,online
Mermaid 预览与可视化编辑 基于 Mermaid.js 实时预览流程图、时序图等图表,支持源码编辑与即时渲染。 在线工具,Mermaid 预览与可视化编辑在线工具,online
随机西班牙地址生成器 随机生成西班牙地址(支持马德里、加泰罗尼亚、安达卢西亚、瓦伦西亚筛选),支持数量快捷选择、显示全部与下载。 在线工具,随机西班牙地址生成器在线工具,online
Gemini 图片去水印 基于开源反向 Alpha 混合算法去除 Gemini/Nano Banana 图片水印,支持批量处理与下载。 在线工具,Gemini 图片去水印在线工具,online
curl 转代码 解析常见 curl 参数并生成 fetch、axios、PHP curl 或 Python requests 示例代码。 在线工具,curl 转代码在线工具,online