From 04b52901597bb1232bf0b13b655137361564d155 Mon Sep 17 00:00:00 2001 From: root <953994191@qq.com> Date: Fri, 1 May 2026 01:13:06 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E6=B7=BB=E5=8A=A0=E5=85=9C=E5=BA=95?= =?UTF-8?q?=E8=BE=93=E5=87=BA=E6=9C=BA=E5=88=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 功能: 1. 当发生错误时,不再只显示错误信息,而是提供友好的兜底回复 2. 兜底回复包含: - 自我介绍(介绍AI助手的功能) - 红色突出显示的错误信息(使用 diff 语法) - 如果是超时/不可用错误,提示用户手动切换模型 3. 同时支持流式和非流式接口的兜底机制 4. 流式接口使用打字机效果显示兜底回复 --- backend/app/backend.py | 104 ++++++++++++++++++++++++++++++++--------- 1 file changed, 83 insertions(+), 21 deletions(-) diff --git a/backend/app/backend.py b/backend/app/backend.py index bbf33e3..e1b0d52 100644 --- a/backend/app/backend.py +++ b/backend/app/backend.py @@ -151,27 +151,62 @@ async def chat_endpoint( raise HTTPException(status_code=400, detail="message required") thread_id = request.thread_id or str(uuid.uuid4()) - result = await agent_service.process_message( - request.message, thread_id, request.model, request.user_id - ) - # 提取 token 统计信息 - token_usage = result.get("token_usage", {}) - input_tokens = token_usage.get('prompt_tokens', token_usage.get('input_tokens', 0)) - output_tokens = token_usage.get('completion_tokens', token_usage.get('output_tokens', 0)) - elapsed_time = result.get("elapsed_time", 0.0) - - actual_model = request.model if request.model in agent_service.graphs else next(iter(agent_service.graphs.keys())) - - return ChatResponse( - reply=result["reply"], - thread_id=thread_id, - model_used=actual_model, - input_tokens=input_tokens, - output_tokens=output_tokens, - total_tokens=input_tokens + output_tokens, - elapsed_time=elapsed_time - ) + try: + result = await agent_service.process_message( + request.message, thread_id, request.model, request.user_id + ) + + # 提取 token 统计信息 + token_usage = result.get("token_usage", {}) + input_tokens = token_usage.get('prompt_tokens', token_usage.get('input_tokens', 0)) + output_tokens = token_usage.get('completion_tokens', token_usage.get('output_tokens', 0)) + elapsed_time = result.get("elapsed_time", 0.0) + + actual_model = request.model if request.model in agent_service.graphs else next(iter(agent_service.graphs.keys())) + + return ChatResponse( + reply=result["reply"], + thread_id=thread_id, + model_used=actual_model, + input_tokens=input_tokens, + output_tokens=output_tokens, + total_tokens=input_tokens + output_tokens, + elapsed_time=elapsed_time + ) + except Exception as e: + error(f"同步响应异常: {e}") + + # === 兜底输出机制 === + error_message = str(e) + is_timeout_error = any(keyword in error_message.lower() for keyword in + ["timeout", "timed out", "超时", "connection", "unavailable", "不可用"]) + + # 1. 自我介绍 + intro_text = "你好!我是 AI 智能助手,我可以帮你处理各种问题,包括查询通讯录、词典翻译、新闻分析、知识库检索、联网搜索等。\n\n" + + # 2. 错误信息(红色突出) + error_display = f"**⚠️ 当前遇到问题**\n\n```diff\n- {error_message}\n```\n\n" + + # 3. 模型切换提示(如果是超时/不可用错误) + switch_hint = "" + if is_timeout_error: + switch_hint = "💡 **提示**:当前模型可能响应超时或不可用,请尝试手动切换到其他模型(如 DeepSeek、智谱AI等)。\n\n" + + # 4. 组合完整兜底回复 + fallback_text = intro_text + error_display + switch_hint + + actual_model = request.model if request.model in agent_service.graphs else next(iter(agent_service.graphs.keys())) + + return ChatResponse( + reply=fallback_text, + thread_id=thread_id, + model_used=actual_model, + input_tokens=0, + output_tokens=0, + total_tokens=0, + elapsed_time=0.0 + ) # ========== 历史查询接口 ========== @app.get("/threads") @@ -225,7 +260,34 @@ async def chat_stream_endpoint( yield "data: [DONE]\n\n" except Exception as e: error(f"流式响应异常: {e}") - yield f"data: {json.dumps({'type': 'error', 'message': str(e)}, ensure_ascii=False)}\n\n" + + # === 兜底输出机制 === + error_message = str(e) + is_timeout_error = any(keyword in error_message.lower() for keyword in + ["timeout", "timed out", "超时", "connection", "unavailable", "不可用"]) + + # 1. 自我介绍 + intro_text = "你好!我是 AI 智能助手,我可以帮你处理各种问题,包括查询通讯录、词典翻译、新闻分析、知识库检索、联网搜索等。\n\n" + + # 2. 错误信息(红色突出) + error_display = f"**⚠️ 当前遇到问题**\n\n```diff\n- {error_message}\n```\n\n" + + # 3. 模型切换提示(如果是超时/不可用错误) + switch_hint = "" + if is_timeout_error: + switch_hint = "💡 **提示**:当前模型可能响应超时或不可用,请尝试手动切换到其他模型(如 DeepSeek、智谱AI等)。\n\n" + + # 4. 组合完整兜底回复 + fallback_text = intro_text + error_display + switch_hint + + # 5. 以 llm_token 方式发送兜底回复,模拟打字机效果 + for char in fallback_text: + yield f"data: {json.dumps({'type': 'llm_token', 'node': 'fallback', 'token': char}, ensure_ascii=False)}\n\n" + import asyncio + await asyncio.sleep(0.01) + + # 6. 发送错误事件 + yield f"data: {json.dumps({'type': 'error', 'message': error_message}, ensure_ascii=False)}\n\n" yield "data: [DONE]\n\n" return StreamingResponse(