diff --git a/astrbot/core/agent/runners/tool_loop_agent_runner.py b/astrbot/core/agent/runners/tool_loop_agent_runner.py index b56d7e62f..256e071aa 100644 --- a/astrbot/core/agent/runners/tool_loop_agent_runner.py +++ b/astrbot/core/agent/runners/tool_loop_agent_runner.py @@ -145,6 +145,7 @@ class ToolLoopAgentRunner(BaseAgentRunner[TContext]): REPEATED_TOOL_NOTICE_L1_THRESHOLD = 3 REPEATED_TOOL_NOTICE_L2_THRESHOLD = 4 REPEATED_TOOL_NOTICE_L3_THRESHOLD = 5 + MALFORMED_TOOL_NAME_PLACEHOLDER = "__malformed_tool_name__" REPEATED_TOOL_NOTICE_L1_TEMPLATE = ( "\n\n[SYSTEM NOTICE] By the way, you have executed the same tool " "`{tool_name}` {streak} times consecutively. Double-check whether another " @@ -532,6 +533,7 @@ class ToolLoopAgentRunner(BaseAgentRunner[TContext]): ) break + self._sanitize_malformed_tool_calls(resp) yield resp return @@ -689,6 +691,22 @@ class ToolLoopAgentRunner(BaseAgentRunner[TContext]): streak=streak, ) + def _sanitize_malformed_tool_calls( + self, + llm_resp: LLMResponse, + ) -> None: + """Normalize malformed tool call names while preserving list alignment. + + Args: + llm_resp: The LLM response whose tool call lists should be sanitized. + """ + llm_resp.tools_call_name = [ + self.MALFORMED_TOOL_NAME_PLACEHOLDER + if tool_name is None or tool_name == "" + else tool_name + for tool_name in llm_resp.tools_call_name + ] + @override async def step(self): """Process a single step of the agent. @@ -1312,6 +1330,7 @@ class ToolLoopAgentRunner(BaseAgentRunner[TContext]): ) if requery_resp: llm_resp = requery_resp + self._sanitize_malformed_tool_calls(llm_resp) # If the re-query still returns no tool calls, and also does not have a meaningful assistant reply, # we consider it as a failure of the LLM to follow the tool-use instruction, @@ -1339,6 +1358,7 @@ class ToolLoopAgentRunner(BaseAgentRunner[TContext]): ) if repair_resp: llm_resp = repair_resp + self._sanitize_malformed_tool_calls(llm_resp) return llm_resp, subset