# How the Agent = LLM + Context + Tools Formula Guides AI Agent Development

> Master AI agent development with the Agent = LLM + Context + Tools formula. Understand how this framework builds modular, traceable, and extensible AI systems for better performance and control.

- Repository: [Bojie Li/ai-agent-book](https://github.com/bojieli/ai-agent-book)
- Tags: tutorial
- Published: 2026-07-25

---

**The *Agent = LLM + Context + Tools* formula structures autonomous agents as three composable components—an LLM reasoning engine, persistent state memory, and executable tool functions—enabling modular, traceable, and extensible AI systems.**

The *Agent = LLM + Context + Tools* formula is the architectural blueprint underlying the `bojieli/ai-agent-book` repository. This decomposition treats an AI agent not as a monolithic black box, but as the tight integration of a reasoning engine, memory mechanism, and action interface. Understanding this formula reveals how modern autonomous systems balance intelligence with interaction according to the source code implementation.

## Deconstructing the Three Core Components

The codebase treats an autonomous **agent** as the combination of three tightly-coupled components that map directly to specific implementation details in [`chapter1/web-search-agent/agent.py`](https://github.com/bojieli/ai-agent-book/blob/main/chapter1/web-search-agent/agent.py).

### The LLM as the Reasoning Engine

The **LLM** provides the reasoning engine that generates thoughts, decides when to act, and produces the final answer. In the repository, the LLM is instantiated through an OpenAI-compatible client with a specific model name.

According to the source code in [`WebSearchAgent.__init__`](https://github.com/bojieli/ai-agent-book/blob/main/chapter1/web-search-agent/agent.py#L94-L100), the LLM backend is configured via the `model` parameter (e.g., `kimi-k3`), allowing any OpenAI-compatible provider (OpenAI, Anthropic, Moonshot, etc.) to be swapped by changing the client configuration.

### Context as the Agent’s Memory

**Context** stores the dialogue history, the ReAct trace, and any intermediate observations so the LLM can "remember" what has been said or done. The context is updated after each step and fed back into the next prompt.

The implementation uses [`WebSearchAgent.conversation_history`](https://github.com/bojieli/ai-agent-book/blob/main/chapter1/web-search-agent/agent.py#L126-L128) to maintain state, while the `_emit` helper appends to `self.trace` to create a readable log of every reasoning step.

### Tools as the Action Interface

**Tools** are external functions that the LLM can call (e.g., `$web_search`). They are described in the model's *tool* list and invoked when the model emits an *action* step.

The available tool set is defined in [`WebSearchAgent._get_tools`](https://github.com/bojieli/ai-agent-book/blob/main/chapter1/web-search-agent/agent.py#L39-L46), while the thin wrapper [`search_impl`](https://github.com/bojieli/ai-agent-book/blob/main/chapter1/web-search-agent/agent.py#L54-L66) forwards arguments to the underlying service.

## The ReAct Loop: Putting the Formula into Motion

Putting these three pieces together yields the **ReAct loop** used throughout the book. This execution cycle demonstrates how the formula translates into runtime behavior:

1. **Thought** – the LLM generates a reasoning step based on current context.
2. **Action** – if the model decides it needs external data, it calls a *tool* (e.g., `$web_search`).
3. **Observation** – the tool returns a result, which is added to the **context**.
4. **Answer** – after enough cycles, the LLM produces the final answer using the accumulated **context**.

The `self.trace` attribute maintains this history, providing transparency for debugging and teaching the design pattern.

## Implementation Deep Dive: WebSearchAgent

The [`chapter1/web-search-agent/agent.py`](https://github.com/bojieli/ai-agent-book/blob/main/chapter1/web-search-agent/agent.py) file demonstrates the concrete realization of the formula. The `WebSearchAgent` class implements the complete integration of LLM, Context, and Tools:

```python

# Creating a web-search agent (LLM + Context + Tools)

from chapter1.web-search-agent.agent import WebSearchAgent

agent = WebSearchAgent(
    api_key="YOUR_MOONSHOT_KEY",   # LLM backend

    model="kimi-k3",               # LLM choice

    verbose=True                   # Show ReAct trace (Context)

)

# Running the ReAct loop

question = "What are the health benefits of blueberries?"
answer = agent.search_and_answer(question, max_iterations=5)

print("🗣️  Final answer:", answer)

# The console displays intermediate Thought → Action → Observation steps

# because `verbose=True` triggers `_emit` which prints each trace entry.

```

The [`chapter1/web-search-agent/config.py`](https://github.com/bojieli/ai-agent-book/blob/main/chapter1/web-search-agent/config.py) file holds configuration constants such as `SEARCH_TIMEOUT` that affect how the LLM and tools interact, further demonstrating the separation of concerns.

## Extending the Formula: Adding Custom Tools

The formula's modularity enables developers to extend agent capabilities without modifying the core reasoning engine. The following example from the repository demonstrates adding a calculator tool by extending `_get_tools` and `_run_tool`:

```python
from chapter1.web-search-agent.agent import WebSearchAgent

class CalcAgent(WebSearchAgent):
    def _get_tools(self):
        # Keep the built-in web search tool and add a calculator

        base = super()._get_tools()
        base.append({
            "type": "function",
            "function": {
                "name": "calc",
                "description": "Simple arithmetic calculator",
                "parameters": {
                    "type": "object",
                    "properties": {
                        "expression": {
                            "type": "string",
                            "description": "Arithmetic expression, e.g. '2+3*4'"
                        }
                    },
                    "required": ["expression"]
                }
            }
        })
        return base

    def _run_tool(self, tool_name, args):
        if tool_name == "calc":
            import math
            try:
                result = eval(args["expression"], {"__builtins__": {}}, {})
                return {"result": result}
            except Exception as e:
                return {"error": str(e)}
        return super()._run_tool(tool_name, args)

calc_agent = CalcAgent()
calc_agent.search_and_answer("What is 7*8 plus 12?")

```

The [`chapter8/self-evolving-tools/agent.py`](https://github.com/bojieli/ai-agent-book/blob/main/chapter8/self-evolving-tools/agent.py) file extends this pattern further, demonstrating agents that can **extend their tool set at runtime**, reinforcing the modularity of the formula.

## Architectural Benefits of the Separation

The *Agent = LLM + Context + Tools* blueprint guides developers to build systems with three specific architectural advantages:

- **Separate concerns** – LLM logic, state management (`conversation_history`), and external services (`search_impl`) live in distinct functions and classes.
- **Enable composability** – New tools are added by appending to the list returned by `_get_tools`, as shown in the `CalcAgent` example.
- **Maintain transparency** – The ReAct trace (`self.trace`) gives a readable log of every step, useful for debugging and for teaching the design pattern.

The abstract `BaseAgent` class in [`chapter8/gaia-experience/AWorld/aworld/core/agent/base.py`](https://github.com/bojieli/ai-agent-book/blob/main/chapter8/gaia-experience/AWorld/aworld/core/agent/base.py) demonstrates how this generic pattern scales across the entire book, providing a foundation for specialized implementations.

## Summary

The *Agent = LLM + Context + Tools* formula provides a robust framework for AI agent development by decomposing complexity into three interchangeable components:

- The **LLM** serves as the reasoning engine, instantiated via `WebSearchAgent.__init__` with configurable model parameters.
- **Context** maintains state through `conversation_history` and `trace`, enabling persistent memory across ReAct loop iterations.
- **Tools** provide action capabilities through the `_get_tools` and `_run_tool` methods, allowing external function execution.

This separation enables swapping LLM providers, enriching memory mechanisms, and extending tool sets without touching the core reasoning logic, as demonstrated by the `CalcAgent` extension pattern and the `BaseAgent` abstraction.

## Frequently Asked Questions

### What is the Agent = LLM + Context + Tools formula?

The *Agent = LLM + Context + Tools* formula is an architectural pattern that defines an autonomous agent as the combination of three components: an LLM that provides reasoning capabilities, a Context structure that stores conversation history and observations, and a Tools interface that enables external actions. According to the `bojieli/ai-agent-book` source code, this decomposition separates reasoning from state management and action execution.

### How does the ReAct loop implement this formula?

The ReAct loop operationalizes the formula through a four-step cycle: the **LLM** generates a Thought, decides to call a **Tool** (Action), receives an Observation that updates the **Context**, and finally produces an Answer using the accumulated context. This loop is implemented in [`chapter1/web-search-agent/agent.py`](https://github.com/bojieli/ai-agent-book/blob/main/chapter1/web-search-agent/agent.py) where `_emit` updates the trace and `search_and_answer` orchestrates the iterations.

### Can I swap the LLM provider while keeping the same agent logic?

Yes. The repository demonstrates this flexibility in `WebSearchAgent.__init__` (lines 94-100) where the LLM is instantiated through an OpenAI-compatible client. Changing the `model` parameter (e.g., from `kimi-k3` to `gpt-4`) or the API endpoint allows you to swap providers without modifying the tool definitions or context management logic, as long as the new LLM supports function calling.

### How do I add new tools to an existing agent?

New tools are added by extending the `_get_tools` method to include the tool schema and implementing the execution logic in `_run_tool`. The `CalcAgent` example in the repository shows this pattern: it appends a new function definition to the base tools list and adds a conditional branch in `_run_tool` to handle the `calc` tool name, demonstrating how the formula enables composable capability extension.