JA EN
LearnAgents
·★ MEMBER·PAPER·8 min read

Build Your Own Agent Loop — The Minimal Shape of Tool Calling

At the center of every AI agent is a single while loop. We build it from scratch without a framework — the shape of JSON function calls, what ReAct actually left behind, and the stopping conditions where nearly every incident originates.

ModalitytextTaskagent

ReAct: Synergizing Reasoning and Acting in Language Models

Primary source — what this article is built on

undefined2022-10-06undefined2026-08-273y 11mo later

ReAct: Synergizing Reasoning and Acting in Language ModelsShunyu Yao, Jeffrey Zhao, Dian Yu et al. · 2022-10-06 · v3arXiv:2210.03629Paper page·PDF
undefined

While large language models (LLMs) have demonstrated impressive capabilities across tasks in language understanding and interactive decision making, their abilities for reasoning (e.g. chain-of-thought prompting) and acting (e.g. action plan generation) have primarily been studied as separate topics. In this paper, we explore the use of LLMs to generate both reasoning traces and task-specific actions in an interleaved manner, allowing for greater synergy between the two: reasoning traces help the model induce, track, and update action plans as well as handle exceptions, while actions allow it to interface with external sources, such as knowledge bases or environments, to gather additional information. We apply our approach, named ReAct, to a diverse set of language and decision making tasks and demonstrate its effectiveness over state-of-the-art baselines, as well as improved human interpretability and trustworthiness over methods without reasoning or acting components. Concretely, on question answering (HotpotQA) and fact verification (Fever), ReAct overcomes issues of hallucination and error propagation prevalent in chain-of-thought reasoning by interacting with a simple Wikipedia API, and generates human-like task-solving trajectories that are more interpretable than baselines without reasoning traces. On two interactive decision making benchmarks (ALFWorld and WebShop), ReAct outperforms imitation and reinforcement learning methods by an absolute success rate of 34% and 10% respectively, while being prompted with only one or two in-context examples. Project site with code: https://react-lm.github.io


The assistant on the phone

Picture asking a very knowledgeable assistant for help — over the phone. They have no hands and no eyes in your room; all they get is your voice. So they say "could you read out what's in that folder?", you read it out, and based on what they hear they tell you what to do next. A few rounds of that, and the job is done.

That is what we currently call an AI agent. A language model is a function from strings to strings. It cannot open a file or send an email. Something outside the model always does the actual work. The core value an agent framework provides is a dozen lines that automate this back-and-forth.

This article builds that loop without a framework. The goal isn't working code you can copy — it's being able to say which line, if you delete it, breaks what.

It's a while loop

Here's the conclusion up front. This is the whole thing:

messages = [{"role": "user", "content": task}]
while True:
    reply = model(messages, tools=TOOLS)     # 1. let it think
    messages.append(assistant_turn(reply))   # 2. append its turn
    calls = tool_calls_in(reply)
    if not calls:                            # 3. no tool = done
        return text_of(reply)
    results = [run(c) for c in calls]        # 4. we do the work
    messages.append(user_turn(results))      # 5. append the results

The word "agent" deserves to lose some of its magic right here. The model is not persistently pursuing anything. On every turn it reads the whole conversation from a cold start, emits exactly one next move, and stops. The thing with continuity is the messages array; nothing carries over on the model's side.

That asymmetry decides most of the design questions that follow. Because the history is the only memory, anything you didn't write into it doesn't exist for the model, every extra step inflates the input, and a failure will repeat unless you write down why it failed.

There are only four parts: the tool list, the model call, the dispatcher (the table mapping tool names to real functions), and the stopping conditions.

How to describe a tool

Handing a model a tool is, in practice, writing a function signature in JSON.

{
  "name": "read_file",
  "description": "Read one text file in the repository. Paths are relative to the repo root. Not for binary files. If you only need to check existence, use list_dir instead.",
  "input_schema": {
    "type": "object",
    "properties": {
      "path": { "type": "string", "description": "e.g. src/main.py" }
    },
    "required": ["path"]
  }
}

Nearly everyone makes the same mistake once: writing description for a human. The reader here is the model, not a colleague. A clever summary matters far less than "when not to use this", a concrete example of the argument format, and what comes back on failure. It is not unusual for one added sentence to change behavior outright.

Whether the returned JSON is syntactically intact is largely solvable with constrained decoding — zeroing out tokens the grammar forbids before they're sampled. But valid syntax and valid arguments are different things: a nonexistent path arrives as a perfectly well-typed string all the time. That boundary is covered in Structured Output and Constrained Decoding.

Temperature — the parameter governing sampling spread — is usually set lower for an agent loop than for prose. A wobble in the spelling of a tool name means calling a function that doesn't exist, and if the model decides differently every time from the same state, debugging stops working.

FIG 1Lower the temperature and probability collapses onto one option; raise it and the distribution flattens. In an agent, tool selection *is* this distribution — at high temperature the odds of picking the second-best tool stop being negligible

That does not make temperature 0 automatically safe. Identical history yields identical output, so once the loop enters a failing state it cannot climb out on its own. Keep it low (roughly 0 to 0.3) and handle stuck states with stopping conditions instead — that's the division of labor in practice.

Now the pseudocode in the shape of a real API. Below is Claude's Messages API; other vendors' function calling differs in naming, not in structure.

What's behind this

§

Members-only from here

371 walkthroughs, 26 textbook chapters, 48 student units and 6 close readings — all included for $4.99/mo, with three new explainers every day. Cancel any time; access runs to the end of the period.

Already a member? Sign in to keep reading

References

  1. Shunyu Yao, Jeffrey Zhao, Dian Yu, Nan Du et al.. (2022-10-06) ReAct: Synergizing Reasoning and Acting in Language Models. arXiv:2210.03629Paper page·PDF

This article is written from the source paper above. Where they differ, the original is authoritative.

Comments

Sign in to comment