{"slug": "show-hn-does-per-step-reasoning-effort-save-money-in-claude-code", "title": "Show HN: Does per-step reasoning effort save money in Claude Code?", "summary": "A research project found that letting TypeSafe's Jev decision model pick Claude Code's reasoning effort per step cut the cost of a coding benchmark at `max` effort by 55% with every test still passing, while the same setup at `high` effort saved only about 1.1%. In a 470-step real session at `max` effort, the estimated saving was 4–9% because cached context re-reads accounted for 82% of spend, though the proxy held a 99.1% cache hit rate with 0 unexpected cache misses in 411 checked steps. The project is unofficial and not affiliated with Anthropic or TypeSafe.", "body_md": "**Does letting Jev choose Claude Code's reasoning effort, step by step, save money? At `high`\neffort, barely: about 1%. At `max` effort, it cut the cost of a coding benchmark by 55% with every\ntest still passing. In a long real session at `max`, the estimated saving was 4–9%, because\nre-reading the conversation's context is most of the bill. Changing effort on every step never\nreset the prompt cache.**\n\nA research project. The code works and the results are reproducible. Unofficial; not affiliated with Anthropic or TypeSafe.\n\n[Jev](https://openrouter.ai/typesafe/jev-1.13) is TypeSafe's small, cheap decision model. After\n[Astra-Ares](https://github.com/miuuyy/Astra-Ares) used it to choose GPT-6 Astra's reasoning\neffort per step in Codex, several tools appeared doing the same for Claude. The pitch: most agent\nsteps are routine, so run them at low effort and keep deep reasoning for the hard ones.\n\nI built a proxy that does this for Claude Code without breaking the prompt cache, confirmed it works, then measured what it saves.\n\n| Test | Result | \n|---|---|\n| Benchmark at `high` (24 sessions, hidden tests) | Cost **−1.1%** , thinking −46%, every test passed in both arms | \n| Benchmark at `max` (24 sessions, hidden tests) | Cost **−55%** (−21% without its one outlier task), thinking −98.5%, wall time −58%, every test passed in both arms | \n| Real session at `max` , shadow mode (470 steps, 92 min) | Estimated saving **4–9%** . Cached context was 82% of spend, thinking 9.5% | \n\nEffort changes how much the model thinks, and the saving follows thinking's share of the bill.\nAt `high`, Claude Opus 5.5 thinks little on routine steps, so there's little to cut. At `max`, it\nthinks a lot even on routine steps, and Jev moves those down. But in a long session every step\nre-reads hundreds of thousands of tokens of context, which effort doesn't touch.\n\nClaude Code caches the conversation so each step only pays full price for what's new. Changing\neffort mid-session used to throw that cache away. It no longer has to: Anthropic's\n[per-message effort](https://platform.claude.com/docs/en/build-with-claude/effort#change-effort-mid-conversation-beta)\nbeta lets an effort-only system message change the level while leaving the cached prefix intact.\n\nIt held inside Claude Code. In the 470-step session with effort chosen on every step: **0\nunexpected cache misses in 411 checked steps, and a 99.1% cache hit rate.** With effort\nalternating low/high on every step of a test task, each request read the whole previous prefix\nfrom cache (26,992 → 34,285 → 35,017 → 35,270 → 35,617 tokens).\n\nThe catch: Claude Code already carries its own effort setting on a system message near the\nstart of the conversation (beta `per-turn-control-2026-07-01`), and the latest setting in a\nrequest wins. An injected effort change has\nto be the last message, or it's silently ignored. Changing the request's top-level effort field is\nignored the same way.\n\n- Claude Code runs a whole session at one effort level (`/effort` : low, medium, high, xhigh,\nmax).\n- Opus 5.5, Fable 5.1, Mythos 5.1, and Opus 5 accept a\n[per-message effort change](https://platform.claude.com/docs/en/build-with-claude/effort#change-effort-mid-conversation-beta) :\nan effort-only system message inside`messages` that leaves the cached prefix intact. So\neffort can change on every step without re-processing the conversation.\n- Research on per-step effort selection ([ARES](https://arxiv.org/abs/2603.07915) ,[TAB](https://arxiv.org/abs/2604.05164) ) reports token savings of 35–53%. Both count tokens,\nnot dollars. The open question was what per-step effort does to the total cost of real Claude\nCode sessions.\n\n**1. Mechanism.** `jev-effort` runs `claude` behind a local proxy (`ANTHROPIC_BASE_URL`). Before\neach model request it sends Jev a trimmed view of the conversation (prompts, Claude's visible\nreplies, the last six tool calls) and asks two questions: which effort the next step needs, and\nfor how many steps to keep it. It applies the answer by appending an effort-only system message,\nand re-inserts earlier ones at their original positions so each request begins with the previous\none. Jev may lower effort but never exceed the session's own setting.\n\n**2. Verification.** On a hard counting problem, a `low` marker produced 438–548 output tokens\nand `max` produced 1,528–1,809, in line with Claude Code's own `--effort` (438 and 1,733). With\neffort alternating on every step, each request read the full previous prefix from cache. In the\nreal session: 0 unexpected cache misses in 411 checked steps, 99.1% cache hit rate.\n\n**3. Controlled benchmark.** Six small coding tasks, each with a visible test and hidden checks.\nEach task ran from identical files twice: once at a fixed effort, once with Jev choosing under\nthat same effort as its ceiling. Two rounds at `high` and two at `max`, 48 headless Claude Code\nsessions in all. A warm-up run came first so neither arm paid to cache the other's system prompt.\n\n**4. Shadow mode on real work.** One 92-minute session of my normal work at `max` effort (470\nmodel steps, Opus 5.5, Claude Code 2.1.280). For every step, Jev's choice was logged and nothing\nwas changed. Each request was priced at Claude API list prices from the usage the API reported:\ncache reads, cache writes, output, and hidden thinking.\n\n| Task | Pass (fixed / Jev) | Cost (fixed / Jev) | Thinking tokens (fixed / Jev) | Jev's effort mix | \n|---|---|---|---|---|\n| expr-eval | 2/2 / 2/2 | $2.00 / $0.32 | 54,397 / 551 | low 5, medium 4 | \n| interval-merge | 2/2 / 2/2 | $0.36 / $0.25 | 2,907 / 45 | low 5, medium 4 | \n| lru-cache | 2/2 / 2/2 | $0.39 / $0.23 | 2,963 / 0 | low 3, medium 4 | \n| paginate-bug | 2/2 / 2/2 | $0.35 / $0.28 | 2,788 / 45 | low 4, medium 5 | \n| rename-refactor | 2/2 / 2/2 | $0.38 / $0.36 | 1,335 / 313 | low 12, high 2 | \n| stats-bugs | 2/2 / 2/2 | $0.27 / $0.28 | 509 / 0 | low 6, medium 4 | \n| **Total** | 12/12 / 12/12 | **$3.76 / $1.70 (−55%)** | 64,899 / 954 (−98.5%) |  | \n\nWall time fell from 913 s to 387 s. One task dominates: on expr-eval, `max` thought for about\n27,000 tokens per run and Jev's low and medium settings passed the same hidden tests with about\n280. Without expr-eval, cost still fell 21%.\n\n**Compared with just using `high`.** The same six tasks at a fixed `high` cost less than fixed\n`max`, so the fair question is whether Jev beats simply turning effort down. On these tasks it\nmatched or beat it. The runs were separate, so treat this as a rough comparison:\n\n| Same six tasks, two runs each | Cost | Thinking tokens | Wall time | Passed | \n|---|---|---|---|---|\n| Fixed `max` | $3.76 | 64,899 | 913 s | 12/12 | \n| Fixed `high` | $1.94 | 1,450 | 393 s | 12/12 | \n| Jev, capped at `high` | $1.92 | 787 | 417 s | 12/12 | \n| Jev, capped at `max` | $1.70 | 954 | 387 s | 12/12 | \n\n|  | Fixed high | Jev | Change | \n|---|---|---|---|\n| Hidden checks passed | 12/12 | 12/12 |  | \n| Thinking tokens | 1,450 | 787 | −45.7% | \n| Output tokens | 20,281 | 20,515 | +1.2% | \n| Cost | $1.94 | $1.92 | −1.1% | \n| Wall time | 393 s | 417 s | +6% | \n\nOpus 5.5 list prices: $0.20 per million tokens read from cache, $8 per million written to the one-hour cache, $20 per million output tokens. I'm on a subscription, so these are API-equivalent figures, not a bill.\n\n| Category | Spend | Share | \n|---|---|---|\n| Cache reads | $41.05 | 63.9% | \n| Cache writes | $11.51 | 17.9% | \n| Hidden thinking | $6.10 | 9.5% | \n| Visible output | $5.13 | 8.0% | \n| Uncached input | $0.45 | 0.7% | \n| **Total** | **$64.23** |  | \n\nThe session averaged 441K tokens of context per step, re-read every time.\n\nJev would have lowered effort on all 470 steps: to high on 261, xhigh on 149, low on 51, and\nmedium on 9. It never kept `max`.\n\n|  | Amount | Share of spend | \n|---|---|---|\n| Ceiling: all thinking removed on those steps | $6.10 | 9.5% | \n| Estimate at the `high` benchmark's thinking cut (46%) | $2.80 | 4.4% | \n| Estimate at the `max` benchmark's thinking cut (98.5%) | $6.01 | 9.4% | \n| Jev's own cost (2.9M input tokens) | −$0.12 |  | \n| **Net** | **$2.68–$5.88** | **4.2–9.2%** | \n\nThe `max` benchmark's cut is likely too high for this session: there Jev mostly chose low and\nmedium, while here it mostly chose high and xhigh. **The break-even is thin.** One extra step\ncosts about $0.09 to re-read the context, so the net saving equals 30–67 extra steps across 470.\nIf lower effort makes Claude take more steps than that, it loses money. Shadow mode can't observe\nthat; the benchmarks found step counts roughly unchanged (92 vs 87 turns at `max`).\n\n- **The mechanism works.** Effort changes take hold, the cache survives, and every hidden test\npassed in both arms at both effort levels.\n- **At `high`, it doesn't pay.** Opus 5.5 at` high` spends little on thinking during routine\nsteps, so a 46% thinking cut moved total cost by about 1%.\n- **At `max`, it can pay.**` max` spends heavily on thinking even when the step doesn't need it.\nWith short contexts the saving is large (55% on the benchmark, and faster). In a long session\nthe same kind of cut is a few percent of the bill, because context dominates.\n- **It isn't much better than turning effort down yourself.** On the benchmark, Jev under a`max` ceiling cost about the same as fixed`high` . What it adds is keeping`max` available for the\nsteps that need it, which small tasks can't show.\n- **It adds latency, but may not cost time.** Jev took 601 ms median and 793 ms at p95 per\ndecision, with no errors. At`max` , less thinking more than made up for it: the Jev arm finished\nthe benchmark in 42% of the time.\n- **Quality at `max` on real work is untested.** The benchmark tasks are small and well specified.\nIf you run`max` because your work needs it, lowering effort may cost more than it saves.\n\n- One real session, at `max` , 92 minutes. There's no real-session measurement at`high` .\n- The benchmark tasks are small, and two runs per task shows direction, not a precise effect\nsize. One task accounts for most of the `max` result.\n- Shadow mode can't measure quality or extra steps on real work, so the real-session saving is a range, not a measurement.\n- Behind any proxy, Claude Code drops a few direct-connection features, including claude.ai-backed\ntools such as Artifacts, so each request carried about 4K fewer tokens than a direct session\nwould ([details](https://github.com/ifoster01/jev-effort/blob/main/docs/usage.md#limitations) ).\n- Dollar figures apply API list prices to subscription usage. How plan limits weigh each token type isn't published.\n\nFound while building this; details and evidence in [docs/how-it-works.md](https://github.com/ifoster01/jev-effort/blob/main/docs/how-it-works.md).\n\n- Claude Code re-sends some messages as a plain string after first sending them as text blocks. The API caches both the same way, but anything that hashes the history must normalize them.\n- Any custom `ANTHROPIC_BASE_URL` makes Claude Code turn off MCP tool search, loading every MCP\ntool definition into every request. With several MCP servers, a fresh session started at\n281K–500K tokens instead of 27K.`ENABLE_TOOL_SEARCH=true` restores it when the proxy forwards`tool_reference` blocks.\n- Claude Code 2.1.260+ has early-access \"function hooks\" (`CLAUDE_CODE_ENABLE_FUNCTION_HOOKS=1` ).\nA`turn.step` hook can rewrite effort per request, and Claude Code then inserts the same\ncache-safe markers itself.\n\n```\nnpx jev-effort --jev-shadow              # use like `claude`; logs Jev's choices, changes nothing\nnpx jev-effort stats                     # where your spend goes and what Jev would save\nnpx jev-effort stats --share             # the same, safe to paste (no prompts or ids)\nnpx jev-effort bench --effort max --runs 2   # the controlled comparison (uses your Claude usage)\n```\n\nSetup, configuration, and privacy details: [docs/usage.md](https://github.com/ifoster01/jev-effort/blob/main/docs/usage.md). The data behind this\npage: [real session at max](https://github.com/ifoster01/jev-effort/blob/main/docs/results/2026-09-23-max-session.json),\n[benchmark at max](https://github.com/ifoster01/jev-effort/blob/main/docs/results/2026-09-23-opus-5-5-max.json), and\n[benchmark at high](https://github.com/ifoster01/jev-effort/blob/main/docs/results/2026-09-23-opus-5-5-high.json). If your numbers look different,\nplease open an issue with `jev-effort stats --share`.\n\n| Project | Approach | \n|---|---|\n| [Astra-Ares](https://github.com/miuuyy/Astra-Ares) | The original: Jev picks GPT-6 Astra's effort per step in Codex. jev-effort adapts its Jev prompt wording (MIT) | \n| [jev-opus](https://github.com/WXK-AI/jev-opus) | Same proxy approach for Claude Code, plus work-phase adjustments | \n| [jev-model-router](https://github.com/moelahmady/jev-model-router) | Claude Code function hooks; effort per prompt, optional model routing | \n| [effort-router](https://github.com/handpickedlab/effort-router) | Claude Code plugin; effort per task | \n| [ARES](https://arxiv.org/abs/2603.07915) ,[TAB](https://arxiv.org/abs/2604.05164) | Research on per-step and per-turn reasoning budgets | \n\nNone of these, as of September 23, 2026, reports total cost against a fixed-effort baseline.\n\nMIT. See [THIRD_PARTY_NOTICES.md](https://github.com/ifoster01/jev-effort/blob/main/THIRD_PARTY_NOTICES.md) for the Astra-Ares attribution.", "url": "https://wpnews.pro/news/show-hn-does-per-step-reasoning-effort-save-money-in-claude-code", "canonical_source": "https://github.com/ifoster01/jev-effort", "published_at": "2026-09-24 01:08:08+00:00", "updated_at": "2026-09-24 01:58:27.594114+00:00", "lang": "en", "topics": ["ai-agents", "large-language-models", "ai-tools", "ai-infrastructure"], "entities": ["Claude Code", "Anthropic", "TypeSafe", "Jev", "Claude Opus 5.5", "Astra-Ares", "GPT-6 Astra", "Codex"], "also_reported_by": [], "alternates": {"html": "https://wpnews.pro/news/show-hn-does-per-step-reasoning-effort-save-money-in-claude-code", "markdown": "https://wpnews.pro/news/show-hn-does-per-step-reasoning-effort-save-money-in-claude-code.md", "text": "https://wpnews.pro/news/show-hn-does-per-step-reasoning-effort-save-money-in-claude-code.txt", "jsonld": "https://wpnews.pro/news/show-hn-does-per-step-reasoning-effort-save-money-in-claude-code.jsonld"}}