Skip to main content

Setting up adaptive effort

Spending more only when it matters​

Effort is the setting that decides how much model work a turn gets: which models run, how deeply they reason, how carefully your rules are checked, and whether replies are reviewed. Most customer turns are routine and should run cheap and fast. A few, such as a disputed charge or a legal threat, deserve more care. Adaptive effort lets the conversation itself signal when a turn matters, so your agent spends more only on those turns.

What you'll learn
  • The five effort levels and the agent's baseline
  • How an observation raises effort for one turn
  • What each level changes: matching, reviews, compaction, and models
  • How your own tools can read the turn's effort
Talk this page through with your coding agent

The trouble with serious turns is that you cannot know in advance which ones they will be. Adaptive effort solves this by raising the level from inside the conversation, the moment a situation you described shows up.

The five levels​

Before we look at how effort adapts, here is what it is.

Effort is one of five levels: MIN, LOW, MEDIUM, HIGH, or MAX. It affects which models run, how deeply they reason, how carefully Parlant matches your rules, and how many chances a review gets.

Your agent sets the baseline that every turn starts from. MEDIUM is the default:

import parlant.sdk as p

agent = await server.create_agent(
id="support",
name="Support Agent",
instructions="Help customers with their orders.",
effort=p.Effort.MEDIUM,
)

Note that rules can raise the level for a single turn, but never lower it. The baseline is a floor.

Raising effort from the conversation​

Now that we know what the levels are, here is how a conversation moves between them.

An order-status question and a disputed charge do not need the same amount of model work. To raise effort only for the second, you first describe the situation that needs more care. Parlant represents that situation with an observation.

Recall that Parlant has two kinds of rule. A policy gives your agent an instruction for a situation. An observation is a rule with a condition and no instruction. It recognizes a situation so that other things can react to it.

One of those things is effort. Give an observation a trigger_effort, and whenever it matches, Parlant runs the rest of that turn at the higher level:

await agent.create_observation(
condition="the customer disputes a charge or threatens legal action",
trigger_effort=p.Effort.HIGH,
)

How the level is resolved​

Parlant resolves the turn's effort as the highest of your agent's baseline and every matched rule's trigger. Two triggers do not add up beyond the highest. If two HIGH observations match at once, the turn runs at HIGH, not MAX.

The raise takes effect immediately, in the same turn. Tool selection, response generation, and reviews all run at the raised level. So a customer who opens with a legal threat gets the careful treatment from the very first reply.

Note that only rules matched right now count. A rule that matched earlier in the session, but not on this turn, has no effect on this turn's effort.

Policies can trigger effort too

Policies accept trigger_effort as well, but an observation is usually the right home for it: the condition names the stakes, and the policies that apply in that situation stay focused on what to do.

Weight and effort are different​

Weight controls how firmly the agent holds a rule. Effort controls the model work invested in the current turn. Leave weights at their defaults when setting up adaptive effort: trigger_effort raises the turn's level, and model routing can select a stronger reply model at that level.

For the disputed-charge example above, test an ordinary order question, a charge dispute, then another ordinary question in the same session. Check that effort rises for the dispute and returns to the baseline once the conversation moves on.

What effort changes​

Once a turn's level is settled, what does it actually buy you? The table below shows the three effects that matter most.

Two terms first. A review is a check Parlant runs on a proposed tool call or a drafted reply before it goes through; the retry count is how many corrected attempts Parlant allows. Compaction is how Parlant summarizes a long session's history once it grows past a threshold. Higher effort keeps more of it intact.

EffortRule matchingReviewsHistory before compaction
MINSimilarity search only, for every ruleNone, even if requested~24k tokens, low detail
LOWSimilarity search only; with a decision model, high-weight rules get a model verdictRequested reviews run; 2 retries~24k tokens, low detail
MEDIUMHigh-weight rules get a model verdict; with a decision model, medium-weight rules too3 retries~32k tokens, medium detail
HIGHMedium- and high-weight rules get a model verdict5 retries~48k tokens, high detail
MAXSame as high, on the largest models10 retries~64k tokens, high detail

A decision model is a small, fast model that answers yes-or-no questions about many rules in one request, instead of one model call per rule. When one is configured for rule matching, Parlant uses it at MIN, LOW, and MEDIUM effort to check a whole batch of rules at once.

Because that costs one request rather than one per rule, Parlant can afford a real verdict for more rules at those levels, as the table shows.

The default OpenRouter provider ships with a decision model, so a server that uses models={"*": "openrouter"} gets this behavior with no extra setup. OpenAI registers one as well; Anthropic and Gemini do not.

Note that some rules always get the careful model verdict regardless of effort, because a wrong match would cost more. These are rules with tools, rules that trigger reviews, rules with labels, rules in priority relationships, and rules that other rules depend on.

Reasoning, re-judging, and canned responses​

Beyond the table, effort reaches three more places.

First, reasoning depth follows the level when a model route does not set reasoning itself, from minimal and hidden at MIN to deeper at higher levels.

Second, above LOW, a step that follows a tool call re-judges the rules gated on it (that is, rules you have declared should be re-evaluated once that tool has run). At MIN and LOW, the turn keeps the match it started with.

Third, above LOW, Parlant ranks canned responses (the approved example messages your agent imitates) by relevance. At MIN and LOW, it shows every eligible one instead, up to a cap.

Model routing​

Effort also reaches model routing. The server's models mapping decides which model runs each kind of work, and each entry is a route. react is the route that writes the reply.

The built-in providers already pick a reply model by effort. With the default OpenRouter provider, MIN, LOW, and MEDIUM turns write replies with anthropic/claude-haiku-5.5 (at minimal, low, and medium reasoning), HIGH turns use openai/gpt-6.1-sol, and MAX turns use anthropic/claude-sonnet-5.5.

With Anthropic directly, MIN through MEDIUM run on claude-haiku-5-5, HIGH on claude-sonnet-5-5, and MAX on claude-opus-5-5. With OpenAI, every level runs on gpt-6-luna except MAX, which runs on gpt-6.1-sol. So raising effort already changes the model, with no routing of your own.

A route can also name a different model per level. This means you can decide yourself which model a HIGH turn runs on, without any change to your rules:

async with p.Server(
models={
"*": "openrouter",
"react[effort>=high]": p.ModelConfig("anthropic:claude-sonnet-5-5"), # reply generation, on HIGH and MAX turns only
},
) as server:
...
info

If model selection also depends on a customer or tenant, choose the model in code. Use effort-qualified route keys when the effort level alone is enough.

Note that MIN is not "a little less than low." It is the cost floor: Parlant skips every optional piece of work, so use it only when your agent's instructions alone are enough.

Reading the level from your code​

So far, effort has changed what Parlant does. You can also let it change what your own code does.

Inside a running turn, p.Session.current.effort is the resolved level. A tool your coding agent writes can scale its own work with it:

@p.tool
async def search_knowledge_base(query: str) -> p.ToolResult:
limit = 20 if p.Session.current.effort >= p.Effort.HIGH else 5
return p.ToolResult(data=await kb.search(query, limit=limit))

On a routine turn, the tool fetches a few results. On a demanding one, it searches wider, with no change to the policy that calls it.

Where effort reaches​

Effort is consulted in more places than this page shows.

Learn more

Model routing selects models by it, Tool reviews and Message reviews size their retry budget by it, Compacting long sessions picks its threshold by it, and Handling sensitive situations puts all of that together in one worked example.