DEV Community

Cover image for The Claude API multi-agent loop, without the framework
João Miguel
João Miguel

Posted on

The Claude API multi-agent loop, without the framework

Most Claude API tutorials show a single tool call. Most frameworks hide the loop behind abstractions you can't read. This post shows the loop directly — what actually happens between "Claude requests a tool" and "Claude finishes."

The loop in plain English

When you give Claude tools, a single API call isn't always enough. Claude decides whether to call a tool, you execute it, then you send the result back. Claude might call another tool, or it might answer. That cycle is the agent loop.

user message
    ↓
Claude responds
    ↓
stop_reason == "tool_use"?  →  execute tools  →  back to Claude
    ↓
stop_reason == "end_turn"
    ↓
return final text
Enter fullscreen mode Exit fullscreen mode

The implementation

The entire loop is in agent.py — about 80 lines.

def run_agent(
    system: str,
    user_message: str,
    tools: list[dict],
    tool_handlers: dict[str, Callable],
    max_rounds: int = 10,
) -> str:
    messages = [{"role": "user", "content": user_message}]

    for round_num in range(max_rounds):
        response = client.messages.create(
            model=MODEL,
            max_tokens=4096,
            system=system,
            tools=tools,
            messages=messages,
        )

        messages.append({"role": "assistant", "content": response.content})

        if response.stop_reason == "end_turn":
            return _extract_text(response.content)

        if response.stop_reason == "tool_use":
            tool_results = []
            for block in response.content:
                if block.type == "tool_use":
                    result = _call_tool(block, tool_handlers)
                    tool_results.append({
                        "type": "tool_result",
                        "tool_use_id": block.id,
                        "content": json.dumps(result),
                    })
            messages.append({"role": "user", "content": tool_results})
            continue

        break

    return _extract_text(response.content)
Enter fullscreen mode Exit fullscreen mode

That's the core. The rest of the file is _call_tool (dispatch to your Python function) and _extract_text (pull text blocks from the response).

Using it

Define tools in Anthropic's schema format:

TOOLS = [
    {
        "name": "read_file",
        "description": "Read the contents of a file.",
        "input_schema": {
            "type": "object",
            "properties": {
                "path": {"type": "string", "description": "File path to read"},
            },
            "required": ["path"],
        },
    },
]
Enter fullscreen mode Exit fullscreen mode

Define handlers as plain Python functions:

def read_file(path: str) -> dict:
    return {"content": Path(path).read_text()}

tool_handlers = {"read_file": read_file}
Enter fullscreen mode Exit fullscreen mode

Run the agent:

result = run_agent(
    system="You are a helpful assistant.",
    user_message="What's in README.md?",
    tools=TOOLS,
    tool_handlers=tool_handlers,
)
Enter fullscreen mode Exit fullscreen mode

Why not use a framework?

Frameworks aren't wrong. But when something breaks in production — and it will — you want to know exactly what message went to Claude and exactly what came back. Abstractions make that harder.

This implementation is meant to be read, modified, and owned. The loop is visible. You can add logging, approval gates, retry logic, or conditional execution exactly where you need it.

Two working examples

The repo includes:

  • example_research.py — an agent with search and read_page tools (swap in your real implementations)
  • example_code.py — an agent with read_file, write_file, and list_files tools

Both run end-to-end with real Claude API calls.

Install

pip install anthropic
export ANTHROPIC_API_KEY=sk-ant-...
python example_code.py
Enter fullscreen mode Exit fullscreen mode

The repo: github.com/espanhol6/claude-multiagent-loop

This pattern is what I used as the foundation for Cluster OS Jarvis — a production multi-agent framework with SSE streaming, up to 6 tool-calling rounds, and cron-scheduled autonomous agents. The loop here is the simplified, standalone version.

If you're building something with Claude and want to understand what's happening under the hood before adding abstractions, this is a good starting point.


João Daniel Espanhol Miguel — AI engineer, Lisbon. Also wrote about debugging a silent native crash in ctranslate2 + WinRT.

Top comments (0)