|
|
@@ -5,14 +5,13 @@ Multi-model consensus for cases where you want more than one model's judgment.
|
|
|
## Table of Contents
|
|
|
|
|
|
- [Overview](#overview)
|
|
|
-- [The Important Mental Model](#the-important-mental-model)
|
|
|
- [Quick Setup](#quick-setup)
|
|
|
- [Configuration](#configuration)
|
|
|
+- [Model Fallback Chain](#model-fallback-chain)
|
|
|
- [Choosing the Council Model vs Councillor Models](#choosing-the-council-model-vs-councillor-models)
|
|
|
- [Preset Examples](#preset-examples)
|
|
|
- [Role Prompts](#role-prompts)
|
|
|
- [Usage](#usage)
|
|
|
-- [Timeouts, Retries, and Failures](#timeouts-retries-and-failures)
|
|
|
- [Compatibility Notes](#compatibility-notes)
|
|
|
- [Troubleshooting](#troubleshooting)
|
|
|
|
|
|
@@ -20,8 +19,8 @@ Multi-model consensus for cases where you want more than one model's judgment.
|
|
|
|
|
|
## Overview
|
|
|
|
|
|
-The **Council agent** runs several **councillors** in parallel, then the
|
|
|
-**Council agent itself** synthesizes their outputs into one answer.
|
|
|
+The **Council agent** runs several **councillors** in parallel, then
|
|
|
+synthesizes their outputs into one answer.
|
|
|
|
|
|
### What you get
|
|
|
|
|
|
@@ -32,15 +31,21 @@ The **Council agent** runs several **councillors** in parallel, then the
|
|
|
|
|
|
### How it works
|
|
|
|
|
|
+Each councillor in a preset is registered as a dynamic subagent named
|
|
|
+`councillor-<name>` (e.g. `councillor-alpha`, `councillor-beta`), each
|
|
|
+with its own configured model. The orchestrator dispatches all councillors
|
|
|
+in parallel via OpenCode's native `task()` tool at depth 1, and each
|
|
|
+councillor appears as its own TUI pane.
|
|
|
+
|
|
|
```text
|
|
|
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)
|
|
|
+ +--> task(): councillor-alpha (configured model)
|
|
|
+ +--> task(): councillor-beta (configured model)
|
|
|
+ +--> task(): councillor-gamma (configured model)
|
|
|
|
|
|
|
v
|
|
|
Council agent synthesizes councillor results
|
|
|
@@ -49,26 +54,8 @@ Council agent synthesizes councillor results
|
|
|
Final answer
|
|
|
```
|
|
|
|
|
|
----
|
|
|
-
|
|
|
-## The Important Mental Model
|
|
|
-
|
|
|
-There are **two separate model layers**:
|
|
|
-
|
|
|
-1. **The Council agent model**
|
|
|
- - This is the model behind `@council` itself.
|
|
|
- - It does the final synthesis.
|
|
|
- - Configure it like any other agent: via your active preset's `council`
|
|
|
- entry or `agents.council` override.
|
|
|
-
|
|
|
-2. **The councillor models**
|
|
|
- - These are the models that actually fan out in parallel.
|
|
|
- - Configure them under `council.presets.<preset>.<councillor>.model`.
|
|
|
-
|
|
|
-If you only remember one thing, remember this:
|
|
|
-
|
|
|
-> `@council` uses the normal agent config for the synthesizer model, and
|
|
|
-> `council.presets` for the fan-out councillor models.
|
|
|
+The council agent waits for all councillors to respond (or fail), then
|
|
|
+synthesizes their results into a single report.
|
|
|
|
|
|
---
|
|
|
|
|
|
@@ -114,9 +101,7 @@ Then use it directly:
|
|
|
{
|
|
|
"council": {
|
|
|
"default_preset": "default",
|
|
|
- "timeout": 180000,
|
|
|
- "councillor_execution_mode": "parallel",
|
|
|
- "councillor_retries": 3,
|
|
|
+
|
|
|
"presets": {
|
|
|
"default": {
|
|
|
"alpha": { "model": "openai/gpt-5.6-luna" }
|
|
|
@@ -130,9 +115,6 @@ Then use it directly:
|
|
|
|---------|------|---------|-------------|
|
|
|
| `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 |
|
|
|
|
|
|
### Councillor config
|
|
|
|
|
|
@@ -144,35 +126,7 @@ Each entry inside a preset is one councillor:
|
|
|
| `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 |
|
|
|
|
|
|
-#### Councillor model fallback
|
|
|
-
|
|
|
-`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:
|
|
|
-
|
|
|
-```jsonc
|
|
|
-{
|
|
|
- "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.
|
|
|
-
|
|
|
-### Council agent config
|
|
|
+### Council agent (synthesizer) config
|
|
|
|
|
|
The **synthesizer model** is **not** configured inside `council.presets`.
|
|
|
|
|
|
@@ -202,8 +156,47 @@ Or with a global override:
|
|
|
|
|
|
---
|
|
|
|
|
|
+## Model Fallback Chain
|
|
|
+
|
|
|
+When `model` is a string, the councillor uses that single model.
|
|
|
+
|
|
|
+When `model` is an array, the councillor walks the chain in order:
|
|
|
+
|
|
|
+```jsonc
|
|
|
+{
|
|
|
+ "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."
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+```
|
|
|
+
|
|
|
+Entries are `provider/model` strings or `{ "id", "variant" }` objects. The
|
|
|
+councillor tries each entry in order until one responds. Empty responses are
|
|
|
+retried once per entry; other failures advance to the next entry. The
|
|
|
+councillor only fails once every entry in the chain is exhausted.
|
|
|
+
|
|
|
+---
|
|
|
+
|
|
|
## Choosing the Council Model vs Councillor Models
|
|
|
|
|
|
+There are **two separate model layers**:
|
|
|
+
|
|
|
+1. **The Council agent model** — the model behind `@council` itself, which
|
|
|
+ does the final synthesis.
|
|
|
+2. **The councillor models** — the models that actually fan out in parallel,
|
|
|
+ configured under `council.presets.<preset>.<councillor>.model`.
|
|
|
+
|
|
|
### Configure the Council agent when you want to change
|
|
|
|
|
|
- the **final synthesizer model**
|
|
|
@@ -269,25 +262,6 @@ Councillor models always come from:
|
|
|
}
|
|
|
```
|
|
|
|
|
|
-### Serial mode for single-model systems
|
|
|
-
|
|
|
-```jsonc
|
|
|
-{
|
|
|
- "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.
|
|
|
-
|
|
|
---
|
|
|
|
|
|
## Role Prompts
|
|
|
@@ -329,65 +303,39 @@ The councillor sees:
|
|
|
|
|
|
## Usage
|
|
|
|
|
|
-### Direct invocation
|
|
|
+### Invocation
|
|
|
|
|
|
```text
|
|
|
@council Should we use a job queue or an outbox pattern here?
|
|
|
```
|
|
|
|
|
|
-### Via orchestrator delegation
|
|
|
+The orchestrator may also delegate to `@council` for high-stakes or
|
|
|
+ambiguous decisions.
|
|
|
+
|
|
|
+### What you see
|
|
|
|
|
|
-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.
|
|
|
+Each councillor appears as its own TUI pane, dispatched in parallel. As they
|
|
|
+complete, their responses stream into the panes. Once all councillors have
|
|
|
+responded (or failed), the council agent synthesizes their results.
|
|
|
|
|
|
-### Output format
|
|
|
+### Output
|
|
|
|
|
|
Council responses include:
|
|
|
|
|
|
-1. `Council Response` - the synthesized final answer.
|
|
|
-2. `Councillor Details` - each responding councillor's individual response,
|
|
|
+1. **Council Response** — the synthesized final answer.
|
|
|
+2. **Councillor Details** — each responding councillor's individual response,
|
|
|
using the councillor names from the configured preset.
|
|
|
-3. `Council Summary` - agreement, disagreement resolution, remaining
|
|
|
+3. **Council Summary** — agreement, disagreement resolution, remaining
|
|
|
uncertainty, and a consensus confidence rating of `unanimous`, `majority`,
|
|
|
or `split`.
|
|
|
|
|
|
-### Output footer
|
|
|
-
|
|
|
-Council responses include a footer like:
|
|
|
+A footer tracks participation:
|
|
|
|
|
|
```text
|
|
|
---
|
|
|
*Council: 2/3 councillors responded (alpha: gpt-5.6-luna, beta: gemini-3-pro)*
|
|
|
```
|
|
|
|
|
|
----
|
|
|
-
|
|
|
-## Timeouts, Retries, and Failures
|
|
|
-
|
|
|
-### Timeout behavior
|
|
|
-
|
|
|
-- `timeout` is **per councillor**
|
|
|
-- timed-out councillors are marked `timed_out`
|
|
|
-- council still synthesizes from successful results
|
|
|
-
|
|
|
-### Model chain fallback
|
|
|
-
|
|
|
-When 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.
|
|
|
-
|
|
|
-### Empty response retries
|
|
|
-
|
|
|
-Some providers silently return zero tokens. Council treats that as a retryable
|
|
|
-failure.
|
|
|
-
|
|
|
-- `councillor_retries` defaults to `3`
|
|
|
-- retries only happen for **empty provider responses**
|
|
|
-- normal failures and timeouts are returned immediately (they advance to the
|
|
|
- next model in the chain, if any)
|
|
|
-
|
|
|
### Failure behavior
|
|
|
|
|
|
| Scenario | Behavior |
|
|
|
@@ -402,21 +350,12 @@ failure.
|
|
|
|
|
|
### Deprecated `master` fields
|
|
|
|
|
|
-Older examples used these fields:
|
|
|
-
|
|
|
-- `council.master`
|
|
|
-- `council.master_timeout`
|
|
|
-- `council.master_fallback`
|
|
|
+Older configs used `council.master` and several other `master`-prefixed
|
|
|
+fields. These fields are deprecated and ignored.
|
|
|
|
|
|
-They are deprecated.
|
|
|
-
|
|
|
-Current behavior:
|
|
|
-
|
|
|
-- `master_timeout` is ignored
|
|
|
-- `master_fallback` is ignored
|
|
|
-- `master` 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 elsewhere
|
|
|
+`master.model` is still accepted as a temporary fallback for the **Council
|
|
|
+agent model only** when no explicit `council` agent model is configured
|
|
|
+elsewhere.
|
|
|
|
|
|
Prefer this instead:
|
|
|
|
|
|
@@ -442,7 +381,7 @@ Prefer this instead:
|
|
|
|
|
|
### `@council` is missing
|
|
|
|
|
|
-Council tools are only registered when `config.council` exists.
|
|
|
+Council is only available when `config.council` exists.
|
|
|
|
|
|
Make sure your config includes a `council` block with at least one preset.
|
|
|
|
|
|
@@ -454,20 +393,8 @@ Check:
|
|
|
2. it exists under `council.presets`
|
|
|
3. `default_preset` points to a real preset when omitted at runtime
|
|
|
|
|
|
-### All councillors timed out
|
|
|
-
|
|
|
-Try:
|
|
|
-
|
|
|
-```jsonc
|
|
|
-{
|
|
|
- "council": {
|
|
|
- "timeout": 300000
|
|
|
- }
|
|
|
-}
|
|
|
-```
|
|
|
-
|
|
|
-Also verify the configured model IDs exist in your OpenCode environment.
|
|
|
-
|
|
|
-### Council recursion prevented
|
|
|
+### All councillors fail
|
|
|
|
|
|
-Council is meant to be a leaf agent. The councillor agent type has `council_session: 'deny'` permission, so councillors cannot invoke the council tool. OpenCode's native `subagent_depth` limit also prevents runaway delegation. Avoid recursive council chains.
|
|
|
+Verify the configured model IDs exist in your OpenCode environment. Each
|
|
|
+councillor model must be a valid `provider/model` identifier your OpenCode
|
|
|
+setup can reach.
|