Implement DuckDuckGo search (#1432)

* Implement DuckDuckGo search

* docs: add DuckDuckGo web search to config example
This commit is contained in:
Ben Piper
2026-03-26 21:20:22 -04:00
committed by GitHub
parent 1c542ab7f1
commit c13793386f
3 changed files with 107 additions and 3 deletions

View File

@@ -0,0 +1,3 @@
from .tools import web_search_tool
__all__ = ["web_search_tool"]

View File

@@ -0,0 +1,95 @@
"""
Web Search Tool - Search the web using DuckDuckGo (no API key required).
"""
import json
import logging
from langchain.tools import tool
from deerflow.config import get_app_config
logger = logging.getLogger(__name__)
def _search_text(
query: str,
max_results: int = 5,
region: str = "wt-wt",
safesearch: str = "moderate",
) -> list[dict]:
"""
Execute text search using DuckDuckGo.
Args:
query: Search keywords
max_results: Maximum number of results
region: Search region
safesearch: Safe search level
Returns:
List of search results
"""
try:
from ddgs import DDGS
except ImportError:
logger.error("ddgs library not installed. Run: pip install ddgs")
return []
ddgs = DDGS(timeout=30)
try:
results = ddgs.text(
query,
region=region,
safesearch=safesearch,
max_results=max_results,
)
return list(results) if results else []
except Exception as e:
logger.error(f"Failed to search web: {e}")
return []
@tool("web_search", parse_docstring=True)
def web_search_tool(
query: str,
max_results: int = 5,
) -> str:
"""Search the web for information. Use this tool to find current information, news, articles, and facts from the internet.
Args:
query: Search keywords describing what you want to find. Be specific for better results.
max_results: Maximum number of results to return. Default is 5.
"""
config = get_app_config().get_tool_config("web_search")
# Override max_results from config if set
if config is not None and "max_results" in config.model_extra:
max_results = config.model_extra.get("max_results", max_results)
results = _search_text(
query=query,
max_results=max_results,
)
if not results:
return json.dumps({"error": "No results found", "query": query}, ensure_ascii=False)
normalized_results = [
{
"title": r.get("title", ""),
"url": r.get("href", r.get("link", "")),
"content": r.get("body", r.get("snippet", "")),
}
for r in results
]
output = {
"query": query,
"total_results": len(normalized_results),
"results": normalized_results,
}
return json.dumps(output, indent=2, ensure_ascii=False)

View File

@@ -231,12 +231,18 @@ tool_groups:
# Configure available tools for the agent to use
tools:
# Web search tool (requires Tavily API key)
# Web search tool (uses DuckDuckGo, no API key required)
- name: web_search
group: web
use: deerflow.community.tavily.tools:web_search_tool
use: deerflow.community.ddg_search.tools:web_search_tool
max_results: 5
# api_key: $TAVILY_API_KEY # Set if needed
# Web search tool (requires Tavily API key)
# - name: web_search
# group: web
# use: deerflow.community.tavily.tools:web_search_tool
# max_results: 5
# # api_key: $TAVILY_API_KEY # Set if needed
# Web search tool (uses InfoQuest, requires InfoQuest API key)
# - name: web_search