Skip to content

[FEATURE] Mixture of Agents (MoA) as a first-class model/preset #40948

Description

@patrickpassosb

[FEATURE] Mixture of Agents (MoA) as a first-class model/preset

Summary

Support Mixture of Agents (MoA): a selectable "model" in the picker that fans every user turn out to N advisor models in parallel, then aggregates their analyses with a final aggregator model — the pattern popularized by the MoA papers and already shipped in other agent products (e.g. Hermes' MOA presets). Today opencode can only run one model per session; MoA would let users pick an ensemble exactly like they pick a single model.

Motivation

  • MoA reliably improves answer quality on reasoning-heavy tasks (parallel independent analyses + aggregation beats a single pass, especially with small models).
  • Users already mix models via providers (OpenRouter-style routing); MoA is the natural next step and the pattern is proven in production elsewhere.
  • A local reference implementation exists and is battle-tested (see below), so the design is validated, not speculative.

Proposed design

1. Presets (config-level)

A preset defines the ensemble:

moa:
  presets:
    captain-test:
      advisors:
        - model: ollama-cloud/glm-5.2
          max_tokens: 600
        - model: ollama-cloud/minimax-m3
          max_tokens: 600
      aggregator:
        model: ollama-cloud/deepseek-v4-flash:0731
        reasoning_effort: max
        max_tokens: 4096
      fanout: user_turn   # every user turn, or explicit
  • Advisors receive the FULL task + context (unbounded input; only output capped).
  • Aggregator receives advisor analyses + original task, produces the final answer.
  • Per-preset token caps, timeouts, and model roles.
  • Presets appear in the model picker as moa/<preset> entries, selectable exactly like any other model.

2. Runtime behavior

  • Per user turn: fan out to advisors in parallel -> collect analyses -> aggregator synthesizes -> stream the final answer back (SSE).
  • Failure isolation: one dead advisor must not kill the turn; aggregate over whatever succeeded.
  • Abort/cancel must cancel in-flight advisor calls cleanly.
  • Token/usage accounting per advisor + aggregator, surfaced in the session UI.

3. Config & UX

  • Presets defined in config (opencode.json or a dedicated moa block), same surface as provider models.
  • Picker shows moa/<preset> entries; switching presets mid-session works like switching models.
  • Traces optional (per-preset, for audit).

Reference implementation

A working local implementation already exists:

  • tools/moa/ — standalone MoA runner (CLI + lib) in a fork of this repo: https://github.com/patrickpassosb/opencode/pull/40927
  • Same wrapper also shipped in the security-lab repo (merged): thread-pool parallel advisors, aggregator synthesis, per-advisor failure isolation, JSON traces, config precedence (defaults -> yaml -> env -> CLI flags), OpenAI-compatible transport (works with any provider, not just OpenAI).
  • 26 tests, live-verified against real models via an OpenAI-compatible proxy.

The core MoA engine (lib/moa.py) is ~450 lines and self-contained; the design above is directly informed by its real-world usage.

Open questions

  1. Should MoA be a provider-level feature (a moa provider wrapping other providers) or a session-level feature (session runs a preset regardless of provider)?
  2. Should presets be per-user (global config) or per-project?
  3. Does the team want fanout modes beyond user_turn (e.g. only on explicit trigger)?

Impact

  • Adds a new selectable model class to the picker without breaking existing single-model behavior.
  • Backwards compatible: no change unless a user configures presets.

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions