Reimplemented SearchWebTool using duckduckgo-search library

Co-authored-by: jxxghp <51039935+jxxghp@users.noreply.github.com>
This commit is contained in:
copilot-swe-agent[bot]
2026-01-23 15:20:06 +00:00
parent 8f17b52466
commit ca18705d88
2 changed files with 51 additions and 63 deletions

View File

@@ -9,7 +9,6 @@ from pydantic import BaseModel, Field
from app.agent.tools.base import MoviePilotTool from app.agent.tools.base import MoviePilotTool
from app.core.config import settings from app.core.config import settings
from app.log import logger from app.log import logger
from app.utils.http import AsyncRequestUtils
class SearchWebInput(BaseModel): class SearchWebInput(BaseModel):
@@ -47,8 +46,8 @@ class SearchWebTool(MoviePilotTool):
# 限制最大结果数 # 限制最大结果数
max_results = min(max(1, max_results or 5), 10) max_results = min(max(1, max_results or 5), 10)
# 使用DuckDuckGo API进行搜索 # 使用 duckduckgo-search 库进行搜索
search_results = await self._search_duckduckgo_api(query, max_results) search_results = await self._search_duckduckgo(query, max_results)
if not search_results: if not search_results:
return f"未找到与 '{query}' 相关的搜索结果" return f"未找到与 '{query}' 相关的搜索结果"
@@ -65,9 +64,9 @@ class SearchWebTool(MoviePilotTool):
return error_message return error_message
@staticmethod @staticmethod
async def _search_duckduckgo_api(query: str, max_results: int) -> list: async def _search_duckduckgo(query: str, max_results: int) -> list:
""" """
使用DuckDuckGo API进行搜索 使用 duckduckgo-search 库进行搜索
Args: Args:
query: 搜索查询 query: 搜索查询
@@ -77,71 +76,59 @@ class SearchWebTool(MoviePilotTool):
搜索结果列表 搜索结果列表
""" """
try: try:
# DuckDuckGo Instant Answer API from duckduckgo_search import DDGS
api_url = "https://api.duckduckgo.com/" import asyncio
params = {
"q": query,
"format": "json",
"no_html": "1",
"skip_disambig": "1"
}
# 使用代理(如果配置了) # duckduckgo-search 是同步库,需要在 executor 中运行
http_utils = AsyncRequestUtils( def sync_search():
proxies=settings.PROXY, results = []
timeout=10 try:
) # 使用代理(如果配置了)
ddgs_kwargs = {}
data = await http_utils.get_json(api_url, params=params) if settings.PROXY:
# duckduckgo-search 支持代理配置
results = [] if isinstance(settings.PROXY, dict):
proxy_url = settings.PROXY.get('http') or settings.PROXY.get('https')
if data: else:
# 处理AbstractText摘要 proxy_url = settings.PROXY
if data.get("AbstractText"):
results.append({ if proxy_url:
"title": data.get("Heading", query), ddgs_kwargs['proxy'] = proxy_url
"snippet": data.get("AbstractText", ""),
"url": data.get("AbstractURL", ""), # 设置超时
"source": "DuckDuckGo Abstract" ddgs_kwargs['timeout'] = 20
})
with DDGS(**ddgs_kwargs) as ddgs:
# 处理RelatedTopics相关主题 # 使用 text 方法进行搜索
related_topics = data.get("RelatedTopics", []) search_results = ddgs.text(
for topic in related_topics[:max_results - len(results)]: keywords=query,
if isinstance(topic, dict): max_results=max_results
text = topic.get("Text", "") )
first_url = topic.get("FirstURL", "")
if text and first_url: for result in search_results:
# 提取标题(通常在" - "之前)
title = text.split(" - ")[0] if " - " in text else text[:100]
snippet = text
results.append({ results.append({
"title": title.strip(), 'title': result.get('title', ''),
"snippet": snippet, 'snippet': result.get('body', ''),
"url": first_url, 'url': result.get('href', ''),
"source": "DuckDuckGo Related" 'source': 'DuckDuckGo'
}) })
except Exception as e:
logger.warning(f"duckduckgo-search 搜索失败: {e}")
raise
# 处理Results搜索结果 return results
api_results = data.get("Results", [])
for result in api_results[:max_results - len(results)]:
if isinstance(result, dict):
title = result.get("Text", "")
url = result.get("FirstURL", "")
if title and url:
results.append({
"title": title,
"snippet": result.get("Text", ""),
"url": url,
"source": "DuckDuckGo Results"
})
return results[:max_results] # 在线程池中运行同步搜索
loop = asyncio.get_event_loop()
results = await loop.run_in_executor(None, sync_search)
return results
except ImportError:
logger.error("duckduckgo-search 库未安装,请运行: pip install duckduckgo-search")
return []
except Exception as e: except Exception as e:
logger.warning(f"DuckDuckGo API搜索失败: {e}") logger.warning(f"DuckDuckGo 搜索失败: {e}")
return [] return []
@staticmethod @staticmethod

View File

@@ -91,3 +91,4 @@ langchain-deepseek~=0.1.4
langchain-experimental~=0.3.4 langchain-experimental~=0.3.4
openai~=1.108.2 openai~=1.108.2
google-generativeai~=0.8.5 google-generativeai~=0.8.5
duckduckgo-search~=7.2.1