fix: sub-agents, timers and follow-ups repeat parts of the chat's system prompt

A sub-agent's system prompt had the chat's skill list, the model's own system prompt, the list of knowledge attached to the model and the connected terminal's instructions twice each, because it inherits the chat's finished prompt and then adds them again. Timers and the follow-up turn after a background sub-agent finishes had the same repeats, plus the folder's system prompt and the full text of a $mentioned skill twice, and a sub-agent got the user's own system prompt twice. Each now appears once, sub-agents still get the text of a $mentioned skill and the user's system prompt, and the chat's own system prompt is unchanged.

Fixes #31568
This commit is contained in:
Classic298 2026-09-29 10:28:15 +02:00
parent 176d31d1db
commit 4dd8b4741d
4 changed files with 35 additions and 6 deletions

View file

@ -1277,6 +1277,7 @@ async def chat_completion(
'variables': form_data.get('variables', {}),
'chat_variables': chat_variables,
'model': model,
'model_system_prompt': model_info_params.get('system'),
'direct': model_item.get('direct', False),
'params': {
'stream_delta_chunk_size': stream_delta_chunk_size,
@ -1787,7 +1788,7 @@ async def chat_completion(
'session_id': metadata.get('session_id'),
'tool_ids': metadata.get('tool_ids') or [],
'skill_ids': metadata.get('skill_ids') or [],
'system_prompt': metadata.get('system_prompt'),
'system_prompt': metadata.get('chat_system_prompt'),
'filter_ids': metadata.get('filter_ids') or [],
'terminal_id': metadata.get('terminal_id'),
'features': metadata.get('features') or {},

View file

@ -2531,6 +2531,7 @@ async def process_chat_payload(request, form_data, user, metadata, model):
) # Required to handle system prompt variables
except Exception:
pass
chat_system_prompt = get_content_from_message(system_message) if system_message else None
form_data = await convert_url_images_to_base64(form_data, user=user)
@ -2665,6 +2666,8 @@ async def process_chat_payload(request, form_data, user, metadata, model):
except Exception as e:
raise Exception(f'{e}')
delegate_system_prompt = get_content_from_message(get_system_message(form_data['messages']) or {})
features = form_data.pop('features', None) or {}
extra_params['__features__'] = features
if features:
@ -2823,11 +2826,15 @@ async def process_chat_payload(request, form_data, user, metadata, model):
skill_manifest = ''
for skill in available_skills:
if skill.id in mentioned_skill_ids or not use_builtin_tools:
skill_context = f'<skill name="{skill.name}">\n{skill.content}\n</skill>'
form_data['messages'] = add_or_update_system_message(
f'<skill name="{skill.name}">\n{skill.content}\n</skill>',
skill_context,
form_data['messages'],
append=True,
)
delegate_system_prompt = (
f'{delegate_system_prompt}\n{skill_context}' if delegate_system_prompt else skill_context
)
else:
view_skill_ids.append(skill.id)
skill_manifest += (
@ -2855,11 +2862,15 @@ async def process_chat_payload(request, form_data, user, metadata, model):
skill_name = unquote(sid.removeprefix(terminal_skill_prefix))
loaded = await get_terminal_skill(request, user.model_dump(), metadata, skill_name, extra_params)
if loaded:
skill_context = format_terminal_skill_context(loaded)
form_data['messages'] = add_or_update_system_message(
format_terminal_skill_context(loaded),
skill_context,
form_data['messages'],
append=True,
)
delegate_system_prompt = (
f'{delegate_system_prompt}\n{skill_context}' if delegate_system_prompt else skill_context
)
else:
view_skill_ids.append(sid)
skill_manifest += format_terminal_skill_manifest_entry(skill)
@ -3193,6 +3204,22 @@ async def process_chat_payload(request, form_data, user, metadata, model):
f'{resolved_model_system_prompt}\n{system_content}' if system_content else resolved_model_system_prompt
)
metadata['system_prompt'] = system_content or None
# Timers/follow-ups continue this chat, sub-agents start their own; the model's own prompt is added in each run
sent_with_request = model_system_prompt != metadata.get('model_system_prompt')
already_in_chat = resolved_model_system_prompt == chat_system_prompt
if resolved_model_system_prompt and sent_with_request and not already_in_chat:
chat_system_prompt = (
f'{resolved_model_system_prompt}\n{chat_system_prompt}'
if chat_system_prompt
else resolved_model_system_prompt
)
delegate_system_prompt = (
f'{resolved_model_system_prompt}\n{delegate_system_prompt}'
if delegate_system_prompt
else resolved_model_system_prompt
)
metadata['chat_system_prompt'] = chat_system_prompt
metadata['delegate_system_prompt'] = delegate_system_prompt
metadata['user_prompt'] = get_last_user_message(form_data['messages'])
metadata['sources'] = sources[:] if sources else []

View file

@ -319,7 +319,8 @@ async def delegate(
'session_id': metadata.get('session_id'),
'tool_ids': copy.deepcopy(metadata.get('tool_ids') or []),
'skill_ids': copy.deepcopy(metadata.get('skill_ids') or []),
'system_prompt': metadata.get('system_prompt'),
'system_prompt': metadata.get('chat_system_prompt'),
'delegate_system_prompt': metadata.get('delegate_system_prompt'),
'tool_servers': [] if background else copy.deepcopy(metadata.get('tool_servers') or []),
'filter_ids': copy.deepcopy(metadata.get('filter_ids') or []),
'terminal_id': metadata.get('terminal_id'),
@ -449,7 +450,7 @@ async def delegate(
try:
child_request = _build_request(request, user.id, internal=True)
child_request.state.max_tool_call_iterations = max_iterations
parent_system_prompt = run.get('system_prompt') or ''
parent_system_prompt = run.get('delegate_system_prompt') or ''
subagent_system_prompt = (
str(config.get('subagents.system_prompt') or '').strip() or DEFAULT_SUBAGENT_SYSTEM_PROMPT
)

View file

@ -108,7 +108,7 @@ async def create_timer(
'session_id': metadata.get('session_id'),
'tool_ids': copy.deepcopy(metadata.get('tool_ids') or []),
'skill_ids': copy.deepcopy(metadata.get('skill_ids') or []),
'system_prompt': metadata.get('system_prompt'),
'system_prompt': metadata.get('chat_system_prompt'),
'filter_ids': copy.deepcopy(metadata.get('filter_ids') or []),
'terminal_id': metadata.get('terminal_id'),
'features': copy.deepcopy(metadata.get('features') or {}),