Compare commits

..

2 Commits

Author SHA1 Message Date
c0ed6cd505 fix: get_agent_config 添加 tools 字段返回 2026-04-13 16:46:00 +08:00
25e92b1fb1 fix: 搜索功能修复
- 添加详细日志帮助调试
- 搜索结果发送到前端展示
- 前端增加搜索结果展示组件
- 修复 datetime 导入错误
2026-04-13 16:34:00 +08:00
3 changed files with 67 additions and 5 deletions

View File

@@ -783,11 +783,19 @@ async def websocket_endpoint(websocket: WebSocket, user_id: str):
# 检查是否需要执行搜索
search_context = None
search_results_for_client = None # 用于发送给前端
logger.info(f"检查搜索条件: agent_tools={agent_tools}, disabled_tools={disabled_tools}")
if 'search' in agent_tools and 'search' not in disabled_tools:
# 只要启用了搜索工具且未禁用,就执行搜索(不再依赖关键词检测)
should_search = True
# 只要启用了搜索工具且未禁用,就执行搜索
logger.info("搜索条件满足,开始执行搜索")
if should_search:
# 执行搜索
tool_service = ToolService(db)
search_tool = tool_service.get_default_tool('search')
logger.info(f"获取到搜索工具: {search_tool.name if search_tool else 'None'}")
if search_tool and search_tool.config.get('api_key'):
# 执行搜索
tool_service = ToolService(db)
search_tool = tool_service.get_default_tool('search')
@@ -815,12 +823,30 @@ async def websocket_endpoint(websocket: WebSocket, user_id: str):
duration_ms = int((time.time() - start_time) * 1000)
if search_result.get("results"):
# 构建搜索上下文
# 构建搜索上下文给LLM
search_context = "\n\n【搜索结果】\n"
for i, r in enumerate(search_result["results"][:5], 1):
search_context += f"{i}. {r.get('title', 'N/A')}\n {r.get('content', r.get('snippet', 'N/A'))[:200]}\n 来源: {r.get('url', 'N/A')}\n"
logger.info(f"搜索完成: {len(search_result['results'])} 条结果")
# 构建搜索结果给前端展示
search_results_for_client = [
{
"title": r.get('title', 'N/A'),
"snippet": r.get('content', r.get('snippet', ''))[:150],
"url": r.get('url', 'N/A')
}
for r in search_result["results"][:5]
]
# 发送搜索结果给前端
await websocket.send_json({
"type": "search_results",
"conversation_id": conversation_id,
"results": search_results_for_client,
"query": message
})
# 更新统计和日志
tool_service.increment_stats(search_tool.id, True)
tool_service.log_usage({

View File

@@ -126,6 +126,7 @@ class AgentService:
'model_override': agent.model_override,
'max_history': agent.max_history,
'temperature_override': agent.temperature_override,
'tools': agent.tools or [], # 工具列表
'is_default': agent.is_default,
'is_active': agent.is_active
},
@@ -531,7 +532,7 @@ class ToolService:
def get_usage_stats(self, days: int = 7) -> Dict:
"""获取工具使用统计"""
from datetime import timedelta
from datetime import datetime, timedelta
start_date = datetime.utcnow() - timedelta(days=days)
# 按工具类型统计

View File

@@ -86,6 +86,13 @@
.thinking-toggle { font-size: 12px; color: #667eea; }
.thinking-content { margin-top: 12px; display: none; }
.thinking-content.expanded { display: block; }
.search-results-box { margin: 8px 0; padding: 12px; background: #f8f9fa; border-radius: 8px; border: 1px solid #e9ecef; }
.search-results-box h5 { margin: 0 0 8px; font-size: 14px; color: #495057; }
.search-result-item { margin-bottom: 8px; padding: 8px; background: white; border-radius: 6px; }
.search-result-item:last-child { margin-bottom: 0; }
.search-result-title { font-size: 13px; color: #10a37f; font-weight: 500; margin-bottom: 4px; }
.search-result-snippet { font-size: 12px; color: #666; line-height: 1.4; }
.search-result-url { font-size: 11px; color: #999; margin-top: 4px; }
/* Agent信息 */
.agent-info { font-size: 12px; color: #999; margin-top: 8px; }
@@ -286,6 +293,7 @@
document.getElementById('sendBtn').disabled = false;
break;
case 'error': showError(data.message); document.getElementById('sendBtn').disabled = false; break;
case 'search_results': displaySearchResults(data.results, data.query); break;
}
}
@@ -623,6 +631,33 @@
container.appendChild(div);
}
function displaySearchResults(results, query) {
if (!results || results.length === 0) return;
const container = document.getElementById('messagesContainer');
const div = document.createElement('div');
div.className = 'message assistant';
let html = `<div class="message-avatar">🔍</div><div class="message-body">
<div class="search-results-box">
<h5>搜索: ${escapeHtml(query.substring(0, 50))}${query.length > 50 ? '...' : ''}</h5>`;
for (const r of results) {
html += `<div class="search-result-item">
<div class="search-result-title">${escapeHtml(r.title)}</div>
<div class="search-result-snippet">${escapeHtml(r.snippet)}</div>
<div class="search-result-url">${escapeHtml(r.url)}</div>
</div>`;
}
html += '</div></div>';
div.innerHTML = html;
container.appendChild(div);
// 滚动到底部
container.scrollTop = container.scrollHeight;
}
// 会话管理
async function loadConversations() {
const res = await fetch('/api/conversations');