diff --git a/agent/deep_assistant.py b/agent/deep_assistant.py index dd04083..dc1548c 100644 --- a/agent/deep_assistant.py +++ b/agent/deep_assistant.py @@ -12,7 +12,7 @@ from utils.fastapi_utils import detect_provider from .guideline_middleware import GuidelineMiddleware from .tool_output_length_middleware import ToolOutputLengthMiddleware from .tool_use_cleanup_middleware import ToolUseCleanupMiddleware -from utils.settings import SUMMARIZATION_MAX_TOKENS, TOOL_OUTPUT_MAX_LENGTH +from utils.settings import SUMMARIZATION_MAX_TOKENS, TOOL_OUTPUT_MAX_LENGTH, MCP_HTTP_TIMEOUT, MCP_SSE_READ_TIMEOUT from agent.agent_config import AgentConfig from agent.prompt_loader import load_system_prompt_async, load_mcp_settings_async from agent.agent_memory_cache import get_memory_cache_manager @@ -54,6 +54,14 @@ async def get_tools_from_mcp(mcp): if "transport" not in cfg: cfg["transport"] = "http" if "url" in cfg else "stdio" + # 为 HTTP/ SSE 传输的 MCP 服务器添加超时配置 + # 如果配置中未设置超时,使用全局默认值 + if cfg.get("transport") in ("http", "sse"): + if "timeout" not in cfg: + cfg["timeout"] = MCP_HTTP_TIMEOUT + if "sse_read_timeout" not in cfg: + cfg["sse_read_timeout"] = MCP_SSE_READ_TIMEOUT + # 确保 mcp[0]["mcpServers"] 是字典类型 if not isinstance(mcp[0]["mcpServers"], dict): return [] diff --git a/agent/tool_output_length_middleware.py b/agent/tool_output_length_middleware.py index 1afaff8..34a939f 100644 --- a/agent/tool_output_length_middleware.py +++ b/agent/tool_output_length_middleware.py @@ -269,6 +269,11 @@ class ToolOutputLengthMiddleware(AgentMiddleware): except: return self._truncate_end(content) + def _format_error_message(self, tool_name: str, error: Exception) -> str: + """格式化错误消息为用户友好的文本。""" + # 用户友好的错误消息,不暴露技术细节 + return f"工具调用失败:{tool_name} 暂时无法使用,请稍后重试。" + def wrap_tool_call( self, request: ToolCallRequest, @@ -297,7 +302,13 @@ class ToolOutputLengthMiddleware(AgentMiddleware): result = handler(request) except Exception as e: logger.error(f"Tool execution failed for '{tool_name}': {e}") - raise + # 返回错误 ToolMessage 而不是重新抛出异常 + error_message = self._format_error_message(tool_name, e) + return ToolMessage( + content=error_message, + tool_call_id=request.tool_call.get('id', ''), + name=tool_name + ) # Handle different return types if isinstance(result, ToolMessage): @@ -402,7 +413,13 @@ class ToolOutputLengthMiddleware(AgentMiddleware): result = await handler(request) except Exception as e: logger.error(f"Tool execution failed for '{tool_name}': {e}") - raise + # 返回错误 ToolMessage 而不是重新抛出异常 + error_message = self._format_error_message(tool_name, e) + return ToolMessage( + content=error_message, + tool_call_id=request.tool_call.get('id', ''), + name=tool_name + ) # Handle different return types (same logic as sync version) if isinstance(result, ToolMessage): diff --git a/utils/settings.py b/utils/settings.py index 9c63797..3119582 100644 --- a/utils/settings.py +++ b/utils/settings.py @@ -33,4 +33,8 @@ TOOL_OUTPUT_TRUNCATION_STRATEGY = os.getenv("TOOL_OUTPUT_TRUNCATION_STRATEGY", " # THINKING ENABLE DEFAULT_THINKING_ENABLE = os.getenv("DEFAULT_THINKING_ENABLE", "true") == "true" +# MCP Tool Timeout Settings +MCP_HTTP_TIMEOUT = int(os.getenv("MCP_HTTP_TIMEOUT", 60)) # HTTP 请求超时(秒) +MCP_SSE_READ_TIMEOUT = int(os.getenv("MCP_SSE_READ_TIMEOUT", 300)) # SSE 读取超时(秒) +