◉ANTIGRAVITY LABJP
Articles/AI Tools
⚙ AI Tools/2026-08-14Beginner

When a Fast Model Feels Slow, Look at Reasoning Effort Before Switching Models

How I choose between Low, Medium, and High reasoning effort in Antigravity by the shape of the task, how that compares with switching models or rewriting the request, and a triage table for when changing the level makes no difference. The logging function is now fixed to run under zsh, and I re-tested it before publishing.

Antigravity414reasoning effortmodel settingsindie development30agent workflow

I once asked an agent to reword a single line on a settings screen in one of my wallpaper apps.

It was a handful of words. Yet there was an odd pause before the reply started, and I could see the agent re-reading the structure of files that had nothing to do with the change.

My first instinct was that I had the wrong model, so I switched to a faster one. It felt almost identical.

What actually helped was leaving the model alone and dropping the reasoning effort by one step.

If you only take one thing from this page, take the order of checks: the effort level, where that setting actually applies, and slowness that has nothing to do with reasoning at all. Walk through those three and you usually have your answer before touching the model. Further down there is a small shell function for keeping score. The version I published in August had a hole that broke it on the default macOS shell, so I re-tested and replaced it in November.

Reasoning effort changes how much thinking happens, not how good the answer is

Antigravity lets you set reasoning effort per model. In the editor it sits next to the model picker; in the CLI you change it with the /effort slash command. Sitting there in the settings panel, it reads like a quality dial — turn it up and the answers get smarter.

What it actually controls is how much deliberation happens before you get an answer.

The names of the levels, and which models offer them, change between versions. I write Low / Medium / High here, but for the exact list as of November 2026, please check the official Choose a model page and the /effort command page against the version you have installed.

On work that genuinely needs deliberation, that time is an investment. On work that does not, it is waiting time and credits draining away.

My one-line rewording was firmly in the second category. The agent was dutifully re-verifying its surroundings for a change that had exactly one correct outcome. Nothing was broken except the setting.

Three misreadings I see most often

The first is "High makes the answer correct." Effort buys more deliberation, not better premises. If the premise you handed over is wrong, you get a carefully reasoned answer built on that wrong premise.

The second is "slow means the model is heavy." That was me at the start of this page. A light model at a high effort level still makes you wait, and if the model you switch to is also set high, nothing changes.

The third is "set it once and it applies everywhere." Effort is stored per model, and the editor and the CLI each keep their own value. Changing it in one place does not mean it reached the work that is running right now. The triage table below goes through this.

Assign the three levels by the shape of the task

I do not sort tasks by difficulty. Difficulty collapses into "did this feel hard to me," which moves around depending on the day. I sort by whether the answer is uniquely determined.

Example taskLevel that fitsWhy
Copy and constant replacement, formatting, reordering importsLowThere is one correct outcome and nothing to weigh
Small edits to an existing function, adding tests, fixing a bug you already understandMediumSeveral options exist, but the scope is closed
Design forks, bugs you cannot reproduce, migrations spanning many filesHighThe work starts by building premises, and the deliberation shapes the result

One more thing this table encodes: the first row is where most day-to-day requests actually land. Renaming a variable across a file, tidying an import block, replacing a string in three places — that is the bulk of what I hand off in a normal afternoon. If your default sits at High, every one of those small errands carries deliberation it will never use.

The row people hesitate over is always the middle one. The test is whether you can honestly say you already understand the bug. If you cannot, it belongs in the third row.

Compare it with the other levers first

When something feels slow, or the result falls short, effort is not the only thing you can touch. These are the four levers I actually reach for, side by side.

LeverHelps whenDoes not help whenHow easy to undo
Lower the effortYou are waiting on a task with one correct outcomeThe task starts by building premises; rework goes upBack to normal on the next request
Raise the effortThe scope is right but shallow deliberation keeps getting rejectedThe request is ambiguous; it gets elaborated as-isEasy to forget, so later small errands stay heavy
Switch modelsContext size or strengths do not fit the workThe cause is the effort level or slowness outside reasoningIn the CLI, switching can change your default too
Rewrite the requestRejections have a different reason each time; scope is not landingIt is purely a waiting-time problemCheapest of the four, no side effects

My rule in one line: if you are waiting, lower the level; if you are missing, fix the request; raise the level only when you still miss after that. Switching models comes after all three.

I wrote up how a CLI model switch can quietly change your saved default in the one small catch with /model switching.

Start at Medium and look for a reason to move

Pinning everything to High means giving up your own headroom before you start. I keep Medium as the default and move only when I find a reason.

As an indie developer, every minute of waiting comes straight out of my own working time. With no colleague reviewing alongside me, whether I can place the next decision the moment my hands are free is what sets the pace of the day.

The clearest reason to go down is that the waiting is breaking the rhythm of the work. If the request fits in a single sentence, Low is usually enough.

The reason to go up is two rejected attempts in a row. The first miss might be my writing. Two in a row suggests the deliberation, not my phrasing, is what ran short.

Even then, I try rewriting the instruction first. Raising the effort does not make an ambiguous request unambiguous.

Triage table: when changing the level makes no difference

If you lower the level and nothing changes, the cause is somewhere else. These are in the order I check them.

SymptomLikely causeHow to checkFix
You lowered it, but the wait is the sameYou switched models mid-session and another model's value took overCompare the model currently selected with that model's effort levelLower the level on the model you are actually using
Light in the editor, heavy only for work launched from a terminalThe editor and the CLI keep separate settingsOpen /effort in the CLI and look at the current valueSet the level on the CLI side; if that gets tedious, make routing automatic
Heavy right after opening, regardless of levelThe workspace is still indexing, or you opened a conversation with a huge output in itSend the same request in a fresh conversation and compareLet indexing finish; split long conversations
In the log, High failures cluster on one taskThe request or the context is short, not the effortRe-read that request and check it states scope and a done conditionAdd one sentence each for scope and done condition before raising the level

For the editor-versus-CLI row, I logged how routing by task class played out in six weeks of /effort routing. For slowness outside reasoning, the order in what to check when Antigravity feels slow separates those cases.

If none of the rows explains it, stop guessing and make the comparison deliberate. Pick one small, repeatable errand — replacing a fixed string, say — and run it twice in a row, changing only the level between the two runs. Two back-to-back runs of the same request tell you more than a week of impressions gathered across different tasks, because everything else is held still.

Correct your judgment with records, not memory

The feeling that "Medium was enough for this one" is completely gone a week later. So I wrap the command in a small function and keep the evidence.

Two holes in the first version

The version I published here in August was written in bash and only ever tested in bash. When I sourced it from zsh in November, it stopped before recording a single row. zsh is the default shell on macOS, so plenty of readers likely hit the same wall.

HoleWhat happened (reproduced locally)Cause
Broken under zshIt prints agy_run:4: read-only variable: status and stops before the wrapped command runsIn zsh, status is a read-only variable holding the last exit code, so local status cannot be declared
A tab in the label shifts the columnsLogging a label that contains a tab produces six columns; the summary then reads the duration column as the exit code and counts a successful run as "failed 1"The label was written out with the same tab used as the field separator

The first one was serious. Because the function stops partway, you lose not just the log but the wrapped agy call itself. I had tested it only in bash and written "this works" — that was my own gap in checking.

Since then, I source any shell function I publish in the shell my readers are most likely to use before it goes out.

The re-tested version

It now rejects misspelled levels (hgih and friends) at the door, lowercases Medium, and turns tabs and newlines in the label into spaces. The exit-code variable is now rc.

# put this in ~/.zshrc or ~/.bashrc (works in both zsh and bash)
agy_run() {
  if [ "$#" -lt 3 ]; then
    echo "usage: agy_run <low|medium|high> <label> <command...>" >&2
    return 2
  fi
  local effort label t0 t1 rc
  effort=$(printf '%s' "$1" | tr '[:upper:]' '[:lower:]')
  case "$effort" in
    low|medium|high) ;;
    *) echo "agy_run: level must be low, medium, or high (got: $1)" >&2
       return 2 ;;
  esac
  label=$(printf '%s' "$2" | tr '\t\n' '  ')
  shift 2
  t0=$(date +%s)
  "$@"; rc=$?
  t1=$(date +%s)
  printf '%s\t%s\t%s\t%s\t%s\n' \
    "$(date +%Y-%m-%dT%H:%M:%S)" "$effort" "$label" "$((t1 - t0))" "$rc" \
    >> "${AGY_EFFORT_LOG:-$HOME/effort-log.tsv}"
  return "$rc"
}

To use it, wrap the call and add the level plus a label for the task. The example uses -p, which sends a single request non-interactively.

agy_run medium "typo-fix" agy -p "reword the settings screen copy"

The medium here is a label for the log. The function does not change the effort level for you; set the real level with /effort in the CLI first. If the label and the actual setting drift apart, the log stops meaning anything, so the one rule I keep is: when I change the level, I write the same word in the label.

One more caution. There is a report on the official repository, issue #76, that agy -p output goes missing when stdout is a pipe or a redirect. The function itself leaves output alone, but if you capture it from the outside, as in agy_run ... | tee, please check that it behaves on your version.

Looking at it by level

After a week or so, summarize by level. I also list the labels of tasks that failed at High.

awk -F'\t' '
  NF != 5 { bad++; next }
  { n[$2]++; s[$2] += $4
    if ($5 != 0) { f[$2]++; if ($2 == "high") hf[$3]++ } }
  END {
    split("low medium high", order, " ")
    for (i = 1; i <= 3; i++) { k = order[i]; if (!(k in n)) continue
      printf "%-7s %3d runs  avg %5.1fs  failed %d\n", k, n[k], s[k] / n[k], f[k] + 0 }
    for (l in hf) printf "  failed at high: %s x%d\n", l, hf[l]
    if (bad) printf "  rows with the wrong column count: %d (skipped)\n", bad
  }' "${AGY_EFFORT_LOG:-$HOME/effort-log.tsv}"

I checked it on Ubuntu 24.04, sourcing it from both zsh 5.9 and bash 5.2. With sleep and false standing in for agy, I ran five cases — a success with a tab in the label and the level written as Low, another success, a failure, a misspelled level, and missing arguments — then appended one malformed row like the old version used to write. The summary came out like this.

low       1 runs  avg   1.0s  failed 0
medium    1 runs  avg   2.0s  failed 0
high      1 runs  avg   0.0s  failed 1
  failed at high: migrate-billing x1
  rows with the wrong column count: 1 (skipped)

The misspelled level and the missing arguments are not logged and return exit code 2. The tabbed label was turned into a space and counted as a success.

The number to care about is not the average duration. It is the rows that failed while set to High. Those are the tasks reasoning effort did not solve, which means the next thing to fix is the instruction or the context you handed over.

Once I started keeping this log, I noticed I had been translating "this looks hard" into High without thinking. In practice, the ambiguity of my request mattered far more than the level did.

The next place you will get stuck

After two weeks of logging, I suspect the next annoyance will be typing the label by hand every time. That is exactly where I stalled too. A small router that picks the level from the wording of the request, plus six weeks of numbers, lives in the /effort routing write-up mentioned above.

The other one is cost. Raising effort increases not only the wait but also the tokens spent on thinking. If you run Gemini 4 Carbon day to day, I walked through estimating the cost of a single session from thinking level and cache rate in estimating a Carbon session's cost. Doing that arithmetic once before you raise the level gives the decision some weight.

What I run today

Medium is my default. I keep exactly three cases reserved for Low: copy replacement, formatting, and reordering dependencies. High comes out a few times a week, for migrations or bugs I cannot yet reproduce.

I am not claiming this split is correct. It should depend on the kind of code you work with, and I keep nudging mine as the log tells me things.

If you try one thing, recall the last task you handed to an agent and find its row in the first table. If a first-row task was running at High, that is the easiest waiting time you will ever get back.

Thank you for reading.

Share

Thank You for Reading

Antigravity Lab is ad-free, supported entirely by members like you. We publish practical guides daily with implementation code, benchmarks, and production-ready patterns. If you've found it useful, we'd love to have you on board.

  • ✦Copy-paste ready implementation code
  • ✦New advanced guides published daily
  • ✦$5/mo or $10 for lifetime access
View Membership →

If you found this article helpful, a small tip ($1.50) would mean a lot to us. Your support helps keep this site ad-free and covers server and hosting costs.

Related Articles

⚙ AI Tools2026-11-02
Attachments Went Up to 20MB. Here Is What I Hand the Agent, and What I Keep Back
Antigravity app v2.28.0 lets you attach any file up to 20MB. Three questions to decide what to hand over as-is, what to trim first, and what to keep out entirely.
⚙ AI Tools2026-10-24
When All You See Is "An Error Occurred": Suspect a 429 First, and Check in This Order
How to tell whether a generic Antigravity error is really a usage limit (HTTP 429) or a network or sign-in problem. Open the debug details, wait the right way, and skip the retry spiral. A sequence a non-engineer can follow.
⚙ AI Tools2026-10-23
Antigravity, AI Studio, or Stitch First? Pick by the Shape of What You're Building
A way for non-coders to decide which Google tool to open first: split Antigravity, AI Studio, and Stitch by the shape of what you want to make — a screen, a small app, or a tool for your own files.