Полное руководство по разработке AI Agent: создаём интеллектуального агента на Python с нуля (2026)

Полное руководство по разработке AI Agent: создаём интеллектуального агента на Python с нуля (2026)

Кратко

  • Ключевая способность AI Agent — Function Calling (вызов инструментов) — позволяет большой языковой модели не только говорить, но и действовать
  • В этой статье мы с нуля реализуем полноценного AI Agent: запрос погоды + поиск по базе данных + вычисления, с готовым к запуску кодом на Python
  • Рассмотрены реализации Tool Use для OpenAI, Claude и Gemini — код можно использовать как есть
  • Ключевые аспекты продакшена: обработка ошибок, многошаговые вызовы инструментов, параллельные вызовы, защита безопасности — всё в одной статье

Содержание

Что такое AI Agent? Эволюция от ChatBot к интеллектуальному агенту

В 2026 году AI Agent эволюционировал из «чат-бота, умеющего вызывать инструменты» в «непрерывно работающую программную систему». Зрелый Agent способен:

  • Самостоятельно планировать многошаговые задачи и разбивать их на подзадачи
  • Вызывать внешние инструменты для получения данных в реальном времени, работы с базами данных, выполнения кода
  • Поддерживать долговременную память, запоминая предпочтения пользователя и контекст между сессиями
  • Взаимодействовать с другими агентами, формируя мультиагентные системы для решения сложных задач
ПараметрТрадиционный ChatBotAI Agent
Формат выводаТолько текстТекст + вызовы инструментов + структурированные данные
Источник информацииТолько обучающие данныеОбучающие данные + API в реальном времени + базы данных
Исполнительные способностиНетВыполнение кода, вызов API, работа с файлами
Сложность задачОдношаговые вопросы-ответыМногошаговое планирование и исполнение
Управление состояниемБез состояния или краткосрочный контекстДолговременная память + управление сессиями

Это не концептуальное различие — это разница между тем, может ли он реально выполнять работу за вас или нет.

Ключевой принцип: как работает Function Calling

Function Calling (также называемый Tool Use / Tool Calling) — это основной двигатель AI Agent. Его работу можно описать одной фразой:

Модель решает, какой инструмент вызвать и с какими параметрами; ваш код выполняет инструмент; результат возвращается модели.

Полный Agent Loop работает следующим образом:

Схема работы AI Agent Loop

Ввод пользователя → Рассуждение модели → Нужен инструмент?
                                           ├─ Нет → Прямой ответ пользователю
                                           └─ Да → Вывод инструкции вызова инструмента

                                                Код разработчика выполняет инструмент

                                                Результат возвращается модели

                                                Модель продолжает рассуждение (может снова вызвать инструмент)

                                                Финальный ответ пользователю

Каждая реализация Function Calling требует трёх ключевых компонентов:

  1. Определение инструмента (Tool Schema): описание инструмента в формате JSON Schema — название, назначение и параметры
  2. Слой исполнения (Execution Layer): ваш код, который фактически выполняет логику инструмента
  3. Интеграция результата (Result Integration): передача результата выполнения обратно модели для продолжения рассуждения

Подготовка окружения и создание проекта

Установка зависимостей

pip install openai httpx

Нам нужен только один SDK — openai — потому что все основные модели (GPT, Claude, Gemini, DeepSeek, Qwen) совместимы с протоколом OpenAI API. Один код — множество моделей.

Настройка API

from openai import OpenAI

# Используем OpenAI-совместимый интерфейс для доступа к 50+ моделям
client = OpenAI(
    api_key="your-api-key",
    base_url="https://api.ofox.ai/v1"  # API-шлюз-агрегатор, низкая задержка из Китая
)

Подсказка: если вы используете официальный API OpenAI напрямую, замените base_url на https://api.openai.com/v1. Логика кода остаётся абсолютно неизменной.

Первый Agent: помощник по погоде

Начнём с самого классического примера — AI Agent, который умеет узнавать погоду.

Шаг 1: Определение инструмента

tools = [
    {
        "type": "function",
        "function": {
            "name": "get_weather",
            "description": "Получить текущую погоду в указанном городе, включая температуру, влажность и погодные условия",
            "parameters": {
                "type": "object",
                "properties": {
                    "city": {
                        "type": "string",
                        "description": "Название города, например 'Москва', 'Токио', 'London'"
                    },
                    "unit": {
                        "type": "string",
                        "enum": ["celsius", "fahrenheit"],
                        "description": "Единица измерения температуры, по умолчанию — градусы Цельсия"
                    }
                },
                "required": ["city"]
            }
        }
    }
]

Ключевой момент: поле description крайне важно — модель принимает решение о вызове инструмента на основе описания. Чем точнее описание, тем правильнее решения модели.

Шаг 2: Реализация функции инструмента

import httpx
import json

def get_weather(city: str, unit: str = "celsius") -> str:
    """Вызов API погоды для получения данных в реальном времени"""
    # Здесь используем wttr.in для примера; в продакшене рекомендуется платный API погоды
    resp = httpx.get(f"https://wttr.in/{city}?format=j1", timeout=10)
    data = resp.json()

    current = data["current_condition"][0]
    temp = current["temp_C"] if unit == "celsius" else current["temp_F"]
    unit_label = "°C" if unit == "celsius" else "°F"

    return json.dumps({
        "city": city,
        "temperature": f"{temp}{unit_label}",
        "humidity": f"{current['humidity']}%",
        "description": current["weatherDesc"][0]["value"],
        "wind": f"{current['windspeedKmph']} km/h"
    }, ensure_ascii=False)

Шаг 3: Реализация Agent Loop

import json

# Таблица маппинга функций инструментов
TOOL_FUNCTIONS = {
    "get_weather": get_weather,
}

def run_agent(user_message: str):
    """Запуск Agent: отправка сообщения → обработка вызовов инструментов → возврат результата"""
    messages = [
        {"role": "system", "content": "Ты умный помощник по погоде. Отвечай на вопросы о погоде на естественном языке."},
        {"role": "user", "content": user_message}
    ]

    while True:
        # 1. Вызов модели
        response = client.chat.completions.create(
            model="gpt-4.1-mini",  # Можно заменить на claude-sonnet-4.5, gemini-2.5-flash и др.
            messages=messages,
            tools=tools,
            tool_choice="auto"
        )

        msg = response.choices[0].message
        messages.append(msg)

        # 2. Если нет вызовов инструментов — модель уже дала финальный ответ
        if not msg.tool_calls:
            return msg.content

        # 3. Выполнение каждого вызова инструмента
        for tool_call in msg.tool_calls:
            func_name = tool_call.function.name
            func_args = json.loads(tool_call.function.arguments)

            print(f"Вызов инструмента: {func_name}({func_args})")

            # Выполнение функции инструмента
            result = TOOL_FUNCTIONS[func_name](**func_args)

            # Возврат результата модели
            messages.append({
                "role": "tool",
                "tool_call_id": tool_call.id,
                "content": result
            })

# Тест
answer = run_agent("Где сейчас теплее — в Москве или в Токио?")
print(answer)

Пример работы:

Вызов инструмента: get_weather({"city": "Москва"})
Вызов инструмента: get_weather({"city": "Токио"})
Сейчас в Москве 18°C, а в Токио 15°C — в Москве на 3 градуса теплее.
В Москве ясно, в Токио облачно — оба города подходят для прогулки.

Обратите внимание: модель автоматически решила сделать два запроса погоды, а затем сравнила и проанализировала результаты. В этом и заключается магия Agent — он не просто передаёт данные, а рассуждает.

Продвинутый уровень: интеллектуальный Agent с несколькими инструментами

В реальных сценариях Agent обычно координирует несколько инструментов. Давайте создадим более сложного помощника — с погодой, поиском по базе данных и вычислениями.

Определение нескольких инструментов

tools = [
    {
        "type": "function",
        "function": {
            "name": "get_weather",
            "description": "Получить текущую погоду в указанном городе",
            "parameters": {
                "type": "object",
                "properties": {
                    "city": {"type": "string", "description": "Название города"}
                },
                "required": ["city"]
            }
        }
    },
    {
        "type": "function",
        "function": {
            "name": "search_database",
            "description": "Поиск в базе данных товаров, возвращает список подходящих товаров",
            "parameters": {
                "type": "object",
                "properties": {
                    "query": {"type": "string", "description": "Ключевое слово для поиска"},
                    "category": {
                        "type": "string",
                        "enum": ["electronics", "clothing", "food", "books"],
                        "description": "Категория товара"
                    },
                    "max_results": {
                        "type": "integer",
                        "description": "Максимальное количество результатов, по умолчанию 5"
                    }
                },
                "required": ["query"]
            }
        }
    },
    {
        "type": "function",
        "function": {
            "name": "calculate",
            "description": "Выполнение математических вычислений: четыре арифметических действия, проценты, конвертация валют и т.д.",
            "parameters": {
                "type": "object",
                "properties": {
                    "expression": {
                        "type": "string",
                        "description": "Математическое выражение, например '100 * 0.85' или '1999 * 3 + 500'"
                    }
                },
                "required": ["expression"]
            }
        }
    }
]

Реализация функций инструментов

def search_database(query: str, category: str = None, max_results: int = 5) -> str:
    """Имитация поиска по базе данных (в реальном проекте — подключение к настоящей БД)"""
    # Демо-данные
    products = [
        {"name": "MacBook Pro 16", "price": 18999, "category": "electronics", "stock": 50},
        {"name": "iPhone 16 Pro", "price": 8999, "category": "electronics", "stock": 200},
        {"name": "AirPods Pro 3", "price": 1899, "category": "electronics", "stock": 500},
        {"name": "Руководство по Python", "price": 89, "category": "books", "stock": 1000},
    ]

    results = [p for p in products if query.lower() in p["name"].lower()
               or (category and p["category"] == category)]

    return json.dumps(results[:max_results], ensure_ascii=False)


def calculate(expression: str) -> str:
    """Безопасные математические вычисления (допускаются только числа и базовые операторы)"""
    import ast
    import operator

    # Безопасная альтернатива выполнению произвольных выражений
    allowed_operators = {
        ast.Add: operator.add,
        ast.Sub: operator.sub,
        ast.Mult: operator.mul,
        ast.Div: operator.truediv,
        ast.Mod: operator.mod,
        ast.Pow: operator.pow,
        ast.USub: operator.neg,
    }

    def safe_compute(node):
        if isinstance(node, ast.Expression):
            return safe_compute(node.body)
        elif isinstance(node, ast.Constant) and isinstance(node.value, (int, float)):
            return node.value
        elif isinstance(node, ast.BinOp) and type(node.op) in allowed_operators:
            left = safe_compute(node.left)
            right = safe_compute(node.right)
            return allowed_operators[type(node.op)](left, right)
        elif isinstance(node, ast.UnaryOp) and type(node.op) in allowed_operators:
            return allowed_operators[type(node.op)](safe_compute(node.operand))
        else:
            raise ValueError(f"Неподдерживаемое выражение")

    try:
        tree = ast.parse(expression, mode="eval")
        result = safe_compute(tree)
        return json.dumps({"expression": expression, "result": result})
    except Exception as e:
        return json.dumps({"error": str(e)})


# Обновление маппинга инструментов
TOOL_FUNCTIONS = {
    "get_weather": get_weather,
    "search_database": search_database,
    "calculate": calculate,
}

Практика: координация нескольких инструментов

answer = run_agent("Найди устройства Apple в категории электроники и посчитай, сколько будет стоить MacBook Pro и AirPods вместе")
print(answer)
Вызов инструмента: search_database({"query": "Mac", "category": "electronics"})
Вызов инструмента: search_database({"query": "AirPods", "category": "electronics"})
Вызов инструмента: calculate({"expression": "18999 + 1899"})

Найдены два устройства Apple:
- MacBook Pro 16: 18 999 руб. (на складе 50 шт.)
- AirPods Pro 3: 1 899 руб. (на складе 500 шт.)

Итого за оба устройства: **20 898 руб.** Если нужно оформить заказ или узнать подробнее о конфигурации — пишите.

Модель автоматически организовала три вызова инструментов: два поиска + одно вычисление. Такая координация нескольких инструментов — это ключевая ценность Agent.

Сравнение Function Calling трёх основных моделей

В 2026 году ведущие модели имеют свои сильные стороны в Function Calling. Вот сравнение на основе реальных тестов:

Параметр сравненияGPT-5.x серияClaude Opus/Sonnet 4.xGemini 3.1 Pro
Точность Tool Use5/55/55/5
Параллельные вызовыПоддержкаПоддержкаПоддержка (лучше всех)
Вызов после сложного рассужденияСильноЛучше всехСильно
Tool Use с длинным контекстом128K200K2M (с отрывом)
Протокол APIНативный OpenAIСовместим с OpenAIСовместим с OpenAI
Прямой доступ из КитаяНужен шлюз-агрегаторНужен шлюз-агрегаторНужен шлюз-агрегатор

Различия на уровне кода

Хорошая новость: если вы используете OpenAI-совместимый интерфейс, код для всех трёх моделей абсолютно одинаковый — нужно лишь менять параметр model:

# GPT-5.4
response = client.chat.completions.create(model="gpt-5.4", messages=messages, tools=tools)

# Claude Sonnet 4.6
response = client.chat.completions.create(model="claude-sonnet-4.6", messages=messages, tools=tools)

# Gemini 3.1 Pro
response = client.chat.completions.create(model="gemini-3.1-pro", messages=messages, tools=tools)

Через API-шлюз-агрегатор (например, Ofox) вы можете одним API Key и base_url вызывать все модели, переключаясь в коде в любой момент, без необходимости управлять множеством ключей.

Рекомендации по выбору модели

  • Универсальная разработка Agent: GPT-4.1-mini или Claude Sonnet 4.6 (лучшее соотношение цена/качество)
  • Agent для сложных рассуждений: Claude Opus 4.6 или Gemini 3.1 Pro
  • Обработка сверхдлинных документов: Gemini 3.1 Pro (контекст 2M)
  • Ограниченный бюджет: DeepSeek V3 или GPT-4.1-nano

Лучшие практики для продакшена

От демо до продакшена — есть несколько ключевых задач, которые нужно решить.

1. Обработка ошибок и повторные попытки

def run_agent_production(user_message: str, max_rounds: int = 10):
    """Продакшен-уровень Agent Loop с обработкой ошибок и лимитом итераций"""
    messages = [
        {"role": "system", "content": "Ты умный помощник. Если вызов инструмента завершился ошибкой, сообщи пользователю причину и предложи альтернативу."},
        {"role": "user", "content": user_message}
    ]

    for round_num in range(max_rounds):
        try:
            response = client.chat.completions.create(
                model="gpt-4.1-mini",
                messages=messages,
                tools=tools,
                tool_choice="auto",
                timeout=30
            )
        except Exception as e:
            return f"Ошибка вызова API: {e}"

        msg = response.choices[0].message
        messages.append(msg)

        if not msg.tool_calls:
            return msg.content

        for tool_call in msg.tool_calls:
            func_name = tool_call.function.name
            try:
                func_args = json.loads(tool_call.function.arguments)
                func = TOOL_FUNCTIONS.get(func_name)

                if not func:
                    result = json.dumps({"error": f"Неизвестный инструмент: {func_name}"})
                else:
                    result = func(**func_args)

            except json.JSONDecodeError:
                result = json.dumps({"error": "Ошибка разбора параметров"})
            except Exception as e:
                result = json.dumps({"error": f"Ошибка выполнения инструмента: {str(e)}"})

            messages.append({
                "role": "tool",
                "tool_call_id": tool_call.id,
                "content": result
            })

    return "Agent достиг максимального числа итераций. Упростите запрос и попробуйте снова."

2. Защита безопасности

Agent может вызывать инструменты, а значит — создавать побочные эффекты. Обязательно обеспечьте защиту:

# Контроль прав доступа к инструментам
TOOL_PERMISSIONS = {
    "get_weather": {"level": "read", "rate_limit": 60},     # Только чтение, 60 раз/мин
    "search_database": {"level": "read", "rate_limit": 30},  # Только чтение
    "calculate": {"level": "compute", "rate_limit": 100},    # Вычисления
    "send_email": {"level": "write", "require_confirm": True}, # Запись — нужно подтверждение
    "delete_record": {"level": "dangerous", "require_confirm": True}, # Опасная операция — нужно подтверждение
}

def execute_tool_safely(func_name: str, func_args: dict) -> str:
    """Безопасное выполнение инструмента с проверкой прав"""
    perm = TOOL_PERMISSIONS.get(func_name)
    if not perm:
        return json.dumps({"error": "Неавторизованный инструмент"})

    # Опасные операции требуют подтверждения человеком (в продакшене — интеграция с системой согласования)
    if perm.get("require_confirm"):
        print(f"Внимание! Операция повышенного риска: {func_name}({func_args})")
        # В веб-приложении можно показать диалог подтверждения
        # confirm = await get_user_confirmation(func_name, func_args)
        return json.dumps({"error": "Эта операция требует подтверждения — запрос на согласование отправлен"})

    return TOOL_FUNCTIONS[func_name](**func_args)

3. Потоковый вывод + вызовы инструментов

В продакшене потоковый вывод значительно улучшает пользовательский опыт:

def run_agent_streaming(user_message: str):
    """Agent с потоковым выводом"""
    messages = [
        {"role": "system", "content": "Ты умный помощник."},
        {"role": "user", "content": user_message}
    ]

    while True:
        stream = client.chat.completions.create(
            model="gpt-4.1-mini",
            messages=messages,
            tools=tools,
            stream=True
        )

        tool_calls_buffer = {}
        content_buffer = ""

        for chunk in stream:
            delta = chunk.choices[0].delta

            # Текстовый контент: вывод в реальном времени
            if delta.content:
                print(delta.content, end="", flush=True)
                content_buffer += delta.content

            # Вызовы инструментов: сбор полной информации о вызове
            if delta.tool_calls:
                for tc in delta.tool_calls:
                    idx = tc.index
                    if idx not in tool_calls_buffer:
                        tool_calls_buffer[idx] = {
                            "id": tc.id or "",
                            "name": "",
                            "arguments": ""
                        }
                    if tc.id:
                        tool_calls_buffer[idx]["id"] = tc.id
                    if tc.function and tc.function.name:
                        tool_calls_buffer[idx]["name"] = tc.function.name
                    if tc.function and tc.function.arguments:
                        tool_calls_buffer[idx]["arguments"] += tc.function.arguments

        # Если нет вызовов инструментов — возвращаем текст
        if not tool_calls_buffer:
            print()  # Перенос строки
            return content_buffer

        # Выполнение вызовов инструментов и добавление результатов в messages (логика та же)
        assistant_msg = {"role": "assistant", "content": content_buffer or None, "tool_calls": []}
        for idx in sorted(tool_calls_buffer.keys()):
            tc = tool_calls_buffer[idx]
            assistant_msg["tool_calls"].append({
                "id": tc["id"],
                "type": "function",
                "function": {"name": tc["name"], "arguments": tc["arguments"]}
            })
        messages.append(assistant_msg)

        for idx in sorted(tool_calls_buffer.keys()):
            tc = tool_calls_buffer[idx]
            func_args = json.loads(tc["arguments"])
            result = TOOL_FUNCTIONS[tc["name"]](**func_args)
            messages.append({
                "role": "tool",
                "tool_call_id": tc["id"],
                "content": result
            })

4. Управление памятью диалога

Долго работающий Agent нуждается в управлении контекстным окном:

def manage_context(messages: list, max_tokens: int = 8000) -> list:
    """Управление контекстным окном: сохранение системного промпта и последних сообщений"""
    # Грубая оценка числа токенов (для кириллицы ~2-3 токена/слово)
    estimated_tokens = sum(len(m.get("content", "") or "") * 1.5 for m in messages)

    if estimated_tokens <= max_tokens:
        return messages

    # Сохраняем системные сообщения и последние сообщения диалога
    system_msgs = [m for m in messages if m.get("role") == "system"]
    other_msgs = [m for m in messages if m.get("role") != "system"]

    # Сохраняем начиная с последних сообщений
    kept = []
    current_tokens = sum(len(m.get("content", "") or "") * 1.5 for m in system_msgs)

    for msg in reversed(other_msgs):
        msg_tokens = len(msg.get("content", "") or "") * 1.5
        if current_tokens + msg_tokens > max_tokens:
            break
        kept.insert(0, msg)
        current_tokens += msg_tokens

    return system_msgs + kept

Часто задаваемые вопросы (FAQ)

В: Нужен ли GPU для разработки AI Agent?

О: Нет. Разработка AI Agent — это чисто инженерная задача. Вы вызываете большие модели в облаке через API, а локально нужен лишь обычный компьютер и среда Python. Вычислительные ресурсы для инференса предоставляет провайдер API.

В: Сколько инструментов оптимально для одного AI Agent?

О: Рекомендуется 5-15 инструментов. Слишком мало — ограниченные возможности; слишком много — снижается точность выбора модели. Если нужно больше возможностей, лучше разделить на несколько специализированных агентов.

В: Как валидировать параметры Function Calling?

О: Рекомендуется использовать Pydantic. Определите параметры инструмента как Pydantic Model — автоматическая генерация JSON Schema и валидация ввода:

from pydantic import BaseModel, Field

class WeatherParams(BaseModel):
    city: str = Field(description="Название города")
    unit: str = Field(default="celsius", description="Единица измерения температуры")

# Автоматическая генерация JSON Schema
schema = WeatherParams.model_json_schema()

В: Сколько стоит один вызов API для AI Agent?

О: Для GPT-4.1-mini: ввод $0.4/млн токенов, вывод $1.6/млн токенов. Типичный диалог Agent (с 2-3 вызовами инструментов) потребляет около 2000-5000 токенов, стоимость ~$0.001-$0.005. Через платформу-агрегатор API обычно можно получить ещё более выгодные цены.

В: Как отладить Function Calling? Что делать, если модель не вызывает инструмент?

О: Три направления проверки: 1) Описание инструмента должно быть чётким — модель решает на основе описания; 2) В system prompt явно укажите, какие инструменты доступны; 3) Используйте tool_choice={"type": "function", "function": {"name": "xxx"}} для принудительного вызова конкретного инструмента при тестировании.

Итоги и план действий

Разработка AI Agent в 2026 году — это уже не передовая технология с высоким порогом входа, а инженерный навык, которым должен владеть каждый разработчик.

Три шага для немедленного старта:

  1. Запустите первого Agent: скопируйте код примера с погодой из этой статьи, подставьте свой API Key — через 5 минут всё заработает
  2. Подключите свои бизнес-инструменты: оберните запросы к базе данных и сторонним API вашего проекта в функции инструментов
  3. Разверните в продакшене: добавьте обработку ошибок, контроль доступа и управление памятью диалога

Если вам нужно быстро подключить несколько моделей для тестирования, какая лучше подходит для вашего Agent-сценария, попробуйте Ofox.ai — один API Key для вызова GPT, Claude, Gemini, DeepSeek и других 50+ моделей, совместимость с OpenAI SDK, узлы ускорения в Китае на Alibaba Cloud / Volcano Cloud, бесплатный баланс при регистрации.

Справочные материалы