Course · OpenCode · Providers & models
Run two providers side by side
Sooner or later you’ll hand OpenCode a real judgment call. feedmill - the course’s illustrative feed aggregator - dedupes items across dozens of feeds by hashing each one, and suppose you’ve been quietly nervous about it: two different items should never collapse into one row in the reading queue, and once in a while an item seems to go missing. So you ask. And with two providers connected, you can ask twice - once on a model from each - and they tell you opposite things. An illustrative pair of answers:
> is the dedupe hash in internal/dedupe collision-safe for our feed volume?
[anthropic/claude-opus-4-5] It's fine. The hash keys on the feed URL plus the entry ID with FNV-64a; at a few hundred thousand items the collision probability is negligible. No change needed.> is the dedupe hash in internal/dedupe collision-safe for our feed volume?
[openai/gpt-5.2] This is risky. FNV-64a is not collision-resistant, and you're keying on fields a feed controls - two different items with a crafted or coincident feed URL and entry ID can hash equal and one silently vanishes from the queue. Move to SHA-256 over the canonical item bytes.One says ship it, the other says you have a latent data-loss bug. You can’t tell who’s right by re-reading the answers - they’re both fluent and confident, and confidence is the one signal an LLM gives you for free regardless of whether it’s correct. What you can do is stop treating this as a debate and turn it into an A/B across two providers.
The technique needs keys for two vendors. Run /connect in the TUI (or opencode auth login in your shell) once per provider, then check that both appear in opencode auth list (the multi-provider setup page covers the details). With only one key, use Zen, described at the end of this page: one Zen key reaches models from several vendors.
Why this is OpenCode’s move
Section titled “Why this is OpenCode’s move”As of September 2026, Claude Code is built around Anthropic’s models and Codex around OpenAI’s, so when either hands you a confident answer, a second opinion from another vendor usually means re-configuring the tool, not summoning a second agent. OpenCode is provider-agnostic, and provider choice is per-agent: every agent carries its own model field, so two agents pointed at two different providers can run the identical prompt and you read the difference.
Be precise about what’s the differentiator here, because it’s narrower than “only OpenCode can run agents in parallel.” It isn’t parallel multi-agent work itself - OpenCode spawns subagents (secondary agents with their own context, invoked by @mention), and other terminal agents have their own multi-agent modes too. What OpenCode adds is two different providers behind the same question. That’s the lever: not that OpenCode can run two agents, but that the two agents can be backed by Anthropic and OpenAI at once. The second vendor matters because models from different vendors are trained on different data with different methods, so they tend to fail in different places. Two models from one vendor share more of their training and more of their blind spots, so their agreement proves less. The disagreement above isn’t a problem to resolve by gut feel; it’s two data points you can lay side by side because the tool let you summon both - from two vendors.
That’s the whole technique. You’re not looking for the model that agrees with you. You’re looking at where two strong models diverge, because the divergence is a map of exactly the claim that needs checking - here, “is FNV-64a collision-resistant, and does it matter at our scale and threat model.”
Set up two agents, one per provider
Section titled “Set up two agents, one per provider”The clean way to make this repeatable is two thin agents that differ only in their model. Drop two files in .opencode/agents/ (the runtime reads the plural directory; the singular .opencode/agent/ is only accepted for backwards compatibility - config documentation, as of October 2026):
---description: Second opinion, Anthropic sidemode: allmodel: anthropic/claude-opus-4-5permission: edit: deny---You are a reviewer. Answer the question directly and justify yourreasoning. Do not edit files.---description: Second opinion, OpenAI sidemode: allmodel: openai/gpt-5.2permission: edit: deny---You are a reviewer. Answer the question directly and justify yourreasoning. Do not edit files.Same prompt body, same edit: deny permission, different model - and because every provider is referenced in the one universal provider/model format, the only line that changes between the two files is the slug after model:. Save them as review-a.md and review-b.md - the file name is the name you @mention. The model slugs are illustrative; use any model your providers list (opencode models prints them). Both are edit: deny, because a second opinion has no business touching the parsers; you want analysis, not action. That key covers the edit, write, and patch tools, not the shell; add bash: deny if the reviewers must not run commands either. mode: all lets you use each agent both by @mention and as the agent for a headless run; the next section depends on it. The cost is that both also appear among the primary agents you cycle through in the TUI. (mode, the model field, and the nested permission block are all documented agent frontmatter as of October 2026, and permission takes ask/allow/deny per tool - agents documentation, permissions documentation.)
You invoke either agent the way you invoke a subagent - by @mention. The transcript below is illustrative, and shows both summoned from one prompt; if your version doesn’t fan a dual mention out cleanly, run them as two turns (or the two headless opencode run invocations shown below) and you get the same A/B:
> @review-a @review-b is the dedupe hash in internal/dedupe collision-safe for our feed volume? Read the code before answering, and be specific about the hash function and what we key on.
⏺ review-a (anthropic/claude-opus-4-5) read internal/dedupe/hash.go → FNV-64a over the feed URL plus the entry ID. At your volume the birthday-bound collision risk is ~1e-9. Safe as a dedupe key. The real exposure isn't math - it's that the inputs are feed-controlled.
⏺ review-b (openai/gpt-5.2) read internal/dedupe/hash.go → FNV is a non-cryptographic hash; collisions are cheap to engineer and the keyed fields come straight from untrusted feeds. For a dedupe key fed by sources you don't control, use SHA-256.Read those two carefully and the “disagreement” has already half-collapsed. The sharper prompt did most of that work: it made both agents read the code and name the hash function before judging. Both agents now agree on the facts - FNV-64a, keyed on feed-controlled fields. Agreement is not proof, so check those facts in the code yourself; here they hold, because Key in internal/dedupe/hash.go hashes the feed URL and entry ID with FNV-64a. They diverge only on the judgment: is feed-controlled input a real threat for feedmill or a theoretical one? That’s no longer a question about hashing. It’s a question about your threat model, and now it’s the only thing you have to decide. The A/B didn’t pick a winner for you - it stripped away everything the models agreed on so the actual decision stands alone.
Diff the outputs, don’t just read them
Section titled “Diff the outputs, don’t just read them”Two short answers you can eyeball. But the comparison gets sharper the longer the outputs get, and eyeballing a pair of three-paragraph answers is exactly where you miss the one sentence that matters. So make the comparison literal: run each side headless, capture its output, and diff the files. Running them as two separate opencode run invocations also sidesteps any question of whether two subagents truly run in parallel in one turn - each invocation is its own clean, non-interactive process.
$ opencode run --agent review-a --format default \ "is the dedupe hash in internal/dedupe collision-safe for our feed \ volume? read the code first. answer in exactly four lines: \ Hash:, Keyed on:, Verdict:, Change:" > a.txt
$ opencode run --agent review-b --format default \ "is the dedupe hash in internal/dedupe collision-safe for our feed \ volume? read the code first. answer in exactly four lines: \ Hash:, Keyed on:, Verdict:, Change:" > b.txt
$ git diff --no-index a.txt b.txtopencode run is OpenCode’s non-interactive entry point - --agent selects which agent answers, and because each agent pins its own provider, the two runs hit two different vendors with one identical prompt. --agent accepts only an agent whose mode is primary or all. Given a mode: subagent agent, run prints agent "review-a" is a subagent, not a primary agent. Falling back to default agent and answers on the default agent’s model, so both runs would silently hit the same vendor (run.ts, as of October 2026). If you see that warning, check the mode line. There’s no --quiet/--print flag to reach for; instead run takes --format with either default (formatted output) or json (raw JSON events) - the documented flag set as of October 2026 is in the CLI reference. Diff the default output, as above. The json event streams also differ in ids, timestamps, and token counts, which buries the disagreement in noise. The fixed four-line shape is what makes the diff useful: two free-form answers share almost no lines, so the diff would mark nearly everything as changed. With labeled lines, the lines where the models agree often match and drop out, and the lines left in red and green are the disagreement.
The price is a doubled spend: every question runs twice, on two paid providers. Save the A/B for judgment calls where a wrong answer costs more than the second run, such as a possible data-loss bug, and ask routine questions once.
When you don’t want to pick the providers yourself
Section titled “When you don’t want to pick the providers yourself”You don’t have to know which two models to pit against each other. If you’d rather trust a vetted shortlist than reason about every provider’s strengths, OpenCode’s Zen is one way to get there - a paid, curated gateway serving models the OpenCode team has tested and verified, behind a single key (as of October 2026; Zen documentation). Reference a Zen model the way you reference any other, as a provider/model slug where the provider is literally opencode - e.g. opencode/gpt-5.5, one of the docs’ own example ids. Pull your review-a / review-b pair from Zen and you’ve skipped the homework of figuring out which models are even worth comparing; it’s pay-as-you-go, charged per request at prices listed per million tokens, which is the trade for not benchmarking the catalog yourself.
What you actually decided
Section titled “What you actually decided”You started with a vendor argument you couldn’t adjudicate and ended with a decision that was yours to make: both models agree the dedupe keys on feed-controlled fields, so whether to move from FNV-64a to SHA-256 comes down to whether a malicious feed dropping an item from your queue is a risk you’ll accept. At a collision risk of ~1e-9, chance collisions don’t explain the items that seem to go missing, so the hash decision covers crafted input only, and the missing items need a separate look. The two providers didn’t settle it - they’re not supposed to. They surfaced the exact claim worth checking and left the call where it belongs. That’s the whole value of running two side by side: not a referee, but a sharper question.
Next you’ll handle the case the cloud can’t: a feed whose contents you’re not allowed to send to any provider at all. Drive it with a local or custom model.