Build the Agent Loop Yourself, Once
Tool use and the agent loop in eleven lines of Python, the four changes that harden it, and when one loop stops being enough.
Key takeaways
- Every agent product runs the same loop: the model asks for a tool, your code runs it, and the result goes back.
- A tool description is a prompt, so it should say when to call the tool; a tool result is context, so keep it short.
- Return all tool results from one turn in a single user message, one tool_result per tool_use.
- Cap the loop, return errors as results, gate destructive tools behind a person and log every call.
- Forced tool_choice returns 400 on Opus 5.5, Sonnet 5.5 and Fable 5.1; use auto, name the tool and check a call was made.
- Use more than one agent only when a single loop hits a wall of context, parallelism or conflicting roles.
Contents
FAQ
Should I write my own loop or use the SDK's tool runner?
Write it once to understand it, then use the SDK tool runner, which is the same loop with hooks for approval, error handling and an iteration cap.
What happens when a tool fails?
Return the error as a tool result marked is_error. The model reads it and usually recovers; crashing the loop teaches it nothing.
Sources
Only you and the owner see your answer and rating. Send private feedback
Enjoyed it? Support fx-blog with a coffee.
Related posts
Your LLM Proxy Retries the Wrong Errors
What an LLM proxy has to decide on every failure: limits are per organization, which statuses to retry, why nothing is retried after the first streamed byte, and the headroom headers the API already sends.
Your LLM Demo Works. What Every Call Needs in Production
One timestamp in the system prompt and every call misses the cache. Pricing each call, making failures loud, logging per feature, and treating outside text as untrusted.
RAG That Works: Why Retrieval Matters More Than the Prompt
Most bad RAG answers are retrieval problems. How RAG works end to end, the three parts of the grounded prompt, how to measure retrieval alone, and when long context is enough.
Comments
Oldest firstNo comments yet. Start the conversation.
Part 1 left the test-case generator writing one case per call. It could not run a test, so it could not check its own work against the orders suite. It needs a tool, and a loop around the tool.
Eleven lines of Python. That is the engine under every agent product you have used. Once you have typed them yourself, an agent that hangs is no longer a mystery: you can say which message it is waiting for.
This series, in six parts:
The model asks; your code acts
You describe a tool with a name, a description and a schema. The model never runs anything. It answers "call
run_test_suitewith this path", your code runs it, and the result goes back in the next message.Here is the generator's first tool, in the SDK's decorator form:
Look at the docstring. It says what the tool does, not when to call it, and the model decides from that text alone. The description is a prompt, so add the when. The result is context too: the whole test log makes the next decision worse than a short list of what failed.
The loop, by hand
This is the guide's loop, unchanged:
Read it for what it leaves out.
while Truenever stops on its own. One exception inrun_toolkills the whole run. Nobody approves anything.Four changes before anyone else runs it
tool_resultwithis_error: true. The model reads the error and recovers; a crash teaches it nothing.I'd write the loop once to understand it, then switch to the SDK's tool runner,
client.beta.messages.tool_runner: the same loop, with hooks for approval, errors and an iteration cap that you no longer maintain.When one loop is not enough
Reach for more agents only when one loop hits a real wall: context size, needed parallelism, or roles that conflict.
Agents share files and structured reports, never raw conversation histories, and every delegated task carries its own full brief. In the guide's words: if a plain
forloop over an API call does the job, that's not a multi-agent system, and that's a compliment.Try it
The test-case generator now runs the orders suite and reads its own failures. Then you edit its system prompt, and the next run's cases look better. Better, or only different? Part 3, Did Your Prompt Change Help?, answers that with a number.
Read this with your AI