How to Build an AI Agent: A Step-by-Step Guide with Code
A practical guide to creating an AI agent: from choosing a model to launching with RAG and MCP. Python code that you can run right away.
What You'll Need
- Python 3.9 or newer.
- An API key from OpenAI (or another provider). You can get one in your account on the provider's platform.
- Installed libraries:
openai,requests,faiss-cpu,numpy. - A text editor or IDE.
- Basic understanding of Python and HTTP requests.
All code examples below assume that you replace YOUR_KEY with the real key. The key can be stored in an environment variable so as not to expose it in the code.
Step 1: Install Dependencies and Set Up the Key
Create a virtual environment and install the packages:
python -m venv agent-env
source agent-env/bin/activate # for Windows: agent-env\Scripts\activate
pip install openai requests faiss-cpu numpy
Save the key in an environment variable (Linux/macOS):
export OPENAI_API_KEY="YOUR_KEY"
For Windows (cmd):
set OPENAI_API_KEY=YOUR_KEY
Check that the key works:
import os
from openai import OpenAI
client = OpenAI(api_key=os.environ["OPENAI_API_KEY"])
response = client.chat.completions.create(
model="gpt-4o-mini",
messages=[{"role": "user", "content": "Hi! Answer with one word."}]
)
print(response.choices[0].message.content)
If you see a response, the key is active.
Step 2: Create a Simple Agent with a Weather Function
An agent differs from a model in that it can call tools. Let's write a function that returns a dummy weather (for demonstration) and teach the model to call it.

import json
from openai import OpenAI
client = OpenAI()
def get_weather(city: str) -> str:
# Stub: in reality, there would be a request to a weather API here.
return f"In {city} it is currently +15°C, cloudy."
tools = [{
"type": "function",
"function": {
"name": "get_weather",
"description": "Returns the current weather in a city",
"parameters": {
"type": "object",
"properties": {
"city": {"type": "string", "description": "City name"}
},
"required": ["city"]
}
}
}]
messages = [{"role": "user", "content": "What is the weather in Moscow?"}]
response = client.chat.completions.create(
model="gpt-4o-mini",
messages=messages,
tools=tools,
tool_choice="auto"
)
msg = response.choices[0].message
if msg.tool_calls:
for call in msg.tool_calls:
args = json.loads(call.function.arguments)
result = get_weather(args["city"])
messages.append(msg)
messages.append({
"role": "tool",
"tool_call_id": call.id,
"content": result
})
final = client.chat.completions.create(
model="gpt-4o-mini",
messages=messages
)
print(final.choices[0].message.content)
else:
print(msg.content)
Run it — the agent will call the function and return a response with the weather.
Step 3: Add RAG for Working with Documents
RAG (Retrieval-Augmented Generation) allows the agent to answer questions based on your data. Let's implement a simple search over text fragments.
import numpy as np
import faiss
from openai import OpenAI
client = OpenAI()
# Example knowledge base: list of texts
docs = [
"Vacation at the company is 28 calendar days.",
"A vacation request is submitted two weeks before the start.",
"Sick leave is paid based on average earnings."
]
# Get embeddings for the documents
embeddings = []
for text in docs:
resp = client.embeddings.create(input=text, model="text-embedding-3-small")
embeddings.append(resp.data[0].embedding)
# Create a FAISS index
index = faiss.IndexFlatL2(len(embeddings[0]))
index.add(np.array(embeddings).astype('float32'))
def retrieve(query: str, k: int = 2):
q_emb = client.embeddings.create(input=query, model="text-embedding-3-small").data[0].embedding
distances, indices = index.search(np.array([q_emb]).astype('float32'), k)
return [docs[i] for i in indices[0]]
<figure class="post-fig"><img src="/media/inline-1793422159.jpg" alt="Шаг 3: Добавьте RAG для работы с документами" loading="lazy"></figure>
# Example query
question = "How many vacation days?"
context = retrieve(question)
prompt = f"Answer the question using the context:\n{context}\n\nQuestion: {question}"
response = client.chat.completions.create(
model="gpt-4o-mini",
messages=[{"role": "user", "content": prompt}]
)
print(response.choices[0].message.content)
The agent will find the relevant fragment and answer based on it.
Step 4: Connect MCP to Call an External Service
MCP (Model Context Protocol) allows the agent to access external tools through a standardized interface. In the example, we use a simple HTTP service (a stub). In reality, an MCP server can be running locally or remotely.
import requests
def call_mcp_tool(tool_name: str, params: dict) -> dict:
# Example: calling a local MCP server (stub)
# In reality, the URL and format depend on the MCP server implementation.
url = "http://localhost:8000/mcp"
payload = {"tool": tool_name, "parameters": params}
try:
resp = requests.post(url, json=payload, timeout=5)
return resp.json()
except Exception as e:
return {"error": str(e)}
# Usage in the agent: add the tool description to tools and handle the call
This step requires an MCP server to be running. Without it, the call will return a connection error — this is normal for testing.
Step 5: Put Everything Together
Combine the weather function, RAG, and MCP call in a single agent. Logic: first search the knowledge base, then call tools if necessary.
# Pseudocode for the structure
# 1. Get the user request.
# 2. Execute retrieve() to find context.
# 3. Pass the context and request to the model with tool descriptions.
# 4. If the model returned tool_calls — execute the corresponding functions.
# 5. Return the final answer.
The full code can be assembled from the previous steps by adding branching.
Common Mistakes and How to Recognize Them
- Authentication error (401) — the key is incorrect or not passed. Check the environment variable.
- The model does not call the tool — the function description lacks clear parameters or the model does not understand the task. Simplify the description, add examples to
description. - RAG returns irrelevant fragments — too few documents or the embeddings are unsuitable. Increase
kor check the quality of the texts. - MCP server is unavailable — check whether the server is running and whether the URL is correct. A connection error is a clear sign.
- The agent loops — limit the number of steps (for example, no more than 5 tool calls).
By following the steps, you will get a working agent that can answer questions based on your data and call external functions.