Урок курса

Знакомство с LangChain – модели и простые цепочки

LangChain 1.0.x: c нуля до продакшн LLM-приложений

🔹 Установка LangChain и базовых сущностей

LangChain – это открытый фреймворк, упрощающий создание приложений на базе LLM. Он не заменяет OpenAI SDK, а использует его внутри, предлагая более высокий уровень абстракций и готовые инструменты для построения цепочек взаимодействия с моделью.

В этом уроке мы познакомимся с этой библиотекой, подключим её к нашему проекту и разберём ключевые понятия:

  • модель (LLM)

  • шаблон промпта (PromptTemplate)

  • цепочку (Chain)

  • память (Memory)

🔸Из чего состоит LangChain

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

💡 langchain-openai это дополнительный модуль к библиотеке langchain. И несмотря на то что пакет openai уже установлен, langchain-openai нужен отдельно — он содержит адаптеры и классы (ChatOpenAI, OpenAIEmbeddings и др.), которые позволяют использовать OpenAI-модели внутри LangChain по единому интерфейсу
💡 langchain-core — это ядро библиотеки, где реализованы базовые абстракции LangChain: промпты, схемы сообщений, раннеры и системы вызова моделей. Он устанавливается вместе с основной библиотекой, но может использоваться отдельно для создания лёгких проектов без лишних зависимостей.
💡 langgraph — пакет для графов и состояний, в него вынесены инструменты работы с памятью диалога, с ними мы познакомимся ниже

🔸Основные сущности LangChain

LLM Wrappers — Обёртки для моделей

Это классы, предоставляющие унифицированный интерфейс к различным моделям.
Например для OpenAI используется класс OpenAI или ChatOpenAI для чат-моделей из пакета langchain_openai.
Эти классы знают, как вызвать API и получить результат, предоставляя удобный интерфейс.:

# Если вы используете фаил .env, не забывйте импортировать переменные.
# В примерах далее мы будем сокращать этот шаг,
# но имейте ввиду, если мы работаем с API, это необходимо делать
import os
from dotenv import load_dotenv
from langchain_openai import ChatOpenAI

# при этом вызове OPENAI_API_KEY и OPENAI_API_BASE подгружаются в окружение
# если вы так делаете, то в вызове класса ChatOpenAI() эти аргументы указывать уже не нужно
load_dotenv() 

# Название модели тоже иногда удобно брать из файла .env
# тут мы берём его из файла, а если такой переменной в файле нет, то подставляем "gpt-5"
MODEL_NAME = os.getenv("OPENAI_API_MODEL", "gpt-5")

model = ChatOpenAI(
    # api_key=API_KEY, # если не используете dotenv то нужно указать
    # base_url=API_BASE, # если не используете dotenv то нужно указать
    model=MODEL_NAME,
    temperature=0.5
)

response = model.invoke("Привет. Что ты умеешь?")
print(response.content)

💡В примере используется модель GPT-5, но советуем вам для подобных тестовых примеров использовать модели попроще. С расценками на токены определённых моделей можно ознакомится в разделе pricing сайта platform.openai.com или аналогично там, где вы оплачиваете доступ к API ваших моделей.

  • PromptTemplate — шаблон промпта

    Это класс для управления текстом запроса с заполнением параметров. По сути, это строка с плейсхолдерами, которые можно заменять перед отправкой модели:

    from langchain_core.prompts import PromptTemplate
    
    template = PromptTemplate.from_template("Перескажи суть теста: {article}")
    prompt = template.invoke({"article": "Здесь находится текст для пересказа."})
    
    print(prompt)
    
    # text='Перескажи суть теста: Здесь находится текст для пересказа.'
    

    💡 Модуль langchain_core — это ядро библиотеки, где реализованы базовые абстракции LangChain. Он устанавливается вместе с основной библиотекой.
    💡 В этом модуле мы лишь слегка коснемся шаблонов (для понимания), а во втором модуле курса – Prompt Engineering углубимся в тонкости.

  • Chains — цепочки обработки

    Ключевая концепция LangChain. Цепочка – это последовательность единиц обработки, которые могут включать LLM, вызовы функций, логические узлы и т.п.

  • Tools (Инструменты)

    Позволяют моделям взаимодействовать с внешними системами (API, базы данных, файловые системы) через структурированные запросы.
    Инструмент — это вызываемая функция с определённой схемой входных данных, которую модель может использовать по необходимости. Инструменты расширяют возможности моделей, позволяя им генерировать корректные запросы к внешним системам.
    Некоторые модели (OpenAI, Anthropic, Gemini) имеют встроенные серверные инструменты, например, веб-поиск или интерпретаторы кода. Пример:

    from langchain.tools import tool
    
    @tool 
    def search_database(query: str, limit: int = 10) -> str: 
        """Ищет записи в базе данных клиентов по запросу.""" 
        return f"Found {limit} results for '{query}'"

    💡 Для создания инструмента используется декоратор @tool

  • Agents (Агенты)

    Агенты объединяют языковые модели и инструменты, чтобы решать задачи, выбирать подходящие инструменты и последовательно продвигаться к решению.

    create_agent создаёт готового к использованию агента.:

    from langchain.agents import create_agent
    
    agent = create_agent( 
        "gpt-5", tools=tools 
    )
  • Memory — память диалога

    Механизм хранения состояния диалога между вызовами позволяя запоминать контекст, например для реализации чат бота, который должен помнить о чём диалог.
    В актуальной версии фреймворка инструменты работы с памятью перенесены в модуль langgraph:

    from langchain.agents import create_agent
    from langgraph.checkpoint.memory import InMemorySaver  
    
    
    agent = create_agent(
        "gpt-5",
        [get_user_info],
        checkpointer=InMemorySaver(),  # хранение контекста в памяти 
    )
    
    agent.invoke(
        {"messages": [{"role": "user", "content": "Привет! Меня зовут Ваня"}]},
        {"configurable": {"thread_id": "1"}},  
    )

    💡 Существуют разные варианты реализации памяти

🔸Почему LangChain?

Можно спросить, зачем нужна эта прослойка, если мы уже умеем вызывать API напрямую? Дело в том, что по мере роста сложности приложения вам придется:

  • Держать где-то промпты (шаблоны) и подставлять данные.

  • Обрабатывать историю диалога.

  • Подключать дополнительные источники данных или инструменты.

  • Менять модели или провайдеров (например, захотеть переключиться с OpenAI на Anthropic или локальную модель).

LangChain берет часть этой рутины на себя. Он предоставляет единый интерфейс ко многим моделям, помощь в форматировании диалогов, структуру для организации многошаговых взаимодействий.
Проще говоря позволяет писать меньше шаблонного кода, сосредоточившись на логике приложения.

🔸Пример базового использования LangChain

Разберём базовый пример по шагам:

from langchain_openai import ChatOpenAI

chat = ChatOpenAI(
    model_name="gpt-5",
    temperature=0.7
)

response = chat.invoke("Какой сегодня день недели?")
print(response)

Что здесь произошло:

  • ChatOpenAI – класс LangChain для чат-моделей OpenAI. Мы указали model_name и temperature. Он автоматически возьмет OPENAI_API_KEY из переменной окружения (если она установлена), а также OPENAI_API_BASE – то есть интеграция с прокси тоже сохраняется (LangChain внутри использует openai пакет, поэтому наши настройки через os.getenv действуют). Мы могли бы явно передать openai_api_key="..." и openai_api_base="..." в конструктор, но если они настроены в окружении, то это не нужно.

  • Метод invoke() у ChatOpenAI принимает строку – ваш пользовательский запрос – и возвращает строку-ответ от модели. За кулисами LangChain сам сформировал messages с одной ролью user, вызвал openai.ChatCompletion.create и вернул content ответа. То есть chat.invoke("Какой сегодня день недели?") примерно эквивалентно тому, что мы делали руками ранее, только короче.

  • Мы сразу печатаем response. Модель, скорее всего, ответит чем-то вроде: "Сегодня среда." (или другой день, модель не знает реального времени, но попробует угадать или скажет, что не имеет доступа к календарю – зависит от её знаний на момент обучения).

⚠️ Прежде чем идти дальше, проверьте, что эти строки выполняются без ошибок: from langchain.agents import create_agent и from langchain.tools import tool.
Если видите ImportError: cannot import name 'ExecutionInfo' from 'langgraph.runtime' — значит, версии пакетов разъехались: обычно так бывает, когда langgraph-prebuilt встал свежее остальных и обращается к тому, чего в установленном langgraph ещё нет. Вернитесь к уроку про подготовку окружения и переустановите зависимости строго по списку с версиями.
Ещё одна частая причина ошибок — версия интерпретатора: LangChain 1.x требует Python 3.10 или новее, проверьте через python --version

🔹 Простейшая цепочка Prompt → LLM → Output

На этом шаге мы разберём создание простейшей цепочки (Chain), основного концепта фреймворка langchain, суть которого кратко мы описывали на предыдущем шаге

Самая простая цепочка – это подача промпта в LLM и получающая ответ. В актуальной версии фреймворка для этого используется класс RunnableSequence

 

🔸Шаги для создания и использования цепочки

  1. Определить шаблон промпта. Например, мы хотим, чтобы модель объяснила какое-то понятие простыми словами. Создадим шаблон: "Объясни простыми словами, что такое {question}"
    Здесь {question} – это переменная. В ChatPromptTemplate мы зададём эту переменную,  а вдальнейшем при вызове эта переменная будет заменятся строкой из запроса.

    from langchain_core.prompts import ChatPromptTemplate
    
    template = ChatPromptTemplate.from_template("Объясни простыми словами, что такое {question}")
    

    Теперь prompt – объект, который умеет принять конкретный question и выдать готовую строку. Например "Объясни простыми словами, что такое машинное обучение.", если в запросе будет указано машинное обучение 

  2. Создать объект модели. Возьмем, например, ChatOpenAI (для GPT-4o).

    from langchain_openai import ChatOpenAI
    
    model = ChatOpenAI(model="gpt-4o", temperature=0)
    

    Здесь мы ставим temperature=0 для максимальной детерминированности ответа (чтобы не расплывался). Объект model готов к использованию.

  3. Создать цепочку (Chain).

    from langchain_core.runnables import RunnableSequence
    
    chain = RunnableSequence(first=prompt, last=model)

    Теперь chain – связывает наш шаблон и модель.

  4. Вызвать цепочку с конкретным входом

    Для нашего шаблона требуется переменная "question". Значит:

    result = chain.invoke({"question": "машинное обучение"})
    print(result.content)

    LangChain сам возьмет шаблон, подставит question="машинное обучение", получит строчку "Объясни простыми словами, что такое машинное обучение.", затем вызовет модель model с этим промптом. Модель вернет ответ вроде  "Машинное обучение – это ... "). RunnableSequence вернёт именно содержимое ответа. В result.content будет строка с объяснением. Мы ее печатаем.

 

🔸Изменение параметров

Мы указали temperature=0, чтобы ответ был более определенным. Попробуйте поменять на 1 и запустить код несколько раз – могут различаться формулировки ответа (он станет более творческим, но и менее стабильным). Это демонстрирует, что RunnableSequence не мешает нам контролировать параметры модели – мы задаем их при создании model.

Если бы шаблон имел несколько переменных, ChatPromptTemplate.from_template("переменная 1: {var1}, переменная 2: {var2}") , то и chain.invoke() нужно было бы вызывать со всеми аргументами. chain.invoke({"var1": "один", "var2": "два"})

 

🔸Вызов модели напрямую vs через RunnableSequence

Можно отметить, что мы могли не использовать RunnableSequence, а делать:

prompt = template.invoke({"question": "машинное обучение"})
response = model.invoke(prompt)

и получить тот же результат.

Да, LangChain допускает и так. Но при усложнении цепочек (несколько шагов, memory, обработка результата) выгоднее сразу держать всё в цепочке.

RunnableSequence обеспечитвает единообразие: вы всегда вызываете .invoke() вне зависимости от того, что внутри – один LLM или сложная последовательность. Позже, если нужно будет поменять логику (например, добавить memory), вы модифицируете chain, а код вызова останется chain.invoke(...).

 

🔸Дополнительный пример (для практики)

Создайте другой шаблон – например, простой переводчик:

from langchain_core.prompts import ChatPromptTemplate
from langchain_openai import ChatOpenAI
from langchain_core.runnables import RunnableSequence

prompt = ChatPromptTemplate.from_template("Переведи на английский: {текст}")

model = ChatOpenAI(model="gpt-4o", temperature=0)

chain = RunnableSequence(first=prompt, last=model)

result = chain.invoke({"текст": "Доброе утро"})
print(result.content)

Ожидаемый ответ – "Good morning" (GPT-4o с temperature 0 даст прямой перевод). Так мы убедимся, что можно переиспользовать RunnableSequence с разными шаблонами.

💡 Обратите внимание, мы можем писать шаблон в том числе и название переменных в шаблоне на русском и модель понимает их, потому что GPT-4o владеет русским языком. LangChain не привязан к языку – шаблоны могут быть на любом языке, главное, чтобы модель понимала

🔹 LCEL (LangChain Expression Language)

На прошлом шаге мы реализовывали цепочку с помощью объекта RunnableSequence, но Langchain в новых версиях сильно поменял свою архитектуру и теперь основной подход по построению цепочек это LCEL.  Данный способ строит цепочки обработки данных через оператор | (pipe). Данные текут слева направо, как в Unix-командах.

 

🔸Базовый синтаксис

import os
from dotenv import load_dotenv
from langchain_openai import ChatOpenAI
from langchain_core.prompts import ChatPromptTemplate

load_dotenv()
MODEL = os.getenv("OPENAI_API_MODEL", "gpt-5")

prompt = ChatPromptTemplate.from_template("Привет. Меня зовут {name}")
llm = ChatOpenAI(model=MODEL)

basic_chain = prompt | llm

result = basic_chain.invoke({"name": "Вася"})
print(result)

По сути мы тут реализуем то же самое что и с RunnableSequence, промпт форматируется → отправляется в LLM. Но нам не нужно делать дополнительных импортов и код становится чище. Но это конечно не все плюсы по сравнению с использованием отдельного класса.

 

🔸Основные компоненты (Runnables)

Runnables — это универсальные объекты, которые можно запускать. Они представляют собой отдельные вычислительные шаги или блоки, которые принимают входные данные и возвращают результат. По сути, это замена старой концепции цепочек (Chains) и отдельных LLM шагов, но с более модульной и гибкой архитектурой.

  • RunnablePassthrough — передача данных без изменений
    Может показаться что он ничего не делает, но на самом деле полезный объект,  дальше это будет видно на практике

    from langchain_core.runnables import RunnablePassthrough
    
    # Просто передаёт входные данные дальше
    chain = RunnablePassthrough() | llm
    result = chain.invoke("Привет!")  # "Привет!" → llm
    print(result)
  • RunnableLambda — применяет функцию
    Польза этого объекта очевидна, можно написать костюмную функцию обработки или короткий скрипт с помощью lambda прямо в аргументе

    from langchain_core.runnables import RunnableLambda
    
    def uppercase(text):
        return text.upper()
    
    chain = RunnableLambda(uppercase) | llm
    chain.invoke("hello")  # "HELLO" → llm
  • StrOutputParser — извлечь текст из ответа LLM
    Простая цепочка выводит нам объект AIMessage и для того чтобы нам из него получить только ответ, нам бы приходилось каждый раз доставать его через атрибут .content, но мы можем упростить это и сразу парсить только ответ, если остальное нам не нужно. Делается это так:

    from langchain_core.output_parsers import StrOutputParser
    
    chain_with_parser = prompt | llm | StrOutputParser()
    result_with_parser = chain_with_parser.invoke({"name": "Вася"})
    
    # С объектом из примере базового синтаксиса
    print(type(basic_chain)) # <class 'langchain_core.messages.ai.AIMessage'>
    print(basic_chain) # content='Привет, Вася! 😊 Как дела?' additional_kwargs={'refusal': None} ... 
    
    # если парсим ответ
    print(type(result_with_parser)) # <class 'str'>
    print(result_with_parser) # Привет, Вася! 😊  Как дела?
  • Работа со словарями
    Как мы уже убедились, часто приходится передовать в аргументах словарь с переменными, для подстановки этих переменных в шаблоны. LCEL позволяет гибко работать с этими словарями.
    Тут мы создаём словарь и аргументы, которые будут переданы при вызове, с помощью класса RunnablePassthrough передаём дальше по цепочке уже в словаре:

    # Создать словарь из входных данных
    chain = {"name": RunnablePassthrough()} | prompt | llm
    
    result = chain.invoke("Ваня") # → {"name": "Ваня"} → prompt → llm
    print(result)

    Тут мы уже добавляем ключи в словарь используя метод .assign()

    chain = (
        {"name": RunnablePassthrough()}
        | RunnablePassthrough.assign(second_name=lambda x: "Иванов")
    )
    
    result = chain.invoke("Вася") 
    print(result)
    # вывод: {'name': 'Вася', 'second_name': "Иванов"}
    ⚠️ Важно чтобы значением был объект runnable, lambda функция подходит для этого
    А тут достаём определённые ключи из словаря методом .pick()
    chain = (
        {"name": RunnablePassthrough(), "age": lambda x: 25, "city": lambda x: "Москва"}
        | RunnablePassthrough().pick(["name", "city"])
    )
    
    result = chain.invoke("Иван")
    print(result)
    # # вывод: {'name': 'Иван', 'city': 'Москва'}
  • Параллельное выполнение — RunnableParallel

    from langchain_core.runnables import RunnableParallel
    
    prompt1 = ChatPromptTemplate.from_template("Плюсы {topic}")
    prompt2 = ChatPromptTemplate.from_template("Минусы {topic}")
    
    # Выполняются ОДНОВРЕМЕННО
    parallel_chain = RunnableParallel(
        pros=prompt1 | llm | StrOutputParser(),
        cons=prompt2 | llm | StrOutputParser()
    )
    
    result = parallel_chain.invoke({"topic": "Python"})
    print(result)
    # result = {"pros": "...", "cons": "..."}
  • Условная логика — RunnableBranch

    from langchain_core.runnables import RunnableBranch
    
    prompt_for_questions = ChatPromptTemplate.from_template("вопрос: {text}")
    prompt_for_statements = ChatPromptTemplate.from_template("не вопрос: {text}")
    
    def is_question(x):
        return "?" in x["text"]
    
    branch = RunnableBranch(
        (is_question, prompt_for_questions | llm),  # Если вопрос
        prompt_for_statements | llm  # Иначе (default)
    )
    
    chain = {"text": RunnablePassthrough()} | branch | StrOutputParser()
    
    result = chain.invoke({"text": "Какая сегодня погода?"})
    print(result)

 

🔸Шпаргалка по компонентам

Компонент Назначение
| Соединяет компоненты в цепочку
RunnablePassthrough Передаёт данные без изменений
RunnableLambda(func) Применяет функцию к данным
.assign(key=value) Добавляет ключ в словарь
.pick([keys]) Выбирает только указанные ключи
RunnableParallel Параллельное выполнение
RunnableBranch Условная логика (if/else)
StrOutputParser() Извлекает текст из ответа LLM

 

🔸Главный принцип

Думайте о данных как о потоке воды:

  • Оператор | — это труба
  • Каждый компонент — фильтр или трансформер
  • Данные текут слева направо
  • Каждый этап получает выход предыдущего

Вместо "создай объект, вызови метод" → "опиши поток данных от входа до выхода".

🔹 Сохранение контекста диалога

На этом этапе мы научимся сохранять контекст, чтобы модель помимо актуального вопроса пользователя, получала в промпте так же как и предыдущие сообщения (и вопросы, и ответы) и могла отвечать более осмысленно.

 

🔸 Современный подход с create_agent

В версии 1.0 радикально изменился подход к работе с памятью:

  • Память встроена в агенты через checkpointer
  • Состояние хранится автоматически между вызовами
  • Поддерживаются разные потоки разговоров через thread_id

Пример:

import os
from langchain.agents import create_agent
from langgraph.checkpoint.memory import InMemorySaver

agent = create_agent(
    model="gpt-4o-mini",
    checkpointer=InMemorySaver(),  # Сохранение в памяти
)

# Конфигурация с уникальным ID потока разговора
conv_1 = {"configurable": {"thread_id": "conversation_001"}}

question1 = "Привет! Меня зовут Алиса"
response1 = agent.invoke(
    {"messages": [{"role": "user", "content": question1}]},
    conv_1
)
print("пользователь 1:", question1)
print("Бот:", response1["messages"][-1].content)

question2 = "Какое у меня имя?"
response2 = agent.invoke(
    {"messages": [{"role": "user", "content": question2}]},
    conv_1
)
print("пользователь 1:", question2)
print("Бот:", response2["messages"][-1].content)

# Новый пользователь с другим thread_id
conv_2 = {"configurable": {"thread_id": "conversation_002"}}

question3 = "Какое у меня имя?"
response3 = agent.invoke(
    {"messages": [{"role": "user", "content": question3}]},
    conv_2
)
print("пользователь 2:", question3)
print("Бот:", response3["messages"][-1].content)

Ожидаемый вывод:

Как это работает:

  1. create_agent — функция для быстрого создания агентов
  2. InMemorySaver() — автоматически сохраняет состояние между вызовами, инициализируется при создании агента в аргументе checkpointer
  3. thread_id — уникальный идентификатор потока разговора, подаётся в словаре в настраеваемых параметрах "configurable" при вызове метода invoke()

 

🔸 Альтернативные варианты

Для production-приложений используйте персистентное хранилище, например SQLite или PostgreSQL:

# SQLite (для production)
from langgraph.checkpoint.sqlite import SqliteSaver
checkpointer = SqliteSaver.from_conn_string("database.db")

# PostgreSQL (для масштабирования)
from langgraph.checkpoint.postgres import PostgresSaver
checkpointer = PostgresSaver.from_conn_string("postgresql://...")
💡 Эти и другие дополнительные модули доустанавливаются отдельно pip install langgraph-checkpoint-sqlite langgraph-checkpoint-postgres и т.д.

 

🔹 Важные моменты работы с памятью:

Просмотр истории:

# Получить состояние потока
state = agent.get_state(config)
print("История сообщений:")
for msg in state.values["messages"]:
    print(f"{msg.type}: {msg.content}")

Ограничения по контексту:
Если пользователь и бот обменялись десятком длинных сообщений, при каждом новом запросе вся история вставляется в промпт. Это может:

  • Выйти за пределы лимита контекста модели

  • Сделать ответ медленнее и дороже

Решения:

  • Ограничить число хранимых сообщений (например, последние 10)

  • Использовать суммаризацию старой истории (сворачивать в краткое резюме)

  • Применить скользящее окно (только N последних реплик)

В этом курсе мы работаем с короткой историей, поэтому это не критично. Для продвинутого управления памятью есть и другие варианты:

from langchain.agents.middleware import before_model
from langchain.agents import AgentState

@before_model
def trim_messages(state: AgentState, runtime) -> dict:
    """Оставить только последние 10 сообщений"""
    messages = state["messages"]
    if len(messages) <= 10:
        return None  # Изменений нет
    
    # Сохраняем первое (системное) и последние 9
    return {
        "messages": [messages[0]] + messages[-9:]
    }

agent = create_agent(
    model="gpt-4o-mini",
    tools=[],
    checkpointer=InMemorySaver(),
    middleware=[trim_messages],  # Добавляем middleware
)

Встраивание system-контекста:
Чтобы задать поведение бота, используйте system-сообщение:

agent = create_agent(
    model="gpt-4o-mini",
    tools=[],
    checkpointer=InMemorySaver(),
    system_message="Ты дружелюбный помощник по математике. "
                   "Объясняй концепции просто и с примерами. "
                   "Используй эмодзи для наглядности."
)

⚠️ При использовании прокси ничего менять не нужно – способ соединения не влияет на память . Она только управляет данными, которые мы отправляем. Больше данных в истории = длиннее запрос = немного дороже и медленнее, но на нескольких репликах это не критично.

Задача 1

Сопоставьте сущность LangChain с описанием:

Сопоставьте элементы

Без регистрации · результат не сохраняется

Задача 2

Как получить «детерминированные» ответы в учебных примерах?
 

Выберите один вариант
Без регистрации · результат не сохраняется

Задача 3

Зачем вообще использовать LangChain поверх прямого вызова API? 
 

Выберите все подходящие варианты
Без регистрации · результат не сохраняется

Задача 4

Для диалога «с памятью» самыё простой в реализации подход это:
 

Выберите один вариант
Без регистрации · результат не сохраняется

Задача 5

Что является риском при работе с контекстом в длинном чате?
 

Выберите один вариант
Без регистрации · результат не сохраняется

Задача 6

🔹 Артефакт – простой чат-бот с контекстом

🔸 Описание задания

Создайте простое консольное приложение, позволяющее пользователю общаться с чат-ботом, который помнит контекст беседы. По сути, это упрощенный вариант ChatGPT

🔸 Требования к функционалу

  • Непрерывный диалог с пользователем: скрипт должен считывать ввод пользователя в цикле while с помощью input(), передавать его модели и печатать ответ. Затем снова ждать ввода – и так, пока пользователь не решит завершить и не напишет "выход"

  • Создайте агента с моделью "gpt-5"

  • Память диалога: бот должен помнить предыдущие реплики. Используем InMemorySaver()

  • Прощание: слово "выход" тоже нужно передать агенту и напечатать его ответ, и только после этого прервать цикл. Если выйти из цикла сразу по условию, прощание в вывод не попадёт и проверка не пройдёт

Необходимые импорты уже произведены:

from langchain.agents import create_agent
from langgraph.checkpoint.memory import InMemorySaver

Ничего импортировать больше не придётся
Реализуйте код в функции solution(). Вызывать её не нужно

⚠️ Библиотеки в коде являются имитацией реальных библиотек в ввиду того что на платформе stepik нет возможности импортирования реальных библиотек. В имитированных библиотеках реализован лишь базовый функционал необходимый для этого задания.

РешениеPython

Без регистрации. Гостевая проверка не сохраняет прогресс. Для сохранения перейдите к уроку.

Отправьте решение, чтобы увидеть результаты тестов.

Продолжить с сохранением прогресса

Перейдите к уроку в учебном кабинете.

Перейти к интерактивному уроку