Skip to content

Commit 0a65e50

Browse files
committed
fix: ensure only one SystemMessage is passed to create_deep_agent to prevent API errors
--bug=1067309@tapd-62980211 --user=刘瑞斌 【应用】AI对话节点设置系统提示词并添加技能后,使用vllm的Qwen3.5模型对话报错 https://www.tapd.cn/62980211/s/1888671
1 parent 51a4849 commit 0a65e50

File tree

1 file changed

+36
-2
lines changed

1 file changed

+36
-2
lines changed

apps/application/flow/tools.py

Lines changed: 36 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@
3131
from deepagents import create_deep_agent
3232
from django.db.models import QuerySet
3333
from django.http import StreamingHttpResponse
34-
from langchain_core.messages import BaseMessageChunk, BaseMessage, ToolMessage, AIMessageChunk
34+
from langchain_core.messages import BaseMessageChunk, BaseMessage, ToolMessage, AIMessageChunk, SystemMessage
3535
from langchain_mcp_adapters.client import MultiServerMCPClient
3636
from langgraph.checkpoint.memory import MemorySaver
3737

@@ -410,11 +410,45 @@ async def _yield_mcp_response(chat_model, message_list, mcp_servers, mcp_output_
410410
if extra_tools:
411411
for tool in extra_tools:
412412
tools.append(tool)
413+
414+
# ---------------------------------------------------------------------------
415+
# Fix: vLLM (and Qwen chat templates) reject conversations that contain more
416+
# than one SystemMessage, or a SystemMessage that is not the very first
417+
# message. create_deep_agent always prepends its own BASE_AGENT_PROMPT as a
418+
# SystemMessage before calling the model (factory.py line ~1319). If
419+
# message_list already contains a SystemMessage (built in base_chat_node.py
420+
# via generate_message_list), the API receives two system messages and raises
421+
# "System message must be at the beginning."
422+
#
423+
# Solution: strip the user-supplied SystemMessage out of message_list and
424+
# pass its text as the system_prompt argument of create_deep_agent.
425+
# deepagents will then merge it with BASE_AGENT_PROMPT into a single
426+
# combined system message, so the model only ever sees one.
427+
# ---------------------------------------------------------------------------
428+
user_system_prompt = None
429+
filtered_message_list = []
430+
for msg in message_list:
431+
if isinstance(msg, SystemMessage):
432+
# Normalise content to plain string regardless of whether the
433+
# message was built with a str or a list of content blocks.
434+
if isinstance(msg.content, str):
435+
user_system_prompt = msg.content
436+
elif isinstance(msg.content, list):
437+
user_system_prompt = ''.join(
438+
item.get('text', '') if isinstance(item, dict) else str(item)
439+
for item in msg.content
440+
)
441+
else:
442+
user_system_prompt = str(msg.content)
443+
else:
444+
filtered_message_list.append(msg)
445+
413446
agent = create_deep_agent(
414447
model=chat_model,
415448
backend=SandboxShellBackend(root_dir=temp_dir, virtual_mode=True),
416449
skills=['/skills'],
417450
tools=tools,
451+
system_prompt=user_system_prompt,
418452
interrupt_on={
419453
"write_file": False,
420454
"read_file": False,
@@ -425,7 +459,7 @@ async def _yield_mcp_response(chat_model, message_list, mcp_servers, mcp_output_
425459
recursion_limit = int(CONFIG.get(
426460
"LANGCHAIN_GRAPH_RECURSION_LIMIT", '100'))
427461
response = agent.astream(
428-
{"messages": message_list},
462+
{"messages": filtered_message_list},
429463
config={"recursion_limit": recursion_limit,
430464
"configurable": {"thread_id": chat_id}},
431465
stream_mode='messages'

0 commit comments

Comments
 (0)