2026-03-30 08:48:36 +00:00
|
|
|
|
"""
|
|
|
|
|
|
tools/web_search.py
|
2026-06-01 07:53:27 +00:00
|
|
|
|
网络搜索工具 —— google搜索
|
2026-03-30 08:48:36 +00:00
|
|
|
|
配置通过 settings.tools['web_search'] 读取
|
|
|
|
|
|
"""
|
2026-02-28 08:21:35 +00:00
|
|
|
|
|
2026-06-01 07:53:27 +00:00
|
|
|
|
from dataclasses import dataclass
|
|
|
|
|
|
from serpapi import SerpApiClient
|
2026-06-06 08:42:07 +00:00
|
|
|
|
from agent.config.settings import settings
|
|
|
|
|
|
from agent.tools.base_tool import BaseTool
|
|
|
|
|
|
from agent.utils.logger import get_logger
|
2026-03-30 08:48:36 +00:00
|
|
|
|
|
|
|
|
|
|
logger = get_logger("TOOL.WebSearch")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def _cfg(key: str, fallback=None):
|
|
|
|
|
|
return settings.tools['web_search'].get(key, fallback)
|
2026-02-28 08:21:35 +00:00
|
|
|
|
|
|
|
|
|
|
|
2026-03-30 08:48:36 +00:00
|
|
|
|
@dataclass
|
|
|
|
|
|
class SearchResult:
|
|
|
|
|
|
title: str
|
|
|
|
|
|
url: str
|
|
|
|
|
|
snippet: str
|
|
|
|
|
|
rank: int = 0
|
2026-02-28 08:21:35 +00:00
|
|
|
|
|
2026-03-30 08:48:36 +00:00
|
|
|
|
def __str__(self) -> str:
|
|
|
|
|
|
return f"[{self.rank}] {self.title}\n {self.url}\n {self.snippet}"
|
|
|
|
|
|
|
|
|
|
|
|
|
2026-04-15 08:20:22 +00:00
|
|
|
|
class Tool(BaseTool):
|
2026-02-28 08:21:35 +00:00
|
|
|
|
name = "web_search"
|
2026-03-30 08:48:36 +00:00
|
|
|
|
description = (
|
|
|
|
|
|
"在互联网上搜索信息,返回相关网页的标题、链接和摘要。"
|
|
|
|
|
|
"适用于需要实时信息、最新资讯或不确定的知识查询。"
|
|
|
|
|
|
)
|
2026-02-28 08:21:35 +00:00
|
|
|
|
parameters = {
|
2026-03-30 08:48:36 +00:00
|
|
|
|
"type": "object",
|
|
|
|
|
|
"properties": {
|
|
|
|
|
|
"query": {
|
|
|
|
|
|
"type": "string",
|
|
|
|
|
|
"description": "搜索关键词或问题,例如: 'Python 3.12 新特性'",
|
2026-06-01 07:53:27 +00:00
|
|
|
|
}
|
2026-03-30 08:48:36 +00:00
|
|
|
|
},
|
|
|
|
|
|
"required": ["query"],
|
2026-02-28 08:21:35 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-06-01 07:53:27 +00:00
|
|
|
|
def execute(self, query: str = "", **_) -> str:
|
|
|
|
|
|
"""
|
|
|
|
|
|
一个基于SerpApi的实战网页搜索引擎工具。
|
|
|
|
|
|
它会智能地解析搜索结果,优先返回直接答案或知识图谱信息。
|
|
|
|
|
|
"""
|
|
|
|
|
|
print(f"🔍 正在执行 [SerpApi] 网页搜索: {query}")
|
2026-03-30 08:48:36 +00:00
|
|
|
|
try:
|
2026-06-01 07:53:27 +00:00
|
|
|
|
api_key = _cfg("api_key")
|
|
|
|
|
|
if not api_key:
|
|
|
|
|
|
return "错误:ApiKey未配置。"
|
|
|
|
|
|
|
|
|
|
|
|
params = {
|
|
|
|
|
|
"engine": "google",
|
|
|
|
|
|
"q": query,
|
|
|
|
|
|
"api_key": api_key,
|
|
|
|
|
|
"gl": "cn", # 国家代码
|
|
|
|
|
|
"hl": "zh-cn", # 语言代码
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
client = SerpApiClient(params)
|
|
|
|
|
|
results = client.get_dict()
|
|
|
|
|
|
|
|
|
|
|
|
# 智能解析:优先寻找最直接的答案
|
|
|
|
|
|
if "answer_box_list" in results:
|
|
|
|
|
|
return "\n".join(results["answer_box_list"])
|
|
|
|
|
|
if "answer_box" in results and "answer" in results["answer_box"]:
|
|
|
|
|
|
return results["answer_box"]["answer"]
|
|
|
|
|
|
if "knowledge_graph" in results and "description" in results["knowledge_graph"]:
|
|
|
|
|
|
return results["knowledge_graph"]["description"]
|
|
|
|
|
|
if "organic_results" in results and results["organic_results"]:
|
|
|
|
|
|
# 如果没有直接答案,则返回前三个有机结果的摘要
|
|
|
|
|
|
snippets = [
|
|
|
|
|
|
f"[{i + 1}] {res.get('title', '')}\n{res.get('snippet', '')}"
|
|
|
|
|
|
for i, res in enumerate(results["organic_results"][:3])
|
|
|
|
|
|
]
|
|
|
|
|
|
return "\n\n".join(snippets)
|
|
|
|
|
|
|
|
|
|
|
|
return f"对不起,没有找到关于 '{query}' 的信息。"
|
2026-03-30 08:48:36 +00:00
|
|
|
|
|
|
|
|
|
|
except Exception as e:
|
2026-06-01 07:53:27 +00:00
|
|
|
|
return f"搜索时发生错误: {e}"
|