A Swarm of Agents for Multi-Angle Analysis: Building a Team of Experts from LLMs

@h100envy
h100envy@h100envy
11 views Jul 16, 2026 ~11 min read
Advertisement

Not about speed. About making several agents with different viewpoints argue over one decision and reach a conclusion better than any of them alone. With full code for the orchestrator, experts, and merge.

Media image

When you ask one model to evaluate a decision, it gives one view, usually averaged and cautious. It tends to agree, to smooth over, to find balance. That is the problem: an important decision cannot be evaluated by one averaged view, it must be attacked from different sides.

A swarm of agents solves this structurally. You create several experts, each with a hard-set role and bias: one thinks only about money, another only about technical risk, a third only about the user. They analyze one decision independently, reach different conclusions, and then you force a reconciliation of those conclusions. The value here is not speed but that disagreement is built into the structure. A single agent tends toward groupthink with itself, a swarm of roles does not.

This article shows how to build such a swarm, with code. We cover three parts: the orchestrator that assigns roles, the experts who analyze independently, and the merge that reconciles them into one conclusion.

Architecture: Orchestrator, Experts, Merge

A swarm for analysis is three components.

The orchestrator takes the task and decides which expert roles are needed. For evaluating a product launch these might be an investor, an engineer, a product specialist, a security person. The orchestrator does not analyze itself, it hands out roles.

The experts work in parallel and independently. Each sees the same decision but through its own lens. Crucially, they do not see each other's conclusions, otherwise conformity sets in. Independence is what produces different viewpoints.

The merge collects the experts' conclusions and reconciles them: where they agree, where they contradict, what the final verdict is across all angles. This is not averaging but a synthesis that keeps disagreement as a signal.

Media image

Step 1: The Basic Client

Start with a simple client to the model. I use an OpenAI-compatible message format, it works with most providers and local Ollama.

import requests
import json
from concurrent.futures import ThreadPoolExecutor

API = "http://localhost:11434/api/chat"   # Ollama, or a provider endpoint
MODEL = "qwen2.5:32b"

def ask(system, user, temperature=0.7):
    resp = requests.post(API, json={
        "model": MODEL,
        "messages": [
            {"role": "system", "content": system},
            {"role": "user", "content": user},
        ],
        "temperature": temperature,
        "stream": False,
    }, timeout=120)
    resp.raise_for_status()
    return resp.json()["message"]["content"]

Step 2: The Orchestrator Assigns Roles

The orchestrator gets the task and decides which experts are needed. Do not hardcode roles in advance, let the model pick them for the specific task, this makes the swarm general. Ask for strict JSON to parse.

ORCHESTRATOR_SYSTEM = """You are the orchestrator of an analytical swarm.
For the task, define 3-5 expert roles that will give maximally DIFFERENT
and conflicting views on the decision. The roles must conflict in their
interests, not complement each other.

For each role give: name, focus (what it fixates on), bias (what it is
biased toward, what it tends to overrate).

Reply ONLY with a JSON array, no explanations:
[{"name": "...", "focus": "...", "bias": "..."}, ...]
"""

def plan_roles(task):
    raw = ask(ORCHESTRATOR_SYSTEM, f"Task to analyze:\n{task}",
              temperature=0.9)   # higher temperature for role diversity
    # cut out the JSON in case the model added text around it
    start, end = raw.find("["), raw.rfind("]") + 1
    return json.loads(raw[start:end])

We keep the temperature high here on purpose: we want diverse, not obvious roles. The requirement "the roles must conflict" in the prompt is the key, without it the model gives three nearly identical roles and the whole point of the swarm is lost.

Step 3: The Experts Analyze in Parallel and Independently

Each expert gets its role and the same decision. Critically: they run in parallel and do not see each other's conclusions. Parallelism here is not only for speed, it guarantees independence, an expert physically cannot adjust to another's opinion.

EXPERT_SYSTEM = """You are an expert with the role: {name}.
Your focus: {focus}.
Your bias: {bias}. Do not fight it, it is your value to the analysis.

Analyze the decision STRICTLY from your position. Do not be balanced,
do not try to account for other viewpoints, other experts will do that.
Your job is to push your angle to the limit.

Give:
- a verdict from your position (for / against / conditional)
- 2-3 main arguments from your angle specifically
- 1 risk that is most visible from your position and others will miss
Short and hard, no fluff."""

def run_expert(role, task):
    system = EXPERT_SYSTEM.format(**role)
    opinion = ask(system, f"Decision to analyze:\n{task}", temperature=0.7)
    return {"role": role["name"], "opinion": opinion}

def run_swarm(roles, task):
    # parallel launch: independence plus speed
    with ThreadPoolExecutor(max_workers=len(roles)) as pool:
        futures = [pool.submit(run_expert, role, task) for role in roles]
        return [f.result() for f in futures]

Note the expert's prompt: we explicitly forbid it from being balanced. This is counterintuitive, but this is the whole point. If each expert tries to account for all sides, you get five identical cautious opinions. By forcing each to push its angle to the limit, you get a real spectrum, which the merge then reconciles.

Step 4: The Merge Reconciles the Conclusions

Now we have several sharp, one-sided opinions. The merge collects them into one verdict, but not by averaging. It looks for where the experts agree (a strong signal), where they contradict (a risk zone requiring a decision), and what outweighs what.

MERGE_SYSTEM = """You are the synthesizer of an analytical swarm. You are
given the opinions of several experts with different biases on one decision.

Your job is NOT to average them. Your job is:
1. Agreement: what the experts agreed on despite different positions.
   This is the most reliable signal, highlight it.
2. Conflict: where the experts directly contradict. Do not smooth it over,
   name the conflict explicitly and say what each side costs.
3. Blind spots: a risk only one expert named, but it matters.
4. Final verdict across everything: for / against / conditional, and under
   what conditions it changes.

Write densely. Keep disagreement as information, do not hide it."""

def merge_opinions(task, opinions):
    block = "\n\n".join(
        f"### Expert: {o['role']}\n{o['opinion']}" for o in opinions
    )
    user = f"Decision:\n{task}\n\nExpert opinions:\n{block}"
    return ask(MERGE_SYSTEM, user, temperature=0.4)   # lower temperature for sober synthesis

We lower the temperature at the merge: if the experts should be diverse (high T), the synthesizer should be sober and consistent (low T). The key instruction here is "do not average, keep disagreement as information." A normal merge collapses everything into mush, "on one hand, on the other hand." A good merge says plainly: here everyone agrees, and here is a conflict and it costs this much.

Step 4.5: A Devil's Advocate Against Fake Agreement

There is a quiet danger: sometimes the experts agree not because the decision is good, but because everyone is looking the same way out of inertia. This is fake agreement, and it is more dangerous than open conflict, because it looks like confidence.

Against this we add one special agent, the devil's advocate. Its only job is to attack the consensus. It sees all the experts' opinions and is obligated to find why they might all be wrong at once. If the swarm unanimously voted "for," the advocate looks for a scenario where it is a catastrophe.

DEVIL_SYSTEM = """You are the devil's advocate in an analytical swarm. You
are given the experts' opinions. Your only job: attack their agreement.

If the experts converged on something, find why they might ALL be wrong AT
ONCE. Look for a shared blind spot: an assumption everyone accepted without
checking, a scenario no one considered because it is inconvenient.

Do not be polite. Your value is that you say what the group does not want
to hear. Give:
- which shared assumption of the experts is the most dangerous
- a scenario in which the swarm's unanimous opinion turns out fatally wrong
- one question the group carefully avoided
If there is no agreement and the experts genuinely disagree, say so plainly
and point to the sharpest unresolved conflict."""

def run_devil(task, opinions):
    block = "\n\n".join(
        f"### {o['role']}\n{o['opinion']}" for o in opinions
    )
    user = f"Decision:\n{task}\n\nSwarm opinions:\n{block}"
    return ask(DEVIL_SYSTEM, user, temperature=0.8)

The advocate runs after the experts but before the merge, and its attack goes into the synthesis alongside the opinions. The point is that even a unanimous swarm gets at least one agent obligated to look for a crack. This is cheap (one call) and structurally breaks groupthink: consensus now has to survive an attack, not just happen.

Step 4.6: A Debate Round to Sharpen the Conflict

The first expert pass is independent, and that is right for diversity. But after the opinions are collected, you can give one debate round: show each expert a summary of the others' opinions and let it object. This sharpens the conflicts, weak arguments fall away, strong ones firm up.

DEBATE_SYSTEM = """You are expert {name} in the second round of analysis.
Your original position:
{own_opinion}

Now you see the other experts' opinions. Do not cave under pressure, but
do not ignore strong arguments either. Give:
- where another's argument genuinely hits your position, admit it honestly
- where you hold your line and why their objection is weak
- whether you changed your verdict after the debate, and if so, how
Short. This is not a repeat of the first opinion, but a reaction to opponents."""

def debate_round(roles, task, opinions):
    others_map = {}
    for o in opinions:
        others = "\n\n".join(
            f"### {x['role']}\n{x['opinion']}" for x in opinions if x is not o
        )
        others_map[o["role"]] = others

    def rebut(o):
        system = DEBATE_SYSTEM.format(name=o["role"], own_opinion=o["opinion"])
        user = (f"Decision:\n{task}\n\n"
                f"Opponents' opinions:\n{others_map[o['role']]}")
        return {"role": o["role"], "opinion": ask(system, user, temperature=0.6)}

    with ThreadPoolExecutor(max_workers=len(opinions)) as pool:
        return list(pool.map(rebut, opinions))

The debate round is also parallel: each expert reacts to all the others at once, again with no real-time conformity. After the debate the opinions are usually sharper: you can see which positions held up under fire and which collapsed. It is these hardened opinions that go into the final merge.

Step 5: Putting It All Together

def analyze(task, debate=True):
    print("Orchestrator is picking roles...")
    roles = plan_roles(task)
    for r in roles:
        print(f"  - {r['name']}: {r['focus']}")

    print(f"\nLaunching {len(roles)} experts in parallel...")
    opinions = run_swarm(roles, task)
    for o in opinions:
        print(f"\n[{o['role']}]\n{o['opinion']}")

    # optional debate round: experts react to each other
    if debate:
        print("\nDebate round, experts rebut each other...")
        opinions = debate_round(roles, task, opinions)

    # devil's advocate attacks the swarm's agreement
    print("\nDevil's advocate looks for a crack in the agreement...")
    devil = run_devil(task, opinions)
    print(f"\n[Devil's advocate]\n{devil}")

    # merge reconciles the conclusions plus the advocate's attack
    print("\nMerge is reconciling the conclusions...")
    opinions_plus = opinions + [{"role": "Devil's advocate", "opinion": devil}]
    verdict = merge_opinions(task, opinions_plus)
    print(f"\n=== FINAL VERDICT ===\n{verdict}")
    return verdict

if __name__ == "__main__":
    analyze(
        "We want to remove the free tier and make the product fully paid "
        "with a 14-day trial. Should we do it?"
    )

Running this, you will see the full pipeline: the orchestrator picks roles, the experts cut the truth from their angles, argue with each other in the debate round, the advocate attacks their agreement, and the merge delivers a verdict across everything, including the attack. A single agent on this same question would give a vague "it depends on your audience," the swarm gives a structured breakdown where the conflicts are explicit and the consensus is stress-tested.

What Makes This Swarm Work

Three things separate a useful swarm from theater made of agents.

Roles must conflict, not complement. If your experts are "a marketer, an SMM specialist, a content manager," they will give nearly identical answers, because their interests coincide. The real value is when interests conflict: growth vs sustainability, speed vs quality, money now vs trust later. A conflict of interests is what cracks the decision open.

Experts must not see each other. As soon as one expert sees another's opinion, conformity begins, it adjusts. Independence is not an implementation detail, it is a working condition. Parallel launch gives it for free.

The merge does not average, it preserves conflict. A bad synthesis turns five sharp opinions into one toothless summary. A good synthesis leaves the conflict visible, because the conflict is the most valuable information: it shows where the decision is genuinely risky, not where everyone nods along.

Where to Extend

This skeleton extends in obvious directions. You can add a debate round: after the first merge, show the experts the summary and let them object, which sharpens the conflicts. You can put a judge on a stronger model over the experts to weigh the arguments. You can make the roles permanent for a recurring type of decision, so you do not generate them every time.

But the base principle stays: different lenses, independent analysis, a synthesis that respects disagreement. A swarm is useful for analysis not because there are many agents, but because they look differently and do not let each other slide into a common denominator. Take a decision you are turning over in your head alone right now, and run it through such a swarm. You will see angles you were not holding.

Actions
What You Can Do
  • Download as PDF
  • Save to Notion
  • Export as Markdown
  • Visual Editor
  • LinkedIn & Instagram Carousel Maker
Create Free Account

Includes 7-day Premium trial

Advertisement