Agent / tools /web_search.py
BiGuan's picture
Upload 13 files
e15103f verified
Raw
History Blame Contribute Delete
3.48 kB
"""
tools/web_search.py —— 工具①:联网搜索
给 agent 一个"上网搜东西"的能力。
"""
import time
from langchain_core.tools import tool
from config import TAVILY_API_KEY
def _tavily(query: str):
"""用 Tavily 搜索。函数名以下划线开头,表示这是"内部辅助函数",只给本文件自己调用。"""
# 没有配密钥就直接返回 None(表示"用不了 Tavily"),让外层去用备胎 DuckDuckGo。
if not TAVILY_API_KEY:
return None
import requests
# 向 Tavily 的接口发一个请求,带上:密钥、搜索词、最多要 8 条结果、并让它顺便给个总结性答案。
r = requests.post(
"https://api.tavily.com/search",
json={"api_key": TAVILY_API_KEY, "query": query, "max_results": 8, "include_answer": True},
timeout=30, # 最多等 30 秒,避免卡死
)
r.raise_for_status() # 如果服务器返回错误状态码,这里会主动报错
data = r.json() # 把返回的 JSON 文本解析成 Python 能用的数据
# 下面把搜索结果拼成一段整齐的文字返回(标题、网址、摘要),方便大模型阅读。
parts = []
if data.get("answer"):
parts.append("Answer: " + data["answer"])
for it in data.get("results", []):
parts.append(f"[{it.get('title')}]({it.get('url')})\n{it.get('content', '')}")
return "## Search Results\n\n" + "\n\n".join(parts) if parts else None
def _duckduckgo(query: str):
"""备用搜索引擎:DuckDuckGo。同样是内部辅助函数。"""
# 不同版本的库名字不一样,try/except 是为了"哪个能导入就用哪个",增强兼容性。
try:
from ddgs import DDGS
except ImportError:
from duckduckgo_search import DDGS
results = DDGS().text(query, max_results=10) # 搜索,最多取 10 条
if not results:
return None
# 同样把结果拼成整齐文字返回。
return "## Search Results\n\n" + "\n\n".join(
f"[{r.get('title')}]({r.get('href') or r.get('url')})\n{r.get('body') or r.get('content', '')}"
for r in results
)
@tool
def web_search(query: str) -> str:
"""Search the web and return the top results (title, url, snippet). Use for general,
up-to-date research; follow up with `visit_webpage` to read a result in full. For
encyclopedic facts prefer `wikipedia_search`, which is more reliable."""
# 第一步:先试 Tavily。成功拿到结果就直接返回。
try:
out = _tavily(query)
if out:
return out
except Exception:
pass # Tavily 出错也不报错中断,继续往下用
# 第二步:用 DuckDuckGo,并且最多重试 4 次(免费引擎容易临时失败,多试几次更稳)。
last_err = None
for attempt in range(4):
try:
out = _duckduckgo(query)
if out:
return out
except Exception as e:
last_err = e
# 每次失败后等一会儿再试,且等待时间越来越长(1.5秒、3秒、4.5秒...),避免被对方当成攻击而封锁。
time.sleep(1.5 * (attempt + 1))
# 两套引擎都没搜到,就返回一句提示,告诉大模型"换个工具或换个说法再试"。
return (
f"Web search returned nothing (last error: {last_err}). "
"Try `wikipedia_search`, rephrase the query, or `visit_webpage` on a known url."
)