Course · OpenCode · Providers & models
Give each agent its own model
Some jobs in feedmill - the course’s illustrative Go feed aggregator, with one feed type suspected of parsing its dates wrong - are mapping jobs: you need to know where in the parser code every date layout actually gets consumed, because the bug might live at any of those sites. That’s a wide, dumb sweep: read a lot of files, grep for a pattern, report back. It is exactly the kind of work a cheap, fast model finishes for pennies.
So watch what it costs when you don’t think about which model does it. You’re sitting on the build agent - OpenCode’s default primary agent, the one your top-level prompts run on - with your strong reasoner behind it, and you fire off the scan:
> find every place in the parser package where we call time.Parse or otherwise turn a feed's raw date string into a time.Time, and list the file and the layout string used at each oneIt works. In feedmill the answer is short - one call site, time.ParseInLocation in internal/parse/date.go, tried against three layouts - and on a real parser package the same prompt reads dozens of files and comes back with a clean table. But it did all that bulk reading and pattern-matching on a frontier reasoning model billed at frontier rates - you paid reasoning prices for grep work. Do that twenty times while exploring an unfamiliar codebase and you’ve paid frontier rates twenty times for the cheapest kind of task you have.
The fix: send the scan to a cheap scout that can’t edit
Section titled “The fix: send the scan to a cheap scout that can’t edit”OpenCode ships a built-in subagent - a secondary agent with its own context window and permissions, invoked by @mention - for precisely this. explore is a fast agent the docs call read-only: its built-in permission map denies the edit tools, so a codebase sweep can’t make a stray edit through them. It still allows bash, so “read-only” covers file edits, not the shell. A top-level permission block in your own config merges after that map and wins, so a global "edit": "allow" reopens the edit tools for explore too (agent behavior as of October 2026; agents documentation, and the explore definition in agent.ts):
> @explore find every place in the parser package where we call time.Parse or turn a feed's raw date string into a time.Time, and list the file and the layout string used at each oneOpenCode spawns explore in its own child session with its own context window, runs the scan there, and returns a summary to your main thread - your build session’s context stays clean instead of filling up with thirty files of parser source. That isolation alone is worth the @mention. But by default, when explore has no model of its own, it runs on the model of whichever primary agent invoked it - which is very likely your expensive one. The lever this page is about is the next step: give explore its own model, a cheap one, so the scan is cheap by construction every time you reach for it.
Pin a model on the agent
Section titled “Pin a model on the agent”Every OpenCode agent - built-in or your own - is configurable, either under the agent key in opencode.json or through a markdown file with YAML frontmatter, and one of the fields is model. Set it and that agent overrides the default; leave it off and the agent inherits a model - but which model depends on what kind of agent it is. A primary agent with no model set uses the globally configured model. A subagent like explore with no model set inherits the model of the primary agent that invoked it - not the global default. That’s exactly why pinning a model on explore is what makes it cheap: otherwise it rides along on whatever your build agent is using. The value is a provider/model-id string, the same shape used throughout OpenCode.
To override a built-in agent, add an entry with its name under agent in the opencode.json at feedmill’s root, or in ~/.config/opencode/opencode.json for every project on the machine (as of October 2026; agents documentation). You only have to specify the fields you’re changing. The docs don’t spell out the merge, but the source does: OpenCode applies each field you set over the built-in definition and keeps the built-in value for every field you leave out, merging your permission entries into the built-in map (agent.ts, as of October 2026). So the rest of explore’s built-in setup, including its prompt and its permission map, stays as shipped.
{ "$schema": "https://opencode.ai/config.json", "agent": { "explore": { "model": "anthropic/claude-haiku-4-5" } }}A markdown file named after the agent, such as .opencode/agents/explore.md, also overrides it, but its body becomes the agent’s prompt. A file with frontmatter only replaces explore’s built-in prompt with an empty one, and OpenCode then uses a generic system prompt (config/agent.ts, as of October 2026). Keep the markdown form for agents you write yourself.
The model ID is illustrative. Use a cheap model from a provider you have connected; opencode models in your shell lists every available ID in provider/model form (as of October 2026; CLI reference).
That’s the whole change. The next time you @explore anything, the scan runs on the cheap model and your wallet stops noticing it. Meanwhile your default build agent - the one that’ll handle the actual date fix - is untouched, still pointed at the strong reasoner. You’ve split the spend exactly where it should split: cheap throughput for reading, strong reasoning for changing.
Why this is the move, not just a tidy trick
Section titled “Why this is the move, not just a tidy trick”The shape you’ve just set up - a cheap explore that can’t edit and a strong build - is the canonical OpenCode posture, and it pays back every time you scale work out across agents. Reading is the cheap, high-volume half of agentic coding: scanning, grepping, mapping, summarizing. Changing is the expensive, low-volume half: the reasoning that has to be right because it touches your code. A single model has to be priced for the hard half, so running the easy half through it is pure overpayment. Per-agent model lets you stop doing that without thinking about it again - the routing is baked into who does the work, not decided fresh on every prompt. That is cost as a design decision rather than a monthly surprise: what a task consumes is set by which model you assigned to it, once.
The widget below prices both mis-settings for a set of sample tasks: the pinned expensive default that overpays for mechanical work, and the false economy of underpowering the hard problems until they fail and get re-run on the strong model anyway. Its model dial, which model answers, is what a per-agent model sets permanently: the scout lives on the light setting, the build agent on the capable one. Its effort dial is a separate setting, the thinking variant, which an agent can pin with its own variant field (config/agent.ts, as of October 2026).
It also composes. When you fan a big job out across subagents, every one of those workers runs on the model you pinned to it, so the cheap-scout / strong-builder split scales instead of being a choice you re-make per task. And it reaches past one vendor: a single-vendor agent can only choose within that vendor’s price ladder, while OpenCode can reach across 75+ providers (as of October 2026; providers documentation) for the scout and keep a frontier reasoner on the build loop - different models, different providers, different prices, one session.
One caveat worth stating plainly: a cheaper scout is cheaper because it’s weaker. For a mechanical sweep - “list every call site of this function” - that’s a fine trade. Don’t quietly let it creep into work that needs judgment; if a scan turns into “and figure out which of these is the bug,” that’s reasoning, and reasoning belongs back on the strong model. Pin the model to match the kind of work the agent does, and re-check the fit when the work changes. The real split is mechanical work against judgment; reading against changing is this page’s case of it. A mechanical edit, such as a copy tweak, can run cheap too, but not on explore, which can’t use the edit tools, so give it to a cheap agent of your own that is allowed to edit.
This is the capability principle, and feedmill shows where it changes the outcome. Ask the scout the mapping question and it lists internal/parse/date.go and its three layouts correctly. Now ask it the judgment question: is the value that parse.Date returns correct for 2026-02-03T09:00:00+05:30? The answer sits in one line after the parse, which rebuilds the time from its wall-clock fields with time.UTC. That line relabels the offset instead of converting it, so the item lands five and a half hours late. Both models have the same file in context, so more context does not help here. What decides the answer is whether the model reads the time.Date call correctly. Give that question to the build model, and let the failing test confirm the answer instead of either model’s word: feedmill already has parser/date_test.go, a separate test package that imports internal/parse and checks that +05:30 input converts to 03:30 UTC, and go test ./parser/ runs it.
Next, point two providers at the same task at once and let them argue: Run two providers side by side.