|
|
|
|
|
from dotenv import load_dotenv
|
|
|
|
|
|
# 加载 .env 文件中的环境变量
|
|
|
|
|
|
load_dotenv()
|
|
|
|
|
|
|
|
|
|
|
|
import os
|
|
|
|
|
|
from serpapi import SerpApiClient
|
|
|
|
|
|
from typing import Dict, Any
|
|
|
|
|
|
|
|
|
|
|
|
def search(query: str) -> str:
|
|
|
|
|
|
"""
|
|
|
|
|
|
一个基于SerpApi的实战网页搜索引擎工具。
|
|
|
|
|
|
它会智能地解析搜索结果,优先返回直接答案或知识图谱信息。
|
|
|
|
|
|
"""
|
|
|
|
|
|
print(f"🔍 正在执行 [SerpApi] 网页搜索: {query}")
|
|
|
|
|
|
try:
|
|
|
|
|
|
api_key = os.getenv("SERPAPI_API_KEY")
|
|
|
|
|
|
if not api_key:
|
|
|
|
|
|
return "错误:SERPAPI_API_KEY 未在 .env 文件中配置。"
|
|
|
|
|
|
|
|
|
|
|
|
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}' 的信息。"
|
|
|
|
|
|
|
|
|
|
|
|
except Exception as e:
|
|
|
|
|
|
return f"搜索时发生错误: {e}"
|
|
|
|
|
|
|
|
|
|
|
|
from typing import Dict, Any
|
|
|
|
|
|
|
|
|
|
|
|
class ToolExecutor:
|
|
|
|
|
|
"""
|
|
|
|
|
|
一个工具执行器,负责管理和执行工具。
|
|
|
|
|
|
"""
|
|
|
|
|
|
def __init__(self):
|
|
|
|
|
|
self.tools: Dict[str, Dict[str, Any]] = {}
|
|
|
|
|
|
|
|
|
|
|
|
def registerTool(self, name: str, description: str, func: callable):
|
|
|
|
|
|
"""
|
|
|
|
|
|
向工具箱中注册一个新工具。
|
|
|
|
|
|
"""
|
|
|
|
|
|
if name in self.tools:
|
|
|
|
|
|
print(f"警告:工具 '{name}' 已存在,将被覆盖。")
|
|
|
|
|
|
|
|
|
|
|
|
self.tools[name] = {"description": description, "func": func}
|
|
|
|
|
|
print(f"工具 '{name}' 已注册。")
|
|
|
|
|
|
|
|
|
|
|
|
def getTool(self, name: str) -> callable:
|
|
|
|
|
|
"""
|
|
|
|
|
|
根据名称获取一个工具的执行函数。
|
|
|
|
|
|
"""
|
|
|
|
|
|
return self.tools.get(name, {}).get("func")
|
|
|
|
|
|
|
|
|
|
|
|
def getAvailableTools(self) -> str:
|
|
|
|
|
|
"""
|
|
|
|
|
|
获取所有可用工具的格式化描述字符串。
|
|
|
|
|
|
"""
|
|
|
|
|
|
return "\n".join([
|
|
|
|
|
|
f"- {name}: {info['description']}"
|
|
|
|
|
|
for name, info in self.tools.items()
|
|
|
|
|
|
])
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# --- 工具初始化与使用示例 ---
|
|
|
|
|
|
if __name__ == '__main__':
|
|
|
|
|
|
# 1. 初始化工具执行器
|
|
|
|
|
|
toolExecutor = ToolExecutor()
|
|
|
|
|
|
|
|
|
|
|
|
# 2. 注册我们的实战搜索工具
|
|
|
|
|
|
search_description = "一个网页搜索引擎。当你需要回答关于时事、事实以及在你的知识库中找不到的信息时,应使用此工具。"
|
|
|
|
|
|
toolExecutor.registerTool("Search", search_description, search)
|
|
|
|
|
|
|
|
|
|
|
|
# 3. 打印可用的工具
|
|
|
|
|
|
print("\n--- 可用的工具 ---")
|
|
|
|
|
|
print(toolExecutor.getAvailableTools())
|
|
|
|
|
|
|
|
|
|
|
|
# 4. 智能体的Action调用,这次我们问一个实时性的问题
|
|
|
|
|
|
print("\n--- 执行 Action: Search['英伟达最新的GPU型号是什么'] ---")
|
|
|
|
|
|
tool_name = "Search"
|
|
|
|
|
|
tool_input = "英伟达最新的GPU型号是什么"
|
|
|
|
|
|
|
|
|
|
|
|
tool_function = toolExecutor.getTool(tool_name)
|
|
|
|
|
|
if tool_function:
|
|
|
|
|
|
observation = tool_function(tool_input)
|
|
|
|
|
|
print("--- 观察 (Observation) ---")
|
|
|
|
|
|
print(observation)
|
|
|
|
|
|
else:
|
|
|
|
|
|
print(f"错误:未找到名为 '{tool_name}' 的工具。")
|