fix(subagents): consolidate system_prompt and skills into single SystemMessage (#2701)
* fix(subagents): consolidate system_prompt and skills into single SystemMessage Some LLM APIs (vLLM, Xinference, Chinese LLM providers) reject multiple system messages with \”System message must be at the beginning.\” The subagent executor was sending separate SystemMessages for the configured system_prompt and each loaded skill, which caused failures when calling task tool with sub-agents. Merge system_prompt and all skill content into one SystemMessage in the initial state, and pass system_prompt=None to create_agent() so the factory doesn't prepend a second one. Fixes #2693 * fix(subagents): update SubagentConfig.system_prompt to str | None and add astream regression test Agent-Logs-Url: https://github.com/bytedance/deer-flow/sessions/2ee03a26-e19b-4106-abc5-c76a2906383b Co-authored-by: WillemJiang <219644+WillemJiang@users.noreply.github.com> * fixed the lint error * fix the lint error in the backend * fix the unit test error of test_subagent_executor --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
This commit is contained in:
@@ -26,7 +26,7 @@ class SubagentConfig:
|
||||
|
||||
name: str
|
||||
description: str
|
||||
system_prompt: str
|
||||
system_prompt: str | None = None
|
||||
tools: list[str] | None = None
|
||||
disallowed_tools: list[str] | None = field(default_factory=lambda: ["task"])
|
||||
skills: list[str] | None = None
|
||||
|
||||
@@ -286,11 +286,13 @@ class SubagentExecutor:
|
||||
# Reuse shared middleware composition with lead agent.
|
||||
middlewares = build_subagent_runtime_middlewares(app_config=app_config, model_name=self.model_name, lazy_init=True)
|
||||
|
||||
# system_prompt is included in initial state messages (see _build_initial_state)
|
||||
# to avoid multiple SystemMessages which some LLM APIs don't support.
|
||||
return create_agent(
|
||||
model=model,
|
||||
tools=tools if tools is not None else self.tools,
|
||||
middleware=middlewares,
|
||||
system_prompt=self.config.system_prompt,
|
||||
system_prompt=None,
|
||||
state_schema=ThreadState,
|
||||
)
|
||||
|
||||
@@ -365,14 +367,25 @@ class SubagentExecutor:
|
||||
Returns:
|
||||
Initial state dictionary and tools filtered by loaded skill metadata.
|
||||
"""
|
||||
|
||||
# Load skills as conversation items (Codex pattern)
|
||||
skills = await self._load_skills()
|
||||
filtered_tools = self._apply_skill_allowed_tools(skills)
|
||||
skill_messages = await self._load_skill_messages(skills)
|
||||
|
||||
# Combine system_prompt and skills into a single SystemMessage.
|
||||
# Some LLM APIs reject multiple SystemMessages with
|
||||
# "System message must be at the beginning."
|
||||
system_parts: list[str] = []
|
||||
if self.config.system_prompt:
|
||||
system_parts.append(self.config.system_prompt)
|
||||
for skill_msg in skill_messages:
|
||||
system_parts.append(skill_msg.content)
|
||||
|
||||
messages: list[Any] = []
|
||||
# Skill content injected as developer/system messages before the task
|
||||
messages.extend(skill_messages)
|
||||
if system_parts:
|
||||
messages.append(SystemMessage(content="\n\n".join(system_parts)))
|
||||
|
||||
# Then the actual task
|
||||
messages.append(HumanMessage(content=task))
|
||||
|
||||
|
||||
Reference in New Issue
Block a user