Executive Snapshot
| Aspect | Detail |
|---|---|
| What it is | A vendor-neutral orchestration pattern that runs OpenAI, Anthropic, and Google models as independent advocates, reconciles one answer, and gates it behind validation |
| Best for | High-stakes AI output where single-model bias, a vendor outage, or lock-in is an unacceptable risk |
| Core moving parts | A normalization layer, a parallel fan-out, an adjudicator, and a validation gate |
| Providers | OpenAI (GPT-5.5), Anthropic (Claude Opus 4.8), Google (Gemini 3.1 Pro) — representative flagships, mid-2026 |
| The catch | Cost and latency multiply, and your data leaves your boundary three times instead of once |
| Assumes | API access to all three providers and a way to validate the output programmatically |
TL;DR
- One vendor is one point of failure — for correctness, for uptime, and for negotiating leverage. Running GPT, Claude, and Gemini against the same brief turns a single opinion into a cross-checked decision.
- The hard part is the normalization layer, not the model calls. Three SDKs, three request/response shapes, and three tool-calling formats have to collapse into one internal contract.
- Adjudicate; never average. A designated judge (model or human) picks or synthesizes one answer and surfaces genuine conflicts instead of blending incompatible ones.
- Diversity is the point. Different model families fail differently, so agreement is real signal and disagreement flags a decision worth a human's time.
- Governance is the price of admission. Fanning one prompt to three external clouds triples cost, latency, and data exposure — redact first, gate hard, and fan out only when the stakes justify it.
Introduction
If you have read the Claude Code advocate/adjudicator tutorial, you have seen the pattern applied inside one vendor's tooling, using Opus and Sonnet as competing advocates. This piece lifts the same logic up a level: across vendors. The advocates become OpenAI's GPT, Anthropic's Claude, and Google's Gemini, each reached through its own API.
Why bother crossing vendor lines? Because two models from the same family share training lineage, and therefore share blind spots. Three models from three vendors are far less likely to be confidently wrong in the same way. Add the operational reality — outages, rate limits, sudden deprecations, price changes, shifting content policies — and cross-vendor orchestration stops being an academic exercise and becomes a resilience and governance strategy. This tutorial shows the architecture, the code seams that matter, the flowcharts that make it legible, and the governance trade-offs an IT team has to own before shipping it.
Prerequisites
- API access to all three providers: an OpenAI key, an Anthropic key, and a Google Gemini (Google AI or Vertex) key, each in your secret store.
- A programmatic validator — a test suite, a schema check, a policy linter — anything that returns pass/fail without a human.
- A redaction or DLP step you can put in front of the fan-out. Sending proprietary context to three vendors is a decision, not an accident.
- Comfort with async orchestration in your language of choice; the examples below are TypeScript for brevity.
1. Why diversify across vendors
Single-vendor AI is convenient and, for most tasks, entirely appropriate. The calculus changes when the output is high-stakes — a customer-facing decision, a code change to a load-bearing system, a compliance determination. There, three independent risks stack up against you: the model can be confidently wrong, the vendor can be unavailable when you need it, and building everything around one provider's quirks quietly erodes your ability to switch.
Cross-vendor orchestration attacks all three at once. Correctness: disagreement between GPT, Claude, and Gemini is a cheap early-warning system that a task is ambiguous or a model is hallucinating. Availability: if one provider returns a 529, the round still completes on the other two. Leverage: an abstraction that treats providers as interchangeable advocates is the same abstraction that lets you renegotiate, re-tier, or drop a vendor without a rewrite. That last point is the one procurement and architecture teams care about most, and it is a direct by-product of the normalization layer you have to build anyway.

2. The architecture at a glance
The shape is the same as the single-vendor pattern — fan out, reconcile, gate — with a redaction step in front and an adapter layer in the middle that hides three very different APIs behind one contract.
flowchart TD
A["Brief or change request"] --> R["Redaction / DLP filter"]
R --> F{"Fan out?"}
F -->|"Low stakes"| S["Single model"]
F -->|"High stakes"| P["Parallel advocates"]
P --> O["OpenAI GPT-5.5"]
P --> C["Anthropic Claude Opus 4.8"]
P --> G["Google Gemini 3.1 Pro"]
O --> N["Adapter layer: normalize to one contract"]
C --> N
G --> N
S --> N
N --> J["Adjudicator"]
J -->|"One answer"| V{"Validation gate"}
J -->|"Conflict"| H["Escalate to human"]
V -->|"Fail (max 2 retries)"| J
V -->|"Pass"| D["Accepted"]
Two design choices in that diagram matter more than the rest. The complexity gate (Fan out?) keeps you from paying triple for a task one model handles fine. The redaction filter sits before any external call, because everything downstream of it leaves your security boundary.
3. The normalization layer
This is where cross-vendor orchestration is won or lost. Each provider has its own SDK, authentication, message schema, and quirks — OpenAI's Responses API, Anthropic's Messages API, and Google's Gen AI SDK do not agree on so much as the name of the field that holds your prompt. The fix is a single internal contract that every advocate must satisfy, plus one thin adapter per provider.

// The one shape every provider must return, regardless of its native API.
export interface AdvocateResult {
vendor: 'openai' | 'anthropic' | 'google'
model: string
answer: string // the candidate implementation or decision
rationale: string
risks: string[]
confidence: number // self-reported 0..1 — a hint, never ground truth
}
export interface Advocate {
vendor: AdvocateResult['vendor']
model: string
plan(brief: string): Promise<AdvocateResult>
}
// A shared system instruction keeps the brief identical across vendors.
const SYSTEM = 'You are one of several independent advocates. Return ONLY JSON ' +
'matching {answer, rationale, risks[], confidence}. Do not assume facts you ' +
'were not given. Bias toward the smallest sufficient answer.'
Now three adapters. Each is deliberately thin — its only job is to translate the native response into AdvocateResult via a shared parse() that enforces the JSON contract.
import OpenAI from 'openai'
const openai = new OpenAI() // reads OPENAI_API_KEY
export const gptAdvocate: Advocate = {
vendor: 'openai',
model: 'gpt-5.5', // GA flagship; the gpt-5.6 "Sol/Terra/Luna" tiers are in preview
async plan(brief) {
const r = await openai.responses.create({
model: this.model,
input: `${SYSTEM}\n\n${brief}`,
})
return parse('openai', this.model, r.output_text)
},
}
import Anthropic from '@anthropic-ai/sdk'
const anthropic = new Anthropic() // reads ANTHROPIC_API_KEY
export const claudeAdvocate: Advocate = {
vendor: 'anthropic',
model: 'claude-opus-4-8', // most capable; claude-sonnet-5 for the balanced tier
async plan(brief) {
const r = await anthropic.messages.create({
model: this.model,
max_tokens: 4096,
system: SYSTEM,
messages: [{ role: 'user', content: brief }],
})
return parse('anthropic', this.model, r.content[0].text)
},
}
import { GoogleGenAI } from '@google/genai'
const genai = new GoogleGenAI({}) // reads GEMINI_API_KEY
export const geminiAdvocate: Advocate = {
vendor: 'google',
model: 'gemini-3.1-pro', // reasoning flagship; gemini-3.5-flash for the fast tier
async plan(brief) {
const r = await genai.models.generateContent({
model: this.model,
contents: `${SYSTEM}\n\n${brief}`,
})
return parse('google', this.model, r.text)
},
}
Notice the model IDs are pinned as constants, in one place. That is not a style preference — it is the seam that lets you re-tier every advocate from one file when a new model ships. And they ship constantly: OpenAI moved GPT-5.6 into preview the same quarter GPT-5.5 went GA, while Google shipped Gemini 3.5 Flash weeks after 3.1 Pro. Treat the model ID as configuration, verify it against each vendor's current model list before you deploy, and never hardcode it three layers deep.
4. Fanning out and adjudicating
With a uniform contract, the fan-out is boring — which is exactly what you want. Run the advocates in parallel and tolerate partial failure so one vendor's outage never sinks the round.
const advocates = [gptAdvocate, claudeAdvocate, geminiAdvocate]
// Promise.allSettled, not Promise.all: a single 529 must not fail the round.
const settled = await Promise.allSettled(advocates.map(a => a.plan(brief)))
const results = settled
.filter((s): s is PromiseFulfilledResult<AdvocateResult> => s.status === 'fulfilled')
.map(s => s.value)
if (results.length === 0) throw new Error('all providers failed — escalate to human')
Then the adjudicator decides. The decision tree is the heart of the pattern: agreement is adopted, a lone dissent is logged as a risk, and a genuine split is escalated — never averaged.
flowchart TD
A["Three normalized candidate answers"] --> B{"Agree on the load-bearing decision?"}
B -->|"Consensus"| C["Adopt the consensus answer"]
B -->|"Majority, one dissent"| D["Adopt majority; log the dissent as a risk"]
B -->|"Genuine split"| E["Do NOT average"]
E --> F["Surface all positions to a human"]
C --> G["Validation gate"]
D --> G
You can implement the adjudicator as a rules engine (string/semantic equality plus a majority vote) or as a designated judge model given all three candidates and an ordered rubric — correctness first, then smallest sufficient answer, then risk coverage. Whichever you choose, encode one hard rule: on a load-bearing conflict, stop and surface every position. Averaging three answers produces a fourth that none of the models vetted and no human chose.
5. The validation gate
An adjudicated answer is still just the best opinion in the room. For anything consequential, put a deterministic gate after it — the same maker-checker discipline from the single-vendor pattern, unchanged by the fact that the answer came from three clouds.
async function accept(candidate: AdvocateResult): Promise<AdvocateResult> {
for (let attempt = 1; attempt <= 3; attempt++) {
const check = await runValidators(candidate.answer) // tests, schema, policy lint
if (check.ok) return candidate
if (attempt === 3) throw new Error(`gate failed after retries: ${check.summary}`)
candidate = await repair(candidate, check.failures) // one bounded correction pass
}
throw new Error('unreachable')
}
Whether runValidators runs a unit suite, validates a JSON schema, or lints against a policy, the principle holds: the gate is code that returns pass/fail, not a prompt that asks a model to grade itself. Bound the correction loop — two retries, then escalate with the failing output — so a stubborn task fails loudly instead of looping forever.
6. The governance reality: cost, latency, and data exposure
Here is where IT ownership matters more than cleverness. Every vendor now ships the same three-tier lineup, so the honest comparison is by role, not by brand:
| Provider | Representative models (mid-2026) | Tiering (flagship / balanced / fast) | Standout trait |
|---|---|---|---|
| OpenAI | GPT-5.5 (GA); GPT-5.6 Sol/Terra/Luna (preview) | Sol / Terra / Luna | Broad tooling, Responses API |
| Anthropic | Claude Opus 4.8, Sonnet 5, Haiku 4.5 | Opus / Sonnet / Haiku | Strong coding, long-horizon reasoning |
| Gemini 3.1 Pro, Gemini 3.5 Flash | Pro / Flash / Flash-Lite | 1M-token context, native grounding |
Three consequences follow directly from fanning out:
- Cost multiplies. Three flagship calls cost roughly three times one. OpenAI's own preview tiers illustrate the spread that lets you manage it — GPT-5.6 lists Sol at $5/$30, Terra at $2.50/$15, and Luna at $1/$6 per million input/output tokens. Match the tier to the task; you rarely need three flagships. (Verify current pricing on each vendor's page — these numbers move.)
- Latency is the slowest of the three, not the average. A parallel fan-out finishes when the last advocate returns. Set an aggressive per-advocate timeout and proceed with whatever came back.
- Data exposure triples. This is the one that should reach a security review. The same proprietary context now crosses into three separate external boundaries.
That last point deserves a picture. Trace exactly where data leaves your control in a single round:
sequenceDiagram
participant Orq as Orchestrator
participant DLP as Redaction / DLP
participant O as OpenAI
participant An as Anthropic
participant Go as Google
participant Adj as Adjudicator
Orq->>DLP: brief + context
DLP-->>Orq: redacted brief
par Fan-out — data leaves your boundary 3x
Orq->>O: redacted brief
Orq->>An: redacted brief
Orq->>Go: redacted brief
end
O-->>Adj: candidate
An-->>Adj: candidate
Go-->>Adj: candidate
Adj-->>Orq: one answer or CONFLICT
Mitigate deliberately: redact or tokenize before the fan-out, prefer each vendor's zero-retention or enterprise endpoints under a signed data-processing agreement, keep a data-residency matrix so you know which providers are even eligible for a given workload, and gate the fan-out itself so sensitive tasks never reach all three. Cross-vendor orchestration is a capability; treating the data egress as an afterthought is how it becomes an incident.

7. When one model is enough
The complexity gate is not a nicety — it is what makes the pattern affordable. Fan out only when a wrong answer is expensive and reasonable models might disagree. For a summarization job, a well-understood transformation, or a low-blast-radius change, one appropriately-tiered model is the correct engineering choice, and paying three vendors to concur is waste dressed up as rigor. Reserve the council for the decisions that earn it.
Troubleshooting
- Adapters return different JSON shapes. Your
parse()is too trusting. Validate every advocate's output against the contract and reject non-conforming responses rather than letting a malformed one reach the adjudicator. - One vendor is always the outlier. Check for an unfair brief — a prompt that leans on one provider's conventions (tool-calling format, system-prompt style) skews the comparison. The brief must be genuinely provider-neutral.
- Costs ballooned. The complexity gate is being skipped, or every advocate is pinned to a flagship tier. Route low-stakes tasks to a single model and reserve the fan-out for high-stakes ones.
- Latency spikes intermittently. You are waiting on the slowest advocate with no timeout. Add a per-advocate deadline and adjudicate over whoever responded.
- A model ID started erroring. It was deprecated or renamed. This is why IDs live in one config block — re-tier there against the vendor's current model list, don't grep three files.
- The adjudicator quietly blended a conflict. Reinforce the hard rule: a load-bearing split stops the pipeline and surfaces every position. A synthesized compromise nobody vetted is worse than an honest escalation.
Key Takeaways
- Cross-vendor orchestration buys correctness, availability, and leverage at once — three uncorrelated risks retired by one abstraction.
- The normalization layer is the real work. One internal contract plus thin per-provider adapters is what makes providers interchangeable.
- Diversity is the feature. Different model families fail differently, so agreement is signal and disagreement is a flag, not noise.
- Adjudicate, never average, and stop on genuine conflicts so a human owns the load-bearing call.
- Keep the validation gate deterministic — code that returns pass/fail, bounded to two correction retries.
- Own the governance. Cost and latency multiply and data exposure triples; redact first, tier deliberately, and gate the fan-out.
- Pin model IDs as config and verify them before every deploy — the lineups change monthly, as GPT-5.6 and Gemini 3.5 both showed this year.
Next Steps
- Build the normalization layer first and test it against all three providers with a fixed brief before you add any adjudication logic.
- Add a redaction step and run a tabletop review of what actually reaches each vendor — treat it as a data-egress change, because it is.
- Instrument per-advocate cost, latency, and agreement rate. If two providers agree 99% of the time on your workload, you may only need two.
- Wire the complexity gate to a real signal (blast radius, reversibility, dollar value) so the fan-out fires only when it earns its cost.
Related Articles
- Multi-Model AI Code Review in Claude Code: The Advocate/Adjudicator Pattern — the single-vendor version of this pattern, implemented on Claude Code's subagents, skills, and hooks.
- Copilot for Microsoft 365: Rollout, Licensing, Data Readiness, and Governance — the same governance-first lens applied to a managed AI rollout.
- What's New in Microsoft 365 Copilot: The Mid-2026 Roundup for IT Pros — how multi-model choice is arriving inside mainstream productivity tooling.
Leave a Reply