Практический курс по созданию агентов на языковых моделях: цикл агента и инструменты, мидлварь, графовая оркестрация LangGraph, стриминг, память и прерывания, управление контекстом, мультиагентные схемы, тестирование и вывод в продакшен. Примеры сверены с актуальной документацией 2026 года.
Агент — это цикл. Модель решает, какой инструмент вызвать; инструмент отрабатывает; результат возвращается модели; так до ответа. Вся остальная сложность — про управление этим циклом: где его прервать и спросить человека, что помнить между запусками, как не дать ему крутиться бесконечно и что показывать пользователю, пока он крутится. LangChain даёт цикл, LangGraph — управление им.
Пятнадцать тем. Начало — единый интерфейс к моделям: invoke, stream, batch, типы сообщений, мультимодальный ввод, структурированный вывод и привязка инструментов. Затем первый агент: инструменты через декоратор @tool, сборка через create_agent, передача контекста. Дальше middleware — хуки before_model, after_model, wrap_model_call, wrap_tool_call и досрочный выход через jump_to: это способ менять поведение агента, не переписывая его.
Вторая половина — LangGraph. Состояние, узлы и рёбра, редукторы, MessagesState. Условная маршрутизация, Command для одновременного обновления состояния и перехода, Send для параллельных ветвей. Потоковая передача событий, включая обнаружение прерываний. Сохранение состояния: чекпоинтеры (InMemorySaver, PostgresSaver) и долговременное хранилище — то, что превращает агента из функции в процесс, переживающий перезапуск. Управление контекстным окном: суммаризация и вытеснение. Человек в цикле: прерывания, одобрение и редактирование действий перед выполнением.
Дальше функциональный API (@entrypoint, @task) как альтернатива графам, мультиагентные схемы с передачей управления, кэширование узлов, наблюдаемость через LangSmith. Отдельная тема — тестирование: подмена модели заглушкой, имитация инструментов, проверка маршрутизаторов, наборы примеров для оценки. Финал — вывод в продакшен: FastAPI, Docker, мониторинг, чек-лист и типичные ошибки.
Примеры сверены с документацией 2026 года. Нужен Python и представление о том, как работает вызов LLM по API.
Обзор экосистемы LangChain, установка, единый интерфейс моделей, выбор провайдера.
Вызов моделей через invoke, stream и batch, типы сообщений, content_blocks, мультимодальный ввод, bind_tools, with_structured_output, учёт токенов.
Создание инструментов через декоратор @tool, конфигурация агента с create_agent, структурированный вывод, контекст выполнения.
Хуки before_model, after_model, wrap_model_call и wrap_tool_call, досрочный выход через jump_to, собственные поля состояния, встроенные мидлвари.
Графовый подход к оркестрации, StateGraph, редукторы, MessagesState, узлы и рёбра.
Условные рёбра, команда Command для обновления состояния и маршрутизации, Send для параллельных веток.
Стриминг с stream_events, асинхронный стриминг, обнаружение прерываний в потоке.
Контрольные точки (чекпоинтеры), долговременная память (хранилище), InMemorySaver, PostgresSaver, Store.
Суммаризация, память, навыки, middleware для управления контекстным окном.
Прерывания (interrupts), паттерны одобрения и редактирования, валидация ввода, правила прерываний.
Точки входа (@entrypoint), задачи (@task), сравнение графового и функционального API, параллельная обработка.
Паттерны мультиагентных систем: субагенты, передача управления, навыки, маршрутизатор, пользовательские рабочие процессы.
Deep Agents, кэширование узлов, обработка ошибок, наблюдаемость с LangSmith.
Тесты инструментов, узлов и маршрутизаторов, подмена модели заглушкой, имитация инструментов, наборы примеров и эксперименты в LangSmith, программные оценщики и модель-судья.
Развёртывание агентов, Docker, FastAPI, мониторинг, чеклист перед продакшеном, типичные ошибки.
Доступен после всех тем (0 из 15)
Доступен после зачёта
Модель, вызывающая инструменты в цикле, пока задача не решена. Состоит из модели и обвязки: промпта, инструментов, мидлварей.
Пример
agent = create_agent(model='openai:gpt-4o-mini', tools=[search], system_prompt='Ты помощник.')Связанные термины
Всё, что окружает цикл вызовов модели: системный промпт, набор инструментов, мидлварь, память и лимиты. Формула: агент — это модель плюс обвязка.
Пример
create_agent(model=..., tools=..., system_prompt=..., middleware=[...])Связанные термины
Повторяющаяся последовательность: модель получает историю и описания инструментов, возвращает вызовы, фреймворк их выполняет и снова обращается к модели. Цикл заканчивается ответом без вызовов инструментов.
Пример
result = agent.invoke({'messages': [{'role': 'user', 'content': 'Погода в Казани?'}]})Связанные термины
Рабочий процесс — это фиксированная последовательность шагов, агент сам решает, что делать дальше. Если порядок шагов известен заранее, агент лишь добавляет недетерминированность и стоимость.
Пример
builder.add_edge('extract', 'classify') # фиксированный порядок вместо агентаСвязанные термины
Единая фабрика моделей: принимает идентификатор вида «провайдер:модель» и возвращает объект с одинаковым интерфейсом для всех провайдеров.
Пример
model = init_chat_model('anthropic:claude-sonnet-4-6', temperature=0)Связанные термины
Часть идентификатора модели до двоеточия. Определяет, какой пакет интеграции будет загружен: openai, anthropic, google_genai, ollama.
Пример
init_chat_model('openai:gpt-4o-mini') # нужен пакет langchain-openaiСвязанные термины
Функция, которую модель может запросить к вызову. Имя, описание и схема аргументов становятся частью промпта, поэтому качество docstring напрямую влияет на корректность вызовов.
Пример
@tool
def search(query: str) -> str:
"""Поиск информации по запросу."""
return f'Результаты для: {query}'Связанные термины
Структура с именем инструмента и аргументами, которую возвращает модель. Сам код модель не выполняет: вызов исполняет фреймворк.
Пример
response.tool_calls # [{'name': 'search', 'args': {'query': '...'}, 'id': 'call_1'}]Связанные термины
Сообщение с результатом инструмента. Связано с вызовом через tool_call_id, и каждый вызов обязан быть закрыт таким сообщением, иначе провайдер отклонит следующий запрос.
Пример
ToolMessage(content='Готово', tool_call_id=runtime.tool_call_id)Связанные термины
Параметр инструмента, который фреймворк подставляет сам и не показывает модели. Даёт доступ к состоянию, контексту запуска, хранилищу и идентификатору вызова.
Пример
@tool
def get_pref(name: str, runtime: ToolRuntime) -> str:
return runtime.state.get('prefs', {}).get(name, '')Связанные термины
Неизменяемые данные запуска: идентификатор пользователя, роль, язык. Задаются снаружи через context_schema и недоступны для подмены моделью.
Пример
agent.invoke(payload, context=Context(user_id='u-42'))Связанные термины
Ответ модели по заданной схеме вместо свободного текста. В агенте задаётся через response_format, у модели через with_structured_output.
Пример
agent = create_agent(model=..., tools=[], response_format=Answer)Связанные термины
Параметр create_agent со схемой финального ответа. Провалидированный объект попадает в ключ structured_response итогового состояния.
Пример
result['structured_response'].temperature_cСвязанные термины
Единица диалога с ролью: SystemMessage задаёт правила, HumanMessage содержит запрос, AIMessage ответ модели, ToolMessage результат инструмента.
Пример
messages = [SystemMessage('Ты помощник.'), HumanMessage('Привет')]Связанные термины
Нормализованное представление содержимого сообщения в виде типизированных блоков: текст, рассуждения, изображения. Одинаково для всех провайдеров, в отличие от сырого поля content.
Пример
for block in response.content_blocks:
print(block['type'])Связанные термины
Часть ответа рассуждающих моделей с ходом мысли. У разных провайдеров называется по-разному, но content_blocks приводит их к общему типу reasoning.
Пример
[b for b in response.content_blocks if b['type'] == 'reasoning']Связанные термины
Метаданные расхода токенов у ответа модели. По ним считают стоимость запуска и замечают распухание контекста.
Пример
response.usage_metadata # {'input_tokens': 8, 'output_tokens': 10}Связанные термины
Метод модели, добавляющий описания инструментов к запросу. Возвращает модель, которая умеет просить вызовы, но не выполняет их.
Пример
model_with_tools = model.bind_tools([get_weather])Связанные термины
Компонент, задающий темп отправки запросов к провайдеру. Помогает против ошибок 429 надёжнее, чем увеличение числа повторов.
Пример
model = init_chat_model('openai:gpt-4o-mini', rate_limiter=InMemoryRateLimiter(requests_per_second=0.5))Связанные термины
Скидка провайдера за повторяющееся начало запроса. В агенте окупается тем, что системный промпт и описания инструментов отправляются на каждом шаге цикла.
Пример
# стабильная часть промпта идёт первой, изменяемая последнейСвязанные термины
Общая структура данных графа, которую читают и обновляют узлы. Описывается через TypedDict, dataclass или Pydantic-модель.
Пример
class State(TypedDict):
messages: Annotated[list[AnyMessage], add_messages]
step: intСвязанные термины
Функция графа: принимает состояние и возвращает частичное обновление. Может быть синхронной или асинхронной и запрашивать конфигурацию и рантайм.
Пример
def my_node(state: State):
return {'step': state['step'] + 1}Связанные термины
Связь между узлами, задающая порядок выполнения. Обычное ребро всегда ведёт в один узел, условное вызывает маршрутизатор.
Пример
builder.add_edge(START, 'node_a')
builder.add_conditional_edges('node_a', route)Связанные термины
Функция условного ребра: получает состояние и возвращает имя следующего узла. Аннотация Literal в возвращаемом типе сообщает графу список достижимых узлов.
Пример
def route(state: State) -> Literal['search', 'answer']:
return 'search' if state['needs_search'] else 'answer'Связанные термины
Правило объединения нового значения со старым. По умолчанию замена, для накопления указывают operator.add или add_messages через Annotated.
Пример
documents: Annotated[list[str], operator.add]Связанные термины
Редуктор для списка сообщений: добавляет новые и заменяет существующие по идентификатору. Позволяет редактировать историю, не пересобирая список.
Пример
messages: Annotated[list[AnyMessage], add_messages]Связанные термины
Готовое состояние с полем messages и редуктором add_messages. Наследуется, когда нужны дополнительные поля.
Пример
class State(MessagesState):
user_id: strСвязанные термины
Строитель графа: принимает схему состояния, накапливает узлы и рёбра, а методом compile превращается в исполняемый объект.
Пример
builder = StateGraph(State, input_schema=InputState, output_schema=OutputState)Связанные термины
Проверка структуры и создание исполняемого графа. На этом шаге задают чекпоинтер, кэш и статические точки останова.
Пример
graph = builder.compile(checkpointer=checkpointer, cache=InMemoryCache())Связанные термины
Одна итерация по узлам графа. Параллельные узлы принадлежат одному супершагу, последовательные разным. Контрольная точка сохраняется по завершении супершага.
Пример
# цикл из трёх узлов расходует лимит рекурсии втрое быстрее одиночногоСвязанные термины
Примитив управления выполнением: обновляет состояние и выбирает следующий узел в одном шаге. С параметром graph=Command.PARENT переводит выполнение в родительский граф.
Пример
return Command(update={'category': 'billing'}, goto='billing')Связанные термины
Механизм динамического параллелизма: создаёт ветку с собственным входным состоянием. Применяется, когда число веток известно только во время выполнения.
Пример
return [Send('summarize_one', {'doc': d}) for d in state['documents']]Связанные термины
Скомпилированный граф, используемый как узел другого графа. По умолчанию его события не попадают в поток родителя, а управление возвращается вызывающей стороне.
Пример
builder.add_node('researcher', researcher_graph)Связанные термины
Максимальное число супершагов за запуск. Защищает от бесконечных циклов, но срабатывает уже после того, как агент долго крутился вхолостую.
Пример
graph.invoke(inputs, config={'recursion_limit': 25})Связанные термины
Снимок состояния графа на конкретном шаге. Даёт возобновление диалога, восстановление после сбоя и возврат к прошлому состоянию.
Пример
checkpointer = InMemorySaver()
graph = builder.compile(checkpointer=checkpointer)Связанные термины
Диалог, идентифицируемый через thread_id. Тот же идентификатор продолжает разговор, новый начинает пустой.
Пример
config = {'configurable': {'thread_id': 'user-42-chat-1'}}Связанные термины
Хранилище контрольных точек. InMemorySaver для тестов, SqliteSaver для локальной работы, PostgresSaver и его асинхронный вариант для продакшена.
Пример
checkpointer = PostgresSaver.from_conn_string(DB_URL)
checkpointer.setup()Связанные термины
Долговременная память вне состояния графа, доступная из любого потока. Адресация идёт тройкой из пространства имён, ключа и значения.
Пример
runtime.store.put((user_id, 'preferences'), 'style', {'value': 'кратко'})Связанные термины
Кортеж строк, разделяющий записи в хранилище по пользователям и типам данных. Поиск возможен по префиксу пространства.
Пример
namespace = (user_id, 'memories')Связанные термины
Режим, в котором записи векторизуются при сохранении, и поиск идёт по смыслу запроса, а не по точному ключу.
Пример
store.search((user_id, 'memories'), query='что человек любит есть', limit=3)Связанные термины
Результат get_state: значения полей, список узлов в next, метаданные и идентификатор контрольной точки. Первое, на что смотрят при разборе остановки.
Пример
snapshot = graph.get_state(config)
print(snapshot.next)Связанные термины
Внешнее изменение состояния потока. Создаёт новую контрольную точку, а значения проходят через редукторы.
Пример
graph.update_state(config, {'escalated': True})Связанные термины
Возобновление с конкретной контрольной точки по её идентификатору: предыдущие узлы пропускаются, последующие выполняются заново.
Пример
config = {'configurable': {'thread_id': '1', 'checkpoint_id': '...'}}
graph.invoke(None, config)Связанные термины
Момент записи контрольной точки: sync перед следующим шагом, async параллельно с ним, exit только при завершении или прерывании.
Пример
graph.stream(inputs, config=config, durability='async')Связанные термины
Остановка выполнения для получения внешнего ввода. Состояние сохраняется, а узел при возобновлении выполняется с начала.
Пример
approved = interrupt({'question': 'Выполнить перевод?', 'sum': 1000})Связанные термины
Продолжение остановленного графа. Значение Command(resume=...) становится результатом вызова interrupt внутри узла.
Пример
graph.invoke(Command(resume=True), config=config)Связанные термины
Свойство операции давать тот же результат при повторном выполнении. Обязательно для кода до прерывания, потому что узел перезапускается при каждом возобновлении.
Пример
# запись через «создать или обновить» вместо «создать»Связанные термины
Мидлварь, которая ставит вызовы указанных инструментов на подтверждение человеком. Решения: одобрить, изменить аргументы, отклонить, ответить вместо инструмента.
Пример
HumanInTheLoopMiddleware(interrupt_on={'write_file': True, 'read_data': False})Связанные термины
Получение промежуточных результатов по ходу выполнения: токены ответа, вызовы инструментов, снимки состояния.
Пример
stream = agent.stream_events(payload, version='v3')
for message in stream.messages:
...Связанные термины
Выбор того, что приходит в поток графа: values для полных снимков, updates для изменений узла, messages для токенов, custom для собственных событий.
Пример
graph.stream(inputs, stream_mode=['updates', 'custom'], version='v2')Связанные термины
Функция для отправки собственных событий из узла, например прогресса долгой операции. Работает только внутри выполнения графа.
Пример
writer = get_stream_writer()
writer({'progress': 'Загружаю страницу 3 из 10'})Связанные термины
Компонент, встраивающийся в известные точки цикла агента: до и после модели, вокруг вызовов модели и инструментов. Позволяет менять поведение без переписывания агента.
Пример
agent = create_agent(model=..., middleware=[SummarizationMiddleware(model=...)])Связанные термины
Точка встраивания: before_agent, before_model, after_model, after_agent выполняются в конкретный момент, wrap_model_call и wrap_tool_call оборачивают вызов.
Пример
@before_model
def log(state, runtime):
return NoneСвязанные термины
Досрочный переход из мидлвари: end завершает работу агента, model и tools отправляют к соответствующему узлу. Цель объявляется заранее через hook_config.
Пример
return {'messages': [AIMessage('Отказ.')], 'jump_to': 'end'}Связанные термины
Замена старой части переписки кратким пересказом при приближении к лимиту окна. Сжатие с потерями, поэтому пороги подбирают осознанно.
Пример
SummarizationMiddleware(model='openai:gpt-4o-mini', trigger=('fraction', 0.8), keep=('messages', 20))Связанные термины
Удаление старых результатов инструментов с сохранением нескольких свежих. Дешевле суммаризации и не тратит вызов модели.
Пример
ContextEditingMiddleware(edits=[ClearToolUsesEdit(trigger=100_000, keep=3)])Связанные термины
Максимальный объём данных, который модель принимает за один вызов. В цикле агента вся история отправляется заново на каждом шаге.
Пример
response.usage_metadata['input_tokens']Связанные термины
Доменные знания и инструкции, загружаемые по требованию. Агент видит только имена и описания, полный текст подтягивается для подходящей задачи.
Пример
SkillsMiddleware(backend=backend, sources=['./skills/'])Связанные термины
Загрузка постоянных инструкций из файлов AGENTS.md в системный промпт при старте агента. В отличие от навыков, содержимое подставляется всегда.
Пример
MemoryMiddleware(sources=['./AGENTS.md'])Связанные термины
Агент, вызываемый как инструмент главным агентом. Работает в изолированном контексте и может использовать собственную модель.
Пример
SubAgentMiddleware(subagents=[{'name': 'researcher', 'tools': [search], 'model': 'openai:gpt-4o-mini'}])Связанные термины
Паттерн, при котором агент отдаёт управление другому через инструмент, возвращающий Command. После передачи новый агент сам ведёт разговор.
Пример
@tool
def transfer_to_billing(runtime: ToolRuntime) -> Command:
return Command(goto='billing_agent', graph=Command.PARENT, update={})Связанные термины
Высокоуровневая обвязка поверх LangChain с планированием, файловыми инструментами, субагентами и управлением контекстом из коробки.
Пример
agent = create_deep_agent(model='openai:gpt-4o-mini', tools=[search])Связанные термины
Место хранения файлов агента: StateBackend в состоянии графа, FilesystemBackend на диске, StoreBackend в долговременном хранилище, CompositeBackend по частям.
Пример
FilesystemMiddleware(backend=StateBackend(), tools=['read_file', 'ls'])Связанные термины
Декоратор функционального API, отмечающий начало рабочего процесса. Принимает ровно один позиционный аргумент и поддерживает внедряемые параметры.
Пример
@entrypoint(checkpointer=InMemorySaver())
def workflow(topic: str) -> dict:
return {'essay': write_essay(topic).result()}Связанные термины
Декоратор функционального API для единицы работы. Результат сохраняется в контрольной точке, поэтому при возобновлении задача не выполняется повторно.
Пример
@task
def fetch_page(url: str) -> str:
return download(url)Связанные термины
Внедряемый параметр точки входа со значением, сохранённым предыдущим запуском на том же потоке. Позволяет накапливать данные без объявления состояния.
Пример
def workflow(value: int, *, previous: Any = None) -> int:
return (previous or 0) + valueСвязанные термины
Способ вернуть вызывающему коду одно значение, а в контрольную точку сохранить другое. Сохранённое придёт в следующий запуск как previous.
Пример
return entrypoint.final(value=result, save=accumulated)Связанные термины
Настройка кэширования узла с временем жизни результата. Ключ считается по входному состоянию, поэтому кэш полезен только при узком входе.
Пример
builder.add_node('search', search_fn, cache_policy=CachePolicy(ttl=60))Связанные термины
Автоматическая повторная попытка при временном сбое, с нарастающей задержкой. Вложенные повторы перемножаются, поэтому их суммарный эффект контролируют.
Пример
ModelRetryMiddleware(max_retries=3)Связанные термины
Переключение на другую модель при отказе основной. Единственная мера, помогающая во время длительной аварии провайдера.
Пример
ModelFallbackMiddleware(first_model, backup_model)Связанные термины
Ограничение числа обращений к модели или инструментам. thread_limit считает вызовы за весь диалог, run_limit за один запуск.
Пример
ModelCallLimitMiddleware(thread_limit=20, run_limit=8)Связанные термины
Расход токенов на запуск как метрика. Растёт быстрее всего из-за объёмных ответов инструментов, которые отправляются модели заново на каждом шаге.
Пример
sum(m.usage_metadata['total_tokens'] for m in ai_messages)Связанные термины
Дерево вызовов запуска с промптами, ответами, аргументами инструментов и задержками. Включается переменными окружения LangSmith без изменения кода.
Пример
export LANGSMITH_TRACING=true
export LANGSMITH_API_KEY=...Связанные термины
Прогон агента по набору примеров с оценщиками. Отвечает на вопрос, стало ли лучше после изменения промпта, чего трассировка не показывает.
Пример
client.evaluate(target, data='support-agent-v1', evaluators=[called_expected_tool])Связанные термины
Список реальных запросов с ожидаемым поведением. Двух-трёх десятков достаточно, чтобы ловить регрессии перед выкладкой.
Пример
client.create_examples(dataset_id=ds.id, examples=[{'inputs': {}, 'outputs': {}}])Связанные термины
Оценщик на основе модели для качества текста. Недетерминирован, поэтому его оценкам верят в агрегате и формулируют критерии максимально узко.
Пример
def judge(inputs, outputs, reference_outputs) -> bool: ...Связанные термины
Мидлварь, подменяющая выполнение инструментов сгенерированными ответами. Позволяет проверять поведение агента без обращения к реальным системам.
Пример
LLMToolEmulator(tools=['charge_card', 'send_email'])Связанные термины
Инструкции во внешнем тексте, которые модель принимает за указания разработчика. Ограничивается правами инструментов и подтверждениями, а не формулировками промпта.
Пример
# страница, открытая инструментом, просит отправить переписку по адресуСвязанные термины
Состав курса, уровни, практика и способы проверки знаний.
Курс включает 15 тем и 204 вопроса с разбором ответа. Начать можно с первой темы курса.
Маршрут охватывает уровни Junior, Middle, Senior. Темы расположены от основы к более сложным инженерным задачам, поэтому можно начать с подходящего места и не пропускать важные зависимости.
После прохождения тем доступен зачёт по курсу «LangChain и LangGraph: от новичка до профессионала» — 20 случайных вопросов с порогом 80%. После зачёта открывается экзамен с развёрнутыми ответами и автоматической оценкой, приближённый к техническому собеседованию.
Да, курс полностью бесплатный: все 15 тем доступны без оплаты.