From f797f132cf10e018b019004a4caf7ec32c8b7a54 Mon Sep 17 00:00:00 2001 From: Soulter <905617992@qq.com> Date: Thu, 15 Jan 2026 17:22:50 +0800 Subject: [PATCH] perf: refine tool call related prompts --- .../agent/runners/tool_loop_agent_runner.py | 18 ++++++++++-------- .../method/agent_sub_stages/internal.py | 10 ++++++++++ astrbot/core/pipeline/process_stage/utils.py | 14 +++++++++++--- 3 files changed, 31 insertions(+), 11 deletions(-) diff --git a/astrbot/core/agent/runners/tool_loop_agent_runner.py b/astrbot/core/agent/runners/tool_loop_agent_runner.py index 6389b48cf..698551d34 100644 --- a/astrbot/core/agent/runners/tool_loop_agent_runner.py +++ b/astrbot/core/agent/runners/tool_loop_agent_runner.py @@ -227,7 +227,8 @@ class ToolLoopAgentRunner(BaseAgentRunner[TContext]): encrypted=llm_resp.reasoning_signature, ) ) - parts.append(TextPart(text=llm_resp.completion_text or "*No response*")) + if llm_resp.completion_text: + parts.append(TextPart(text=llm_resp.completion_text)) self.run_context.messages.append(Message(role="assistant", content=parts)) # call the on_agent_done hook @@ -277,7 +278,8 @@ class ToolLoopAgentRunner(BaseAgentRunner[TContext]): encrypted=llm_resp.reasoning_signature, ) ) - parts.append(TextPart(text=llm_resp.completion_text or "*No response*")) + if llm_resp.completion_text: + parts.append(TextPart(text=llm_resp.completion_text)) tool_calls_result = ToolCallsResult( tool_calls_info=AssistantMessageSegment( tool_calls=llm_resp.to_openai_to_calls_model(), @@ -361,7 +363,7 @@ class ToolLoopAgentRunner(BaseAgentRunner[TContext]): ToolCallMessageSegment( role="tool", tool_call_id=func_tool_id, - content=f"error: 未找到工具 {func_tool_name}", + content=f"error: Tool {func_tool_name} not found.", ), ) continue @@ -427,7 +429,7 @@ class ToolLoopAgentRunner(BaseAgentRunner[TContext]): ToolCallMessageSegment( role="tool", tool_call_id=func_tool_id, - content="返回了图片(已直接发送给用户)", + content="The tool has successfully returned an image and sent directly to the user. You can describe it in your next response.", ), ) yield MessageChain(type="tool_direct_result").base64_image( @@ -452,7 +454,7 @@ class ToolLoopAgentRunner(BaseAgentRunner[TContext]): ToolCallMessageSegment( role="tool", tool_call_id=func_tool_id, - content="返回了图片(已直接发送给用户)", + content="The tool has successfully returned an image and sent directly to the user. You can describe it in your next response.", ), ) yield MessageChain( @@ -463,7 +465,7 @@ class ToolLoopAgentRunner(BaseAgentRunner[TContext]): ToolCallMessageSegment( role="tool", tool_call_id=func_tool_id, - content="返回的数据类型不受支持", + content="The tool has returned a data type that is not supported.", ), ) @@ -480,7 +482,7 @@ class ToolLoopAgentRunner(BaseAgentRunner[TContext]): ToolCallMessageSegment( role="tool", tool_call_id=func_tool_id, - content="*工具没有返回值或者将结果直接发送给了用户*", + content="The tool has no return value, or has sent the result directly to the user.", ), ) else: @@ -492,7 +494,7 @@ class ToolLoopAgentRunner(BaseAgentRunner[TContext]): ToolCallMessageSegment( role="tool", tool_call_id=func_tool_id, - content="*工具返回了不支持的类型,请告诉用户检查这个工具的定义和实现。*", + content="*The tool has returned an unsupported type. Please tell the user to check the definition and implementation of this tool.*", ), ) 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 3e39dc8a1..43d88c5ad 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 @@ -36,6 +36,7 @@ from .....astr_agent_tool_exec import FunctionToolExecutor from ....context import PipelineContext, call_event_hook from ...stage import Stage from ...utils import ( + CHATUI_EXTRA_PROMPT, EXECUTE_SHELL_TOOL, FILE_DOWNLOAD_TOOL, FILE_UPLOAD_TOOL, @@ -43,6 +44,7 @@ from ...utils import ( LLM_SAFETY_MODE_SYSTEM_PROMPT, PYTHON_TOOL, SANDBOX_MODE_PROMPT, + TOOL_CALL_PROMPT, decoded_blocked, retrieve_knowledge_base, ) @@ -657,6 +659,14 @@ class InternalAgentSubStage(Stage): if event.get_platform_name() == "webchat": asyncio.create_task(self._handle_webchat(event, req, provider)) + # 注入 ChatUI 额外 prompt + # 比如 follow-up questions 提示等 + req.system_prompt += f"\n{CHATUI_EXTRA_PROMPT}\n" + + # 注入基本 prompt + if req.func_tool and req.func_tool.tools: + req.system_prompt += f"\n{TOOL_CALL_PROMPT}\n" + await agent_runner.reset( provider=provider, request=req, diff --git a/astrbot/core/pipeline/process_stage/utils.py b/astrbot/core/pipeline/process_stage/utils.py index d1dd22139..4b9507267 100644 --- a/astrbot/core/pipeline/process_stage/utils.py +++ b/astrbot/core/pipeline/process_stage/utils.py @@ -36,9 +36,17 @@ SANDBOX_MODE_PROMPT = ( # "Use `cat /app/skills/{skill_name}/SKILL.md` to read the documentation of a specific skill." # "SKILL.md might be large, you can read the description first, which is located in the YAML frontmatter of the file." # "Use shell commands such as grep, sed, awk to extract relevant information from the documentation as needed.\n" - "Note:\n" - "1. If you use shell, your command will always runs in the /home//workspace directory.\n" - "2. If you use IPython, you would better use absolute paths when dealing with files to avoid confusion.\n" +) + +TOOL_CALL_PROMPT = ( + "You MUST NOT return an empty response, especially after invoking a tool." + "Before calling any tool, provide a brief explanatory message to the user stating the purpose of the tool call." + "After the tool call is completed, you must briefly summarize the results returned by the tool for the user." +) + +CHATUI_EXTRA_PROMPT = ( + 'When you answered, you need to add a follow up question / summarization but do not add "Follow up" words. ' + "Such as, user asked you to generate codes, you can add: Do you need me to run these codes for you?" )