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:
chapter9/self-evolving-tools/agent.py– Creates theLLMClientand handles the--offlineflag for testing.chapter9/gaia-experience/gaia/llm_env.py– Centralized environment parsing for provider selection and authentication.chapter9/gaia-experience/AWorld/aworld/core/agent/base.py– GenericBaseAgentclass implementing the LLM-tool loop.chapter9/gaia-experience/AWorld/aworld/core/tool/base.py– AbstractBaseTooldefinitions and theToolsManagerregistry.chapter5/coding-agent/tools/web_search_tool.py– Reference implementation showing tool structure and error handling.chapter9/self-evolving-tools/demo.py– CLI entry point demonstrating both offline and online execution modes.
Summary
- LLM Context configuration relies on environment variables (
LLM_PROVIDER,LLM_MODEL) parsed byllm_env.pyto instantiate a provider-agnostic client. - Tool Library extensions require subclassing
BaseTooland registering instances withToolsManagerbefore agent initialization. - Agent Core orchestration uses
BaseAgent.run_once()or the continuous loop inLLMAgentto alternate between LLM reasoning and tool execution. - Offline testing is available via the
--offlineCLI flag that bypasses API calls while validating tool pipelines. - The architecture is generic—any LLM backend or tool set can be plugged into the
BaseAgentwithout 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:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →