How AI Agents Are Built: Architecture, ReAct, and Practical Implementation in Python
AI agents are not just 'smart chatbots'; they are autonomous systems capable of planning, selecting tools, making external calls, and adjusting their actions in real time. Unlike RAG, which merely provides context before generation, an agent manages the entire task-solving cycle—from goal analysis to the final result with self-checking. This represents a fundamentally new level of automation for developers, especially in scenarios requiring multi-step interactions with APIs, code, databases, or external services.
Why RAG Is Insufficient for Complex Tasks
RAG addresses one specific problem—updating knowledge through external sources. However, it is static: the model receives a set of fragments and generates an answer only once. There’s no mechanism for re-querying when results are irrelevant, no assessment of the reliability of intermediate outputs, and no replanning. In real-world workflows, this is critical:
- When analyzing technical documentation, you may need to follow links sequentially, extract code from different files, and validate it;
- When generating a report based on Excel data, you first need to read the table, then determine metrics, calculate them, and only then format the output;
- When automating CI/CD, you must interpret error logs, run diagnostics, apply fixes, and confirm the result.
All these scenarios go beyond single-shot generation. That’s where a dynamic architecture—i.e., an agent—is needed.
Key Components of a Modern AI Agent
An agent isn’t a monolith; it’s a coordinated system of interacting modules. Each component performs a strictly defined function and can be replaced or expanded without rewriting the entire logic.
LLM as the Decision-Making Core
The model doesn’t generate the final answer directly; instead, it acts as the “brain”: it formulates a plan, selects tools, interprets call results, and decides on the next step. Its role is strategic management, not text production.
Tools as the Interface to the Environment
These are any external capabilities: HTTP requests to REST APIs, running Python code in a sandbox, reading/writing files, executing SQL queries, calling CLI utilities. A tool must have a clear description (docstring), typed arguments, and error handling—only then can the LLM use it safely.
Memory: Short-Term and Long-Term
Short-term memory consists of the current dialogue context and intermediate results (e.g., the ID of a created resource). Long-term memory includes vector-based knowledge bases, cached responses, and historical user session data. It allows the agent to “remember” context between calls and learn from experience.
Planner and Error Controller
The planner breaks down the goal into steps, tracks progress, and prevents looping. The error controller verifies the validity of tool results, cross-checks facts against sources, and initiates retries if something goes wrong. Without these components, the agent is unstable and unpredictable.
ReAct: A Transparent Behavior Pattern for LLMs
ReAct (Reason + Act + Observe) isn’t a framework—it’s a behavior template that defines a clear agent workflow cycle. It makes the model’s internal reasoning visible and controllable:
- Reason—the model analyzes the current state, formulates a hypothesis about the next action, and explains why it’s necessary;
- Act—the model calls a tool with specific arguments;
- Observe—the model receives the call result and evaluates its applicability to the task.
This cycle repeats until the goal is achieved. For example, when searching for information about library compatibility with Python 3.12, the model first “reasons” that it needs to check PyPI and GitHub; then “acts” by calling get_pypi_info('requests'); after “observing” the response, it decides whether to analyze the repository’s source code.
The advantage of ReAct is reproducibility and debuggability. Every step is logged: plan → call → result → new decision. This is crucial for production systems, where auditing and tracing are required.
Multi-Agent Systems: Scaling Through Specialization
A single universal agent quickly hits a complexity limit. Multi-agent systems (MAS) solve this by dividing roles. Instead of a monolith, there’s a team of specialized agents coordinated by an orchestrator.
Typical roles in MAS include:
- Planner—decomposes the goal, determines the order of subtasks, and assigns them to executors;
- Researcher—works with external sources: APIs, documentation, databases;
- Executor—runs code, modifies files, applies configurations;
- Validator—checks results for correctness, compliance with requirements, and safety.
The coordinator collects results, resolves conflicts, and forms the final answer. This approach offers three key advantages:
- Parallelism: multiple agents can handle independent subtasks simultaneously;
- Increased reliability: the validator catches errors that the executor might miss;
- Simplified debugging: a failure in one role doesn’t paralyze the entire system.
However, MAS requires a well-thought-out interaction protocol and protection against cyclical dependencies—for example, when the researcher requests data from the validator, but the validator waits for confirmation from the researcher.
Practice: A Minimal Assistant Agent on LangChain
Let’s build a simple yet fully functional agent capable of fetching weather, finding events, and sending emails. The goal is to show how the components come together into a unified system, rather than creating a production-ready solution.
Initializing the Model and Environment
We’ll use Qwen 3.5 (9B) via Ollama—a model powerful enough for agent tasks and small enough to run in Google Colab. The connection is standard:
from langchain_openai import ChatOpenAI
llm = ChatOpenAI(
model="qwen3.5:9b",
base_url="http://localhost:11434/v1",
api_key="ollama",
temperature=0.3,
)
Defining Tools
Each tool is a Python function decorated with @tool, complete with a clear docstring and typed arguments. For example, get_weather:
@tool
def get_weather(city: str) -> str:
"""Returns the current weather in the specified city.
Accepts the city name in Russian or English."""
# ... implementation using the Open-Meteo API
return f"{name}: {weather['temperature_2m']}°C, {desc}, wind {weather['wind_speed_10m']} km/h"
It’s important that the function handles network errors, non-existent cities, and invalid weather codes—otherwise the agent will receive unstructured text instead of the expected answer.
User Prompt and Context
The prompt defines the role, constraints, and tone of the response. For a technical agent, it’s crucial to explicitly state:
- Which tools are available and when to use them;
- What to do in case of an error (retry, ask the user for clarification, or refuse);
- How to format the final answer (e.g., only JSON, no explanations).
USER_CONTEXT contains personalized data—name, preferences, interaction history. This allows the agent to tailor the tone and level of detail.
What Matters
- An AI agent isn’t an LLM with an extended prompt; it’s an architecturally complex system with a clear division of responsibilities among components.
- ReAct isn’t just a “feature”—it’s a necessary pattern for ensuring transparency, debuggability, and safety in production.
- Tools must be typed, documented, and resilient to errors—otherwise the agent loses control over execution.
- Multi-agent systems aren’t justified by trendiness; they’re needed to scale complexity: one agent can’t reliably manage dozens of tools and sources.
- Production agents require tracing mechanisms, logging every step of the cycle, and strict validation of input/output data.
— Editorial Team
No comments yet.