From 400eff6ec16c9a4af261459df803b49364acf89d Mon Sep 17 00:00:00 2001 From: Hola-Gracias <2229508793@qq.com> Date: Mon, 27 Apr 2026 22:40:20 +0800 Subject: [PATCH 1/2] Refine error handling for function calling support --- astrbot/core/provider/sources/openai_source.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/astrbot/core/provider/sources/openai_source.py b/astrbot/core/provider/sources/openai_source.py index 512e47233a..9da9748edf 100644 --- a/astrbot/core/provider/sources/openai_source.py +++ b/astrbot/core/provider/sources/openai_source.py @@ -1122,15 +1122,17 @@ async def _handle_api_error( image_fallback_used=True, ) + err_lower = str(e).lower() if ( "Function calling is not enabled" in str(e) - or ("tool" in str(e).lower() and "support" in str(e).lower()) - or ("function" in str(e).lower() and "support" in str(e).lower()) + or ("tool" in err_lower and "support" in err_lower and "tool_choice" not in err_lower) + or ("function" in err_lower and "support" in err_lower) ): # openai, ollama, gemini openai, siliconcloud 的错误提示与 code 不统一,只能通过字符串匹配 logger.info( f"{self.get_model()} 不支持函数工具调用,已自动去除,不影响使用。", ) + logger.debug(f"原始错误: {e}") payloads.pop("tools", None) return ( False, @@ -1143,7 +1145,7 @@ async def _handle_api_error( ) # logger.error(f"发生了错误。Provider 配置如下: {self.provider_config}") - if "tool" in str(e).lower() and "support" in str(e).lower(): + if "tool" in err_lower and "support" in err_lower and "tool_choice" not in err_lower: logger.error("疑似该模型不支持函数调用工具调用。请输入 /tool off_all") if is_connection_error(e): From dcae4f3c5abe066011a4323ff35a55289b4bfdb2 Mon Sep 17 00:00:00 2001 From: Hola-Gracias <2229508793@qq.com> Date: Mon, 27 Apr 2026 22:46:34 +0800 Subject: [PATCH 2/2] Refactor requery logic to use _requery method --- .../agent/runners/tool_loop_agent_runner.py | 57 +++++++++++++------ 1 file changed, 41 insertions(+), 16 deletions(-) diff --git a/astrbot/core/agent/runners/tool_loop_agent_runner.py b/astrbot/core/agent/runners/tool_loop_agent_runner.py index d96a4c92cb..3a47649aa4 100644 --- a/astrbot/core/agent/runners/tool_loop_agent_runner.py +++ b/astrbot/core/agent/runners/tool_loop_agent_runner.py @@ -1287,14 +1287,9 @@ async def _resolve_tool_exec( ) if param_subset.tools and tool_names: contexts = self._build_tool_requery_context(tool_names) - requery_resp = await self.provider.text_chat( - contexts=self._sanitize_contexts_for_provider(contexts), - func_tool=param_subset, - model=self.req.model, - session_id=self.req.session_id, - extra_user_content_parts=self.req.extra_user_content_parts, - tool_choice="required", - abort_signal=self._abort_signal, + requery_resp = await self._requery( + contexts=contexts, + param_subset=param_subset, ) if requery_resp: llm_resp = requery_resp @@ -1313,20 +1308,50 @@ async def _resolve_tool_exec( tool_names, extra_instruction=self.SKILLS_LIKE_REQUERY_REPAIR_INSTRUCTION, ) - repair_resp = await self.provider.text_chat( - contexts=self._sanitize_contexts_for_provider(repair_contexts), - func_tool=param_subset, - model=self.req.model, - session_id=self.req.session_id, - extra_user_content_parts=self.req.extra_user_content_parts, - tool_choice="required", - abort_signal=self._abort_signal, + repair_resp = await self._requery( + contexts=repair_contexts, + param_subset=param_subset, ) if repair_resp: llm_resp = repair_resp return llm_resp, subset + async def _requery( + self, + contexts: list, + param_subset: ToolSet, + ) -> LLMResponse | None: + """Send a re-query with tool_choice='required', falling back to 'auto' if + the provider does not support the 'required' value (e.g. deepseek-reasoner). + """ + try: + return await self.provider.text_chat( + contexts=self._sanitize_contexts_for_provider(contexts), + func_tool=param_subset, + model=self.req.model, + session_id=self.req.session_id, + extra_user_content_parts=self.req.extra_user_content_parts, + tool_choice="required", + abort_signal=self._abort_signal, + ) + except Exception as e: + if "tool_choice" in str(e).lower(): + logger.info( + f"tool_choice='required' 不被当前模型支持,降级为 'auto' 重试。", + ) + logger.debug(f"原始错误: {e}") + return await self.provider.text_chat( + contexts=self._sanitize_contexts_for_provider(contexts), + func_tool=param_subset, + model=self.req.model, + session_id=self.req.session_id, + extra_user_content_parts=self.req.extra_user_content_parts, + tool_choice="auto", + abort_signal=self._abort_signal, + ) + raise + def done(self) -> bool: """检查 Agent 是否已完成工作""" return self._state in (AgentState.DONE, AgentState.ERROR)