Сегодня появляется все больше и больше приложений на основе больших языковых моделей — условным чат-ботом в Telegram уже никого не удивить. В рамках обучения в магистратуре AI Talent Hub мне не раз приходилось разрабатывать такие приложения с использованием ChatGPT или GigaChat. В этой статье я расскажу о полезном инструменте для работы с LLM - мы рассмотрим главные возможности фреймворка LangChain, а также методы мониторинга и проверки качества существующего приложения с ИИ.

Пара слов об LLM
Большие языковые модели (LLM) оперируют огромными объемами данных, что позволяет им эффективно решать различные задачи — от генерации и суммирования текста до анализа и понимания контекста.Однако работа с такими моделями сопряжена с рядом сложностей: высокая вычислительная нагрузка, сложная интеграция и необходимость обеспечения стабильной работы в продакшене. Для упрощения этого процесса существуют специализированные инструменты, которые облегчают взаимодействие с LLM.
Одним из таких инструментов является LangChain, предоставляющий удобные возможности для подключения моделей, создания пайплайнов, мониторинга и проверки качества приложений на основе больших языковых моделей. Использование LangChain позволяет значительно сократить затраты времени и ресурсов на разработку, делая работу с LLM более доступной и эффективной.
LangChain
LangChain — это фреймворк для разработки приложений с LLM. Он облегчает каждый этап - от разработки до деплоя, от тестирования до мониторинга. Фреймворк состоит из нескольких open-source библиотек:
langchain-core — пакет для работы с базовыми абстракциями.
Пакеты с интеграциями, например, langchain-openai, langchain-anthropic.
langchain — основной пакет с агентами, цепочками и другой функциональностью.
langchain-community — интеграции с различными сервисами (например, GigaChat от Сбера).
langgraph — пакет для внедрения компонентов LangChain в продакшен.
Рассмотрим основные концепции, которые предоставляет langchain для лёгкого построения модульной системы.
Chains
Последовательности шагов, компоненты, которые возможно переиспользовать, связанные вместе. Такие элементы позволяют создать пайплайн, который объединяет разные функциональности (подготовка данных, внешние API вызовы, преобразование данных, вызов LLM).
Пример простой цепочки, которая создает шаблонизированный запрос и получает ответ от модели:
from langchain_openai import ChatOpenAI
from langchain import PromptTemplate
from langchain.chains import LLMChain
# Инициализация модели LLM
llm = ChatOpenAI(model_name='gpt-3.5-turbo')
# Шаблон промпта для помощника по учёбе
study_helper_template = """
I want you to act as a study helper for a student.
Provide a list of study tips and recommend resources that would be beneficial for {study_topic}.
Ensure that the tips are practical and the resources are reliable and relevant.
What are some effective study strategies and resources for {study_topic}?
"""
# Создание объекта PromptTemplate с необходимыми переменными
prompt_template = PromptTemplate(
input_variables=["study_topic"],
template=study_helper_template,
)
# Описание учебной задачи или предмета
study_description = "preparing for a final exam in advanced calculus"
# Форматирование промпта с использованием описания
prompt_template.format(study_topic=study_description)
chain = LLMChain(llm=llm, prompt=prompt_template)
# Запуск цепочки и вывод результата
response = chain.run(study_description)
print(response)
Prompts
Это сами текстовые запросы, которые отправляются в LLM для генерации ответа. LangChain предоставляет инструменты для создания и работы с шаблонами промптов (PromptTemplate). Шаблоны позволяют динамически подставлять данные в промпт. В инструментарии есть возможность разделять промпты по ролям - системное сообщение, пользовательское сообщение, AI сообщение и т.д. Также LangChain предоставляет инструменты по работе с few-shot examples при формировании запросов к модели.
Идея few-shot examples состоит в том, чтобы, прежде чем отправлять пользовательский запрос, предоставлять несколько примеров ответов.
Пример кода, как это может выглядеть:
from langchain_core.prompts import (
ChatPromptTemplate,
FewShotChatMessagePromptTemplate,
)
# Описание примеров вопросов и ответов
examples = [
{"input": "2+2", "output": "4"},
{"input": "2+3", "output": "5"},
]
# Форматирование примеров с помощью шаблонов промптов
example_prompt = ChatPromptTemplate.from_messages(
[
("human", "{input}"),
("ai", "{output}"),
]
)
few_shot_prompt = FewShotChatMessagePromptTemplate(
example_prompt=example_prompt,
examples=examples,
)
# Создание итогового промпта с системным промптом и несколькими примерами ответов
final_prompt = ChatPromptTemplate.from_messages(
[
("system", "You are a wondrous wizard of math."),
few_shot_prompt,
("human", "{input}"),
]
)
Memory
Отвечает за сохранение состояния и контекста между вызовами LLM. Это особенно полезно для создания интерактивных приложений, таких как чат-боты, где важно помнить предыдущие взаимодействия с пользователем.
Существуют различные типы памяти:
ConversationBufferMemory - такой тип памяти позволяет сохранять сообщения как есть и извлекать их, если требуется.
ConversationBufferWindowMemory - так же, как и ConversationBufferMemory, сохраняет сообщения, однако в этом случае он хранит последние k взаимодействий. С помощью такого скользящего окна можно рассматривать только самые последние сообщения, таким образом, чтобы буфер не стал слишком большим.
ConversationSummaryMemory - такой тип памяти позволяет создавать суммаризацию переписки. В процессе общения будет обрабатываться переписка, суммаризироваться и итог сохраняться в памяти. Это может быть полезно в долгих переписках, где нужно запоминать детали, но хранить всю переписку займет слишком много токенов.
ConversationSummaryBufferMemory - тип памяти, который совмещает две идеи: он хранит суммаризацию старых сообщений, а последние сообщения хранит классическим способом в буфере. Вместо количества взаимодействий, как в ConversationBufferWindowMemory, этот тип памяти принимает решения о необходимости суммаризации сообщений по ограничению на количество последних токенов, которые стоит помнить.
Пример использования ConversationBufferWindowMemory в LLM-цепочке:
from langchain_openai import OpenAI
from langchain.chains import ConversationChain
conversation_with_summary = ConversationChain(
llm=OpenAI(temperature=0),
# Устанавливаем низкое значение 2, чтобы сохранить последние 2 взаимодействия
memory=ConversationBufferWindowMemory(k=2),
verbose=True
)
conversation_with_summary.predict(input="Hi, what's up?")
Embeddings
Числовые представления текстов, которые позволяют моделям определять смысловую близость между определёнными текстовыми записями. Они широко используются в решении задач в области обработки естественного языка. LangChain поддерживает создание эмбеддингов, их хранение в векторных базах данных и поиск по ним. В LangChain есть удобные инструменты для создания и работы с векторными хранилищами. Эти инструменты позволяют эффективно работать с эмбэддингами. Также в LangChain доступны retrievers, которые выполняют поиск релевантных документов или информации в векторных хранилищах. На основе этих инструментов можно построить RAG-систему, где поиск по запросу пользователя модель будет делать по внутренним данным, к примеру.
Пример создания такой системы:
from langchain_community.vectorstores import FAISS
from langchain_openai import OpenAIEmbeddings
from langchain_text_splitters import CharacterTextSplitter
# Загружаем документы и обрабатываем их
documents = loader.load()
text_splitter = CharacterTextSplitter(chunk_size=1000, chunk_overlap=0)
texts = text_splitter.split_documents(documents)
# Используем эмбеддинги на основе OpenAi
embeddings = OpenAIEmbeddings()
# Загружаем документы в векторное хранилище, текст из документов обрабатываем с помощью эмбеддингов
db = FAISS.from_documents(texts, embeddings)
# Создаем retriever
retriever = db.as_retriever()
# Ищем ответ на пользовательский запрос в наших документах
docs = retriever.invoke("what did he say about ketanji brown jackson")
Tool
Компоненты, через которые цепочки, LLM или агенты, о которых мы поговорим чуть позже, могут взаимодействовать с внешними источниками. Инструмент должен содержать: название, описание, JSON-схему входных данных, вызов функции и факт возврата результата вызова напрямую пользователю. Эти данные позволяют встроенному механизму принятия решений понять, когда какой инструмент стоит использовать. Понимание JSON-схемы входных данных нужно системе для правильного промптирования запросов. Чем легче входные данные инструмента, тем легче LLM сможет использовать данный инструмент.
В самом langchain есть реализации многих полезных инструментов, которые необходимо только импортировать для работы, к примеру: обертки вокруг вызова поисковых движков, использование различных плагинов ChatGPT, интеграции с базами данных, Wikipedia, YouTube и множество других.
Использовать готовые инструменты можно следующим образом:
from langchain_community.tools import WikipediaQueryRun
from langchain_community.utilities import WikipediaAPIWrapper
api_wrapper = WikipediaAPIWrapper(top_k_results=1, doc_content_chars_max=100)
tool = WikipediaQueryRun(api_wrapper=api_wrapper)
tool.run("langchain")
Для создания собственного инструмента необходимо воспользоваться декоратором @tool на желаемой функции.
Agents
Основная идея агентов заключается в том, что языковая модель, зная доступные инструменты, сама принимает решение о последовательности и необходимости вызова для ответа на пользовательский запрос. Если в цепочках порядок вызовов был строго определен с самого начала, то в агентах это решение передается самому механизму.
Агенты в LangChain функционируют как промежуточный слой между пользователем и набором доступных инструментов. Когда пользователь отправляет запрос, агент анализирует его, определяет, какие инструменты необходимы для выполнения задачи, и организует последовательность их вызовов. Это позволяет создавать более гибкие и адаптивные приложения, которые могут обрабатывать сложные и многогранные запросы без необходимости заранее определять жесткую последовательность действий.
Основные шаги работы агента в LangChain:
Анализ запроса: Агент принимает пользовательский ввод и анализирует его с помощью LLM для определения намерений и необходимых действий.
Выбор инструментов: На основе анализа запроса агент выбирает соответствующие инструменты из доступного набора, которые помогут выполнить задачу.
Организация последовательности действий: Агент определяет порядок вызовов выбранных инструментов, обеспечивая эффективное и логичное выполнение задачи.
Выполнение и интеграция результатов: Агент выполняет вызовы к инструментам, собирает результаты и интегрирует их в окончательный ответ для пользователя.
Пример создания агента, который может выполнять поисковые запросы в Википедии и отвечать на вопросы пользователя, используя найденную информацию.
from langchain import OpenAI, AgentExecutor, Tool
from langchain.tools import WikipediaAPIWrapper
# Инициализация модели LLM
llm = OpenAI(model_name='gpt-4', temperature=0)
# Создание инструмента для поиска в Википедии
wikipedia = WikipediaAPIWrapper()
# Определение инструмента с необходимыми параметрами
tools = [
Tool(
name="Wikipedia",
func=wikipedia.run,
description="Use this to search for lastest information in Wikipedia."
)
]
# Создание агента с доступными инструментами
agent = AgentExecutor.from_llm_and_tools(llm, tools)
# Пример запроса пользователя
user_query = "tell about lastest news in AI"
# Запуск агента и получение ответа
response = agent.run(user_query)
print(response)
Также из коробки LangChain поддерживает мониторинг и логирование через LangSmith. Для этого нужно включить трейсинг.
os.environ["LANGCHAIN_TRACING_V2"] = "true"
os.environ["LANGCHAIN_PROJECT"] = "" # Наименование проекта для мониторинга и логгирования
os.environ["LANGCHAIN_ENDPOINT"] = "https://api.smith.langchain.com"
os.environ["LANGCHAIN_API_KEY"] = "" # API ключ от langSmith
API ключ можно получить непосредственно на странице в LangSmith. Этот ключ нужен для интеграции и мониторинга запросов к моделям.
Мониторинг и логирование с LangChain
Для полного покрытия мониторингом и логами запросов к моделям необходимо использовать все реализации из библиотеки LangChain. Это обеспечивает всесторонний контроль над взаимодействием с большими языковыми моделями и позволяет отслеживать все этапы обработки запросов.
Аннотирование функций с @traceable
Определенные функции, внутри которых происходит собственное обращение к базе данных, RAG или LLM, и которые реализованы не с помощью цепочек, можно обернуть аннотацией @traceable из пакета LangSmith. Это позволяет автоматически отслеживать выполнение этих функций и интегрировать их в систему мониторинга.
Подробный мониторинг выполнения задач
С помощью LangSmith можно увидеть подробное состояние каждого шага во время выполнения задачи:
Запрос пользователя: исходный запрос, поступивший от пользователя.
Найденные документы: документы, отобранные в системе RAG.
Сформированный промпт: текст запроса, подготовленный по заданному шаблону.
Ответ модели: ответ, сгенерированный моделью LLM.
Сбор и привязка пользовательских оценок
Дополнительно можно реализовать запрос оценки от пользователя, например, через UI форму, и привязать эту оценку к пайплайну по run_id
. Это позволяет собирать обратную связь и улучшать качество ответов модели на основе реальных оценок пользователей.
На скриншоте ниже видно обращение к векторной базе данных, а затем формирование запроса в ChatGPT. Справа отображается оценка ответа модели, предоставленная пользователем.

Также в LangSmith есть возможность проводить проверку качества LLM модели на своих данных. Для этого необходимо предсоздать набор данных с запросом и ожидаемым результатом и запустить на нем свою модель с помощью evaluate из пакета langsmith.evaluation
. Можно перезапускать проверку с разными видами моделей и смотреть результат проверки через UI.

Заключение
LangChain — действительно полезный инструмент для быстрого и удобного создания приложений на основе больших языковых моделей. Он упрощает интеграцию LLM, предоставляет базовые строительные блоки (chains, prompts, memory, agents и т.д.) и даёт готовые инструменты для мониторинга и оценки качества. Всё это ускоряет разработку и упрощает отладку, позволяя сосредоточиться на логике приложения, а не на низкоуровневой интеграции.
Лично мне LangChain понравился за гибкость: легко комбинировать инструменты, подключать внешние сервисы и строить сложные пайплайны. А встроенный мониторинг (через LangSmith) даёт возможность видеть полный трекинг вызовов, что особенно важно при работе с LLM. Если вы работали с LangChain, расскажите в комментариях, чем он вам полезен или какие сложности возникли. Буду рада услышать ваше мнение!
С подробной документацией фреймворка можно ознакомиться тут
Материал подготовила магистрантка AI Talent Hub, Елизавета Воляница
Комментарии (4)
Roshalsky
13.02.2025 05:21Мне, как человеку близкому к программированию, но не программисту, было сложно разобраться в новых понятиях и абстракциях, которые зачем-то ввели разработчики. На поверку многие из них оказывались более привычными вещами, просто их назвали иначе. Надеюсь, что разработчики старались сделать как лучше и не хотели никого запутать.... Если что-то не работает, то очень сложно разобраться почему, потому что куча оберток прячет за собой всю логику. Причем код старых версий ограниченно совместим с новыми. Документация вообще живёт в каком-то другом мире. Возможно, что это мои личные ощущения не профессионала... Хорошо работали только примеры готового кода, если их удавалось запустить из-за несовместимости версий. Адаптировать под себя пытался, но хотелось бы поменьше усилий и пониже порог входа. Ситуация на мой непрофессиональный взгляд очень напоминает 1с, в которую на всякий случай напихали всего, чего в 90% не понадобится и до кучи собственный язык.
Кажется, что разработчики тоже начали что-то подозревать и выпустили LangGraph. Он оказался более понятным, но не целиком самостоятельным, потому что в каких-то штуках полагается на LangChain. По LangGraph хотя бы есть обучающий курс, и идея графов мне очень понравилась, включая их визуализацию. Но снова оттолкнула необходимость изучать новые старые понятия и дополнительные инструменты дебаггинга. Плюнул и написал свои графы на минималках, которые хотя бы можно нормально дебажить...
Авторам статьи большое спасибо, что смогли разобраться и делитесь своим опытом. Пусть будет легче тем, кто идёт следом. Пусть все будет не зря
Gras37
Есть ли возможность используя langchain-openai ChatOpenAI задавать "стартовую фразу", с которой llm будет начинать ответ иди продолжать генерацию (вызывая какую-то команду и передавай предыдущий вывод модели) в случае остановки по лимиту количества токенов?
TerryChan2003
Более удобно делать такую конструкцию в langgraph
Gras37
В некоторых Веб-интерфейсах есть кнопка "продолжить", можно ли как-то реализовать такой функционал используя ChatOpenAI у langchain?