Compare commits

..

8 Commits

Author SHA1 Message Date
daccc625c3 revert: 撤回错误的修改,恢复原版本
用户指出SiliconFlow平台确实支持标准tool消息类型,之前的修改是错误的

版本: v3.0.7
2026-04-15 10:01:59 +08:00
a2a7fd46c3 chore: 版本号更新到v3.0.6 2026-04-15 09:52:31 +08:00
baf5913bfb fix: SiliconFlow平台Function Calling第二轮调用兼容
问题:SiliconFlow平台不支持标准tool消息类型,第二轮调用返回参数无效

修复:将tool消息转换为user消息格式
- 收集所有tool消息的内容
- 合并为一个用户消息发送给模型
- 添加明确的提示让模型直接根据结果回答

版本: v3.0.6
2026-04-15 09:52:19 +08:00
ae08e01e55 fix: Kimi模型伪工具调用格式过滤
修复Kimi-K2.5模型在第二轮调用时输出伪工具调用格式的问题:
- 添加系统提示告诉模型直接根据工具结果回答
- 过滤 <|tool_calls_section_begin|> 等内部格式标记
- 清理多余空行

版本: v3.0.1
2026-04-15 09:45:08 +08:00
9048d94e33 fix: 添加详细日志诊断工具调用消息格式 2026-04-15 02:25:05 +08:00
291de733a4 fix: chat_with_tool_results不重复添加tool结果,修正消息格式 2026-04-15 01:03:10 +08:00
10f67a807a fix: get_agent_config添加supports_vision和supports_function_calling字段 2026-04-14 19:20:17 +08:00
d9ac2c78f6 feat: 对话区左侧显示Agent信息 2026-04-14 19:14:31 +08:00
4 changed files with 108 additions and 18 deletions

View File

@@ -1121,7 +1121,6 @@ async def websocket_endpoint(websocket: WebSocket, user_id: str):
messages=history_with_tools,
provider_config=agent_config['provider'],
agent_config=agent_config['agent'],
tool_results=tool_results,
enable_thinking=enable_thinking
)

View File

@@ -137,6 +137,9 @@ class AgentService:
'api_key': provider.api_key if provider else None,
'supports_thinking': provider.supports_thinking if provider else False,
'thinking_model': provider.thinking_model if provider else None,
'supports_vision': provider.supports_vision if provider else False,
'vision_model': provider.vision_model if provider else None,
'supports_function_calling': provider.supports_function_calling if provider else False,
'default_model': provider.default_model if provider else 'auto',
'max_tokens': provider.max_tokens if provider else 4096,
'temperature': provider.temperature if provider else 0.7,

View File

@@ -514,17 +514,15 @@ class LLMService:
messages: List[Dict],
provider_config: dict,
agent_config: dict,
tool_results: List[Dict],
enable_thinking: bool = True
) -> Tuple[str, Optional[str]]:
"""
第二阶段调用:将工具执行结果返回给LLM
第二阶段调用:使用包含工具调用和结果的完整消息历史
Args:
messages: 对话历史(包含工具调用和结果)
messages: 已包含assistant tool_calls和tool结果的完整消息历史
provider_config: LLM Provider配置
agent_config: Agent配置
tool_results: 工具执行结果 [{"tool_call_id": "xxx", "content": "..."}]
Returns:
Tuple[str, Optional[str]]: (回复内容, 思考过程)
@@ -535,14 +533,8 @@ class LLMService:
max_tokens = provider_config.get('max_tokens', 4096)
temperature = agent_config.get('temperature_override') or provider_config.get('temperature', 0.7)
# 将工具结果添加到消息历史
# 消息历史已经包含了assistant的tool_calls和tool结果直接使用
final_messages = messages.copy()
for result in tool_results:
final_messages.append({
"role": "tool",
"tool_call_id": result['tool_call_id'],
"content": result['content']
})
# 调用LLM生成最终回复
url = f"{api_base.rstrip('/')}/chat/completions"
@@ -557,14 +549,14 @@ class LLMService:
"max_tokens": max_tokens
}
logger.info(f"工具结果返回LLM: url={url}, model={model}")
logger.info(f"工具结果返回LLM: url={url}, model={model}, 消息数={len(final_messages)}")
try:
async with httpx.AsyncClient(timeout=60.0) as client:
response = await client.post(url, headers=headers, json=payload)
if response.status_code != 200:
logger.error(f"API返回错误: status={response.status_code}")
logger.error(f"API返回错误: status={response.status_code}, body={response.text[:500]}")
response.raise_for_status()
data = response.json()

View File

@@ -134,6 +134,23 @@
/* 工具调用记录显示 */
.tool-call-record { margin-top: 8px; padding: 8px 12px; background: #e8f5e9; border-radius: 8px; font-size: 12px; color: #10a37f; }
.tool-call-record i { margin-right: 4px; }
/* Agent信息侧边栏 */
.agent-info-sidebar { width: 200px; background: #f8f9fa; border-right: 1px solid #e0e0e0; padding: 16px; display: flex; flex-direction: column; }
.agent-info-header { display: flex; align-items: center; gap: 8px; margin-bottom: 12px; }
.agent-avatar { width: 48px; height: 48px; background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); border-radius: 12px; display: flex; align-items: center; justify-content: center; color: white; font-size: 24px; }
.agent-name-area { flex: 1; }
.agent-name-area h3 { font-size: 16px; margin: 0; color: #333; }
.agent-name-area small { color: #999; font-size: 12px; }
.agent-info-section { margin-top: 16px; }
.agent-info-section h4 { font-size: 13px; color: #666; margin: 0 0 8px 0; font-weight: 500; }
.agent-info-section p { font-size: 12px; color: #333; line-height: 1.5; margin: 0; }
.agent-capabilities { display: flex; flex-wrap: wrap; gap: 6px; margin-top: 8px; }
.capability-tag { padding: 4px 8px; background: #e8f5e9; border-radius: 6px; font-size: 11px; color: #10a37f; }
.capability-tag.disabled { background: #f5f5f5; color: #999; }
.agent-model-info { margin-top: 12px; padding: 8px; background: white; border-radius: 8px; border: 1px solid #e0e0e0; }
.agent-model-info label { font-size: 11px; color: #999; }
.agent-model-info span { font-size: 12px; color: #333; display: block; margin-top: 2px; }
.add-phrase-btn { padding: 6px 10px; background: #f0f0f0; border: 1px solid #ddd; border-radius: 6px; cursor: pointer; font-size: 12px; color: #666; white-space: nowrap; flex-shrink: 0; }
.add-phrase-btn:hover { background: #e8e8e8; }
.phrase-list-wrapper { flex: 1; overflow-x: auto; overflow-y: hidden; scrollbar-width: thin; }
@@ -191,8 +208,37 @@
</div>
</div>
<div class="messages-container" id="messagesContainer">
<div class="welcome"><h2>👋 开始对话</h2><p>选择Agent开始聊天</p></div>
<!-- 对话区域左侧Agent信息 + 右侧消息 -->
<div class="chat-area" style="display:flex;flex:1;overflow:hidden;">
<!-- Agent信息侧边栏 -->
<div class="agent-info-sidebar" id="agentInfoSidebar">
<div class="agent-info-header">
<div class="agent-avatar" id="agentAvatar">🤖</div>
<div class="agent-name-area">
<h3 id="agentDisplayName">加载中...</h3>
<small id="agentName">agent-name</small>
</div>
</div>
<div class="agent-info-section">
<h4>简介</h4>
<p id="agentDescription">-</p>
</div>
<div class="agent-info-section">
<h4>能力</h4>
<div class="agent-capabilities" id="agentCapabilities">
<!-- 动态渲染 -->
</div>
</div>
<div class="agent-model-info">
<label>模型</label>
<span id="agentModelInfo">-</span>
</div>
</div>
<!-- 消息容器 -->
<div class="messages-container" id="messagesContainer" style="flex:1;">
<div class="welcome"><h2>👋 开始对话</h2><p>选择Agent开始聊天</p></div>
</div>
</div>
<div class="input-container">
@@ -293,8 +339,8 @@
const res = await fetch('/api/v2/providers');
const data = await res.json();
providers = data.providers || [];
// 加载后检查工具支持如果agents已加载
if (agents.length > 0) showToolWarning();
// 加载后更新Agent信息侧边栏如果agents已加载
if (agents.length > 0) renderAgentInfoSidebar();
} catch (e) { console.error('加载Provider失败:', e); }
}
@@ -323,9 +369,58 @@
const defaultAgent = agents.find(a => a.is_default) || agents[0];
if (defaultAgent) currentAgentId = defaultAgent.id;
renderAgentSelect();
renderAgentInfoSidebar(); // 渲染Agent信息侧边栏
} catch (e) { console.error('加载Agent失败:', e); }
}
// 渲染Agent信息侧边栏
function renderAgentInfoSidebar() {
const agent = agents.find(a => a.id === currentAgentId);
if (!agent) return;
// 更新名称
document.getElementById('agentDisplayName').textContent = agent.display_name || agent.name;
document.getElementById('agentName').textContent = agent.name;
// 更新头像用emoji或首字母
const avatar = document.getElementById('agentAvatar');
avatar.textContent = agent.display_name?.charAt(0) || agent.name?.charAt(0) || '🤖';
// 更新描述
document.getElementById('agentDescription').textContent = agent.description || '暂无描述';
// 更新能力标签
const capabilitiesHtml = [];
// 检查思考能力
const provider = providers.find(p => p.id === agent.llm_provider_id);
if (provider) {
if (provider.supports_thinking) {
capabilitiesHtml.push('<span class="capability-tag"><i class="ri-lightbulb-line"></i> 思考</span>');
}
if (provider.supports_vision) {
capabilitiesHtml.push('<span class="capability-tag"><i class="ri-image-line"></i> 视觉</span>');
}
if (provider.supports_function_calling) {
capabilitiesHtml.push('<span class="capability-tag"><i class="ri-tools-line"></i> 工具调用</span>');
} else {
capabilitiesHtml.push('<span class="capability-tag disabled"><i class="ri-tools-line"></i> 工具(手动)</span>');
}
// 更新模型信息
const model = agent.model_override || provider.default_model || 'auto';
document.getElementById('agentModelInfo').textContent = model;
}
// 检查工具配置
const agentTools = agent.tools || [];
if (agentTools.includes('search')) {
capabilitiesHtml.push('<span class="capability-tag"><i class="ri-search-line"></i> 搜索</span>');
}
document.getElementById('agentCapabilities').innerHTML = capabilitiesHtml.join('') || '<span class="capability-tag disabled">基础对话</span>';
}
function renderAgentSelect() {
const select = document.getElementById('agentSelect');
select.innerHTML = agents.filter(a => a.is_active).map(a =>
@@ -345,6 +440,7 @@
if (ws?.readyState === WebSocket.OPEN) ws.send(JSON.stringify({ action: 'switch_agent', agent_id: currentAgentId }));
await createNewConversation();
showAgentSwitchNotice();
renderAgentInfoSidebar(); // 更新侧边栏信息
}
}