Catalog PyPI
PACKAGE EXPLORERView in registry

agent-council

PyPI

Config-driven multi-agent debate council — add a reliability layer to any LLM pipeline

agentsanthropiccouncildebatellmmulti-agentollamaopenaireliability
Versions collected1
Direct dependencies9From release 0.2.1
Last observed20 Oct, 21:44 UTC

README

Version 0.2.1 documentation

agent-council

A reliability layer for LLM pipelines. Multiple agents with distinct personas debate a topic across iterative rounds; a judge synthesizes a final verdict.

Drop it into any agent to pressure-test a decision before committing to it.

Inspired by mshumer/llmcouncil and the Mixture of Agents research.


Install

pip install agent-council

Requires Python ≥ 3.11.


Programmatic usage

This is the primary interface — use it inside your own agents and pipelines.

from agent_council import CouncilOrchestrator, MemberConfig, JudgeConfig

orchestrator = CouncilOrchestrator(
    members=[
        MemberConfig(
            id="analyst",
            name="The Analyst",
            provider="anthropic",
            model="claude-sonnet-4-6",
            persona="Rigorous analytical thinker. Evidence-based, structured.",
        ),
        MemberConfig(
            id="skeptic",
            name="The Skeptic",
            provider="openai",
            model="gpt-4o",
            persona="Challenge every assumption. Surface hidden risks.",
        ),
    ],
    judge=JudgeConfig(provider="anthropic", model="claude-opus-4-6"),
    rounds=3,
    early_exit_threshold=0.85,
)

session, verdict = await orchestrator.run("Should we adopt microservices?")

print(verdict.verdict)           # synthesized conclusion
print(verdict.consensus_level)   # ConsensusLevel.STRONG / MODERATE / WEAK / NONE
print(verdict.consensus_score)   # float 0–1
print(verdict.key_agreements)    # list[str]
print(verdict.dissenting_views)  # list[str]

API keys are read from environment variables (ANTHROPIC_API_KEY, OPENAI_API_KEY) by default.

Streaming callbacks

Callbacks fire as each member finishes — no waiting for a full round to complete. Both sync and async callbacks are supported.

def on_member(resp):
    print(f"[{resp.member_name}] {resp.stance} ({resp.confidence:.0%} confident)")

async def on_round(round_):
    await db.save_round(round_)   # async is fine too

session, verdict = await orchestrator.run(
    "Should we rewrite in Rust?",
    on_member_response=on_member,
    on_round_complete=on_round,
)

Provider overrides

Override temperature, token limits, or use a custom Ollama endpoint:

from agent_council import (
    CouncilOrchestrator, MemberConfig, JudgeConfig,
    ProvidersConfig, AnthropicProviderConfig, OllamaProviderConfig,
)

orchestrator = CouncilOrchestrator(
    members=[...],
    judge=JudgeConfig(provider="anthropic", model="claude-opus-4-6"),
    provider_configs=ProvidersConfig(
        anthropic=AnthropicProviderConfig(temperature=0.5, max_tokens=1024),
        ollama=OllamaProviderConfig(base_url="http://my-ollama:11434"),
    ),
)

Result types

@dataclass FinalVerdict:
    topic: str
    verdict: str
    consensus_level: ConsensusLevel      # STRONG / MODERATE / WEAK / NONE
    consensus_score: float               # 0–1
    key_agreements: list[str]
    dissenting_views: list[str]
    rounds_completed: int
    early_exit: bool
    total_duration_seconds: float

@dataclass CouncilSession:
    session_id: str
    topic: str
    started_at: datetime
    rounds: list[DebateRound]            # full transcript
    verdict: FinalVerdict | None

@dataclass MemberResponse:
    member_id, member_name, round_number: str / int
    content: str                         # full response text
    stance: str                          # one-line position summary
    confidence: float                    # 0–1
    changed_position: bool

Why Reliability

LLMs can be confident and wrong. A single-shot response hides uncertainty and failure modes (hallucinations, missed trade‑offs, overfitting to prompt). Agent Council creates informed disagreement and then reconciles it:

  • Independent agents surface blind spots and competing views.
  • Iterative rounds reward stable, consistent positions (consensus score).
  • A neutral judge synthesizes agreements and dissent for transparent decision‑making.

What improves:

  • Decision quality: fewer unexamined assumptions, clearer trade‑offs.
  • Traceability: full transcript and structured verdict for audits/reviews.
  • Safety: configurable early exit threshold to avoid premature consensus.
  • Extensibility: mix providers/models (Anthropic, OpenAI, OpenRouter, Ollama).

Examples & Use Cases

  • Architecture choices: “Monolith vs microservices for product X?”
  • Launch reviews: “Are we production‑ready? What risks remain?”
  • AI safety checks: “Could this prompt produce unsafe output?”
  • Product strategy: “Should pricing move to usage‑based?”
  • Code migration: “Rewrite to Rust? What are the costs/benefits?”

Programmatic snippet (minimal):

session, verdict = await CouncilOrchestrator(
    members=[
        MemberConfig(id="analyst", name="Analyst", provider="openrouter", model="anthropic/claude-3.5-sonnet"),
        MemberConfig(id="skeptic", name="Skeptic", provider="openrouter", model="openai/gpt-4o"),
    ],
    judge=JudgeConfig(provider="openrouter", model="anthropic/claude-3.5-sonnet"),
).run("Should we adopt microservices?")
print(verdict.verdict)

CLI:

export OPENROUTER_API_KEY=sk-or-...
council review "Remote work vs office?" -c config/council.yaml

HTTP (server):

pip install "agent-council[server]"
council serve
# POST http://127.0.0.1:8000/review {"topic":"Should we rewrite in Rust?"}

How the debate works

Round 1 — all members respond independently to the topic
Round 2..N — each member reads all peers' responses and may revise
             → early exit if consensus score ≥ threshold
Judge — reads full transcript, synthesizes final verdict

Consensus score = avg_confidence × (1 − changed_fraction) Rewards both high confidence and stability across rounds.


Adding a provider

Implement BaseModelAdapter (one method) and register it in the factory:

# agent_council/adapters/my_provider.py
from agent_council.adapters.base import BaseModelAdapter

class MyProviderAdapter(BaseModelAdapter):
    async def complete(self, system: str, user: str) -> str:
        # call your model here
        ...
# agent_council/adapters/__init__.py — add to build_adapter()
case "myprovider":
    return MyProviderAdapter(member_cfg, provider_cfg)

Config file (optional)

For teams who prefer YAML over code:

# config/council.yaml
council:
  debate_rounds: 3
  early_exit_threshold: 0.85

members:
  - id: "analyst"
    name: "The Analyst"
    provider: "anthropic"
    model: "claude-sonnet-4-6"
    persona: "Rigorous analytical thinker."

  - id: "skeptic"
    name: "The Skeptic"
    provider: "openai"
    model: "gpt-4o"
    persona: "Challenge every assumption."

judge:
  provider: "anthropic"
  model: "claude-opus-4-6"

providers:
  # Optional: use OpenRouter with OpenAI-compatible models
  openrouter:
    api_key_env: "OPENROUTER_API_KEY"
    base_url: "https://openrouter.ai/api/v1"
    max_tokens: 2048
    temperature: 0.7
orchestrator = CouncilOrchestrator.from_config_file("config/council.yaml")

CLI (convenience)

# Install with CLI support (included by default)
pip install agent-council

export ANTHROPIC_API_KEY=sk-ant-...
council review "Is Python the best language for data science?"
council review "Should we rewrite in Rust?" --config config/council.yaml
council review "Remote work vs office?" --no-rounds

HTTP server (optional)

pip install "agent-council[server]"
council serve                       # http://127.0.0.1:8000
MethodPathDescription
GET/healthLiveness check
POST/reviewRun debate, return full JSON result
POST/review/streamRun debate, stream events via SSE

Interactive docs at http://localhost:8000/docs.

SSE event stream (POST /review/stream):

data: {"event": "member_response", "data": {...}}
data: {"event": "round_complete",  "data": {"round_number": 1, "consensus_score": 0.62}}
data: {"event": "verdict",         "data": {...}}

Tracing

Enable JSON traces to inspect every response, round, and the final verdict. Two ways:

  1. Programmatic recorder
from agent_council.tracing import TraceRecorder

rec = TraceRecorder()
session, verdict = await orchestrator.run("Your topic", trace=rec)
path = rec.save("traces/")
print("Trace saved to", path)
  1. Build a trace from a finished session
from agent_council.tracing import TraceRecorder
rec = TraceRecorder.from_session(session, verdict)
rec.save("traces/")

Each trace is a single JSON file containing per-member responses, round summaries, and the final verdict.


Budget Awareness (optional)

Control spend with a simple per‑call budget guard. Set these env vars:

  • COUNCIL_COST_PER_CALL_USD — estimated cost charged per model call.
  • COUNCIL_MAX_BUDGET_USD — cap for the whole run; if exceeded and COUNCIL_BUDGET_HARD_STOP is true (default), the run stops.
  • COUNCIL_BUDGET_HARD_STOP — set 0/false for soft cap.

This wraps each provider adapter and enforces the budget before calling the model. For precise accounting, plug in your own adapter with exact token‑based pricing.


Project layout

agent_council/
├── __init__.py          # public API
├── orchestrator.py      # CouncilOrchestrator — primary entry point
├── member.py            # prompt construction + JSON parsing
├── debate.py            # round loop + consensus scoring
├── judge.py             # synthesis + FinalVerdict
├── config.py            # Pydantic config schema
├── types.py             # result dataclasses
├── server.py            # FastAPI app (optional)
└── adapters/
    ├── base.py
    ├── anthropic_adapter.py
    ├── openai_adapter.py
    └── ollama_adapter.py

License

MIT

Downloads & popularity

6downloads
1-day window · source-reported counts20 Oct 2026
94.50
2026-07-23: 4 · observed 2026-10-21T19:53:20.2138527Z · period 2026-07-23–2026-07-232026-07-24: 3 · observed 2026-10-21T19:53:20.2138527Z · period 2026-07-24–2026-07-242026-07-25: 2 · observed 2026-10-21T19:53:20.2138527Z · period 2026-07-25–2026-07-252026-07-27: 5 · observed 2026-10-21T19:53:20.2138527Z · period 2026-07-27–2026-07-272026-07-30: 2 · observed 2026-10-21T19:53:20.2138527Z · period 2026-07-30–2026-07-302026-07-31: 2 · observed 2026-10-21T19:53:20.2138527Z · period 2026-07-31–2026-07-312026-08-02: 3 · observed 2026-10-21T19:53:20.2138527Z · period 2026-08-02–2026-08-022026-08-04: 2 · observed 2026-10-21T19:53:20.2138527Z · period 2026-08-04–2026-08-042026-08-06: 2 · observed 2026-10-21T19:53:20.2138527Z · period 2026-08-06–2026-08-062026-08-09: 3 · observed 2026-10-21T19:53:20.2138527Z · period 2026-08-09–2026-08-092026-08-10: 2 · observed 2026-10-21T19:53:20.2138527Z · period 2026-08-10–2026-08-102026-08-12: 5 · observed 2026-10-21T19:53:20.2138527Z · period 2026-08-12–2026-08-122026-08-14: 6 · observed 2026-10-21T19:53:20.2138527Z · period 2026-08-14–2026-08-142026-08-16: 2 · observed 2026-10-21T19:53:20.2138527Z · period 2026-08-16–2026-08-162026-08-17: 8 · observed 2026-10-21T19:53:20.2138527Z · period 2026-08-17–2026-08-172026-08-18: 3 · observed 2026-10-21T19:53:20.2138527Z · period 2026-08-18–2026-08-182026-08-21: 2 · observed 2026-10-21T19:53:20.2138527Z · period 2026-08-21–2026-08-212026-08-22: 6 · observed 2026-10-21T19:53:20.2138527Z · period 2026-08-22–2026-08-222026-08-23: 3 · observed 2026-10-21T19:53:20.2138527Z · period 2026-08-23–2026-08-232026-08-24: 8 · observed 2026-10-21T19:53:20.2138527Z · period 2026-08-24–2026-08-242026-08-28: 1 · observed 2026-10-21T19:53:20.2138527Z · period 2026-08-28–2026-08-282026-08-31: 2 · observed 2026-10-21T19:53:20.2138527Z · period 2026-08-31–2026-08-312026-09-01: 2 · observed 2026-10-21T19:53:20.2138527Z · period 2026-09-01–2026-09-012026-09-02: 2 · observed 2026-10-21T19:53:20.2138527Z · period 2026-09-02–2026-09-022026-09-04: 1 · observed 2026-10-21T19:53:20.2138527Z · period 2026-09-04–2026-09-042026-09-07: 1 · observed 2026-10-21T19:53:20.2138527Z · period 2026-09-07–2026-09-072026-09-09: 1 · observed 2026-10-21T19:53:20.2138527Z · period 2026-09-09–2026-09-092026-09-12: 1 · observed 2026-10-21T19:53:20.2138527Z · period 2026-09-12–2026-09-122026-09-14: 6 · observed 2026-10-21T19:53:20.2138527Z · period 2026-09-14–2026-09-142026-09-16: 2 · observed 2026-10-21T19:53:20.2138527Z · period 2026-09-16–2026-09-162026-09-18: 1 · observed 2026-10-21T19:53:20.2138527Z · period 2026-09-18–2026-09-182026-09-20: 7 · observed 2026-10-21T19:53:20.2138527Z · period 2026-09-20–2026-09-202026-09-22: 1 · observed 2026-10-21T19:53:20.2138527Z · period 2026-09-22–2026-09-222026-09-23: 1 · observed 2026-10-21T19:53:20.2138527Z · period 2026-09-23–2026-09-232026-09-26: 1 · observed 2026-10-21T19:53:20.2138527Z · period 2026-09-26–2026-09-262026-09-27: 1 · observed 2026-10-21T19:53:20.2138527Z · period 2026-09-27–2026-09-272026-09-29: 3 · observed 2026-10-21T19:53:20.2138527Z · period 2026-09-29–2026-09-292026-09-30: 2 · observed 2026-10-21T19:53:20.2138527Z · period 2026-09-30–2026-09-302026-10-01: 2 · observed 2026-10-21T19:53:20.2138527Z · period 2026-10-01–2026-10-012026-10-02: 1 · observed 2026-10-21T19:53:20.2138527Z · period 2026-10-02–2026-10-022026-10-04: 1 · observed 2026-10-21T19:53:20.2138527Z · period 2026-10-04–2026-10-042026-10-05: 4 · observed 2026-10-21T19:53:20.2138527Z · period 2026-10-05–2026-10-052026-10-06: 2 · observed 2026-10-21T19:53:20.2138527Z · period 2026-10-06–2026-10-062026-10-08: 2 · observed 2026-10-21T19:53:20.2138527Z · period 2026-10-08–2026-10-082026-10-09: 4 · observed 2026-10-21T19:53:20.2138527Z · period 2026-10-09–2026-10-092026-10-10: 6 · observed 2026-10-21T19:53:20.2138527Z · period 2026-10-10–2026-10-102026-10-11: 5 · observed 2026-10-21T19:53:20.2138527Z · period 2026-10-11–2026-10-112026-10-12: 4 · observed 2026-10-21T19:53:20.2138527Z · period 2026-10-12–2026-10-122026-10-13: 3 · observed 2026-10-21T19:53:20.2138527Z · period 2026-10-13–2026-10-132026-10-14: 5 · observed 2026-10-21T19:53:20.2138527Z · period 2026-10-14–2026-10-142026-10-15: 3 · observed 2026-10-21T19:53:20.2138527Z · period 2026-10-15–2026-10-152026-10-16: 2 · observed 2026-10-21T19:53:20.2138527Z · period 2026-10-16–2026-10-162026-10-17: 9 · observed 2026-10-21T19:53:20.2138527Z · period 2026-10-17–2026-10-172026-10-18: 4 · observed 2026-10-21T19:53:20.2138527Z · period 2026-10-18–2026-10-182026-10-19: 1 · observed 2026-10-21T19:53:20.2138527Z · period 2026-10-19–2026-10-192026-10-20: 6 · observed 2026-10-21T19:53:20.2138527Z · period 2026-10-20–2026-10-20
23 Jul20 Oct

pypistats · 56 observations shown. Missing days are unknown. Source category: overall: without mirrors.

View observed values
Date / source periodValueObserved at
2026-10-20 – 2026-10-2062026-10-21T19:53:20.2138527Z
2026-10-19 – 2026-10-1912026-10-21T19:53:20.2138527Z
2026-10-18 – 2026-10-1842026-10-21T19:53:20.2138527Z
2026-10-17 – 2026-10-1792026-10-21T19:53:20.2138527Z
2026-10-16 – 2026-10-1622026-10-21T19:53:20.2138527Z
2026-10-15 – 2026-10-1532026-10-21T19:53:20.2138527Z
2026-10-14 – 2026-10-1452026-10-21T19:53:20.2138527Z
2026-10-13 – 2026-10-1332026-10-21T19:53:20.2138527Z
2026-10-12 – 2026-10-1242026-10-21T19:53:20.2138527Z
2026-10-11 – 2026-10-1152026-10-21T19:53:20.2138527Z
2026-10-10 – 2026-10-1062026-10-21T19:53:20.2138527Z
2026-10-09 – 2026-10-0942026-10-21T19:53:20.2138527Z
2026-10-08 – 2026-10-0822026-10-21T19:53:20.2138527Z
2026-10-06 – 2026-10-0622026-10-21T19:53:20.2138527Z
2026-10-05 – 2026-10-0542026-10-21T19:53:20.2138527Z
2026-10-04 – 2026-10-0412026-10-21T19:53:20.2138527Z
2026-10-02 – 2026-10-0212026-10-21T19:53:20.2138527Z
2026-10-01 – 2026-10-0122026-10-21T19:53:20.2138527Z
2026-09-30 – 2026-09-3022026-10-21T19:53:20.2138527Z
2026-09-29 – 2026-09-2932026-10-21T19:53:20.2138527Z
2026-09-27 – 2026-09-2712026-10-21T19:53:20.2138527Z
2026-09-26 – 2026-09-2612026-10-21T19:53:20.2138527Z
2026-09-23 – 2026-09-2312026-10-21T19:53:20.2138527Z
2026-09-22 – 2026-09-2212026-10-21T19:53:20.2138527Z
2026-09-20 – 2026-09-2072026-10-21T19:53:20.2138527Z
2026-09-18 – 2026-09-1812026-10-21T19:53:20.2138527Z
2026-09-16 – 2026-09-1622026-10-21T19:53:20.2138527Z
2026-09-14 – 2026-09-1462026-10-21T19:53:20.2138527Z
2026-09-12 – 2026-09-1212026-10-21T19:53:20.2138527Z
2026-09-09 – 2026-09-0912026-10-21T19:53:20.2138527Z
2026-09-07 – 2026-09-0712026-10-21T19:53:20.2138527Z
2026-09-04 – 2026-09-0412026-10-21T19:53:20.2138527Z
2026-09-02 – 2026-09-0222026-10-21T19:53:20.2138527Z
2026-09-01 – 2026-09-0122026-10-21T19:53:20.2138527Z
2026-08-31 – 2026-08-3122026-10-21T19:53:20.2138527Z
2026-08-28 – 2026-08-2812026-10-21T19:53:20.2138527Z
2026-08-24 – 2026-08-2482026-10-21T19:53:20.2138527Z
2026-08-23 – 2026-08-2332026-10-21T19:53:20.2138527Z
2026-08-22 – 2026-08-2262026-10-21T19:53:20.2138527Z
2026-08-21 – 2026-08-2122026-10-21T19:53:20.2138527Z
2026-08-18 – 2026-08-1832026-10-21T19:53:20.2138527Z
2026-08-17 – 2026-08-1782026-10-21T19:53:20.2138527Z
2026-08-16 – 2026-08-1622026-10-21T19:53:20.2138527Z
2026-08-14 – 2026-08-1462026-10-21T19:53:20.2138527Z
2026-08-12 – 2026-08-1252026-10-21T19:53:20.2138527Z
2026-08-10 – 2026-08-1022026-10-21T19:53:20.2138527Z
2026-08-09 – 2026-08-0932026-10-21T19:53:20.2138527Z
2026-08-06 – 2026-08-0622026-10-21T19:53:20.2138527Z
2026-08-04 – 2026-08-0422026-10-21T19:53:20.2138527Z
2026-08-02 – 2026-08-0232026-10-21T19:53:20.2138527Z
2026-07-31 – 2026-07-3122026-10-21T19:53:20.2138527Z
2026-07-30 – 2026-07-3022026-10-21T19:53:20.2138527Z
2026-07-27 – 2026-07-2752026-10-21T19:53:20.2138527Z
2026-07-25 – 2026-07-2522026-10-21T19:53:20.2138527Z
2026-07-24 – 2026-07-2432026-10-21T19:53:20.2138527Z
2026-07-23 – 2026-07-2342026-10-21T19:53:20.2138527Z

Versions 1

Collected releases
VersionPublishedDirect dependenciesOpen version
0.2.127 Feb 20269

Package advisories

0
No matching advisories collected

This describes our collected data; it is not a security assessment.

Repository

PENDING
supreethravi/agent-council

Shared repository data · GitHub

License detected in repository
Not collected

Metadata history

Meaningful changes
Current collected state Current

Observed since 20 Oct, 21:44 UTC

No superseded metadata states collected. Unchanged observations do not create new revisions.

Record identity

package:2c34cb6c2c4f8b7486d39f1b498c10b1630979500ebf9e7254b13498b31f2d84