# How the Full Coding Agent Loop Works in chapter5/coding-agent/

> Explore the full Coding Agent loop implementation in chapter5/coding-agent/. Discover its fetch-process-execute-feedback cycle for LLM queries and tool execution.

- Repository: [Bojie Li/ai-agent-book](https://github.com/bojieli/ai-agent-book)
- Tags: deep-dive
- Published: 2026-08-23

---

**The Coding Agent implements a streaming fetch-process-execute-feedback cycle that repeatedly queries an LLM with dynamic system hints, executes requested tools via a lightweight registry, and appends results to the conversation history until the task completes or reaches the maximum iteration limit.**

The `chapter5/coding-agent/` directory in the `bojieli/ai-agent-book` repository contains a self-contained implementation of an autonomous coding agent. This module demonstrates how to orchestrate a full Coding Agent loop that leverages streaming LLM responses and a pure-Python tool registry to perform software engineering tasks iteratively without external dependencies beyond the LLM client libraries.

## Architecture of the Coding Agent Loop

The core iteration cycle lives in `CodingAgent.run` within [`agent.py`](https://github.com/bojieli/ai-agent-book/blob/main/agent.py). The implementation follows a seven-phase architecture that maintains state across turns through the `SystemState` class while delegating tool execution to the `ToolRegistry`.

### Phase 1: Initialization and Context Setup

The agent initializes its state in `CodingAgent.__init__` (lines 24-45). This method instantiates `SystemState` to track the working directory and tool statistics, loads the declarative tool schemas from [`tools.json`](https://github.com/bojieli/ai-agent-book/blob/main/tools.json) via `_load_tools`, and loads the system prompt template via `_load_system_prompt`. The initialization also configures the appropriate LLM client—either Anthropic or OpenAI/OpenRouter—based on the provider parameter.

```python

# agent.py lines 24-45

def __init__(self, api_key, model, provider="anthropic"):
    self.system_state = SystemState()
    self.tool_registry = ToolRegistry()
    self._load_tools("tools.json")
    self._load_system_prompt("system_prompt.md")
    # Provider selection logic at lines 47-61

```

### Phase 2: Message Preparation and System Hint Injection

When `agent.run(user_message)` is invoked, the loop timestamps the input and appends it to the conversation history (lines 33-38). Before each LLM call, the agent generates a dynamic **system hint** by calling `self.system_state.get_system_hint()` (lines 48-53). This hint includes the current working directory, OS information, Python version, tool-call statistics, and TODO list, providing the LLM with fresh environmental context every iteration.

```python

# agent.py lines 33-38, 48-53

self.messages.append({
    "role": "user",
    "content": f"[{timestamp}] {user_message}"
})

# Inside the loop:

system_hint = self.system_state.get_system_hint()
self.messages.append({"role": "user", "content": system_hint})

```

### Phase 3: Provider-Specific Streaming Implementation

The loop selects the appropriate streaming handler based on the provider type. For Anthropic models, `_run_anthropic_iteration` processes `content_block_delta` events (lines 95-104), while OpenAI models use `_run_openai_iteration` to handle `tool_calls` in streaming deltas (lines 31-38 and 40-55). Both methods yield `text_delta` events for streaming text output and collect tool invocations for subsequent execution.

For Anthropic (lines 9-16):
- Emits `text_delta` when `type == "content_block_delta"`
- Captures `tool_use` blocks when `type == "content_block_start"`

For OpenAI (lines 31-55):
- Aggregates `tool_calls` arrays from response deltas
- Handles function name and argument accumulation across chunks

### Phase 4: Tool Discovery and Execution

After collecting all tool calls from the streaming response, the loop enters the execution phase (lines 68-84). For each `tool_use` block discovered, the agent retrieves the implementation via `self.tool_registry.get_tool(name)` and invokes `tool.execute(**arguments)`. The tool registry maps names to concrete implementations such as `Read`, `Write`, `Grep`, and `Bash` defined in the codebase.

```python

# agent.py lines 68-84

for tool_call in tool_calls:
    tool = self.tool_registry.get_tool(tool_call["name"])
    result = tool.execute(**tool_call["arguments"])
    # Results prepared for message history

```

### Phase 5: Result Aggregation and History Management

Tool execution results are wrapped as `tool_result` messages and appended to `self.messages` (Anthropic: lines 92-98, OpenAI: lines 13-15). This closes the feedback loop, providing the LLM with concrete execution outcomes in the next iteration. The `SystemState` updates internal counters and the TODO list during this phase through subsequent calls to `get_system_hint()`.

### Phase 6: Termination Checking

The loop evaluates termination conditions at lines 53-57. If the LLM response contains no tool calls, the agent yields a `done` event and exits. Otherwise, the cycle repeats up to `max_iterations`, preventing infinite loops from malformed or overly aggressive tool usage.

## Key Implementation Files

The Coding Agent loop spans several tightly-coupled components:

- **[`agent.py`](https://github.com/bojieli/ai-agent-book/blob/main/agent.py)**: Contains `CodingAgent.run`, provider abstraction, and streaming handlers (lines 55-63 for provider selection, lines 95-122 for Anthropic streaming).
- **[`system_state.py`](https://github.com/bojieli/ai-agent-book/blob/main/system_state.py)**: Implements `SystemState.get_system_hint()` (lines 26-52) which generates the contextual metadata injected each iteration.
- **[`tool_registry.py`](https://github.com/bojieli/ai-agent-book/blob/main/tool_registry.py)**: Provides `ToolRegistry.get_tool()` for runtime tool resolution.
- **[`tools.json`](https://github.com/bojieli/ai-agent-book/blob/main/tools.json)**: Declarative JSON schema defining available tools, their descriptions, and input parameters for the LLM.
- **[`main.py`](https://github.com/bojieli/ai-agent-book/blob/main/main.py)**: Thin CLI wrapper that instantiates `CodingAgent` and streams events to stdout.

## Practical Usage Examples

### Running the Interactive CLI

The default entry point provides an interactive session:

```bash
python main.py

```

This script loads environment variables, creates a `CodingAgent` instance, and enters a read-eval-print loop that processes natural language requests through the full iteration cycle.

### One-Shot Programmatic Execution

Integrate the Coding Agent into existing Python workflows by consuming the event iterator directly:

```python
from chapter5.coding-agent.agent import CodingAgent
import os

agent = CodingAgent(
    api_key=os.getenv("ANTHROPIC_API_KEY"),
    model="claude-sonnet-5",
    provider="anthropic"
)

for event in agent.run("List all Python files in the current directory"):
    if event["type"] == "text_delta":
        print(event["delta"], end="", flush=True)
    elif event["type"] == "tool_call":
        print(f"\n[Calling: {event['tool']}]")
    elif event["type"] == "done":
        print("\n\n✅ Completed")

```

### Jupyter Notebook Integration

For exploratory development, iterate over the agent events to display incremental progress:

```python
agent = CodingAgent(api_key="key", provider="openai", model="gpt-4")

for ev in agent.run("Search for TODO comments"):
    if ev["type"] == "text_delta":
        display(ev["delta"])  # IPython.display

    elif ev["type"] == "tool_execution_complete":
        print(f"Result: {ev['result']}")

```

## Summary

- The **Coding Agent loop** is implemented as a Python generator in [`agent.py`](https://github.com/bojieli/ai-agent-book/blob/main/agent.py) that yields streaming events (`text_delta`, `tool_call`, `done`).
- **Dynamic context** is injected via `SystemState.get_system_hint()` before every LLM call, preventing stale environmental assumptions.
- **Provider abstraction** supports both Anthropic and OpenAI streaming APIs with unified tool execution semantics.
- **Tool execution** occurs through `ToolRegistry`, which maps LLM-requested function names to pure-Python implementations loaded from [`tools.json`](https://github.com/bojieli/ai-agent-book/blob/main/tools.json) schemas.
- **Termination safety** is enforced through iteration counting and detection of null tool-call responses.

## Frequently Asked Questions

### How does the Coding Agent prevent infinite tool loops?

The loop enforces a `max_iterations` parameter checked at lines 53-57 in [`agent.py`](https://github.com/bojieli/ai-agent-book/blob/main/agent.py). Additionally, `SystemState` tracks tool-call statistics across iterations, injecting this data into the system hint to inform the LLM of its execution history. The agent only continues if the LLM explicitly requests additional tools; otherwise, it yields a `done` event and terminates.

### What information does the system hint contain?

According to [`system_state.py`](https://github.com/bojieli/ai-agent-book/blob/main/system_state.py) (lines 26-52), the system hint generated by `get_system_hint()` includes the current working directory, operating system details, Python version, accumulated TODO list, and counters for previous tool invocations. This contextual refresh prevents the agent from operating on stale assumptions about the filesystem or execution environment.

### How does the agent handle different LLM providers?

The `CodingAgent.__init__` method (lines 47-61) instantiates provider-specific clients. The `run` method then dispatches to `_run_anthropic_iteration` or `_run_openai_iteration` based on the provider type. Anthropic responses parse `content_block_delta` events (lines 95-104), while OpenAI responses handle `tool_calls` arrays in streaming deltas (lines 31-55), normalizing both into the internal tool execution format.

### Can the Coding Agent execute tools without user intervention?

Yes. Once initialized with an API key, the `agent.run()` method operates autonomously through the full loop: receiving the user request, streaming LLM responses, executing requested tools, and feeding results back. The CLI wrapper in [`main.py`](https://github.com/bojieli/ai-agent-book/blob/main/main.py) demonstrates unsupervised operation for single-turn tasks, while the event iterator allows programmatic monitoring of each iteration's progress.