fix(backend): 统一为本地及MCP动态工具注入handle_tool_error容错

- 在 mcp_service 的 get_mcp_tools 加载出口全局设置 tool.handle_tool_error = True
- 在 toolkits registry 的 @tool 装饰器中对本地工具默认开启 handle_tool_error = True
- 修复因 MCP/本地工具内部执行出错抛出 ToolException 时击穿流服务导致 crash 的 bug
- 新增两个单元测试进行完备性验证
This commit is contained in:
supreme0597 2026-05-23 13:48:29 +08:00
parent 29d5da0bd9
commit ef75bd262f
4 changed files with 45 additions and 0 deletions

View File

@ -89,6 +89,7 @@ def tool(
)
# 自动收集工具实例
tool_obj.handle_tool_error = True
_all_tool_instances.append(tool_obj)
return tool_obj

View File

@ -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:

View File

@ -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()

View File

@ -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