mirror of
https://hubproxy.babadafafafafa.cn/https://github.com/jxxghp/MoviePilot.git
synced 2026-09-20 08:03:34 +08:00
fix(agent): normalize legacy tool failures
This commit is contained in:
@@ -130,6 +130,23 @@ def serialize_tool_result_for_agent(result: Any) -> str:
|
||||
return str(result)
|
||||
|
||||
|
||||
def normalize_tool_failure_for_agent(result: Any, *, tool_name: str) -> Any:
|
||||
"""将旧工具返回的裸错误文本统一成模型可恢复的结构化失败回执。"""
|
||||
if not isinstance(result, str):
|
||||
return result
|
||||
text = result.strip()
|
||||
if not text or text.startswith("{") or text.startswith("["):
|
||||
return result
|
||||
markers = ("错误", "操作失败", "浏览器操作失败", "工具执行异常")
|
||||
if not text.startswith(markers):
|
||||
return result
|
||||
return json.dumps({
|
||||
"success": False,
|
||||
"execution_outcome": "failed",
|
||||
"tool": tool_name,
|
||||
"error": text,
|
||||
"recovery": "根据错误信息修正输入或改用正确工具后重试;不要重复未确认的写入。",
|
||||
}, ensure_ascii=False)
|
||||
TOOL_RESULT_RECORDER: ContextVar[Optional[Callable[[str, str], dict[str, Any]]]] = ContextVar(
|
||||
"agent_tool_result_recorder", default=None,
|
||||
)
|
||||
@@ -537,7 +554,9 @@ class MoviePilotTool(BaseTool, metaclass=ABCMeta):
|
||||
# 执行具体工具逻辑
|
||||
try:
|
||||
result = await self.run_with_timeout(**kwargs)
|
||||
formatted_result = self.format_agent_result(result, **kwargs)
|
||||
formatted_result = normalize_tool_failure_for_agent(
|
||||
self.format_agent_result(result, **kwargs), tool_name=self.name,
|
||||
)
|
||||
|
||||
logger.info(
|
||||
f"Agent工具 {self.name} 返回结果,状态: {inspect_tool_result(formatted_result).value},"
|
||||
|
||||
@@ -16,7 +16,7 @@ from app.agent.api.executor import ApiExecutionContext, MoviePilotApiExecutor
|
||||
from app.agent.middleware.policy import AgentPolicyMiddleware
|
||||
from app.agent.policy.contracts import AuthSource, ExecutionOutcome, PrincipalType, ToolOrigin, ToolPolicyContext
|
||||
from app.agent.policy.orchestrator import AgentToolPolicyOrchestrator
|
||||
from app.agent.tools.base import MoviePilotTool
|
||||
from app.agent.tools.base import MoviePilotTool, normalize_tool_failure_for_agent
|
||||
from app.agent.tools.impl.api import MoviePilotApiTool
|
||||
from app.agent.tools.impl.mcp import McpExternalTool
|
||||
from app.agent.tools.manager import MoviePilotToolsManager
|
||||
@@ -182,6 +182,14 @@ def test_mcp_error_with_text_content_does_not_lose_error_flag():
|
||||
assert McpExternalTool._format_mcp_result({"content": payload["content"]}) == "operation rejected"
|
||||
|
||||
|
||||
def test_legacy_tool_error_text_gets_structured_recovery_contract():
|
||||
"""旧浏览器、文件和命令工具的裸错误在 Agent 入口统一可恢复。"""
|
||||
payload = json.loads(normalize_tool_failure_for_agent("错误:文件不存在", tool_name="read_file"))
|
||||
assert payload["execution_outcome"] == "failed"
|
||||
assert payload["tool"] == "read_file"
|
||||
assert "修正输入" in payload["recovery"]
|
||||
|
||||
|
||||
def _api_tool(request: AsyncMock) -> MoviePilotApiTool:
|
||||
"""把内存 HTTP 请求替身接到真实 API 工具,覆盖异常映射全链路。"""
|
||||
executor = MoviePilotApiExecutor(
|
||||
|
||||
Reference in New Issue
Block a user