Skip to content
agentFast
Build

Shaping behaviour

Prompts, budgets, and deciding what needs a human.

In short: three dials control how your agent acts. What you tell it to do (the prompt), how far it's allowed to go (budgets), and what it must ask permission for (risk). Most of the behaviour you want comes from these, not from code.

The prompt

Your agent's instructions live in the template you scaffolded — for the support agent, in templates/support/langgraph/prompt.py. It's a plain string. Edit it.

What belongs in it:

SYSTEM_PROMPT = """You are a support agent for Northwind, a B2B logistics company.

Always search the knowledge base before answering a policy question. Never
guess at a policy — if the knowledge base doesn't cover it, say so and escalate.

Refunds under $200 for damaged goods are standard. Anything larger, or anything
where the customer has already been refunded this quarter, needs a human.

Be brief. Customers are operations staff who are busy — three sentences beats
three paragraphs.
"""
TipWrite policy, not personality

Most prompt effort goes into tone and produces very little. The lines that change outcomes are the ones that tell the agent when to use which tool and when to stop and ask. "Be friendly" changes almost nothing; "always search the KB before answering a policy question" changes everything.

Behaviour belongs in the prompt, not in if statements. agentFast's templates deliberately contain no intent-matching regexes — routing a request by keyword is brittle in a way that instructions aren't, and it's the first thing that breaks when a customer phrases something unexpectedly.

Budgets

Budgets are what stop a stuck agent becoming an invoice. Checked before every model call:

budget:
  max_iterations: 50        # how many times it can think
  max_tokens: 150000        # total input + output for the run
  max_duration_ms: 300000   # wall clock — five minutes

Rough guidance:

| Kind of agent | iterations | tokens | duration | |---|---|---|---| | Support ticket | 20–50 | 150k | 5 min | | Research task | 60–100 | 400k | 20 min | | Code change | 80–150 | 500k | 30 min |

Hitting a limit ends the run as error with a readable reason — not a silent loop, and not a half-finished job that looks finished.

Letting the agent size itself

Rather than picking one budget for every request, the pre-flight classifier reads the incoming task and adjusts:

complexity_classifier: heuristic   # off | heuristic | llm

heuristic is free and works on task shape. llm uses a cheap model call for a better read, worth it when your workload varies a lot. off pins every run to the configured budget.

The decision shows up in the trace as a planning step, so you can see what it decided and why.

What needs a human

The most consequential configuration in the file:

tool_overrides:
  refund_request:
    risk: high
    hitl_mode: suspend      # stop and wait
  send_customer_email:
    risk: high
    hitl_mode: defer        # queue it, keep working
  order_lookup:
    risk: low               # just do it

A useful test for each tool: if the agent did this a hundred times by mistake overnight, would you be fine in the morning? If yes, low risk. If you'd be on the phone to your bank, high.

suspend when the agent can't sensibly continue without an answer. defer when it can get on with something else and this can happen later.

Choosing a model

model: claude-sonnet-4-5

Any LangChain model id works — openai:gpt-4o, anthropic:claude-haiku-4-5, and so on. A cheaper model on a well-instructed agent with good tools usually beats an expensive one with vague instructions, so try tightening the prompt before reaching for a bigger model.

scripted-support runs the whole harness deterministically with no API key — useful in tests and in CI.

Check you didn't break it

Every behaviour change should be followed by:

agentfast eval

This is the point of evals. Prompt edits are the single most common way an agent silently regresses — you improve one case and quietly break three others, and nothing errors. The eval gate is what turns that into a failed build instead of a support ticket.