diff --git a/astrbot/core/config/default.py b/astrbot/core/config/default.py index 527cfbf9be..bff117d5f7 100644 --- a/astrbot/core/config/default.py +++ b/astrbot/core/config/default.py @@ -134,6 +134,7 @@ "streaming_response": False, "show_tool_use_status": False, "show_tool_call_result": False, + "save_failed_agent_history": False, "sanitize_context_by_modalities": False, "max_quoted_fallback_images": 20, "quoted_message_parser": { @@ -2777,6 +2778,9 @@ class ChatProviderTemplate(TypedDict): "show_tool_call_result": { "type": "bool", }, + "save_failed_agent_history": { + "type": "bool", + }, "unsupported_streaming_strategy": { "type": "string", }, @@ -3543,6 +3547,14 @@ class ChatProviderTemplate(TypedDict): "provider_settings.show_tool_use_status": True, }, }, + "provider_settings.save_failed_agent_history": { + "description": "失败时保存本轮记录", + "type": "bool", + "hint": "启用后,当 Agent 本轮运行失败时(如模型返回空输出),也会将本轮记录保存到会话历史中,包括用户输入、工具调用记录和失败提示。", + "condition": { + "provider_settings.agent_runner_type": "local", + }, + }, "provider_settings.sanitize_context_by_modalities": { "description": "按模型能力清理历史上下文", "type": "bool", diff --git a/astrbot/core/pipeline/process_stage/method/agent_sub_stages/internal.py b/astrbot/core/pipeline/process_stage/method/agent_sub_stages/internal.py index e0ba2463ca..cbc011105f 100644 --- a/astrbot/core/pipeline/process_stage/method/agent_sub_stages/internal.py +++ b/astrbot/core/pipeline/process_stage/method/agent_sub_stages/internal.py @@ -67,6 +67,9 @@ async def initialize(self, ctx: PipelineContext) -> None: self.show_tool_use: bool = settings.get("show_tool_use_status", True) self.show_tool_call_result: bool = settings.get("show_tool_call_result", False) self.show_reasoning = settings.get("display_reasoning_text", False) + self.save_failed_agent_history: bool = settings.get( + "save_failed_agent_history", False + ) self.sanitize_context_by_modalities: bool = settings.get( "sanitize_context_by_modalities", False, @@ -296,6 +299,7 @@ async def process( agent_runner.run_context.messages, agent_runner.stats, user_aborted=agent_runner.was_aborted(), + save_failed_history=self.save_failed_agent_history, ) elif streaming_response and not stream_to_general: @@ -369,6 +373,7 @@ async def process( agent_runner.run_context.messages, agent_runner.stats, user_aborted=agent_runner.was_aborted(), + save_failed_history=self.save_failed_agent_history, ) asyncio.create_task( @@ -412,6 +417,7 @@ async def _save_to_history( all_messages: list[Message], runner_stats: AgentStats | None, user_aborted: bool = False, + save_failed_history: bool = False, ) -> None: if not req or not req.conversation: return @@ -433,10 +439,22 @@ async def _save_to_history( not llm_response.completion_text and not req.tool_calls_result and not user_aborted + and not save_failed_history ): logger.debug("LLM 响应为空,不保存记录。") return + if save_failed_history and not llm_response.completion_text: + logger.info( + "Saving failed agent history as save_failed_agent_history is enabled." + ) + all_messages.append( + Message( + role="assistant", + content="[Agent run failed. History saved for debugging.]", + ) + ) + message_to_save = [] skipped_initial_system = False for message in all_messages: