Skip to content

Agent configuration (agents.yaml)

A workflow’s delegate: states say what kind of thinking a step needs — coding, reasoning, prose — without naming a model. agents.yaml is where you bind those labels to actual models, once, and share them across every workflow you run. Without it you’d be hard-coding claude-opus-4-7 into each workflow and editing fifty files when a better model ships; with it you change one line and the swap propagates.

The shape is deliberately small: one mandatory default: list, plus sparse overrides keyed by affinity, tier, or both. (The file, its resolver, and the validation below are all part of the open-source platform — you author the bindings here regardless of which runtime ends up driving them.)

.flowgate/agents.yaml
version: 1
default:
- provider: { name: anthropic }
model: claude-sonnet-4-6

That’s enough. Every delegate: state in every workflow you run will resolve to Claude Sonnet 4.6. Add overrides as you need them.

The keys you can use in overrides: are fixed. This is deliberate — agents.yaml is a contract between operators (who pick models) and workflow authors (who pick affinities). A closed enum keeps that contract testable.

Affinity (5):

  • coding — code generation / refactoring
  • reasoning — multi-step planning, debugging, math
  • prose — natural-language writing
  • web-search — model that benefits from a search-augmented endpoint (e.g., Perplexity)
  • recon — local summarization and codebase navigation

Tier (3):

  • frontier — the most capable model in the provider’s catalog
  • standard — the default workhorse model
  • commoditized — small/cheap/fast

Provider (5 + escape hatch): anthropic | openai | google | ollama | lmstudio | custom. custom requires an endpoint: field.

Enum additions in future flowgate releases are minor-version compatible; removals are major. Workflow authors get a stable contract.

You don’t need to define every affinity × tier combination. The resolver walks from most-specific to least:

  1. <affinity>-<tier> (exact match)
  2. <affinity> alone
  3. <tier> alone
  4. default

Affinity wins the tiebreaker. If a workflow asks for coding-frontier and you’ve defined both coding and frontier (but not coding-frontier), the resolver picks coding.

version: 1
default:
- provider: { name: anthropic }
model: claude-sonnet-4-6
overrides:
coding-frontier:
- provider: { name: anthropic }
model: claude-opus-4-7
features:
extended_thinking: true
thinking_budget_tokens: 16000
prose:
- provider: { name: anthropic }
model: claude-haiku-4-5-20251001

Each override is a list of bindings. The resolver tries them in order, falling through to the next on infrastructure failures (401, 403, 429, 404, network timeouts). It surfaces content failures (other 4xx, schema, safety) to the caller.

overrides:
coding-frontier:
- provider: { name: anthropic }
model: claude-opus-4-7
- provider: { name: openai }
model: gpt-5

If your Anthropic key is rate-limited, the resolver tries OpenAI. If Anthropic returns 400 because of a malformed request, the error surfaces — that’s not the kind of failure a fallback can fix.

When a runtime starts a workflow, it probes every distinct primary binding (the first entry in each list, plus the default’s first entry) once. If a provider’s API key env var is missing or the credentials are rejected, startup fails with the offending binding named.

This means you find out about an expired Anthropic key at startup — not three states in, after the workflow has already done work.

Bypass with FLOWGATE_SKIP_PREFLIGHT=1 (useful for CI / disconnected dev).

Provider credentials come from the environment — set ANTHROPIC_API_KEY and friends in your shell rc or CI secrets. See Provider API keys.

If you want the specificity walk to be EXACT only — no fall-through from coding-frontier to coding — set:

version: 1
strict_specificity: true
default:
- provider: { name: anthropic }
model: claude-sonnet-4-6
overrides:
coding-frontier:
- provider: { name: anthropic }
model: claude-opus-4-7

In strict mode, a workflow asking for coding-frontier matches the exact key or errors at load. A workflow asking for reasoning-frontier (no exact match defined) walks to default as normal.

  1. --agents-config <PATH> (or $FLOWGATE_AGENTS_CONFIG).
  2. .flowgate/agents.yaml in the current working directory (project).
  3. ~/.config/flowgate/agents.yaml (user — dirs::config_dir()).
  4. --agent name=provider/model CLI flags (deprecated v0.2 path).

When both project and user files exist, project wins whole-file — not slot-level merge. Doctor reports the shadowed user file so the surprise is visible.

If you mix --agent flags and an on-disk agents.yaml, startup fails with AmbiguousAgentSource. Choose one source.

The v0.2 --agent planning=anthropic/claude-sonnet-4 flag form still works in runtimes that accept it, but the on-disk file is the supported path. To migrate, write the equivalent file:

version: 1
default:
- provider: { name: anthropic }
model: claude-sonnet-4-6

You don’t need to name affinities in default: — the default catches everything. Adding affinity-specific overrides is purely incremental.

Per-provider feature structs use #[serde(deny_unknown_fields)], so typos fail at load with the offending key named. extended_thinking on an OpenAI binding fails; reasoning_effort on an Anthropic binding fails.

Anthropic: extended_thinking: bool, thinking_budget_tokens: u32. OpenAI: reasoning_effort: "low" | "medium" | "high" | "xhigh". Google: thinking_budget_tokens: u32.

Each provider exposes “think harder” differently — Anthropic takes a thinking-token budget, OpenAI takes an effort level. At spawn time flowgate translates your toggle to one internal effort level and then back out to whatever shape the target provider’s API wants, so you express the intent once and it lands correctly on each provider. Mapping:

  • Anthropicextended_thinking: true with no explicit budget defaults to high effort. A thinking_budget_tokens value snaps to the nearest effort level (≤2048 → low, ≤6144 → medium, ≤16384 → high, larger → xhigh), and when set, the explicit budget wins over the boolean.
  • OpenAIlow|medium|high|xhigh pass straight through. Any other value is dropped with a warning so the typo is visible.
  • Google — same budget-to-effort mapping as Anthropic.

The same loader that resolves agents.yaml at workflow start (AgentsFile::from_path) runs at config load, so mcp-flowgate check surfaces a malformed bindings file before anything tries to use it. The stable error_kind contract — MISSING_DEFAULT, EMPTY_DEFAULT, MISSING_PROVIDER_MODEL, UNKNOWN_OVERRIDE_KEY, UNKNOWN_FEATURE_KEY, PROVIDER_ENDPOINT_REQUIRED, VERSION_MISMATCH, YAML_SYNTAX, IO — is what cap.implement.write-agents-config switches on for its round-trip step, and what your own pre-commit hooks and editor integrations can rely on.

Terminal window
mcp-flowgate check --config gateway.yaml

A bad override key fails the check with the offending key named, e.g. `vision-frontier` is not a valid <affinity> | <tier> | <affinity>-<tier>.

Hand-authoring agents.yaml is fine for two-line files, but as soon as you want per-affinity overrides + CoR fallbacks across multiple providers, the flowgate-meta repo ships an orchestrator that walks you through it. Loaded into your gateway config and driven through an MCP host (or the open agentic runtime), meta/flow.configure-models runs five stages:

  1. cap.research.model-inventory — probes each provider you name (anthropic,openai,google,ollama,lmstudio) for its model catalog. Failures are classified per the same FailureClass enum the resolver uses (Auth401, RateLimit429, NetworkTimeout, etc.) so a missing API key surfaces explicitly, not silently.

  2. cap.plan.suggest-bindings — an agent composes one opinionated plan: a default: list, sparse overrides keyed to the affinity-tier pairs your workflows actually reference, plus rationale + assumptions for each non-obvious choice.

  3. cap.gate.human-approve-plan — HITL gate. Two modes:

    • review_plan (default) — surfaces the plan + rationale + assumptions for your approval. You can hand back an edited plan instead of approving the original.
    • auto — skips the gate. Useful for CI / scripted setup.
  4. cap.implement.write-agents-config — atomic write (temp file in the target dir → mcp-flowgate check round-trip → rename). A failed round-trip rolls back the temp file rather than replacing a working agents.yaml with a broken one.

  5. cap.verify.auth-only-smoke-test — 1-token completion per binding in the just-written file. This proves auth, not capability. Output names the limitation explicitly:

    Auth verified for the bindings reported above. CAPABILITY NOT TESTED — a model that authenticates may still fail to code, reason, or follow safety constraints in your actual workflows.

    The v0.4 roadmap replaces this with a capability harness that exercises bindings against real workflow tasks. Until then, the smoke test is honest about what it does and doesn’t cover.

Inputs (passed when the orchestrator is started via flowgate.command):

{
"providers": "anthropic,openai,google",
"delegates": ["coding-frontier", "reasoning-frontier", "prose-standard"],
"mode": "review_plan",
"target_path": ".flowgate/agents.yaml"
}

If the round-trip fails or you reject the plan, the orchestrator terminates in failed rather than done — and your existing agents.yaml (if any) is untouched.

  • The full validation rules cover every schema check the loader enforces.
  • v0.3.1 wires per-list runtime CoR over actual provider failures + per-provider feature-toggle translation. The classifier, structured AgentResolutionExhausted, and typed feature structs are already in place.
  • v0.4 ships periodic re-probing of model availability + the capability harness that replaces the auth-only smoke test.