From ef75bd262f691e7c49422e6b26bfb9b85d6d7237 Mon Sep 17 00:00:00 2001 From: supreme0597 Date: Sat, 23 May 2026 13:48:29 +0800 Subject: [PATCH] =?UTF-8?q?fix(backend):=20=E7=BB=9F=E4=B8=80=E4=B8=BA?= =?UTF-8?q?=E6=9C=AC=E5=9C=B0=E5=8F=8AMCP=E5=8A=A8=E6=80=81=E5=B7=A5?= =?UTF-8?q?=E5=85=B7=E6=B3=A8=E5=85=A5handle=5Ftool=5Ferror=E5=AE=B9?= =?UTF-8?q?=E9=94=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 在 mcp_service 的 get_mcp_tools 加载出口全局设置 tool.handle_tool_error = True - 在 toolkits registry 的 @tool 装饰器中对本地工具默认开启 handle_tool_error = True - 修复因 MCP/本地工具内部执行出错抛出 ToolException 时击穿流服务导致 crash 的 bug - 新增两个单元测试进行完备性验证 --- .../package/yuxi/agents/toolkits/registry.py | 1 + backend/package/yuxi/services/mcp_service.py | 2 ++ .../test/unit/services/test_mcp_service.py | 24 +++++++++++++++++++ .../test/unit/toolkits/test_tool_registry.py | 18 ++++++++++++++ 4 files changed, 45 insertions(+) create mode 100644 backend/test/unit/toolkits/test_tool_registry.py diff --git a/backend/package/yuxi/agents/toolkits/registry.py b/backend/package/yuxi/agents/toolkits/registry.py index a8628da4..10aeafe6 100644 --- a/backend/package/yuxi/agents/toolkits/registry.py +++ b/backend/package/yuxi/agents/toolkits/registry.py @@ -89,6 +89,7 @@ def tool( ) # 自动收集工具实例 + tool_obj.handle_tool_error = True _all_tool_instances.append(tool_obj) return tool_obj diff --git a/backend/package/yuxi/services/mcp_service.py b/backend/package/yuxi/services/mcp_service.py index 54b680e5..ebec9f9d 100644 --- a/backend/package/yuxi/services/mcp_service.py +++ b/backend/package/yuxi/services/mcp_service.py @@ -275,6 +275,8 @@ async def get_mcp_tools( if tool.metadata is None: tool.metadata = {} tool.metadata["id"] = unique_id + # 开启错误处理,防止工具调用抛出 ToolException 时击穿服务 + tool.handle_tool_error = True all_processed_tools.append(tool) if cache: diff --git a/backend/test/unit/services/test_mcp_service.py b/backend/test/unit/services/test_mcp_service.py index ebef8565..bd38ba83 100644 --- a/backend/test/unit/services/test_mcp_service.py +++ b/backend/test/unit/services/test_mcp_service.py @@ -112,3 +112,27 @@ async def test_get_tools_from_all_servers_loads_names_from_db_once(monkeypatch): ("alpha", server_configs), ("beta", server_configs), ] + + +async def test_get_mcp_tools_sets_handle_tool_error(monkeypatch): + mcp_service.clear_mcp_cache() + + config = {"transport": "stdio", "command": "demo-tool", "disabled_tools": []} + + async def fake_get_enabled_mcp_server_config(server_name: str, db=None): + del db + return config + + async def fake_get_mcp_client(server_configs): + tool = SimpleNamespace(name="demo_tool", metadata={}) + return _FakeClient([tool]) + + monkeypatch.setattr(mcp_service, "get_enabled_mcp_server_config", fake_get_enabled_mcp_server_config) + monkeypatch.setattr(mcp_service, "get_mcp_client", fake_get_mcp_client) + + tools = await mcp_service.get_mcp_tools("demo") + assert len(tools) == 1 + assert tools[0].handle_tool_error is True + + mcp_service.clear_mcp_cache() + diff --git a/backend/test/unit/toolkits/test_tool_registry.py b/backend/test/unit/toolkits/test_tool_registry.py new file mode 100644 index 00000000..95922f2b --- /dev/null +++ b/backend/test/unit/toolkits/test_tool_registry.py @@ -0,0 +1,18 @@ +from __future__ import annotations + +from yuxi.agents.toolkits.registry import tool + + +def test_tool_decorator_sets_handle_tool_error(): + """测试通过 @tool 装饰器注册的工具是否自动设置了 handle_tool_error 为 True""" + + @tool( + category="test", + display_name="测试工具", + description="这是一个单元测试工具", + ) + def my_test_tool(arg: str) -> str: + return f"hello {arg}" + + assert my_test_tool.name == "my_test_tool" + assert my_test_tool.handle_tool_error is True