#2200·swarms

[BUG][LLMCouncil] The Chairman gets the whole council as one user blob (#2053 follow-up)

Author: kyegomezCreated Sep 8, 2026Updated Sep 13, 2026
Labelsbug

Summary

Split out of #2053, which was closed by #2189 while this file was still outstanding. The Chairman in LLMCouncil still receives the whole council as one user string.

Site

swarms/structs/llm_council.py

Line Expression
:221-226 responses_section = "\n\n".join([f"=== {name} ===\n{response}" ...])
:228-233 evaluations_section = "\n\n".join([f"=== Evaluation by {name} ===\n{evaluation}" ...])
:514 final_response = self.chairman.run(task=synthesis_prompt) — no messages=

get_synthesis_prompt builds the members' responses and the members' evaluations into a single f-string and the Chairman gets it as one turn. The shared self.conversation already holds every one of those entries as a typed turn (:447, :499-501), so the role attribution is thrown away and reconstructed as === name === prose.

Because the whole council lands in one message, the prefix the Chairman sees is different on every run and prompt caching never hits. LLMCouncil is one of the largest contexts in the repo: N responses plus N evaluations, each up to a full model answer.

Related

  • #2184 (open) fixes the recording half for this file: councillors are built with output_type="final" and councillor_answers() reduces transcripts to answers before anonymisation. It deliberately leaves the flattening at :221-233 / :514 alone, so this issue is not a duplicate of it.
  • advisor_swarm.py (#2188) and heavy_swarm.py (#2189) show the target shape.

Fix

The shape every converted structure now uses:

python
from swarms.structs.context_utils import messages_for, split_last_turn

prior, task = split_last_turn(messages_for("Chairman", self.conversation))
final_response = self.chairman.run(task=task, messages=prior)

get_synthesis_prompt keeps its instruction and the id_to_member key and loses the two interpolated sections, which now arrive as turns above it.

Found at 7b709c95b.