Друзья, приветствую! Надеюсь, успели соскучиться.
Последние пару месяцев я с головой ушёл в исследование интеграции ИИ-агентов в собственные Python-проекты. В процессе накопилось немало практических знаний и наблюдений, которыми просто грех не поделиться. Поэтому сегодня я возвращаюсь на Хабр — с новой темой, свежим взглядом и с намерением писать чаще.
На повестке дня — LangGraph и MCP: инструменты, с помощью которых можно создавать действительно полезных ИИ-агентов.
Если раньше мы спорили о том, какая нейросеть лучше отвечает на русском языке, то сегодня поле битвы сместилось в сторону более прикладных задач: кто лучше справляется с ролью ИИ-агента? Какие фреймворки действительно упрощают разработку? И как интегрировать всё это добро в реальный проект?
Но прежде чем нырнуть в практику и код, давайте разберёмся с базовыми понятиями. Особенно с двумя ключевыми: ИИ-агенты и MCP. Без них разговор про LangGraph будет неполным.
ИИ-агенты простыми словами
ИИ-агенты — это не просто «прокачанные» чат-боты. Они представляют собой более сложные, автономные сущности, которые обладают двумя важнейшими особенностями:
-
Умение взаимодействовать и координироваться.
Современные агенты способны делить задачи на подзадачи, вызывать других агентов, запрашивать внешние данные, работать в команде. Это уже не одиночный ассистент, а распределённая система, где каждый компонент может вносить свой вклад. -
Доступ к внешним ресурсам.
ИИ-агент больше не ограничен рамками диалога. Он может обращаться к базам данных, выполнять вызовы к API, взаимодействовать с локальными файлами, векторными хранилищами знаний и даже запускать команды в терминале. Всё это стало возможным благодаря появлению MCP — нового уровня интеграции между моделью и средой.
Что такое MCP (Model Context Protocol)
Если говорить просто: MCP — это мост между нейросетью и её окружением. Он позволяет модели «понимать» контекст задачи, получать доступ к данным, выполнять вызовы и формировать обоснованные действия, а не просто выдавать текстовые ответы.
Представим аналогию:
-
У вас есть нейросеть — она умеет рассуждать и генерировать тексты.
-
Есть данные и инструменты — документы, API, базы знаний, терминал, код.
-
И есть MCP — это интерфейс, который позволяет модели взаимодействовать с этими внешними источниками так, как если бы они были частью её внутреннего мира.
Без MCP:
Модель — это изолированный диалоговый движок. Вы подаёте ей текст — она отвечает. И всё.
С MCP:
Модель становится полноценным исполнителем задач:
-
получает доступ к структурам данных и API;
-
вызывает внешние функции;
-
ориентируется в текущем состоянии проекта или приложения;
-
может запоминать, отслеживать и изменять контекст по мере диалога;
-
использует расширения, такие как инструменты поиска, код-раннеры, базу векторных эмбеддингов и пр.
В техническом смысле MCP — это протокол взаимодействия между LLM и её окружением, где контекст подаётся в виде структурированных объектов (вместо «сырого» текста), а вызовы оформляются как интерактивные операции (например, function calling, tool usage или agent actions). Именно это и превращает обычную модель в настоящего ИИ-агента, способного делать больше, чем просто «поговорить».
А теперь — к делу!
Теперь, когда мы разобрались с базовыми понятиями, логично задаться вопросом: «Как всё это реализовать на практике в Python?»
Вот здесь и вступает в игру LangGraph — мощный фреймворк для построения графов состояний, поведения агентов и цепочек мышления. Он позволяет «прошивать» логику взаимодействия между агентами, инструментами и пользователем, создавая живую архитектуру ИИ, адаптирующуюся к задачам.
В следующих разделах мы посмотрим, как:
-
строится агент с нуля;
-
создаются состояния, переходы и события;
-
интегрируются функции и инструменты;
-
и как вся эта экосистема работает в реальном проекте.
Немного теории: что такое LangGraph
Прежде чем приступить к практике, нужно сказать пару слов о самом фреймворке.
LangGraph — это проект от команды LangChain, тех самых, кто первыми предложили концепцию «цепочек» (chains) взаимодействия с LLM. Если раньше основной упор делался на линейные или условно-ветвящиеся пайплайны (langchain.chains), то теперь разработчики делают ставку на графовую модель, и именно LangGraph они рекомендуют как новое «ядро» для построения сложных ИИ-сценариев.
LangGraph — это фреймворк для построения конечных автоматов и графов состояний, в которых каждый нода (или узел) представляет собой часть логики агента: вызов модели, внешний инструмент, условие, пользовательский ввод и т.д.
Как это работает: графы и узлы
Концептуально, LangGraph строится на следующих идеях:
-
Граф — это структура, которая описывает возможные пути выполнения логики. Можно думать о нём как о карте: из одной точки можно перейти в другую в зависимости от условий или результата выполнения.
-
Узлы (ноды) — это конкретные шаги внутри графа. Каждый узел выполняет какую-то функцию: вызывает модель, вызывает внешний API, проверяет условие или просто обновляет внутреннее состояние.
-
Переходы между узлами — это логика маршрутизации: если результат предыдущего шага такой-то, то идём туда-то.
-
Состояние (state) — передаётся между узлами и накапливает всё, что нужно: историю, промежуточные выводы, пользовательский ввод, результат работы инструментов и т.д.
Таким образом, мы получаем гибкий механизм управления логикой агента, в котором можно описывать как простые, так и очень сложные сценарии: циклы, условия, параллельные действия, вложенные вызовы и многое другое.
Почему это удобно?
LangGraph позволяет строить прозрачную, воспроизводимую и расширяемую логику:
-
легко отлаживать;
-
легко визуализировать;
-
легко масштабировать под новые задачи;
-
легко интегрировать внешние инструменты и MCP-протоколы.
По сути, LangGraph — это «мозг» агента, где каждый шаг задокументирован, контролируем и может быть изменён без хаоса и «магии».
Ну а теперь — хватит теории!
Можно ещё долго рассказывать о графах, состояний, композиции логики и преимуществах LangGraph над классическими пайплайнами. Но, как показывает практика, лучше один раз увидеть в коде.
Пора перейти к практике. Дальше — пример на Python: создадим простого, но полезного ИИ-агента на базе LangGraph, который будет использовать внешние инструменты, память и принимать решения сам.
Подготовка: облачные и локальные нейросети
Для того чтобы приступить к созданию ИИ-агентов, нам в первую очередь нужен мозг — языковая модель. Здесь есть два подхода:
-
использовать облачные решения, где всё готово «из коробки»;
-
или поднять модель локально — для полной автономии и конфиденциальности.
Рассмотрим оба варианта.
Облачные сервисы: быстро и удобно
Самый простой путь — воспользоваться мощностями крупных провайдеров: OpenAI, Anthropic, DeepSeek, Groq, Mistral и других. Вам достаточно приобрести API-ключ и начать использовать модели через стандартные HTTP-запросы.
Где взять ключи и токены:
-
OpenAI — ChatGPT и другие продукты;
-
Anthropic — Claude;
-
OpenRouter.ai — десятки моделей (один токен — множество моделей через OpenAI-совместимый API);
-
Amvera Cloud — возможность подключить LLaMA с оплатой рублями и встроенным проксированием до OpenAI и Anthropic.
Этот путь удобен, особенно если вы:
-
не хотите настраивать инфраструктуру;
-
разрабатываете с упором на скорость;
-
работаете с ограниченными ресурсами.
Локальные модели: полный контроль
Если вам важна приватность, работа без интернета или вы хотите строить полностью автономные агенты, то имеет смысл развернуть нейросеть локально.
Основные преимущества:
-
Конфиденциальность — данные остаются у вас;
-
Работа без интернета — полезно в изолированных сетях;
-
Отсутствие подписок и токенов — бесплатно после настройки.
Недостатки очевидны:
-
Требования к ресурсам (особенно к видеопамяти);
-
Настройка может занять время;
-
Некоторые модели сложно развернуть без опыта.
Тем не менее, есть инструменты, которые делают локальный запуск проще. Один из лучших на сегодня — это Ollama.
Развёртывание локальной LLM через Ollama + Docker
Мы подготовим локальный запуск модели Qwen 2.5 (qwen2.5:32b) с использованием Docker-контейнера и системы Ollama. Это позволит интегрировать нейросеть с MCP и использовать её в собственных агентах на базе LangGraph.
Если вычислительных ресурсов вашего компьютера или сервера окажется недостаточно для работы с данной версией модели, вы всегда можете выбрать менее «прожорливую» нейросеть — процесс установки и запуска останется аналогичным.
📦 Быстрая установка (сводка шагов)
-
Установите Docker + Docker Compose
-
Создайте структуру проекта:
mkdir qwen-local && cd qwen-local -
Создайте
docker-compose.yml
(универсальный вариант, GPU определяется автоматически)services: ollama: image: ollama/ollama:latest container_name: ollama_qwen ports: - '11434:11434' volumes: - ./ollama_data:/root/.ollama - /tmp:/tmp environment: - OLLAMA_HOST=0.0.0.0 - OLLAMA_ORIGINS=* restart: unless-stopped -
Запустите контейнер:
docker compose up -d -
Загрузите модель:
docker exec -it ollama_qwen ollama pull qwen2.5:32b -
Проверьте работу через API:
curl http://localhost:11434/api/generate \ -H "Content-Type: application/json" \ -d '{"model": "qwen2.5:32b", "prompt": "Привет!", "stream": false}'

-
Интеграция с Python:
import requests def query(prompt): res = requests.post("http://localhost:11434/api/generate", json={ "model": "qwen2.5:32b", "prompt": prompt, "stream": False }) return res.json()['response'] print(query("Объясни квантовую запутанность"))
Теперь у вас полноценная локальная LLM, готовая к работе с MCP и LangGraph.
Что дальше?
У нас есть выбор между облачными и локальными моделями, и мы научились подключать обе. Самое интересное впереди — создание ИИ-агентов на LangGraph, которые используют выбранную модель, память, инструменты и собственную логику.
Переходим к самому вкусному — коду и практике!
Подготовка к написанию кода
Перед тем как перейти к практике, важно подготовить рабочее окружение. Я предполагаю, что вы уже знакомы с основами Python, знаете, что такое библиотеки и зависимости, и понимаете, зачем использовать виртуальное окружение.
Если всё это вам в новинку — рекомендую сначала пройти короткий курс или гайд по Python-базе, а затем возвращаться к статье.
Шаг 1: Создание виртуального окружения
Создайте новое виртуальное окружение в папке проекта:
python -m venv venv source venv/bin/activate # для Linux/macOS venv\Scripts\activate # для Windows
Шаг 2: Установка зависимостей
Создайте файл requirements.txt и добавьте в него следующие строки:
langchain==0.3.26 langchain-core==0.3.69 langchain-deepseek==0.1.3 langchain-mcp-adapters==0.1.9 langchain-ollama==0.3.5 langchain-openai==0.3.28 langgraph==0.5.3 langgraph-checkpoint==2.1.1 langgraph-prebuilt==0.5.2 langgraph-sdk==0.1.73 langsmith==0.4.8 mcp==1.12.0 ollama==0.5.1 openai==1.97.0
⚠️ Актуальные версии указаны на 21 июля 2025 года. С момента публикации они могли измениться — проверяйте актуальность перед установкой.
Затем установите зависимости:
pip install -r requirements.txt
Шаг 3: Конфигурация переменных окружения
Создайте в корне проекта файл .env и добавьте в него нужные API-ключи:
OPENAI_API_KEY=sk-proj-1234 DEEPSEEK_API_KEY=sk-123 OPENROUTER_API_KEY=sk-or-v1-123 BRAVE_API_KEY=BSAj123KlbvBGpH1344tLwc
Назначение переменных:
-
OPENAI_API_KEY— ключ для доступа к GPT-моделям от OpenAI; -
DEEPSEEK_API_KEY— ключ для использования моделей Deepseek; -
OPENROUTER_API_KEY— единый ключ для доступа к множеству моделей через OpenRouter (можно получить бесплатно); -
BRAVE_API_KEY— API-ключ для MCP-модуля web-поиска через Brave Search (можно получить бесплатно).
Некоторые MCP-инструменты (например,
brave-web-search) требуют ключ для работы. Без него они просто не активируются.
А если у вас нет API-ключей?
Не проблема. Вы можете начать разработку с локальной моделью (например, через Ollama), не подключая ни одного внешнего сервиса. В этом случае файл .env можно не создавать вовсе.
Готово! Теперь у нас есть всё необходимое для начала — изолированное окружение, зависимости, и, при необходимости, доступ к облачным нейросетям и MCP-интеграциям.
Далее — запустим нашего LLM-агента разными способами.
Простой запуск LLM-агентов через LangGraph: базовая интеграция
Начнём с самого простого: как «подключить мозг» к будущему агенту. Мы разберём базовые способы запуска языковых моделей (LLM) с помощью LangChain, чтобы в следующем шаге перейти к интеграции с LangGraph и построению полноценного ИИ-агента.
Импорты
import os from dotenv import load_dotenv from langchain_openai import ChatOpenAI from langchain_ollama import ChatOllama from langchain_deepseek import ChatDeepSeek
-
osиload_dotenv()— для загрузки переменных из.env-файла. -
ChatOpenAI,ChatOllama,ChatDeepSeek— обёртки для подключения языковых моделей через LangChain.
💡 Если вы используете альтернативные подходы к работе с конфигурациями (например, Pydantic Settings), можете заменить
load_dotenv()на свой привычный способ.
Загрузка переменных окружения
load_dotenv()
Это подгрузит все переменные из .env, включая ключи для доступа к API OpenAI, DeepSeek, OpenRouter и другим.
Простые функции для получения LLM
OpenAI
def get_openai_llm(): return ChatOpenAI(model="gpt-4o-mini", api_key=os.getenv("OPENAI_API_KEY"))
Если переменная OPENAI_API_KEY корректно задана, LangChain подставит её автоматически — явное указание api_key=... здесь опционально.
DeepSeek
def get_deepseek_llm(): return ChatDeepSeek(model="deepseek-chat", api_key=os.getenv("DEEPSEEK_API_KEY"))
Аналогично, но используем обёртку ChatDeepSeek.
OpenRouter (и другие совместимые API)
def get_openrouter_llm(model="moonshotai/kimi-k2:free"): return ChatOpenAI( model=model, api_key=os.getenv("OPENROUTER_API_KEY"), base_url="https://openrouter.ai/api/v1", temperature=0 )
Особенности:
-
ChatOpenAIиспользуется, несмотря на то что модель не от OpenAI — потому что OpenRouter использует тот же протокол. -
base_urlобязателен: он указывает на OpenRouter API. -
Модель
moonshotai/kimi-k2:freeвыбрана как один из наиболее сбалансированных вариантов по качеству и скорости на момент написания статьи. -
API-ключ OpenRouter нужно передавать явно — автоматическая подстановка здесь не работает.
Мини-тест: проверка работы модели
if __name__ == "__main__": llm = get_openrouter_llm(model="moonshotai/kimi-k2:free") response = llm.invoke("Кто ты?") print(response.content)

Если всё настроено правильно, вы получите осмысленный ответ от модели. Поздравляю — первый шаг сделан!
Но это ещё не агент
На текущем этапе мы подключили LLM и сделали простой вызов. Это больше похоже на консольного чат-бота, чем на ИИ-агента.
Почему?
-
Мы пишем синхронный, линейный код без логики состояния или целей.
-
Агент не принимает решений, не запоминает контекст и не использует инструменты.
-
MCP и LangGraph пока не задействованы.
Что дальше?
Далее мы реализуем полноценного ИИ-агента с использованием LangGraph — сначала без MCP, чтобы сфокусироваться на архитектуре, состояниях и логике самого агента.
Погружаемся в настоящую агентную механику. Поехали!
Агент классификации вакансий: от теории к практике
Создадим реального ИИ-агента, который будет решать конкретную бизнес-задачу — автоматическую классификацию описаний вакансий и услуг. Этот пример покажет, как применить концепции LangGraph на практике и создать полезный инструмент для HR-платформ и бирж фриланса.
Задача агента
Наш агент принимает на вход текстовое описание вакансии или услуги и выполняет трёхуровневую классификацию:
-
Тип работы: проектная работа или постоянная вакансия
-
Категория профессии: из 45+ предопределённых специальностей
-
Тип поиска: ищет ли человек работу или ищет исполнителя
Результат возвращается в структурированном JSON-формате с оценкой уверенности для каждой классификации.
🏗️ Архитектура агента на LangGraph
Следуя принципам LangGraph, создаём граф состояний из четырёх узлов:
📝 Входное описание ↓ 🔍 Узел классификации типа работы ↓ 📂 Узел классификации категории ↓ 🎯 Узел определения типа поиска ↓ 📊 Узел расчёта уверенности ↓ ✅ JSON-результат
Каждый узел — это специализированная функция, которая:
-
Получает текущее состояние агента
-
Выполняет свою часть анализа
-
Обновляет состояние и передаёт его дальше
Управление состоянием
Определяем структуру памяти агента через TypedDict:
class State(TypedDict): """Состояние агента для хранения информации о процессе классификации""" description: str job_type: str category: str search_type: str confidence_scores: Dict[str, float] processed: bool
Это рабочая память агента — всё, что он помнит и накапливает в процессе анализа. Подобно тому, как человек-эксперт держит в уме контекст задачи при анализе документа.
Давайте рассмотрим полный код, а после сконцентрируемся на основных моментах.
import asyncio import json from typing import TypedDict, Dict, Any from enum import Enum from langgraph.graph import StateGraph, END from langchain.prompts import PromptTemplate from langchain_openai import ChatOpenAI from langchain.schema import HumanMessage # Категории профессий CATEGORIES = [ "2D-аниматор", "3D-аниматор", "3D-моделлер", "Бизнес-аналитик", "Блокчейн-разработчик", ... ] class JobType(Enum): PROJECT = "проектная работа" PERMANENT = "постоянная работа" class SearchType(Enum): LOOKING_FOR_WORK = "поиск работы" LOOKING_FOR_PERFORMER = "поиск исполнителя" class State(TypedDict): """Состояние агента для хранения информации о процессе классификации""" description: str job_type: str category: str search_type: str confidence_scores: Dict[str, float] processed: bool class VacancyClassificationAgent: """Асинхронный агент для классификации вакансий и услуг""" def __init__(self, model_name: str = "gpt-4o-mini", temperature: float = 0.1): """Инициализация агента""" self.llm = ChatOpenAI(model=model_name, temperature=temperature) self.workflow = self._create_workflow() def _create_workflow(self) -> StateGraph: """Создает рабочий процесс агента на основе LangGraph""" workflow = StateGraph(State) # Добавляем узлы в граф workflow.add_node("job_type_classification", self._classify_job_type) workflow.add_node("category_classification", self._classify_category) workflow.add_node("search_type_classification", self._classify_search_type) workflow.add_node("confidence_calculation", self._calculate_confidence) # Определяем последовательность выполнения узлов workflow.set_entry_point("job_type_classification") workflow.add_edge("job_type_classification", "category_classification") workflow.add_edge("category_classification", "search_type_classification") workflow.add_edge("search_type_classification", "confidence_calculation") workflow.add_edge("confidence_calculation", END) return workflow.compile() async def _classify_job_type(self, state: State) -> Dict[str, Any]: """Узел для определения типа работы: проектная или постоянная""" prompt = PromptTemplate( input_variables=["description"], template=""" Проанализируй следующее описание и определи тип работы. Описание: {description} Ответь только одним из двух вариантов: - "проектная работа" - если это временная задача, проект, фриланс, разовая работа - "постоянная работа" - если это постоянная должность, штатная позиция, долгосрочное трудоустройство Тип работы: """ ) message = HumanMessage(content=prompt.format(description=state["description"])) response = await self.llm.ainvoke([message]) job_type = response.content.strip().lower() # Нормализуем ответ if "проектная" in job_type or "проект" in job_type or "фриланс" in job_type: job_type = JobType.PROJECT.value else: job_type = JobType.PERMANENT.value return {"job_type": job_type} async def _classify_category(self, state: State) -> Dict[str, Any]: """Узел для определения категории профессии""" categories_str = "\n".join([f"- {cat}" for cat in CATEGORIES]) prompt = PromptTemplate( input_variables=["description", "categories"], template=""" Проанализируй описание вакансии/услуги и определи наиболее подходящую категорию из списка. Описание: {description} Доступные категории: {categories} Выбери ТОЧНО одну категорию из списка выше, которая лучше всего соответствует описанию. Ответь только названием категории без дополнительных пояснений. Категория: """ ) message = HumanMessage(content=prompt.format( description=state["description"], categories=categories_str )) response = await self.llm.ainvoke([message]) category = response.content.strip() # Проверяем, есть ли категория в списке доступных if category not in CATEGORIES: # Ищем наиболее похожую категорию category = self._find_closest_category(category) return {"category": category} async def _classify_search_type(self, state: State) -> Dict[str, Any]: """Узел для определения типа поиска""" prompt = PromptTemplate( input_variables=["description"], template=""" Проанализируй описание и определи, кто и что ищет. Описание: {description} Ответь только одним из двух вариантов: - "поиск работы" - если соискатель ищет работу/заказы - "поиск исполнителя" - если работодатель/заказчик ищет исполнителя Обрати внимание на ключевые слова: - "ищу работу", "резюме", "хочу работать" = поиск работы - "требуется", "ищем", "вакансия", "нужен специалист" = поиск исполнителя Тип поиска: """ ) message = HumanMessage(content=prompt.format(description=state["description"])) response = await self.llm.ainvoke([message]) search_type = response.content.strip().lower() # Нормализуем ответ if "поиск работы" in search_type or "ищу работу" in search_type: search_type = SearchType.LOOKING_FOR_WORK.value else: search_type = SearchType.LOOKING_FOR_PERFORMER.value return {"search_type": search_type} async def _calculate_confidence(self, state: State) -> Dict[str, Any]: """Узел для расчета уровня уверенности в классификации""" prompt = PromptTemplate( input_variables=["description", "job_type", "category", "search_type"], template=""" Оцени уверенность классификации по шкале от 0.0 до 1.0 для каждого параметра: Описание: {description} Тип работы: {job_type} Категория: {category} Тип поиска: {search_type} Ответь в формате JSON: {{ "job_type_confidence": 0.0-1.0, "category_confidence": 0.0-1.0, "search_type_confidence": 0.0-1.0 }} """ ) message = HumanMessage(content=prompt.format( description=state["description"], job_type=state["job_type"], category=state["category"], search_type=state["search_type"] )) response = await self.llm.ainvoke([message]) try: confidence_scores = json.loads(response.content.strip()) except: # Fallback значения если парсинг не удался confidence_scores = { "job_type_confidence": 0.7, "category_confidence": 0.7, "search_type_confidence": 0.7 } return { "confidence_scores": confidence_scores, "processed": True } def _find_closest_category(self, predicted_category: str) -> str: """Находит наиболее похожую категорию из списка доступных""" # Простая эвристика поиска по вхождению ключевых слов predicted_lower = predicted_category.lower() for category in CATEGORIES: category_lower = category.lower() if predicted_lower in category_lower or category_lower in predicted_lower: return category # Если ничего не найдено, возвращаем первую категорию как fallback return CATEGORIES[0] async def classify(self, description: str) -> Dict[str, Any]: """Основной метод для классификации вакансии/услуги""" initial_state = { "description": description, "job_type": "", "category": "", "search_type": "", "confidence_scores": {}, "processed": False } # Запускаем рабочий процесс result = await self.workflow.ainvoke(initial_state) # Формируем итоговый ответ в формате JSON classification_result = { "job_type": result["job_type"], "category": result["category"], "search_type": result["search_type"], "confidence_scores": result["confidence_scores"], "success": result["processed"] } return classification_result async def main(): """Демонстрация работы агента""" agent = VacancyClassificationAgent() # Тестовые примеры test_cases = [ "Требуется Python разработчик для создания веб-приложения на Django. Постоянная работа, полный рабочий день.", "Ищу заказы на создание логотипов и фирменного стиля. Работаю в Adobe Illustrator.", "Нужен 3D-аниматор для краткосрочного проекта создания рекламного ролика.", "Резюме: опытный маркетолог, ищу удаленную работу в сфере digital-маркетинга", "Ищем фронтенд-разработчика React в нашу команду на постоянную основе" ] test_cases = [] print("🤖 Демонстрация работы агента классификации вакансий\n") for i, description in enumerate(test_cases, 1): print(f"📋 Тест {i}:") print(f"Описание: {description}") try: result = await agent.classify(description) print("Результат классификации:") print(json.dumps(result, ensure_ascii=False, indent=2)) except Exception as e: print(f"❌ Ошибка: {e}") print("-" * 80) if __name__ == "__main__": asyncio.run(main())
Асинхронная архитектура
В отличие от простых примеров, наш агент работает полностью асинхронно:
async def _classify_job_type(self, state: State) -> Dict[str, Any]: """Узел для определения типа работы: проектная или постоянная""" response = await self.llm.ainvoke([message]) # Обработка ответа... return {"job_type": job_type}
Это позволяет:
-
Обрабатывать множественные запросы параллельно
-
Интегрироваться с асинхронными веб-фреймворками
-
Эффективно использовать ресурсы при масштабировании
Специализированные промпты для каждого узла
Каждый узел использует целевой промпт, оптимизированный под свою задачу:
For job type classification:
template=""" Проанализируй следующее описание и определи тип работы. Описание: {description} Ответь только одним из двух вариантов: - "проектная работа" - если это временная задача, проект, фриланс - "постоянная работа" - если это постоянная должность, штатная позиция Тип работы: """
Такой подход обеспечивает высокую точность каждого этапа классификации, так как модель получает чёткие, специфичные инструкции.
Обработка ошибок и fallback-механизмы
Агент включает умную обработку неожиданных ситуаций:
def _find_closest_category(self, predicted_category: str) -> str: """Находит наиболее похожую категорию из списка доступных""" predicted_lower = predicted_category.lower() for category in CATEGORIES: category_lower = category.lower() if predicted_lower in category_lower or category_lower in predicted_lower: return category # Fallback: возвращаем первую категорию return CATEGORIES[0]
Это делает агента устойчивым к ошибкам — даже если модель вернёт неточный результат, система найдёт наиболее подходящий вариант из допустимых.
Оценка уверенности
Финальный узел рассчитывает метрики качества классификации:
async def _calculate_confidence(self, state: State) -> Dict[str, Any]: """Узел для расчета уровня уверенности в классификации""" # Запрос к LLM для оценки уверенности по шкале 0.0-1.0 # Возврат структурированного JSON с метриками
Это позволяет:
-
Отслеживать качество работы агента
-
Выделять случаи, требующие ручной проверки
-
Оптимизировать промпты на основе статистики
Практическое применение
async def main(): agent = VacancyClassificationAgent() description = "Требуется Python разработчик для создания веб-приложения" result = await agent.classify(description) print(json.dumps(result, ensure_ascii=False, indent=2))
Результат:
{ "job_type": "постоянная работа", "category": "Бэкенд-разработчик (Node.js, Python, PHP, Ruby)", "search_type": "поиск исполнителя", "confidence_scores": { "job_type_confidence": 0.95, "category_confidence": 0.88, "search_type_confidence": 0.92 }, "success": true }
Ключевые преимущества архитектуры
-
Модульность — каждый узел решает одну задачу, легко тестировать и улучшать отдельно
-
Расширяемость — можно добавлять новые узлы анализа без изменения существующих
-
Прозрачность — весь процесс принятия решений документирован и отслеживаем
-
Производительность — асинхронная обработка множественных запросов
-
Надёжность — fallback-механизмы и обработка ошибок
Реальная польза
Такой агент может использоваться в:
-
HR-платформах для автоматической категоризации резюме и вакансий
-
Биржах фриланса для улучшения поиска и рекомендаций
-
Внутренних системах компаний для обработки заявок и проектов
-
Аналитических решениях для исследования рынка труда
MCP в действии: создаём агента с файловой системой и веб-поиском
После того как мы разобрались с базовыми принципами LangGraph и создали простого классификатора, пора перейти к настоящей магии — интеграции агента с внешним миром через MCP (Model Context Protocol).
Сейчас мы создадим полноценного ИИ-помощника, который сможет:
-
Работать с файловой системой (читать, создавать, изменять файлы)
-
Искать актуальную информацию в интернете
-
Запоминать контекст диалога
-
Обрабатывать ошибки и восстанавливаться после сбоев
От теории к реальным инструментам
Помните, как в начале статьи мы говорили о том, что MCP — это мост между нейросетью и её окружением? Сейчас вы увидите это на практике. Наш агент получит доступ к реальным инструментам:
# Инструменты файловой системы - read_file — чтение файлов - write_file — запись и создание файлов - list_directory — просмотр содержимого папок - create_directory — создание папок # Инструменты веб-поиска - brave_web_search — поиск в интернете - get_web_content — получение содержимого страниц
Это уже не «игрушечный» агент — это рабочий инструмент, который может решать реальные задачи.
Кода и текста уже накопилось достаточно много, поэтому далее я приведу лишь общее описание принципов и концепции разработки подобных ИИ-агентов с интеграцией MCP. Полный пример кода интеграции с MCP — как для одного сервера, так и для нескольких — вы найдете в моём бесплатном телеграм-канале «Лёгкий путь в Python». Сообщество уже насчитывает около 4000 участников, присоединяйтесь.
🏗️ Архитектура: от простого к сложному
1. Конфигурация как основа стабильности
@dataclass class AgentConfig: """Упрощенная конфигурация AI-агента""" filesystem_path: str = "/path/to/work/directory" model_provider: ModelProvider = ModelProvider.OLLAMA use_memory: bool = True enable_web_search: bool = True def validate(self) -> None: """Валидация конфигурации""" if not os.path.exists(self.filesystem_path): raise ValueError(f"Путь не существует: {self.filesystem_path}")
Почему это важно? В отличие от примера с классификацией, здесь агент взаимодействует с внешними системами. Одна ошибка в пути к файлам или отсутствующий API-ключ — и весь агент перестаёт работать. Валидация на старте экономит часы отладки.
2. Фабрика моделей: гибкость выбора
class ModelFactory: """Упрощенная фабрика моделей""" @staticmethod def create_model(config: AgentConfig): """Создает модель согласно конфигурации""" provider = config.model_provider.value if provider == "ollama": return ChatOllama(model="qwen2.5:32b", base_url="http://localhost:11434") elif provider == "openai": return ChatOpenAI(model="gpt-4o-mini", api_key=os.getenv("OPENAI_API_KEY")) # ... другие провайдеры
Один код — множество моделей. Хотите бесплатную локальную модель? Используйте Ollama. Нужна максимальная точность? Переключитесь на GPT-4. Важна скорость? Попробуйте DeepSeek. Код остаётся тем же.
3. MCP-интеграция: подключение к реальному миру
async def _init_mcp_client(self): """Инициализация MCP клиента""" mcp_config = { "filesystem": { "command": "npx", "args": ["-y", "@modelcontextprotocol/server-filesystem", self.filesystem_path], "transport": "stdio" }, "brave-search": { "command": "npx", "args": ["-y", "@modelcontextprotocol/server-brave-search@latest"], "transport": "stdio", "env": {"BRAVE_API_KEY": os.getenv("BRAVE_API_KEY")} } } self.mcp_client = MultiServerMCPClient(mcp_config) self.tools = await self.mcp_client.get_tools()
Здесь происходит ключевая работа MCP: мы подключаем к агенту внешние MCP-серверы, которые предоставляют набор инструментов и функций. Агент при этом получает не просто отдельные функции, а полноценное контекстное понимание того, как работать с файловой системой и интернетом.
Для корректной работы указанных MCP-серверов потребуется установить Node.js и npm последней версии. После этого глобально установите необходимые MCP-серверы с помощью команд:
npm install -g @modelcontextprotocol/server-filesystem npm install -g @modelcontextprotocol/server-brave-search@latest
Если вы планируете использовать сервер server-brave-search, обязательно получите бесплатный API-ключ на сайте Brave и установите его в переменную окружения BRAVE_API_KEY.
Таким образом, MCP-сервера предоставляют вашему ИИ-агенту расширенные возможности через стандартизированный протокол, позволяющий интегрировать разные внешние сервисы и инструменты для работы с реальным миром.
Устойчивость к ошибкам
В реальном мире всё ломается: сеть недоступна, файлы заблокированы, API-ключи просрочены. Наш агент готов к этому:
@retry_on_failure(max_retries=2, delay=1.0) async def process_message(self, user_input: str, thread_id: str = "default") -> str: """Обработка сообщения пользователя с повторными попытками""" try: config = {"configurable": {"thread_id": thread_id}} message_input = {"messages": [HumanMessage(content=user_input)]} response = await self.agent.ainvoke(message_input, config) return response["messages"][-1].content except Exception as e: error_msg = f"❌ Ошибка обработки: {e}" logger.error(error_msg) return error_msg
Декоратор @retry_on_failure автоматически повторяет операции при временных сбоях. Пользователь даже не заметит, что что-то пошло не так.
Контекстная память
if self.config.use_memory: self.checkpointer = InMemorySaver() logger.info("Память агента включена")
Агент помнит всю историю разговора. Вы можете сказать «создай файл config.py«, затем «добавь в него настройки базы данных», и агент поймёт, что речь идёт о том же файле. Это кардинально меняет пользовательский опыт.
Умный системный промпт
def _get_system_prompt(self) -> str: base_prompt = ( "Ты — умный AI-ассистент, который помогает пользователю работать с файлами " "и искать информацию в интернете. Всегда внимательно анализируй запрос " "и выбирай наиболее подходящий инструмент." ) if self.config.enable_web_search: base_prompt += ( " Если требуется найти актуальные данные (погоду, новости, цены), " "обязательно используй веб-поиск и предоставляй самую свежую информацию." ) return base_prompt
Промпт адаптируется к возможностям агента. Если веб-поиск отключён, агент не будет предлагать найти что-то в интернете. Если включён — будет активно использовать эту возможность.
🚀 Практические сценарии использования
Работа с кодом:
> Создай структуру проекта Flask с папками templates, static и models ✅ Создание папок... 📁 Создал директорию /project/templates 📁 Создал директорию /project/static 📁 Создал директорию /project/models 📄 Создал базовый app.py с настройками Flask
Актуальная информация:
> Какая сейчас погода в Москве? 🔍 Ищу актуальную информацию о погоде... 🌤️ В Москве сейчас +25°C, переменная облачность, ветер 3 м/с
Анализ и обработка файлов:
> Прочитай все .py файлы в папке и создай документацию 📖 Читаю Python файлы... 📄 Найдено 5 файлов: app.py, models.py, views.py, utils.py, config.py 📝 Создаю документацию на основе анализа кода... ✅ Документация сохранена в README.md
Ключевые отличия от простого чат-бота
-
Реальные действия — агент не просто говорит, что делать, а делает сам
-
Контекстная память — помнит всю историю и может ссылаться на предыдущие действия
-
Актуальные данные — может получать свежую информацию из интернета
-
Обработка ошибок — graceful degradation при проблемах с инструментами
-
Адаптивность — поведение зависит от доступных инструментов
От примера к продакшену
Этот код демонстрирует архитектурные паттерны для создания продакшн-готовых агентов:
-
Модульная конфигурация — легко менять поведение без изменения кода
-
Абстракция провайдеров — поддержка множества LLM из коробки
-
Graceful error handling — система не падает при проблемах
-
Расширяемость — новые инструменты добавляются декларативно
-
Наблюдаемость — подробное логирование для отладки
Итоги: от теории к практике ИИ-агентов
Сегодня мы прошли путь от базовых концепций до создания работающих ИИ-агентов. Давайте подведём итоги того, что мы изучили и чего достигли.
Что мы освоили
1. Фундаментальные концепции
-
Разобрались с различием между чат-ботами и настоящими ИИ-агентами
-
Поняли роль MCP (Model Context Protocol) как моста между моделью и внешним миром
-
Изучили архитектуру LangGraph для построения сложной логики агентов
2. Практические навыки
-
Настроили рабочее окружение с поддержкой облачных и локальных моделей
-
Создали агента-классификатора с асинхронной архитектурой и управлением состояниями
-
Построили MCP-агента с доступом к файловой системе и веб-поиску
3. Архитектурные паттерны
-
Научились проектировать графы состояний для сложной логики
-
Освоили модульную конфигурацию и фабрики моделей
-
Внедрили обработку ошибок и retry-механизмы для продакшн-готовых решений
Ключевые преимущества подхода
LangGraph + MCP дают нам:
-
Прозрачность — каждый шаг агента документирован и отслеживаем
-
Расширяемость — новые возможности добавляются декларативно
-
Надёжность — встроенная обработка ошибок и восстановление
-
Гибкость — поддержка множества моделей и провайдеров из коробки
Практическая ценность
Созданные примеры — это не просто демонстрация технологий. Это готовые решения для реальных задач:
-
Агент-классификатор можно внедрить в HR-платформы и биржи фриланса
-
MCP-агент подходит для автоматизации рабочих процессов и анализа данных
-
Архитектурные паттерны масштабируются на проекты любой сложности
Уровень подготовки
Сегодня я намеренно не усложнял материал глубокими техническими деталями и сложной терминологией. Цель была простая — преодолеть базовый порог входа в тему интеграции ИИ-агентов с собственными проектами.
Этой информации достаточно, чтобы:
-
Понять принципы работы современных ИИ-агентов
-
Начать экспериментировать с собственными решениями
-
Оценить потенциал технологий для ваших задач
-
Сделать осознанный выбор инструментов для проекта
Что дальше?
Мы затронули лишь верхушку айсберга. LangGraph и MCP предлагают гораздо более широкие возможности:
-
Мультиагентные системы — координация команд специализированных агентов
-
Продвинутые MCP-серверы — интеграция с базами данных, CRM, API сервисов
-
Сложные графы состояний — циклы, условные переходы, параллельное выполнение
-
Production deployment — масштабирование, мониторинг, A/B тестирование
Где найти больше материалов
Полный исходный код всех примеров, а также эксклюзивные материалы, которые я не публикую на Хабре, вы найдёте в моём телеграм-канале «Легкий путь в Python».
Обратная связь важна
Если вам интересны более глубокие темы:
-
LLM и ИИ-агенты в интеграции с собственными проектами
-
MCP-серверы и создание кастомных инструментов
-
Детальное рассмотрение LangGraph — продвинутые паттерны и оптимизации
Дайте знать своим:
-
👍 Лайком этой статьи
-
📱 Подпиской на телеграм-канал
-
💬 Комментарием с вопросами и предложениями тем
Ваша активность показывает, что тема востребована, и мотивирует создавать ещё больше качественного контента.
Заключение
ИИ-агенты — это не футуристическая фантастика, а реальная технология сегодняшнего дня. С помощью LangGraph и MCP мы можем создавать системы, которые решают конкретные бизнес-задачи, автоматизируют рутину и открывают новые возможности.
Главное — начать. Возьмите код из примеров, адаптируйте под свои задачи, экспериментируйте. Каждый проект — это новый опыт и шаг к мастерству в области ИИ-разработки.
Удачи в ваших проектах!
Amvera Cloud – облако для простого запуска проектов со встроенным CI/CD (деплой идёт через Git или загрузку файлов в интерфейсе), встроенным проксированием до ведущих LLM и собственным инференсом LLaMA. Вам не нужно думать о настройке NGINX, виртуальных машин и другой инфраструктуры. Зарегистрируйтесь и получите 111 рублей на тест.
ссылка на оригинал статьи https://habr.com/ru/articles/929568/
Добавить комментарий