Multi-model consensus for cases where you want more than one model's judgment.
The Council agent runs several councillors in parallel, then the Council agent itself synthesizes their outputs into one answer.
User / Orchestrator
|
v
Council agent (@council, your configured synthesizer model)
|
+--> launches Councillor A (preset model)
+--> launches Councillor B (preset model)
+--> launches Councillor C (preset model)
|
v
Council agent synthesizes councillor results
|
v
Final answer
There are two separate model layers:
The Council agent model
@council itself.council
entry or agents.council override.The councillor models
council.presets.<preset>.<councillor>.model.If you only remember one thing, remember this:
@counciluses the normal agent config for the synthesizer model, andcouncil.presetsfor the fan-out councillor models.
Add a council model and at least one council preset to your plugin config:
~/.config/opencode/oh-my-opencode-slim.json
{
"preset": "openai",
"presets": {
"openai": {
"council": { "model": "openai/gpt-5.6" }
}
},
"council": {
"presets": {
"default": {
"alpha": { "model": "openai/gpt-5.6-luna" },
"beta": { "model": "google/gemini-3-pro" },
"gamma": { "model": "openai/gpt-5.3-codex" }
}
}
}
}
Then use it directly:
@council What is the safest migration strategy for this schema change?
{
"council": {
"default_preset": "default",
"timeout": 180000,
"councillor_execution_mode": "parallel",
"councillor_retries": 3,
"presets": {
"default": {
"alpha": { "model": "openai/gpt-5.6-luna" }
}
}
}
}
| Setting | Type | Default | Description |
|---|---|---|---|
presets |
object | - | Required. Named councillor presets |
default_preset |
string | "default" |
Preset used when none is specified |
timeout |
number | 180000 |
Per-councillor timeout in ms |
councillor_execution_mode |
string | "parallel" |
parallel runs all councillors concurrently; serial runs them one at a time |
councillor_retries |
number | 3 |
Retries per councillor on empty provider responses |
Each entry inside a preset is one councillor:
| Field | Type | Required | Description |
|---|---|---|---|
model |
string | array | Yes | A provider/model string, or an ordered fallback chain tried until one responds |
variant |
string | No | Optional variant/reasoning setting (applies to chain entries without their own) |
prompt |
string | No | Optional role guidance prepended to the user prompt |
model also accepts an ordered chain. When the primary model fails or times
out, the councillor advances to the next model instead of dropping out of the
council. Entries are provider/model strings or { "id", "variant" } objects:
{
"council": {
"presets": {
"review": {
"reviewer": {
"model": [
"openai/gpt-5.6",
{ "id": "google/gemini-3-pro", "variant": "high" },
"anthropic/claude-opus-4-6"
],
"prompt": "Focus on bugs, edge cases, and failure modes."
}
}
}
}
}
Empty-response retries (councillor_retries) apply per model before the chain
advances. A single string keeps the previous single-model behavior.
The synthesizer model is not configured inside council.presets.
Configure it using the normal agent system:
{
"presets": {
"openai": {
"council": { "model": "openai/gpt-5.6", "variant": "high" }
}
}
}
Or with a global override:
{
"agents": {
"council": {
"temperature": 0.2
}
}
}
agents.councillor can change shared councillor settings such as temperature,
MCPs, and skills, but it does not choose the councillor model.
Councillor models always come from:
council.presets.<preset>.<councillor>.model
{
"presets": {
"openai": {
"council": { "model": "openai/gpt-5.6" }
}
},
"council": {
"presets": {
"second-opinion": {
"reviewer": { "model": "openai/gpt-5.6-luna" }
}
}
}
}
{
"presets": {
"openai": {
"council": { "model": "openai/gpt-5.6" }
}
},
"council": {
"default_preset": "balanced",
"presets": {
"balanced": {
"alpha": { "model": "openai/gpt-5.6-luna" },
"beta": { "model": "google/gemini-3-pro" },
"gamma": { "model": "anthropic/claude-opus-4-6" }
}
}
}
}
{
"council": {
"councillor_execution_mode": "serial",
"presets": {
"default": {
"alpha": { "model": "openai/gpt-5.6-luna" },
"beta": { "model": "openai/gpt-5.6-luna" }
}
}
}
}
Use serial when parallel councillor launches would contend for the same
underlying provider/session limits.
Each councillor can receive its own steering prompt:
{
"council": {
"presets": {
"review-board": {
"reviewer": {
"model": "openai/gpt-5.6-luna",
"prompt": "Focus on bugs, edge cases, and failure modes."
},
"architect": {
"model": "google/gemini-3-pro",
"prompt": "Focus on maintainability, boundaries, and long-term design."
},
"optimizer": {
"model": "openai/gpt-5.3-codex",
"prompt": "Focus on performance, latency, and resource usage."
}
}
}
}
}
The councillor sees:
<role prompt>
---
<user prompt>
@council Should we use a job queue or an outbox pattern here?
The orchestrator may delegate to @council for high-stakes or ambiguous
decisions, but it does so sparingly because council is usually the most
expensive path.
Council responses include:
Council Response - the synthesized final answer.Councillor Details - each responding councillor's individual response,
using the councillor names from the configured preset.Council Summary - agreement, disagreement resolution, remaining
uncertainty, and a consensus confidence rating of unanimous, majority,
or split.Council responses include a footer like:
---
*Council: 2/3 councillors responded (alpha: gpt-5.6-luna, beta: gemini-3-pro)*
timeout is per councillortimed_outWhen a councillor's model is an array, the councillors walks the chain in
order. Empty-response retries apply per model; any other failure or timeout
advances to the next model. The councillor only fails once every model in the
chain is exhausted, and the reported model reflects the one that responded.
Some providers silently return zero tokens. Council treats that as a retryable failure.
councillor_retries defaults to 3| Scenario | Behavior |
|---|---|
| Some councillors fail | Synthesize from the successful ones |
| All councillors fail | Return an error |
| Preset has zero councillors | Return an error |
master fieldsOlder examples used these fields:
council.mastercouncil.master_timeoutcouncil.master_fallbackThey are deprecated.
Current behavior:
master_timeout is ignoredmaster_fallback is ignoredmaster is deprecated, but master.model is still accepted as a temporary
fallback for the Council agent model only when no explicit council
agent model is configured elsewherePrefer this instead:
{
"presets": {
"openai": {
"council": { "model": "openai/gpt-5.6" }
}
}
}
master is ignoredcouncillors objects are still accepted for backward
compatibility@council is missingCouncil tools are only registered when config.council exists.
Make sure your config includes a council block with at least one preset.
Check:
council.presetsdefault_preset points to a real preset when omitted at runtimeTry:
{
"council": {
"timeout": 300000
}
}
Also verify the configured model IDs exist in your OpenCode environment.
Council is meant to be a leaf agent. Avoid recursive council chains.