diff --git a/astrbot/core/agent/runners/tool_loop_agent_runner.py b/astrbot/core/agent/runners/tool_loop_agent_runner.py
index cb7e22f341..93f45475e5 100644
--- a/astrbot/core/agent/runners/tool_loop_agent_runner.py
+++ b/astrbot/core/agent/runners/tool_loop_agent_runner.py
@@ -930,6 +930,7 @@ async def _resolve_tool_exec(
func_tool=param_subset,
model=self.req.model,
session_id=self.req.session_id,
+ extra_user_content_parts=self.req.extra_user_content_parts,
abort_signal=self._abort_signal,
)
if requery_resp:
diff --git a/tests/test_tool_loop_agent_runner.py b/tests/test_tool_loop_agent_runner.py
index f983c5b4d4..ccab267351 100644
--- a/tests/test_tool_loop_agent_runner.py
+++ b/tests/test_tool_loop_agent_runner.py
@@ -699,6 +699,87 @@ async def test_follow_up_ticket_not_consumed_when_no_next_tool_call(
assert ticket.consumed is False
+@pytest.mark.asyncio
+async def test_skills_like_requery_passes_extra_user_content_parts():
+ """skills-like 模式 re-query 时应传递 extra_user_content_parts(如 image_caption)"""
+ from astrbot.core.agent.message import TextPart
+
+ captured_kwargs = {}
+
+ class SkillsLikeProvider(MockProvider):
+ async def text_chat(self, **kwargs) -> LLMResponse:
+ self.call_count += 1
+ if self.call_count == 1:
+ # 第一次调用:返回工具选择(light schema)
+ return LLMResponse(
+ role="assistant",
+ completion_text="选择工具",
+ tools_call_name=["test_tool"],
+ tools_call_args=[{"query": "test"}],
+ tools_call_ids=["call_1"],
+ usage=TokenUsage(input_other=10, output=5),
+ )
+ if self.call_count == 2:
+ # 第二次调用:re-query with param schema
+ captured_kwargs.update(kwargs)
+ return LLMResponse(
+ role="assistant",
+ completion_text="调用工具",
+ tools_call_name=["test_tool"],
+ tools_call_args=[{"query": "actual"}],
+ tools_call_ids=["call_2"],
+ usage=TokenUsage(input_other=10, output=5),
+ )
+ # 后续调用:正常回复
+ return LLMResponse(
+ role="assistant",
+ completion_text="最终回复",
+ usage=TokenUsage(input_other=10, output=5),
+ )
+
+ provider = SkillsLikeProvider()
+ tool = FunctionTool(
+ name="test_tool",
+ description="测试",
+ parameters={"type": "object", "properties": {"query": {"type": "string"}}},
+ handler=AsyncMock(),
+ )
+ tool_set = ToolSet(tools=[tool])
+
+ caption_part = TextPart(text="一张猫的照片")
+ req = ProviderRequest(
+ prompt="看看这张图",
+ func_tool=tool_set,
+ contexts=[],
+ extra_user_content_parts=[caption_part],
+ )
+
+ event = MockEvent(umo="test_umo", sender_id="test_sender")
+ ctx = MockAgentContext(event)
+ run_context = ContextWrapper(context=ctx)
+ runner = ToolLoopAgentRunner()
+
+ await runner.reset(
+ provider=provider,
+ request=req,
+ run_context=run_context,
+ tool_executor=MockToolExecutor(),
+ agent_hooks=MockHooks(),
+ tool_schema_mode="skills_like",
+ )
+
+ async for _ in runner.step():
+ pass
+
+ # 验证 re-query 调用包含了 extra_user_content_parts
+ assert "extra_user_content_parts" in captured_kwargs, (
+ "re-query 应该传递 extra_user_content_parts"
+ )
+ parts = captured_kwargs["extra_user_content_parts"]
+ assert len(parts) == 1
+ assert parts[0].text == "一张猫的照片"
+
+
@pytest.mark.asyncio
async def test_follow_up_accepted_when_active_and_not_stopping(
runner, mock_provider, provider_request, mock_tool_executor, mock_hooks