Опубликовано 9 июня 2026 г.
Как создать своего первого AI-агента в 2026: полный гайд для новичков
Собери рабочего AI-агента на чистом Python — без фреймворков и магии. LLM-вызов, инструменты, память и цикл агента ты напишешь сам.
AI-агент — это цикл: ты отправляешь сообщение в большую языковую модель (LLM), она либо отвечает, либо просит вызвать инструмент, ты выполняешь инструмент и возвращаешь результат — и так по кругу, пока задача не решена. Вот и вся идея, никакой фреймворк не нужен. Скелет в три строки:
while response.stop_reason == "tool_use": # модель хочет инструмент
result = run_tool(block.name, block.input) # ты его выполняешь
messages.append(tool_result(result)) # возвращаешь результат, цикл повторяетсяК концу гайда у тебя будет настоящий агент на чистом Python: он скажет время, посчитает математику точно и удержит диалог — собранный из четырёх примитивов, которые ты поймёшь построчно: вызов LLM, инструменты, память и управляющий цикл. Возьмём Anthropic Python SDK и дешёвую модель, так что целый разговор обойдётся в доли цента. А потом покажу, на какой production-SDK переходить, когда концепция уляжется в голове.
Зачем строить без фреймворка
В 2026 громче всего советуют «просто возьми фреймворк». Но разработчики раз за разом обжигаются о магию — в этом месяце завирусился тред про фрилансера, которому заплатили, чтобы он выпилил AI из инструмента, потому что в команде никто не понимал, что тот делает. Фреймворк прячет цикл, а цикл, который ты не понимаешь, нельзя отладить в три часа ночи.
Поэтому цикл соберём сами. Это примерно 60 строк. Как только ты их напишешь, любой агентный фреймворк — Claude Agent SDK, OpenAI Agents SDK, Google ADK — перестаёт быть магией и становится «а, это же то, что я уже сделал, только с типами поприятнее». Это самый быстрый способ реально понять агентов.
Шаг 1: вызов LLM
Поставь SDK и задай ключ:
pip install anthropic
export ANTHROPIC_API_KEY=sk-ant-... # ключ берётся на console.anthropic.comМинимальный вызов — текст на входе, текст на выходе:
from anthropic import Anthropic
client = Anthropic() # читает ANTHROPIC_API_KEY из окружения
response = client.messages.create(
model="claude-haiku-4-5", # дёшево и быстро; позже поменяешь на sonnet/opus
max_tokens=1024,
messages=[{"role": "user", "content": "Одним предложением: что такое AI-агент?"}],
)
print(response.content[0].text)Это чат-бот, а не агент. Разница в том, что агент умеет действовать — а для этого ему нужны инструменты.
Шаг 2: дай ему инструмент
LLM не умеет смотреть на часы и надёжно перемножать большие числа — она предсказывает текст, а не вычисляет. Поэтому мы выдаём ей инструменты: обычные функции Python плюс описание в формате JSON Schema, чтобы модель знала, когда их звать.
TOOLS = [
{
"name": "get_current_time",
"description": "Return the current date and time. Call when the user asks the time or date.",
"input_schema": {"type": "object", "properties": {}},
},
]
response = client.messages.create(
model="claude-haiku-4-5",
max_tokens=1024,
tools=TOOLS,
messages=[{"role": "user", "content": "Сколько сейчас времени?"}],
)
print(response.stop_reason) # -> "tool_use"Модель сама ничего не выполняет. Она останавливается с stop_reason == "tool_use" и блоком tool_use, который говорит: «пожалуйста, вызови get_current_time». Выполнить — твоя задача. Это граница безопасности, и это плюс: ты решаешь, что реально запустится.
from datetime import datetime
def get_current_time() -> str:
return datetime.now().strftime("%Y-%m-%d %H:%M:%S")Затем ты возвращаешь результат блоком tool_result с тем же tool_use_id, и модель превращает его в обычное предложение. Делай так в цикле — и получишь агента.
Шаг 3: цикл агента (целиком)
Вот полный агент. Два инструмента, цикл, который их выполняет, и чат-REPL. Список messages — это и есть память: каждый ход в него дописывается, поэтому модель помнит разговор.
# agent.py — крошечный AI-агент на чистом Python. Без фреймворков.
# Setup: pip install anthropic && export ANTHROPIC_API_KEY=sk-ant-...
import ast
import operator
from datetime import datetime
from anthropic import Anthropic
client = Anthropic()
MODEL = "claude-haiku-4-5" # дёшево; поменяй на "claude-sonnet-4-6" или "claude-opus-4-8"
# --- 1. Инструменты: обычные функции Python ---------------------------------
def get_current_time() -> str:
"""The model has no clock — this gives it one."""
return datetime.now().strftime("%Y-%m-%d %H:%M:%S")
_OPS = {
ast.Add: operator.add, ast.Sub: operator.sub,
ast.Mult: operator.mul, ast.Div: operator.truediv,
ast.Pow: operator.pow, ast.USub: operator.neg,
}
def _eval(node):
# A tiny, safe arithmetic evaluator — never eval() untrusted input.
if isinstance(node, ast.Constant):
return node.value
if isinstance(node, ast.BinOp):
return _OPS[type(node.op)](_eval(node.left), _eval(node.right))
if isinstance(node, ast.UnaryOp):
return _OPS[type(node.op)](_eval(node.operand))
raise ValueError("unsupported expression")
def calculate(expression: str) -> str:
"""Deterministic math — don't trust an LLM to multiply big numbers."""
return str(_eval(ast.parse(expression, mode="eval").body))
# --- 2. Описываем инструменты для модели (JSON Schema) ---------------------
TOOLS = [
{
"name": "get_current_time",
"description": "Return the current date and time. Call when the user asks the time or date.",
"input_schema": {"type": "object", "properties": {}},
},
{
"name": "calculate",
"description": "Evaluate a basic arithmetic expression like '23 * 47 + 10'. Call for any math.",
"input_schema": {
"type": "object",
"properties": {
"expression": {"type": "string", "description": "e.g. '2 ** 10 / 4'"},
},
"required": ["expression"],
},
},
]
def run_tool(name: str, tool_input: dict) -> str:
if name == "get_current_time":
return get_current_time()
if name == "calculate":
return calculate(tool_input["expression"])
return f"Unknown tool: {name}"
# --- 3. Цикл агента --------------------------------------------------------
def agent_turn(messages: list) -> str:
"""Run one user turn to completion, executing tools until the model is done."""
for _ in range(10): # жёсткий лимит, чтобы модель не зациклилась навсегда
response = client.messages.create(
model=MODEL,
max_tokens=1024,
tools=TOOLS,
messages=messages,
)
# Keep the assistant turn (incl. any tool_use blocks) in memory.
messages.append({"role": "assistant", "content": response.content})
if response.stop_reason != "tool_use":
return "".join(b.text for b in response.content if b.type == "text")
# The model asked for tools. Run them, feed the results back, loop.
results = []
for block in response.content:
if block.type == "tool_use":
output = run_tool(block.name, block.input)
results.append({
"type": "tool_result",
"tool_use_id": block.id, # must match the tool_use block
"content": output,
})
messages.append({"role": "user", "content": results})
return "Stopped: too many tool calls."
# --- 4. Чат-цикл — разговор и ЕСТЬ память -----------------------------------
if __name__ == "__main__":
messages = [] # весь разговор живёт здесь
print("Agent ready. Ctrl-C to quit.\n")
while True:
messages.append({"role": "user", "content": input("you> ")})
print(f"agent> {agent_turn(messages)}\n")Запусти python agent.py и спроси: «сколько будет 4871 умножить на 209 и сколько сейчас времени?» — модель вызовет оба инструмента, получит точные ответы и ответит одним предложением. Ты только что собрал агента.
Память между запусками
Сейчас память умирает, когда ты закрываешь скрипт. Чтобы она сохранялась, пиши messages в файл при выходе и читай при старте — блоки content сериализуются в JSON:
import json, pathlib
STORE = pathlib.Path("memory.json")
messages = json.loads(STORE.read_text()) if STORE.exists() else []
# ... после чат-цикла или при выходе:
STORE.write_text(json.dumps(messages, default=lambda o: o.model_dump()))Это четвёртый примитив. В реальных агентах плоский файл меняют на базу данных или векторное хранилище, но идея та же: состояние, которое ты переносишь между вызовами.
Стриминг, чтобы было приятнее
Для чат-интерфейсов обычно хочется, чтобы токены появлялись по мере генерации. Тот же вызов, но потоком:
with client.messages.stream(
model=MODEL,
max_tokens=1024,
messages=[{"role": "user", "content": "Объясни AI-агентов пятилетнему ребёнку."}],
) as stream:
for text in stream.text_stream:
print(text, end="", flush=True)
print()На что переходить дальше
Теперь ты понимаешь каждую деталь. Production-SDK просто упаковывает эти примитивы в типы, ретраи и встроенный tool-runner, чтобы цикл не писать руками:
| Что написал ты | Что даёт SDK |
|---|---|
вызов messages.create | тот же вызов, полностью типизированный |
цикл tool_use | автоматический tool-runner |
список messages | хелперы для сессий и состояния |
диспетчер run_tool | инструменты из сигнатур функций |
Когда будешь готов, естественные следующие шаги — Claude Agent SDK, OpenAI Agents SDK или Google ADK. Берись за них, когда нужна production-обвязка, не раньше. Для обучения эти 60 строк сильнее любого фреймворка.
Частые ошибки (и как чинить)
- Ошибка: отправить
tool_result, не дописав сперва ход ассистента сtool_use. Фикс: всегда добавляйresponse.contentвmessagesдо результатов — API должен видеть вызов, на который отвечает результат, иначе вернёт 400. - Ошибка:
tool_result, у которогоtool_use_idне совпадает с блокомtool_use. Фикс: копируйblock.idв результат дословно. - Ошибка: доверять модели вычисления или знание текущего времени. Фикс: ровно для этого и нужны инструменты — детерминированную работу держи в коде.
- Ошибка: прогонять ввод от модели через
eval(). Фикс: валидируй и изолируй каждый инструмент, никогда не выполняй сырые выражения (см. безопасныйast-вычислитель выше). - Ошибка: цикл без ограничителя. Фикс: потолок
range(10)не даст агенту молотить счёт бесконечно.
Что дальше
Добавь инструмент, который делает что-то полезное лично тебе — читает файл, дёргает API, ходит в базу — и у тебя настоящий ассистент. Паттерн не меняется: опиши инструмент, выполни его, верни результат. Когда плоского файла под память станет мало — это и есть сигнал тянуться за фреймворком.
Источники
-
Anthropic Python SDK — вызов
messages.create, стриминг и блоки tool-use, которые мы используем по всему гайду. https://github.com/anthropics/anthropic-sdk-python -
Claude Tool Use (overview) — схема
tool_use/tool_result, обработкаstop_reasonи агентный цикл. https://platform.claude.com/docs/en/agents-and-tools/tool-use/overview Дата обращения: 9 июня 2026. -
Claude Models Overview — актуальные ID моделей (
claude-haiku-4-5,claude-sonnet-4-6,claude-opus-4-8) и размеры контекста. https://platform.claude.com/docs/en/about-claude/models/overview Дата обращения: 9 июня 2026. -
Claude Pricing — Haiku 4.5 примерно $1/$5 за 1M входных/выходных токенов (отсюда оценка стоимости; цены в USD и меняются — сверяйся с живой страницей). https://platform.claude.com/docs/en/pricing Дата обращения: 9 июня 2026.
Нужен AI-агент под твой продукт? Начать проект — я строю AI-агентов, MCP-серверы и системы агентных платежей, которые работают на твоём сервере.