В конце первого модуля вы написали цикл агента своими руками: отправляли запрос модели, проверяли, не попросила ли она вызвать функцию, выполняли функцию, добавляли результат в историю и повторяли всё снова, пока не придёт финальный ответ. Это полезное упражнение, но в реальном проекте такой код приходится писать заново для каждого агента, а потом поддерживать: добавлять память, лимиты, обработку ошибок, смену провайдера модели.
LangChain 1.x берёт эту рутину на себя. Цикл «модель — инструмент — модель» уже реализован в функции create_agent, а модели разных провайдеров и сообщения приведены к единому виду. После урока вы сможете подключить модель через LangChain, отправить ей список сообщений и собрать первого агента с инструментом в десяток строк.
Что уже умеем
Вы работали с Responses API, понимаете, что такое токены и контекст, умеете получать ответ по схеме Pydantic и сами писали цикл агента с function calling и ограничением max_steps. Сегодня мы переносим эти знания на LangChain: увидим, какие части вашего ручного цикла превратились в готовые объекты фреймворка.
Что вы узнаете в рамках статьи
- как установить LangChain и подключить модель через
init_chat_modelилиChatOpenAI; - какие типы сообщений есть в LangChain и чем они отличаются;
- как отправить модели диалог и прочитать ответ и расход токенов;
- как создать агента функцией
create_agentи вызвать его; - как читать список сообщений, который возвращает агент.
Ключевые термины
- LangChain — библиотека для создания приложений и агентов на языковых моделях с единым интерфейсом для разных провайдеров.
- Чат-модель — объект LangChain, который принимает список сообщений и возвращает сообщение модели.
- Сообщение (message) — элемент диалога с ролью и содержимым: системное, пользовательское, ответ модели или результат инструмента.
create_agent— функция LangChain, которая собирает агента: модель, инструменты и системный промпт, работающие в цикле.- Состояние агента — данные, которые агент накапливает за запуск; главное в нём — список сообщений
messages.
Подключаем модель
Установите пакеты в виртуальное окружение. Пакет langchain содержит агентов и общие классы, langchain-openai — интеграцию с OpenAI. Ключ, как и раньше, берётся из переменной окружения OPENAI_API_KEY.
pip install -U langchain langchain-openai
Подключить модель можно двумя способами. Универсальный — функция init_chat_model: в строке указывается провайдер и имя модели через двоеточие. Второй способ — создать объект класса конкретного провайдера, например ChatOpenAI. Результат одинаковый, а параметры знакомы вам по Responses API.
from langchain.chat_models import init_chat_model
from langchain_openai import ChatOpenAI
model = init_chat_model("openai:gpt-6-luna", temperature=0, max_tokens=300)
# то же самое через класс провайдера:
# model = ChatOpenAI(model="gpt-6-luna", temperature=0, max_tokens=300)
response = model.invoke("Объясни одним предложением, что такое токен.")
print(response.content)
print(response.usage_metadata)
Что увидим в консоли: короткое объяснение и словарь вроде {'input_tokens': 19, 'output_tokens': 31, 'total_tokens': 50}. Метод invoke принял строку, сам превратил её в пользовательское сообщение и вернул объект AIMessage. Текст ответа лежит в .content, а расход токенов — в .usage_metadata. Именно эти числа нужны, чтобы считать стоимость, как в уроке 1.2.
Преимущество init_chat_model в том, что для смены провайдера достаточно поменять строку, а остальной код останется прежним.
Сообщения: единый язык диалога
В Responses API вы передавали историю списком словарей с ролями. LangChain работает так же, но у каждой роли есть свой класс: SystemMessage для инструкций, HumanMessage для реплик пользователя, AIMessage для ответов модели и ToolMessage для результатов инструментов.
from langchain.messages import SystemMessage, HumanMessage, AIMessage
conversation = [
SystemMessage("Ты помощник по Python. Отвечай кратко."),
HumanMessage("Как создать пустой список?"),
AIMessage("Используйте []: items = []"),
HumanMessage("А пустой словарь?"),
]
response = model.invoke(conversation)
print(type(response).__name__, "->", response.content)
Что увидим: AIMessage -> Используйте {}: data = {}. Модель увидела весь диалог и ответила в заданном стиле, потому что каждое сообщение пришло со своей ролью. Вместо классов можно передавать привычные словари {"role": "user", "content": "..."} — LangChain сам превратит их в объекты. Словари удобны для коротких примеров, а классы — когда нужно проверить тип сообщения или прочитать его поля.
У AIMessage есть важное поле tool_calls. Если модель решила вызвать инструмент, там окажется список запросов с именем функции, аргументами и id. Это то же самое, что вы разбирали вручную в уроке 1.6, только уже в едином формате для любого провайдера. ToolMessage хранит результат функции и поле tool_call_id, по которому модель понимает, на какой из её запросов пришёл ответ.
Первый агент на create_agent
Теперь соберём агента. Функции create_agent нужны модель, список инструментов и системный промпт. Инструментом в простейшем случае может быть обычная функция Python с аннотациями типов и строкой документации: LangChain построит из них описание для модели. Подробно об инструментах поговорим в следующем уроке.
from langchain.agents import create_agent
def get_weather(city: str) -> str:
"""Возвращает текущую погоду в указанном городе."""
return f"В городе {city} +12 °C, облачно"
agent = create_agent(
model="openai:gpt-6-luna",
tools=[get_weather],
system_prompt="Ты вежливый помощник. Отвечай коротко.",
)
result = agent.invoke(
{"messages": [{"role": "user", "content": "Какая погода в Казани?"}]}
)
print(result["messages"][-1].content)
Что увидим: «В Казани сейчас +12 °C и облачно». В параметр model можно передать как строку, так и готовый объект модели, например созданный выше model с нужной температурой.
Сравните с вашим кодом из урока 1.6. Там был цикл for step in range(max_steps), разбор tool_calls, вызов функции по имени и добавление результата в историю. Здесь всё это делает create_agent: агент вызывает модель, выполняет запрошенные инструменты, возвращает результаты модели и останавливается, когда модель отвечает без вызовов инструментов. Лимит шагов в LangChain тоже есть, мы подключим его в уроке 2.5.
Что лежит в результате агента
Метод invoke принимает и возвращает состояние агента — словарь, главный ключ которого messages. На входе вы передаёте начальные сообщения, на выходе получаете всю цепочку, которая возникла за запуск. Последний элемент — финальный ответ, но полезно уметь смотреть и на промежуточные шаги.
for message in result["messages"]:
kind = type(message).__name__
if kind == "AIMessage" and message.tool_calls:
print(kind, "-> вызов", message.tool_calls[0]["name"], message.tool_calls[0]["args"])
else:
print(kind, "->", message.content)
Что увидим в консоли:
HumanMessage -> Какая погода в Казани?
AIMessage -> вызов get_weather {'city': 'Казань'}
ToolMessage -> В городе Казань +12 °C, облачно
AIMessage -> В Казани сейчас +12 °C и облачно.
Четыре сообщения и два обращения к модели: первое закончилось запросом инструмента, второе — финальным ответом. Обратите внимание, что системного промпта в списке нет: агент добавляет его перед каждым вызовом модели, но не хранит в состоянии. Каждое обращение к модели оплачивается отдельно, и на втором вызове во вход снова уходит вся цепочка. Расход токенов каждого шага можно увидеть в usage_metadata у соответствующего AIMessage.
Частые ошибки
- Брать устаревший код из старых статей.
AgentExecutor,initialize_agentиcreate_tool_calling_agentотносятся к прошлым версиям LangChain. В версии 1.x агента создаётcreate_agentизlangchain.agents. - Передавать в агента строку вместо состояния.
agent.invoke("Привет")не сработает: агент ждёт словарь с ключомmessages, внутри которого список сообщений. - Печатать весь результат вместо ответа.
print(result)выведет всё состояние. Финальный текст находится вresult["messages"][-1].content. - Забыть про пакет провайдера. Строка
"openai:gpt-6-luna"работает, только если установленlangchain-openaiи заданOPENAI_API_KEY.
Наиболее частые вопросы
- Зачем LangChain, если ручной цикл уже работает? — Фреймворк даёт готовые память, лимиты, структурированный ответ и обработку ошибок, а также единый код для разных провайдеров. Вы пишете логику задачи, а не обвязку.
- Что выбрать:
init_chat_modelилиChatOpenAI? — Оба варианта корректны.init_chat_modelудобнее, когда провайдер может поменяться, класс провайдера — когда нужны его особые параметры. - Можно ли передавать сообщения словарями? — Да, LangChain принимает словари с полями
roleиcontentи сам преобразует их в объекты сообщений. - Где посмотреть, сколько токенов потратил агент? — В поле
usage_metadataкаждогоAIMessageизresult["messages"]. Сложив значения, вы получите расход за весь запуск.
Итоги
Отличная работа! Вы перенесли знания из первого модуля на LangChain: подключили модель одной строкой, научились говорить с ней на языке сообщений и собрали агента, который сам вызывает инструмент и отвечает пользователю. Главное — вы понимаете, что происходит внутри create_agent, потому что однажды уже писали этот цикл сами.
В следующем уроке займёмся инструментами всерьёз: узнаем, как декоратор @tool превращает функцию в инструмент, почему описание и аргументы так сильно влияют на поведение агента и что делать, когда инструмент выдаёт ошибку.