Files
ailine/backend/app/core/web_search.py
root a362459d1f
Some checks failed
构建并部署 AI Agent 服务 / deploy (push) Failing after 6m13s
fix: 修复嵌入维度不匹配和Web搜索优化
- 将默认嵌入模型从 embedding-3 (2048维) 改为 embedding-2 (1024维),解决Qdrant维度不匹配问题
- 优化web_search.py,增加详细日志输出和更好的错误处理
- 更新模拟搜索结果,增加提示信息
2026-04-30 22:15:09 +08:00

181 lines
6.1 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"""
联网搜索公共工具 - 无需 API Key免费使用 DuckDuckGo
Web Search Public Utility - Free, no API Key, using DuckDuckGo
"""
from typing import List, Dict, Any, Optional
from dataclasses import dataclass
from datetime import datetime
@dataclass
class SearchResult:
"""搜索结果数据类"""
title: str
url: str
snippet: str
source: str = "DuckDuckGo"
timestamp: datetime = None
def __post_init__(self):
if self.timestamp is None:
self.timestamp = datetime.now()
class WebSearchTool:
"""联网搜索公共工具类"""
def __init__(self, max_results: int = 5):
self.max_results = max_results
def search(self, query: str, max_results: Optional[int] = None) -> List[SearchResult]:
"""
使用 DuckDuckGo 搜索
Args:
query: 搜索关键词
max_results: 返回结果数量,默认使用初始化时的设置
Returns:
搜索结果列表
"""
num_results = max_results or self.max_results
# 方法1: 尝试使用 duckduckgo-search 库
try:
from duckduckgo_search import DDGS
print(f"[WebSearch] 使用 DuckDuckGo 搜索: {query}")
with DDGS() as ddgs:
results = list(ddgs.text(query, max_results=num_results))
if results:
search_results = []
for r in results:
search_results.append(SearchResult(
title=r.get("title", ""),
url=r.get("href", ""),
snippet=r.get("body", ""),
source="DuckDuckGo"
))
print(f"[WebSearch] DuckDuckGo 返回 {len(search_results)} 条结果")
return search_results
except ImportError:
print("[WebSearch] duckduckgo-search 未安装,尝试备用方案")
except Exception as e:
print(f"[WebSearch] DuckDuckGo 搜索失败: {e}")
# 方法2: 尝试使用 requests 直接调用简单搜索API
try:
import requests
print(f"[WebSearch] 使用备用搜索方案")
# 使用百度搜索的简易接口(仅作演示)
# 或者返回一些模拟的提示结果
return self._search_mock(query, num_results)
except Exception as e:
print(f"[WebSearch] 备用方案也失败: {e}")
# 方法3: 返回模拟数据作为最后兜底
return self._search_mock(query, num_results)
def _search_mock(self, query: str, max_results: Optional[int] = None) -> List[SearchResult]:
"""模拟搜索结果(兜底方案)"""
print(f"[WebSearch] 使用模拟搜索结果 (查询: {query})")
# 根据查询内容生成更有意义的模拟结果
mock_templates = [
{
"title": f"关于「{query}」的相关介绍",
"snippet": "这是一个模拟的搜索结果。在实际部署中需要确保网络连接正常或者配置其他可用的搜索API。",
"url": "https://example.com/about"
},
{
"title": f"{query}」 - 最新动态",
"snippet": "搜索功能暂时使用模拟数据。请检查容器网络配置,或联系管理员配置可用的搜索服务。",
"url": "https://example.com/latest"
},
{
"title": f"了解更多关于「{query}」的内容",
"snippet": "提示:如果需要真实的联网搜索,可以考虑使用 Bing Search API、Google Custom Search JSON API 或其他商用搜索服务。",
"url": "https://example.com/more"
}
]
num = max_results or self.max_results
results = []
for i, template in enumerate(mock_templates[:num]):
results.append(SearchResult(
title=template["title"],
url=template["url"],
snippet=template["snippet"],
source="模拟数据"
))
return results
def format_search_results(self, results: List[SearchResult]) -> str:
"""
格式化搜索结果(带引用溯源)
Args:
results: 搜索结果列表
Returns:
格式化后的 Markdown 文本
"""
if not results:
return "未找到相关搜索结果"
lines = []
lines.append("## 🔍 联网搜索结果\n")
for idx, result in enumerate(results, 1):
lines.append(f"### [{idx}] {result.title}")
lines.append(f"- 🔗 来源:[{result.url}]({result.url})")
lines.append(f"- 📝 摘要:{result.snippet}")
lines.append(f"- 📅 时间:{result.timestamp.strftime('%Y-%m-%d %H:%M:%S')}")
lines.append("")
# 添加引用溯源说明
lines.append("---")
lines.append("💡 **引用溯源说明**")
lines.append("- 以上搜索结果均标注了来源链接")
lines.append("- 使用方括号数字标识引用(如 [1]、[2]")
lines.append("- 可通过链接追溯原始信息")
return "\n".join(lines)
# 单例实例
_web_search_tool = None
def get_web_search_tool() -> WebSearchTool:
"""获取联网搜索工具单例"""
global _web_search_tool
if _web_search_tool is None:
_web_search_tool = WebSearchTool()
return _web_search_tool
def web_search(query: str, max_results: int = 5) -> str:
"""
便捷函数:联网搜索并返回格式化结果
Args:
query: 搜索关键词
max_results: 返回结果数量
Returns:
格式化后的搜索结果文本
"""
tool = get_web_search_tool()
results = tool.search(query, max_results)
return tool.format_search_results(results)