lolspider / notes
Clipping · 8 分钟

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

Not about speed. About making several agents with different viewpoints argue over one decision and reach a conclusion better than any of the...

x.com @h100envy

📝 摘要

本文提出一种"智能体群体(Swarm of Agents)"架构,用于对单一决策进行多角度对抗式分析。核心动机是:单个 LLM 倾向于给出"平均化、谨慎、一团和气"的单一视角,而重要决策必须从冲突的利益方各自被推到极限的立场出发,再经过合成得到优于任何单一视角的结论。文章用 Python 实现了完整的 Orchestrator → Experts → Merge 管线,并加入了 Devil's Advocate(魔鬼代言人)攻击共识、Debate Round(辩论轮)锐化冲突两个增强环节。

核心概念: 多智能体分析的价值不在于"多"或"快",而在于让持有冲突利益的专家角色彼此独立地把各自的视角推到极限,再把分歧作为信号保留在最终合成中——分歧本身就是最有价值的信息,而非需要被抹平的噪声。

关键技术栈 / 关键要点:

  1. 三段式架构(Orchestrator / Experts / Merge) — 编排者动态生成 3-5 个"利益冲突"的专家角色(非预先固化),专家并行且互相不可见地独立分析,合成器以"保留分歧"而非"平均"的方式收敛结论。
  2. 温度梯度调控 — 角色生成用高温度(0.9)保证多样性,专家分析用中温度(0.7)推动立场到极限,合成用低温度(0.4)保持冷静一致;温度本身成为控制多样性 vs 一致性的工程旋钮。
  3. Devil's Advocate(魔鬼代言人) — 一个专门攻击群体共识的对抗性 Agent,寻找所有专家可能共同犯错的"共享盲点",廉价(一次调用)且结构性地打破群体思维。
  4. Debate Round(辩论轮) — 在独立首轮后,让每个专家看到他人意见并反驳,弱论证崩塌、强论证固化,把"经过火力检验"的硬化意见送入最终合成。
  5. Python + OpenAI 兼容 API — 使用 requests + ThreadPoolExecutor 调用本地 Ollama(qwen2.5:32b),并行执行既为速度也为"物理上无法趋同"的独立性保证。
  6. 三大设计铁律 — 角色必须冲突不能互补;专家必须互相不可见(独立性是工作条件而非实现细节);合成必须保留冲突而非平均化(避免变成"一方面…另一方面…"的 mush)。

关键结论 / 实践建议: 当面对一个你独自反复权衡的决策时,把它喂给这样一个群体——你会看到自己未曾持有的角度。可用性扩展方向包括:在首轮合并后再来一轮辩论、用更强模型做 Judge 加权论证、为某类重复决策固化角色集。


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.

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.

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.

python
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.

python
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.

python
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.

python
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.

python
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.

python
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

python
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.