How to Set Up AI Agents with LLM, Context, and Tools: A Complete Implementation Guide

Building AI agents requires three core components: an LLM client for reasoning, a tool registry for actions, and an agent loop that orchestrates the interaction between them.

The bojieli/ai-agent-book repository provides a modular framework for setting up AI agents with LLM, Context, and Tools using a clean, extensible architecture. This implementation separates concerns into distinct layers—the reasoning engine, the action interface, and the orchestration logic—allowing developers to swap LLM providers or add capabilities without rewriting core logic. Understanding these three pillars is essential for building production-ready autonomous systems.

Understanding the Three-Pillar Architecture

LLM Context (The Reasoning Engine)

The LLM Context provides the cognitive capabilities that allow the agent to interpret tasks and generate responses. In chapter9/gaia-experience/gaia/llm_env.py, the framework implements a unified configuration system that reads from environment variables including LLM_PROVIDER, LLM_MODEL, and LLM_API_KEY. This design supports multiple backends—OpenAI, Moonshot, and ARK—through a thin wrapper class called LLMClient instantiated in chapter9/self-evolving-tools/agent.py.

Tool Library (The Action Interface)

Tools are concrete actions the agent can perform on external systems, encapsulated as subclasses of BaseTool or AsyncBaseTool. The Tool Library registers these capabilities in a global ToolsManager class defined in chapter9/gaia-experience/AWorld/aworld/core/tool/base.py. Concrete implementations like the web search tool in chapter5/coding-agent/tools/web_search_tool.py demonstrate how each tool exposes a run() method that accepts structured JSON arguments and returns a ToolResult object.

Agent Core (The Orchestration Layer)

The Agent Core implements the execution loop that ties reasoning to action. chapter9/gaia-experience/AWorld/aworld/core/agent/base.py defines BaseAgent[Observation, List[ActionModel]], a generic class that handles the LLM-to-tool cycle. The concrete LLMAgent in chapter9/gaia-experience/AWorld/aworld/agents/llm_agent.py executes the loop: sending prompts to the LLM, parsing tool_calls JSON payloads, invoking registered tools, and feeding results back to the LLM for subsequent reasoning rounds.

Environment Configuration and Setup

Before running agents, configure the LLM context through environment variables or CLI flags. The framework supports deterministic testing through an offline mode and production execution with live APIs.

Set the required environment variables:

export LLM_PROVIDER=openai            # Options: openai, moonshot, ark

export LLM_MODEL=gpt-4o-mini
export LLM_API_KEY=your-api-key-here
export LLM_BASE_URL=https://api.openai.com/v1  # Optional custom endpoint

Configuration is centralized in chapter9/prompt-auto-optimization/config.py, which validates these settings before initializing the LLMClient. The --model and --temperature CLI flags in chapter9/self-evolving-tools/demo.py allow runtime overrides without modifying environment variables.

Running the Agent Loop

Offline Mode for Testing

Use the --offline flag to test tool pipelines without consuming LLM API quota:

python -m chapter9.self-evolving-tools.demo --offline

When --offline is detected in chapter9/self-evolving-tools/agent.py, the system sets model = None and bypasses network calls, executing only the tool registration and validation logic.

Full LLM-Backed Execution

For production runs with reasoning capabilities:

python -m chapter9.self-evolving-tools.demo \
    --model gpt-4o-mini \
    --temperature 0.2

This command loads the LLMClient with the specified provider, instantiates all registered tools via ToolsManager, and enters the interaction loop defined in the agent's run() method.

Extending with Custom Tools

To add new capabilities, subclass BaseTool and register it with the global manager:


# my_weather_tool.py

from chapter5.coding_agent.tools.base import BaseTool, ToolResult

class WeatherTool(BaseTool):
    """Fetch current weather for any city."""
    name = "weather"
    
    async def run(self, args: dict) -> ToolResult:
        city = args.get("city", "San Francisco")
        # Implementation details...

        return ToolResult(success=True, output=f"Weather in {city}: 72°F, sunny")

Register the tool before initializing the agent:

from chapter9.gaia_experience.AWorld.aworld.core.tool.base import ToolsManager
from my_weather_tool import WeatherTool

ToolsManager.register(WeatherTool())

Once registered, the LLM can emit tool calls like {"name": "weather", "arguments": {"city": "Tokyo"}}, which the agent automatically routes to your implementation.

Direct Programmatic Invocation

For integration into existing applications, instantiate the components directly:

from chapter9.gaia_experience.AWorld.aworld.core.agent.base import BaseAgent
from chapter9.gaia_experience.gaia.llm_env import get_llm_client
from chapter9.gaia_experience.AWorld.aworld.core.tool.base import ToolsManager

# Initialize components

llm = get_llm_client()                 # Parses LLM_PROVIDER/LLM_MODEL from env

tools = ToolsManager()                 # Auto-loads built-in tools

agent = BaseAgent(llm=llm, tools=tools)

# Execute one reasoning cycle

prompt = "Summarize the latest news about AI safety."
response = agent.run_once(prompt)      # One LLM call + optional tool execution

print(response)

The run_once() method performs a single LLM request, parses any tool_calls fields, executes the corresponding tools, and returns the final synthesized output.

Key Implementation Files

Understanding the source structure helps when debugging or extending the framework:

Summary

  • LLM Context configuration relies on environment variables (LLM_PROVIDER, LLM_MODEL) parsed by llm_env.py to instantiate a provider-agnostic client.
  • Tool Library extensions require subclassing BaseTool and registering instances with ToolsManager before agent initialization.
  • Agent Core orchestration uses BaseAgent.run_once() or the continuous loop in LLMAgent to alternate between LLM reasoning and tool execution.
  • Offline testing is available via the --offline CLI flag that bypasses API calls while validating tool pipelines.
  • The architecture is generic—any LLM backend or tool set can be plugged into the BaseAgent without modifying core loop logic.

Frequently Asked Questions

How does the agent decide when to invoke a tool?

The LLM generates a structured JSON payload containing a tool_calls field when it determines that external data or action is required to answer the prompt. The BaseAgent in chapter9/gaia-experience/AWorld/aworld/core/agent/base.py parses this field, extracts the tool name and arguments, and dispatches execution through ToolsManager before returning results to the LLM context.

Can I run agents without an external LLM API?

Yes. The --offline flag forces the agent into a testing mode where the LLM client is bypassed entirely. In chapter9/self-evolving-tools/agent.py, this sets model = None and allows you to validate tool registration, argument parsing, and execution logic without network calls or API costs.

What is the difference between BaseAgent and LLMAgent?

BaseAgent is the generic abstract class defined in chapter9/gaia-experience/AWorld/aworld/core/agent/base.py that handles the core observation-action loop. LLMAgent, located in chapter9/gaia-experience/AWorld/aworld/agents/llm_agent.py, is a concrete implementation that specifically manages LLM API calls, conversation history, and tool call parsing for language model-based agents.

How do I configure multiple LLM providers in the same application?

The get_llm_client() function in chapter9/gaia-experience/gaia/llm_env.py reads environment variables to instantiate a single client. For multiple providers simultaneously, instantiate separate LLMClient objects manually with different configurations and inject them into distinct BaseAgent instances, as the framework does not enforce singleton patterns on the client level.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →