| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264 |
- """搜索工具 - HelloAgents原生搜索实现"""
- import os
- from typing import Optional, Dict, Any, List
- from ..base import Tool, ToolParameter
- class SearchTool(Tool):
- """
- 智能混合搜索工具
- 支持多种搜索引擎后端,智能选择最佳搜索源:
- 1. 混合模式 (hybrid) - 智能选择TAVILY或SERPAPI
- 2. Tavily API (tavily) - 专业AI搜索
- 3. SerpApi (serpapi) - 传统Google搜索
- """
- def __init__(self, backend: str = "hybrid", tavily_key: Optional[str] = None, serpapi_key: Optional[str] = None):
- super().__init__(
- name="search",
- description="一个智能网页搜索引擎。支持混合搜索模式,自动选择最佳搜索源。当你需要回答关于时事、事实以及在你的知识库中找不到的信息时,应使用此工具。"
- )
- self.backend = backend
- self.tavily_key = tavily_key or os.getenv("TAVILY_API_KEY")
- self.serpapi_key = serpapi_key or os.getenv("SERPAPI_API_KEY")
- self.available_backends = []
- self._setup_backends()
- def _setup_backends(self):
- """设置搜索后端"""
- # 检查Tavily可用性
- if self.tavily_key:
- try:
- from tavily import TavilyClient
- self.tavily_client = TavilyClient(api_key=self.tavily_key)
- self.available_backends.append("tavily")
- print("✅ Tavily搜索引擎已初始化")
- except ImportError:
- print("⚠️ Tavily未安装,无法使用Tavily搜索")
- else:
- print("⚠️ TAVILY_API_KEY未设置")
- # 检查SerpApi可用性
- if self.serpapi_key:
- try:
- import serpapi
- self.available_backends.append("serpapi")
- print("✅ SerpApi搜索引擎已初始化")
- except ImportError:
- print("⚠️ SerpApi未安装,无法使用SerpApi搜索")
- else:
- print("⚠️ SERPAPI_API_KEY未设置")
- # 确定最终使用的后端
- if self.backend == "hybrid":
- if self.available_backends:
- print(f"🔧 混合搜索模式已启用,可用后端: {', '.join(self.available_backends)}")
- else:
- print("⚠️ 没有可用的搜索后端,请配置API密钥")
- elif self.backend == "tavily" and "tavily" not in self.available_backends:
- print("⚠️ Tavily不可用,请检查TAVILY_API_KEY配置")
- elif self.backend == "serpapi" and "serpapi" not in self.available_backends:
- print("⚠️ SerpApi不可用,请检查SERPAPI_API_KEY配置")
- elif self.backend not in ["tavily", "serpapi", "hybrid"]:
- print("⚠️ 不支持的搜索后端,将使用hybrid模式")
- self.backend = "hybrid"
- def run(self, parameters: Dict[str, Any]) -> str:
- """
- 执行搜索
- Args:
- parameters: 包含input参数的字典
- Returns:
- 搜索结果
- """
- query = parameters.get("input", "").strip()
- if not query:
- return "错误:搜索查询不能为空"
- print(f"🔍 正在执行搜索: {query}")
- try:
- if self.backend == "hybrid":
- return self._search_hybrid(query)
- elif self.backend == "tavily":
- if "tavily" not in self.available_backends:
- return self._get_api_config_message()
- return self._search_tavily(query)
- elif self.backend == "serpapi":
- if "serpapi" not in self.available_backends:
- return self._get_api_config_message()
- return self._search_serpapi(query)
- else:
- return self._get_api_config_message()
- except Exception as e:
- return f"搜索时发生错误: {str(e)}"
- def _search_hybrid(self, query: str) -> str:
- """混合搜索 - 智能选择最佳搜索源"""
- # 检查是否有可用的搜索源
- if not self.available_backends:
- return self._get_api_config_message()
- # 优先使用Tavily(AI优化的搜索)
- if "tavily" in self.available_backends:
- try:
- print("🎯 使用Tavily进行AI优化搜索")
- return self._search_tavily(query)
- except Exception as e:
- print(f"⚠️ Tavily搜索失败: {e}")
- # 如果Tavily失败,尝试SerpApi
- if "serpapi" in self.available_backends:
- print("🔄 切换到SerpApi搜索")
- return self._search_serpapi(query)
- # 如果Tavily不可用,使用SerpApi
- elif "serpapi" in self.available_backends:
- try:
- print("🎯 使用SerpApi进行Google搜索")
- return self._search_serpapi(query)
- except Exception as e:
- print(f"⚠️ SerpApi搜索失败: {e}")
- # 如果都失败了,返回API配置提示
- return "❌ 所有搜索源都失败了,请检查网络连接和API密钥配置"
- def _search_tavily(self, query: str) -> str:
- """使用Tavily搜索"""
- response = self.tavily_client.search(
- query=query,
- search_depth="basic",
- include_answer=True,
- max_results=3
- )
- result = f"🎯 Tavily AI搜索结果:{response.get('answer', '未找到直接答案')}\n\n"
- for i, item in enumerate(response.get('results', [])[:3], 1):
- result += f"[{i}] {item.get('title', '')}\n"
- result += f" {item.get('content', '')[:200]}...\n"
- result += f" 来源: {item.get('url', '')}\n\n"
- return result
- def _search_serpapi(self, query: str) -> str:
- """使用SerpApi搜索"""
- try:
- from serpapi import SerpApiClient
- except ImportError:
- return "错误:SerpApi未安装,请运行 pip install serpapi"
- params = {
- "engine": "google",
- "q": query,
- "api_key": self.serpapi_key,
- "gl": "cn",
- "hl": "zh-cn",
- }
- client = SerpApiClient(params)
- results = client.get_dict()
- result_text = "🔍 SerpApi Google搜索结果:\n\n"
- # 智能解析:优先寻找最直接的答案
- if "answer_box" in results and "answer" in results["answer_box"]:
- result_text += f"💡 直接答案:{results['answer_box']['answer']}\n\n"
- if "knowledge_graph" in results and "description" in results["knowledge_graph"]:
- result_text += f"📖 知识图谱:{results['knowledge_graph']['description']}\n\n"
- if "organic_results" in results and results["organic_results"]:
- result_text += "🔗 相关结果:\n"
- for i, res in enumerate(results["organic_results"][:3], 1):
- result_text += f"[{i}] {res.get('title', '')}\n"
- result_text += f" {res.get('snippet', '')}\n"
- result_text += f" 来源: {res.get('link', '')}\n\n"
- return result_text
- return f"对不起,没有找到关于 '{query}' 的信息。"
- def _get_api_config_message(self) -> str:
- """获取API配置提示信息"""
- tavily_key = os.getenv("TAVILY_API_KEY")
- serpapi_key = os.getenv("SERPAPI_API_KEY")
- message = "❌ 没有可用的搜索源,请检查以下配置:\n\n"
- # 检查Tavily
- message += "1. Tavily API:\n"
- if not tavily_key:
- message += " ❌ 环境变量 TAVILY_API_KEY 未设置\n"
- message += " 📝 获取地址: https://tavily.com/\n"
- else:
- try:
- import tavily
- message += " ✅ API密钥已配置,包已安装\n"
- except ImportError:
- message += " ❌ API密钥已配置,但需要安装包: pip install tavily-python\n"
- message += "\n"
- # 检查SerpAPI
- message += "2. SerpAPI:\n"
- if not serpapi_key:
- message += " ❌ 环境变量 SERPAPI_API_KEY 未设置\n"
- message += " 📝 获取地址: https://serpapi.com/\n"
- else:
- try:
- import serpapi
- message += " ✅ API密钥已配置,包已安装\n"
- except ImportError:
- message += " ❌ API密钥已配置,但需要安装包: pip install google-search-results\n"
- message += "\n配置方法:\n"
- message += "- 在.env文件中添加: TAVILY_API_KEY=your_key_here\n"
- message += "- 或在环境变量中设置: export TAVILY_API_KEY=your_key_here\n"
- message += "\n配置后重新运行程序。"
- return message
- def get_parameters(self) -> List[ToolParameter]:
- """获取工具参数定义"""
- return [
- ToolParameter(
- name="input",
- type="string",
- description="搜索查询关键词",
- required=True
- )
- ]
- # 便捷函数
- def search(query: str, backend: str = "hybrid") -> str:
- """
- 便捷的搜索函数
- Args:
- query: 搜索查询关键词
- backend: 搜索后端 ("hybrid", "tavily", "serpapi")
- Returns:
- 搜索结果
- """
- tool = SearchTool(backend=backend)
- return tool.run({"input": query})
- # 专用搜索函数
- def search_tavily(query: str) -> str:
- """使用Tavily进行AI优化搜索"""
- tool = SearchTool(backend="tavily")
- return tool.run({"input": query})
- def search_serpapi(query: str) -> str:
- """使用SerpApi进行Google搜索"""
- tool = SearchTool(backend="serpapi")
- return tool.run({"input": query})
- def search_hybrid(query: str) -> str:
- """智能混合搜索,自动选择最佳搜索源"""
- tool = SearchTool(backend="hybrid")
- return tool.run({"input": query})
|