From 4ea2a0e546b16e3273c896d1698c3d60d524eead Mon Sep 17 00:00:00 2001 From: Teknium <127238744+teknium1@users.noreply.github.com> Date: Wed, 12 Aug 2026 21:37:42 -0700 Subject: [PATCH] Revert "Inspired by Perplexity Computer: Model Council mode for Mixture of Agents" This reverts commit 8d9e18d40b60426113adec32cfb6c7a6a816bdd2. --- agent/conversation_loop.py | 3 - agent/moa_loop.py | 102 +++--------- cli.py | 25 --- gateway/run.py | 29 ---- hermes_cli/commands.py | 4 +- hermes_cli/moa_cmd.py | 3 - hermes_cli/moa_config.py | 57 +------ tests/agent/test_moa_council_style.py | 147 ------------------ website/docs/reference/slash-commands.md | 2 - .../user-guide/features/mixture-of-agents.md | 59 ------- 10 files changed, 24 insertions(+), 407 deletions(-) delete mode 100644 tests/agent/test_moa_council_style.py diff --git a/agent/conversation_loop.py b/agent/conversation_loop.py index aeb7bf10ca..95d48421c1 100644 --- a/agent/conversation_loop.py +++ b/agent/conversation_loop.py @@ -2001,9 +2001,6 @@ def run_conversation( degraded_reference_policy=str( moa_config.get("degraded_reference_policy") or "loud" ), - synthesis_style=str( - moa_config.get("synthesis_style") or "guidance" - ), agent=agent, ) if _moa_context: diff --git a/agent/moa_loop.py b/agent/moa_loop.py index fda8a0654a..0e15492a8f 100644 --- a/agent/moa_loop.py +++ b/agent/moa_loop.py @@ -1217,7 +1217,6 @@ def aggregate_moa_context( reference_max_tokens: int | None = None, reference_timeout: float | None = None, degraded_reference_policy: str = "loud", - synthesis_style: str = "guidance", agent: Any = None, ) -> str: """Run configured reference models and synthesize their advice. @@ -1241,14 +1240,6 @@ def aggregate_moa_context( ``agent``, when passed, lets the reference fan-out be aborted early on a user interrupt — see ``_run_references_parallel``'s docstring. - - ``synthesis_style`` selects how advisor output is synthesized (see - ``hermes_cli.moa_config.coerce_synthesis_style``): ``"guidance"`` (the - default) produces private context for the acting model; ``"council"`` - (inspired by Perplexity Computer's Model Council, Aug 2026) makes the - aggregator act as a council *chair*, producing a user-facing deliberation - report that surfaces agreement, disagreement, per-model unique - contributions, and a recommendation with confidence. """ reference_models = [slot for slot in reference_models if slot.get("enabled", True)] reference_outputs: list[tuple[str, str, Any]] = [] @@ -1306,36 +1297,15 @@ def aggregate_moa_context( f"{notice}" ) - council = str(synthesis_style or "guidance").strip().lower() == "council" - if council: - synth_prompt = ( - "You are the CHAIR of a model council. Several independent frontier " - "models were each asked the same question and answered without seeing " - "each other's responses. Chair the deliberation: produce a council " - "report with these sections —\n" - "1. Consensus: where the models agree (consensus supports acting with " - "confidence).\n" - "2. Disagreements: where they diverge, attributing each position to " - "its model by label, and identifying the differing starting " - "assumptions behind each divergence.\n" - "3. Unique contributions: what each model uniquely surfaced that the " - "others missed.\n" - "4. Chair's recommendation: your synthesized position, with an " - "explicit confidence level and what evidence would change it.\n" - "Be concrete and attribute positions to models by name.\n\n" - f"Original user prompt:\n{user_prompt}\n\n" - f"Council member responses:\n{joined}" - ) - else: - synth_prompt = ( - "You are the aggregator in a Mixture of Agents process. Synthesize the " - "reference responses into concise, actionable guidance for the main " - "Hermes agent. Focus on next steps, tool-use strategy, risks, and any " - "disagreements. Do not answer the user directly unless that is all that " - "is needed; produce context the main agent should use in its normal loop.\n\n" - f"Original user prompt:\n{user_prompt}\n\n" - f"Reference responses:\n{joined}" - ) + synth_prompt = ( + "You are the aggregator in a Mixture of Agents process. Synthesize the " + "reference responses into concise, actionable guidance for the main " + "Hermes agent. Focus on next steps, tool-use strategy, risks, and any " + "disagreements. Do not answer the user directly unless that is all that " + "is needed; produce context the main agent should use in its normal loop.\n\n" + f"Original user prompt:\n{user_prompt}\n\n" + f"Reference responses:\n{joined}" + ) agg_label = _slot_label(aggregator) agg_runtime = _slot_runtime(aggregator) @@ -1381,18 +1351,6 @@ def aggregate_moa_context( if not synthesis: synthesis = joined - if council: - return ( - "[Model Council report — a board of independent models deliberated " - "on the user's question and the chair synthesized their positions. " - "Present this deliberation to the user: preserve the " - "consensus/disagreement structure and the per-model attributions, " - "and add your own judgement where useful.]\n" - f"Chair: {agg_label}\n" - f"Council members: {', '.join(_slot_label(slot) for slot in reference_models)}\n\n" - f"{synthesis.strip()}" - ) - return ( "[Mixture of Agents context — use this as private guidance for the " "normal Hermes agent loop. You may call tools, continue reasoning, or " @@ -2269,39 +2227,15 @@ class MoAChatCompletions: elif joined or degraded: if degraded: joined = f"{joined}\n\n{degraded}" if joined else degraded - if str(preset.get("synthesis_style") or "guidance").strip().lower() == "council": - # Council style (inspired by Perplexity Computer's Model - # Council, Aug 2026): the acting model chairs a deliberation - # instead of consuming private advice. Its user-facing answer - # must surface consensus, disagreements (with per-model - # attribution and the differing assumptions behind them), - # unique contributions, and a recommendation with confidence. - guidance = ( - "[Model Council deliberation]\n" - f"Preset: {self.preset_name}\n" - f"Chair (you): {_slot_label(aggregator)}\n" - f"Council members: {', '.join(label for label, _, _ in _agg_refs)}\n\n" - "You are the chair of a model council. The council member " - "responses below are independent answers to the current " - "state of the task. Chair the deliberation in your reply: " - "surface where the members agree (consensus), where they " - "disagree (attribute positions to members by name and name " - "the differing assumptions), what each uniquely surfaced, " - "and close with your own recommendation and an explicit " - "confidence level. You may still call tools when the task " - "requires acting rather than deliberating.\n\n" - f"{joined}" - ) - else: - guidance = ( - "[Mixture of Agents reference context]\n" - f"Preset: {self.preset_name}\n" - f"Aggregator/acting model: {_slot_label(aggregator)}\n" - f"References: {', '.join(label for label, _, _ in _agg_refs)}\n\n" - "Use the reference responses below as private context. You are the aggregator and acting model: " - "answer the user directly or call tools as needed.\n\n" - f"{joined}" - ) + guidance = ( + "[Mixture of Agents reference context]\n" + f"Preset: {self.preset_name}\n" + f"Aggregator/acting model: {_slot_label(aggregator)}\n" + f"References: {', '.join(label for label, _, _ in _agg_refs)}\n\n" + "Use the reference responses below as private context. You are the aggregator and acting model: " + "answer the user directly or call tools as needed.\n\n" + f"{joined}" + ) _attach_reference_guidance(agg_messages, guidance) prepared_request = { diff --git a/cli.py b/cli.py index 8505bfaba7..2e822be374 100644 --- a/cli.py +++ b/cli.py @@ -10678,31 +10678,6 @@ class HermesCLI(CLIAgentSetupMixin, CLICommandsMixin, CLIBillingMixin): self._pending_moa_disable_after_turn = True self._pending_agent_seed = payload _cprint(f" MoA one-shot queued with preset {preset}; previous model will be restored after this turn.") - elif canonical == "council": - # /council — one-shot Model Council (inspired by - # Perplexity Computer's Model Council, Aug 2026): the default MoA - # preset's reference models answer the question independently, the - # preset's aggregator chairs the deliberation, and the council - # report (consensus / disagreements / unique contributions / - # recommendation with confidence) is handed to the current session - # model to present. The session model is never switched. - from hermes_cli.moa_config import build_moa_turn_prompt - - parts = cmd_original.split(None, 1) - payload = parts[1].strip() if len(parts) > 1 else "" - if not payload: - _cprint(" Usage: /council (runs the default MoA preset's models as an independent council and reports consensus vs disagreement)") - return True - try: - moa_cfg = self.config.get("moa") if isinstance(self.config, dict) else {} - encoded = build_moa_turn_prompt( - payload, moa_cfg, synthesis_style="council" - ) - except Exception as exc: - _cprint(f" Council setup failed: {exc}") - return True - self._pending_input.put(encoded) - _cprint(" ⚖ Council convened — reference models are deliberating.") elif canonical == "subgoal": self._handle_subgoal_command(cmd_original) elif canonical == "skin": diff --git a/gateway/run.py b/gateway/run.py index 0391ca6679..4957b56f63 100644 --- a/gateway/run.py +++ b/gateway/run.py @@ -16132,35 +16132,6 @@ class GatewayRunner(GatewayAuthorizationMixin, GatewayKanbanWatchersMixin, Gatew if canonical == "refine": return await self._handle_refine_command(event) - if canonical == "council": - # /council — one-shot Model Council (inspired by - # Perplexity Computer's Model Council, Aug 2026). Encodes the - # question as a hidden MoA one-shot marker with the council - # synthesis style forced on; run_conversation decodes it, the - # default preset's reference models answer independently, and - # the chair's consensus/disagreement report reaches the session - # model as context. No model switch, no agent eviction. - from hermes_cli.moa_config import build_moa_turn_prompt - from hermes_cli.config import load_config - - council_payload = event.get_command_args().strip() - if not council_payload: - return ( - "Usage: /council (runs the default MoA preset's " - "models as an independent council and reports consensus vs " - "disagreement)" - ) - try: - cfg = load_config() - event.text = build_moa_turn_prompt( - council_payload, - cfg.get("moa") if isinstance(cfg, dict) else {}, - synthesis_style="council", - ) - except Exception: - return "Failed to prepare council turn." - # Fall through to _handle_message_with_agent with the rewritten text. - if canonical == "moa": # /moa is one-shot sugar only: run a single prompt through the # default MoA preset, then restore the prior model. To *switch* to a diff --git a/hermes_cli/commands.py b/hermes_cli/commands.py index e9bb08b23c..1f9e70e430 100644 --- a/hermes_cli/commands.py +++ b/hermes_cli/commands.py @@ -170,8 +170,6 @@ COMMAND_REGISTRY: list[CommandDef] = [ args_hint="[focus instructions]"), CommandDef("moa", "Run one prompt through the default Mixture of Agents preset, then restore your model", "Session", args_hint="", busy_policy="reject", busy_handler="moa"), - CommandDef("council", "Convene a model council: reference models answer independently, a chair synthesizes consensus and disagreements", "Session", - args_hint="", busy_policy="reject"), CommandDef("subgoal", "Add or manage extra criteria on the active goal", "Session", args_hint="[text | remove N | clear]", busy_policy="dispatch"), CommandDef("status", "Show session, model, token, and context info", "Session", @@ -1279,7 +1277,7 @@ _SLACK_PRIORITY_ALIASES = ("btw", "bg") # native slash. # - pause: global emergency stop; reached via /hermes pause [off] on # Slack. Added at the 50-cap — a native slot would clamp /platform. -_SLACK_VIA_HERMES_ONLY = frozenset({"topup", "moa", "council", "debug", "egress", "init", "version", "diff", "update", "heartbeat", "refine", "pause"}) +_SLACK_VIA_HERMES_ONLY = frozenset({"topup", "moa", "debug", "egress", "init", "version", "diff", "update", "heartbeat", "refine", "pause"}) def _sanitize_slack_name(raw: str) -> str: diff --git a/hermes_cli/moa_cmd.py b/hermes_cli/moa_cmd.py index 0a6d00c61a..8fcd8174e6 100644 --- a/hermes_cli/moa_cmd.py +++ b/hermes_cli/moa_cmd.py @@ -89,9 +89,6 @@ def _print_config(config: dict[str, Any]) -> None: print(f" {idx}. {_format_slot(slot)}") agg = preset["aggregator"] print(f" Aggregator: {_format_slot(agg)}") - style = str(preset.get("synthesis_style") or "guidance") - if style != "guidance": - print(f" Synthesis style: {style}") def cmd_moa(args) -> None: diff --git a/hermes_cli/moa_config.py b/hermes_cli/moa_config.py index fe62433581..24d36e4a19 100644 --- a/hermes_cli/moa_config.py +++ b/hermes_cli/moa_config.py @@ -71,26 +71,6 @@ def _coerce_degraded_reference_policy(value: Any) -> str: return policy if policy in {"loud", "silent"} else "loud" -def coerce_synthesis_style(value: Any) -> str: - """Normalize the MoA synthesis style; unknown values degrade to 'guidance'. - - - ``'guidance'`` (default): the classic MoA shape — advisor responses are - synthesized into PRIVATE context for the acting model, which then - answers the user normally. Advisor perspectives never surface as a - deliverable. - - ``'council'``: multi-model deliberation shape (inspired by Perplexity - Computer's Model Council, Aug 2026): each reference model weighs in - independently and the acting model becomes the council *chair*, - producing a USER-FACING report that explicitly surfaces where the - models agree, where they disagree (and why — differing starting - assumptions), what each uniquely contributes, and a recommended - position with confidence. Built for ambiguous judgment calls where - disagreement between frontier models is itself the signal. - """ - style = str(value or "guidance").strip().lower() - return style if style in {"guidance", "council"} else "guidance" - - def _coerce_int(value: Any, default: int) -> int: if value is None or value == "": return default @@ -326,7 +306,6 @@ def _default_preset() -> dict[str, Any]: "max_tokens": 4096, "reference_max_tokens": None, "fanout": "user_turn", - "synthesis_style": "guidance", "enabled": True, } @@ -387,11 +366,6 @@ def _normalize_preset(raw: Any) -> dict[str, Any]: # last advisor run. Also accepts the mapping form # {mode: every_n, n: N}, normalized to the canonical string. "fanout": _coerce_fanout(raw.get("fanout")), - # How advisor output is synthesized for the acting model. "guidance" - # (default) keeps advisor perspectives as private context; "council" - # turns the acting model into a council chair producing a user-facing - # agree/disagree report (see coerce_synthesis_style). - "synthesis_style": coerce_synthesis_style(raw.get("synthesis_style")), } @@ -441,7 +415,6 @@ def normalize_moa_config(raw: Any) -> dict[str, Any]: "max_tokens": active["max_tokens"], "reference_max_tokens": active.get("reference_max_tokens"), "fanout": active.get("fanout", "user_turn"), - "synthesis_style": active.get("synthesis_style", "guidance"), "enabled": active["enabled"], # MoA-level (not per-preset) toggles ride at the top level alongside # save_traces. privacy_filter: '' (off, default) | 'display' | 'full' @@ -502,24 +475,11 @@ def set_active_moa_preset(config: Any, name: str | None) -> dict[str, Any]: return cfg -def encode_moa_turn( - prompt: str, - config: Any = None, - preset: str | None = None, - synthesis_style: str | None = None, -) -> str: - """Encode a /moa one-shot turn for frontends that can only send text. - - ``synthesis_style``, when given, overrides the resolved preset's style for - this one turn (used by ``/council`` to force a council deliberation over - the default preset without touching config). - """ - turn_config = resolve_moa_preset(config or {}, preset) - if synthesis_style is not None: - turn_config["synthesis_style"] = coerce_synthesis_style(synthesis_style) +def encode_moa_turn(prompt: str, config: Any = None, preset: str | None = None) -> str: + """Encode a /moa one-shot turn for frontends that can only send text.""" payload = { "prompt": str(prompt or ""), - "config": turn_config, + "config": resolve_moa_preset(config or {}, preset), } encoded = base64.urlsafe_b64encode( json.dumps(payload, separators=(",", ":"), ensure_ascii=False).encode("utf-8") @@ -540,16 +500,9 @@ def decode_moa_turn(message: Any) -> tuple[str, dict[str, Any] | None]: return prompt, _normalize_preset(payload.get("config") or {}) -def build_moa_turn_prompt( - user_prompt: str, - config: Any = None, - preset: str | None = None, - synthesis_style: str | None = None, -) -> str: +def build_moa_turn_prompt(user_prompt: str, config: Any = None, preset: str | None = None) -> str: """Build the hidden one-shot payload used by TUI/gateway routing.""" - return encode_moa_turn( - user_prompt, config, preset=preset, synthesis_style=synthesis_style - ) + return encode_moa_turn(user_prompt, config, preset=preset) def moa_usage() -> str: diff --git a/tests/agent/test_moa_council_style.py b/tests/agent/test_moa_council_style.py deleted file mode 100644 index aaefbeac83..0000000000 --- a/tests/agent/test_moa_council_style.py +++ /dev/null @@ -1,147 +0,0 @@ -"""Tests for the MoA 'council' synthesis style (Model Council). - -Inspired by Perplexity Computer's Model Council (Aug 2026): reference models -answer independently, and the aggregator acts as a council chair producing a -user-facing consensus/disagreement report instead of private guidance. -""" - -from __future__ import annotations - -from types import SimpleNamespace - -import pytest - -from hermes_cli.moa_config import ( - build_moa_turn_prompt, - coerce_synthesis_style, - decode_moa_turn, - normalize_moa_config, -) - - -# ── coerce_synthesis_style normalization ──────────────────────────────────── - - -def test_coerce_synthesis_style_defaults_and_tolerance(): - assert coerce_synthesis_style(None) == "guidance" - assert coerce_synthesis_style("") == "guidance" - assert coerce_synthesis_style("guidance") == "guidance" - assert coerce_synthesis_style("council") == "council" - assert coerce_synthesis_style("COUNCIL ") == "council" - # Unknown / bad types degrade to the default (tolerant-read contract). - assert coerce_synthesis_style("debate") == "guidance" - assert coerce_synthesis_style(42) == "guidance" - - -def test_normalize_preset_carries_synthesis_style(): - cfg = normalize_moa_config( - {"presets": {"p": { - "reference_models": [{"provider": "openrouter", "model": "openai/gpt-5.5"}], - "aggregator": {"provider": "openrouter", "model": "anthropic/claude-opus-4.8"}, - "synthesis_style": "council", - }}} - ) - assert cfg["presets"]["p"]["synthesis_style"] == "council" - # Flattened compatibility view mirrors the default preset. - assert cfg["synthesis_style"] == "council" - - -def test_normalize_preset_synthesis_style_defaults_to_guidance(): - cfg = normalize_moa_config({}) - assert cfg["presets"]["default"]["synthesis_style"] == "guidance" - assert cfg["synthesis_style"] == "guidance" - - -# ── one-shot marker round-trip (/council) ─────────────────────────────────── - - -def test_council_one_shot_marker_round_trip(): - encoded = build_moa_turn_prompt( - "should we launch in Q4 or Q1?", {}, synthesis_style="council" - ) - prompt, config = decode_moa_turn(encoded) - assert prompt == "should we launch in Q4 or Q1?" - assert config is not None - assert config["synthesis_style"] == "council" - - -def test_moa_one_shot_marker_keeps_guidance_default(): - encoded = build_moa_turn_prompt("hello", {}) - _prompt, config = decode_moa_turn(encoded) - assert config is not None - assert config["synthesis_style"] == "guidance" - - -# ── aggregate_moa_context council path ────────────────────────────────────── - - -def _response(content: str = "ok"): - message = SimpleNamespace(content=content, tool_calls=[]) - choice = SimpleNamespace(message=message, finish_reason="stop") - return SimpleNamespace(choices=[choice], usage=None, model="fake") - - -@pytest.fixture -def hermes_home(tmp_path, monkeypatch): - home = tmp_path / ".hermes" - home.mkdir() - monkeypatch.setenv("HERMES_HOME", str(home)) - return home - - -def _run_aggregate(monkeypatch, synthesis_style): - from agent.moa_loop import aggregate_moa_context - - calls: list[dict] = [] - - def fake_call_llm(**kwargs): - calls.append(kwargs) - return _response( - "advice" if kwargs.get("task") == "moa_reference" else "synthesis" - ) - - monkeypatch.setattr("agent.moa_loop.call_llm", fake_call_llm) - - result = aggregate_moa_context( - user_prompt="buy or lease?", - api_messages=[{"role": "user", "content": "buy or lease?"}], - reference_models=[{"provider": "openrouter", "model": "openai/gpt-5.5"}], - aggregator={"provider": "openrouter", "model": "anthropic/claude-opus-4.8"}, - synthesis_style=synthesis_style, - ) - return result, calls - - -def _flatten(content) -> str: - if isinstance(content, str): - return content - return "".join( - part.get("text", "") for part in content if isinstance(part, dict) - ) - - -def test_council_style_uses_chair_prompt_and_report_framing(hermes_home, monkeypatch): - result, calls = _run_aggregate(monkeypatch, "council") - - agg_calls = [c for c in calls if c.get("task") == "moa_aggregator"] - assert len(agg_calls) == 1 - synth_prompt = _flatten(agg_calls[0]["messages"][-1]["content"]) - assert "CHAIR of a model council" in synth_prompt - assert "Council member responses" in synth_prompt - - # The returned context is a user-facing council report, not private - # guidance. - assert result.startswith("[Model Council report") - assert "Chair: openrouter:anthropic/claude-opus-4.8" in result - assert "Council members: openrouter:openai/gpt-5.5" in result - - -def test_guidance_style_unchanged(hermes_home, monkeypatch): - """Default style must be byte-compatible with the classic MoA framing.""" - result, calls = _run_aggregate(monkeypatch, "guidance") - - agg_calls = [c for c in calls if c.get("task") == "moa_aggregator"] - synth_prompt = _flatten(agg_calls[0]["messages"][-1]["content"]) - assert "aggregator in a Mixture of Agents process" in synth_prompt - assert result.startswith("[Mixture of Agents context") - assert "CHAIR" not in synth_prompt diff --git a/website/docs/reference/slash-commands.md b/website/docs/reference/slash-commands.md index 21575d1563..bbf996ac84 100644 --- a/website/docs/reference/slash-commands.md +++ b/website/docs/reference/slash-commands.md @@ -56,7 +56,6 @@ Type `/` in the CLI to open the autocomplete menu. Built-in commands are case-in | `/heartbeat every ` (alias: `/hb`) | Set a recurring prompt that re-enters **this session** as a normal user turn whenever it's idle and the interval has elapsed (min 60s; missed ticks coalesce). Subcommands: `/heartbeat status`, `/heartbeat pause`, `/heartbeat resume`, `/heartbeat clear`. Session-scoped and in-process — use `hermes cron` for durable isolated schedules. See [Session Heartbeats](/user-guide/features/heartbeat). | | `/refine [focus]` | Run the background memory/skill self-improvement review **now** instead of waiting for the automatic post-turn trigger. Optional focus text steers the review (e.g. `/refine save the deploy workflow as a skill`). Runs in a background fork against a conversation snapshot — the live session and prompt cache are untouched; results are reported when done. | | `/moa ` | Run a single prompt through the default [Mixture of Agents](/user-guide/features/mixture-of-agents) preset, then restore your current model. One-shot — does not change your session model. | -| `/council ` | Convene a **model council**: the default MoA preset's reference models answer the question independently, and the aggregator chairs the deliberation — reporting where the models agree, where they disagree (with per-model attribution and the differing assumptions behind each divergence), what each uniquely surfaced, and a recommendation with an explicit confidence level. Built for ambiguous judgment calls where model disagreement is itself the signal. One-shot — does not change your session model. See [Council mode](/user-guide/features/mixture-of-agents#council-mode). | | `/resume [name]` | Resume a previously-named session | | `/sessions` (TUI alias: `/switch`) | Classic CLI: browse and resume previous sessions in an interactive picker. TUI: open the live session switcher for currently open TUI sessions. Use `/sessions new` in the TUI to start another live session immediately. | | `/egress [status]` | Show Docker egress proxy status — enabled/configured/running state, credential source, token mappings, uncovered providers, and next remediation step. Works in CLI, TUI, Desktop chat, and messaging gateway. | @@ -257,7 +256,6 @@ The messaging gateway supports the following built-in commands inside Telegram, | `/heartbeat every ` (alias: `/hb`) | Set a recurring prompt that re-enters this session when idle. Subcommands: `status`, `pause`, `resume`, `clear`. On Slack use `/hermes heartbeat …`. | | `/refine [focus]` | Run the memory/skill self-improvement review now, optionally with focus instructions. On Slack use `/hermes refine …`. | | `/moa ` | Run one prompt through the default [Mixture of Agents](/user-guide/features/mixture-of-agents) preset, then restore the session model. | -| `/council ` | One-shot [model council](/user-guide/features/mixture-of-agents#council-mode): reference models answer independently; the chair reports consensus, disagreements, and a recommendation with confidence. | | `/branch [name]` (alias: `/fork`) | Branch the current session (explore a different path). | | `/agents` (alias: `/tasks`) | Show active agents and running tasks. | | `/sessions` | Browse and resume previous sessions. | diff --git a/website/docs/user-guide/features/mixture-of-agents.md b/website/docs/user-guide/features/mixture-of-agents.md index cf7a653685..55c6d23791 100644 --- a/website/docs/user-guide/features/mixture-of-agents.md +++ b/website/docs/user-guide/features/mixture-of-agents.md @@ -230,65 +230,6 @@ moa: Omit `reasoning_effort` to use the provider/Hermes default for that slot. -## Council mode - -*Inspired by Perplexity Computer's Model Council (Aug 2026).* - -By default, MoA advisor perspectives are **private context** — the acting model -consumes them silently and answers normally. Council mode flips that: the -reference models become an explicit **board of independent models**, and the -acting model becomes the council **chair**, producing a user-facing -deliberation report that surfaces: - -1. **Consensus** — where the models agree (consensus supports acting with - confidence). -2. **Disagreements** — where they diverge, attributed per model, with the - differing starting assumptions behind each divergence. -3. **Unique contributions** — what each model surfaced that the others missed. -4. **Chair's recommendation** — a synthesized position with an explicit - confidence level and what evidence would change it. - -Council mode is built for ambiguous questions: judgment calls, trade-offs, -risk assessments — situations where there is no single right answer and -disagreement between frontier models is itself the signal. - -### One-shot: `/council` - -``` -/council should we launch this feature in Q4 or Q1? -``` - -Runs the default MoA preset's reference models independently against your -question, chairs the deliberation, and reports back — then your session -continues on your normal model. Works on the CLI, TUI, and every messaging -platform (on Slack: `/hermes council …`). - -### Persistent: `synthesis_style: council` - -Set it per preset to make every turn of a MoA session deliberate as a council: - -```yaml -moa: - presets: - board: - reference_models: - - provider: openrouter - model: openai/gpt-5.5 - - provider: openrouter - model: google/gemini-3.1-pro - aggregator: - provider: openrouter - model: anthropic/claude-opus-4.8 - synthesis_style: council # default: guidance -``` - -`guidance` (the default) is the classic MoA shape — advisor output stays -private. Unknown values degrade to `guidance`. - -Tips (they sharpen council output noticeably): ask for decisions rather than -summaries ("what would you bet on and why"), include numbers/timelines/budget, -and ask each model to state the assumptions behind its conclusion. - ## Terminal preset management ```bash