Skip to content

Course · Codex · Models & effort

Switch models with /model, --model, and -c to match the task

Effort stays put this time - low on the pattern-following category filter, high on the categorizer-wiring fork, the levels each task shape earned in the effort lesson - and the other knob moves: which model answers at all. A lighter model and a more capable one differ in what they can reason through, the way effort differs in how hard one model reasons. Confuse the two and you reach for the wrong knob when a task goes wrong.

The category filter again, light then capable

Section titled “The category filter again, light then capable”

Same task, same effort, only the brain changes.

> add an optional category query parameter to GET /transactions in
app/main.py, filtering with ledger.by_category
(model: light, model_reasoning_effort = low)
• Read app/main.py
• Edited app/main.py (+3) - category param; by_category when set
Done.

Matched - the same three lines as every low-effort stop in the last lesson. A light model reading an existing pattern needs nothing more from either dial.

> (same prompt)
(model: capable, model_reasoning_effort = low)
• Read app/main.py
• Edited app/main.py (+3) - same three lines
Done.

Overkill. Identical output, from a brain with more reach than the task asked for - the file ten lines away had settled every question before the model arrived.

Now hold effort at high - the level that earned the design last lesson - and swap only the model.

> wire categorise() into the CSV import path so imported transactions
get their keyword-table category
(model: light, model_reasoning_effort = high)
Thinking…
Matching: the table already does substring matching. Where to
hook it: the route, or the parser? Either works. Going with
the route - it's the loop I can see.
• Edited app/main.py (+2) - txn.category in import_csv's loop
Done.

Underpowered. More deliberation text than low produced a page ago, and the same shallow landing - it noticed a fork existed, picked the first branch it could see, and moved on. A light model thinking longer about a hard fork doesn’t think deeper; it narrates the same guess at more length.

> wire categorise() into the CSV import path so imported transactions
get their keyword-table category
(model: capable, model_reasoning_effort = high)
Thinking…
Hook: parse_csv or the route? parse_csv is where a description
first becomes data; the route is its only caller; the ledger
stores whatever it's handed.
Conflicts: "Amazon Whole Foods Market" contains two keywords -
table order is the priority, first match wins.
Boundary: POST /transactions keeps its sender's category; the
parse path is imports only.
Proposed before writing: one import line, one category= argument
in parse_csv, tests for hits and fallback.

Matched - the same surfacing the last lesson’s high produced, because that is what it was: a capable model with room to spend. The design only appears when both dials clear their bars together. Hold the model light and high narrates a guess; hold effort at low and the capable model is never asked to think at all.

The two knobs are independent, and the check holds by construction: four runs, one dial moved at a time, and the cells that changed name the dial that moved them. The filter stayed matched or overkill regardless of brain; the wiring stayed underpowered until effort and model cleared the bar together. Three ways to set the model follow, in the order you’ll actually reach for them.

When you’re already in a session and the next task wants a different gear, switch in place:

/model

It opens a picker for the active model - and, where the model supports it, the reasoning effort alongside, so you choose the brain and how hard it thinks in one place. The change takes effect on your next prompt; nothing about the session resets. This is the move when the afternoon turns: the filter shipped on the light model, the wiring fork is next, and you bump the gear without leaving the conversation you’ve built context in.

When you know the model before the session even starts, set it on the launch command:

Terminal window
codex -m <model> "wire categorise() into the CSV import path"

--model (short -m) overrides the model from your configuration for that run. It’s the right form when the whole session is one kind of work - a heavy design afternoon, or a long run of mechanical edits - and you don’t expect to switch partway through.

The third form is the same -c key=value override you met on the effort dial, pointed at the model key. It sets the model for one invocation without changing any config:

Terminal window
codex -c model="<model>" -c model_reasoning_effort="high" "..."

This is how you set both knobs for a single run - a specific model and a specific effort - without touching your standing defaults. When a dedicated flag exists, prefer it: -m reads better than -c model=…. The -c form earns its place when you’re setting several keys at once, which is exactly what the next lesson bundles into a reusable profile.

The current flags and their short forms live in the CLI reference - pin to it rather than to memory, since these shift.

Why this lesson names no model, and why you should not memorize one

Section titled “Why this lesson names no model, and why you should not memorize one”

You’ll notice that no command above, and no trace before them, names an actual model - and that’s deliberate. The available models and capabilities depend on the surface, the account, the workspace policy, and the authentication method. ChatGPT sign-in and API-key workflows are not guaranteed to expose the same catalog, and model names change faster than a course can track.

Read the picker and the authentication documentation, then trust what the current session actually offers. For codex exec automation, use CODEX_API_KEY, and pin the model explicitly only after checking it is available to the runner.

One lever here isn’t about reasoning at all - it’s about latency. Some sessions are interactive in a way where waiting hurts: live iteration, a prompt every half-minute, response time as the bottleneck rather than the thinking. For that there is Fast mode (formerly Priority processing) - faster responses, by prioritizing the infrastructure rather than dialling the model down.

Persistently it is a config key: service_tier = "fast" in config.toml. The interactive toggle is a slash command in surfaces that expose it - don’t memorize the literal spelling; open the TUI and check what the current build offers. The current behavior, naming, and billing are documented under speed.

Two things to keep honest about it. First, it buys speed, not capability - it will not make a light model reason like a heavy one, and it will not turn low into high. Second, the billing depends on how you authenticated: signed-in and API-key workflows can bill this tier differently, so read the speed page for your auth method before leaving it on.

Both knobs, and all three timings for setting them - mid-session, at launch, per invocation. Setting them by hand every time gets old fast when the same combinations keep recurring. The next lesson bundles them into a named profile you select with one flag.