ym88659208ym87991671
Разработка чат-бота | Документация для разработчиков

Разработка чат-бота

Обновлено 6 августа 2024

Раздел содержит пример разработки чат-бота на основе LLM. Этот чат-бот может вести беседу и запоминать предыдущие действия пользователя.

В примере рассмотрен чат-бот, который для ведения беседы использует только языковую модель. Также существуют другие способы разработки чат-ботов, которые могут вас заинтересовать:

  • Агенты — чат-боты, которые могут выполнять действия.

Здесь вы найдете базовую информацию о разработке чат-ботов, которая будет полезна при работе с приведенными выше разделами. Но, если нужно, вы можете сразу начать с более сложных чат-ботов.

Основные компоненты

Пример чат-бота показывает, как работать с такими компонентами, как:

  • Чат-модели. Чат-боты работают с данными, представленными в виде сообщений, а не в виде необработанного текста. Поэтому для разработки лучше использовать чат-модели, а не текстовые LLM, которые возвращают простой текст.
  • Шаблоны промптов. Шаблоны упрощают создание промптов, которые объединяют стандартные сообщения, ввод пользователя, историю чатов и, если нужно, дополнительный контекст.
  • История чата. История позволяет чат-боту сохранять прошлые взаимодействия с пользователем и учитывать их при ответе на последующие вопросы.

Подготовка к разработке

Jupyter-блокноты

Это руководство, как и большинство других в документации, использует Jupyter-блокноты. Они отлично подходят для изучения работы с LLM-системами, так как предоставляют интерактивную среду для работы с руководствами и позволяют работать с непредвиденными ситуациями: недоступностью API, нетипичным выводом и другими.

Подробнее об установке jupyter — в официальной документации.

Установка

Для установки GigaChain выполните команду:

pip install gigachain-community

Подробнее об установке — в разделе Установка.

Быстрый старт

Сначала ознакомьтесь, как использовать языковую модель отдельно. Хотя GigaChain поддерживает различные языковые модели, основным преимуществом библиотеки является возможность работы с моделями GigaChat.

# | output: false
# | echo: false

from langchain.chat_models.gigachat import GigaChat

model = GigaChat(
credentials="<авторизационные_данные>",
scope="GIGACHAT_API_PERS",
model="GigaChat-Pro",
verify_ssl_certs=False,
)

Объект GigaChat принимает параметры:

  • credentials — авторизационные данные для обмена сообщениями с GigaChat API. О том, как получить атворизационные данные — в разделе Быстрый старт.

  • scope — необязательный параметр. Задает версию API, к которой нужно обратиться. Возможные значения:

    • GIGACHAT_API_PERS — версия API для физических лиц;
    • GIGACHAT_API_CORP — версия API для ИП и юрлиц.

    По умолчанию запросы передаются в версию для физических лиц.

  • model — необязательный параметр, в котором можно явно задать модель GigaChat.

    В примере используется модель GigaChat-Pro. Ответы других моделей могут отличаться.

  • verify_ssl_certs — необязательный параметр, с помощью которого можно отключить проверку сертификатов НУЦ Минцифры.

Подробнее о параметрах GigaChat.

Попробуйте обратиться к модели напрямую.

Объекты ChatModel — это экземпляры Runnable-интерфейса GigaChain. Все экземпляры Runnable предоставляют стандартный интерфейс для взаимодействия.

Так, чтобы обратиться к модели, достаточно вызвать метод .invoke() со списком сообщений.

from langchain_core.messages import HumanMessage

model.invoke([HumanMessage(content="Привет! Меня зовут Вася")])
    AIMessage(content='Здравствуйте, Вася! Я генеративная языковая модель от Сбера. Готова ответить на ваши вопросы.', response_metadata={'token_usage': Usage(prompt_tokens=19, completion_tokens=28, total_tokens=47), 'model_name': 'GigaChat-Pro:2.2.25.3', 'finish_reason': 'stop'}, id='run-5e4e4ab8-8ed6-4004-90a5-fa0c8dfdb8ce-0')

Сама модель не сохраняет информацию о состоянии. В этом можно убедиться, если задать ей дополнительный вопрос:

model.invoke([HumanMessage(content="Как меня зовут?")])
    AIMessage(content='Я не могу знать вашего имени, но вы можете сообщить его мне.', response_metadata={'token_usage': Usage(prompt_tokens=16, completion_tokens=16, total_tokens=32), 'model_name': 'GigaChat-Pro:2.2.25.3', 'finish_reason': 'stop'}, id='run-1ed92f73-e945-40ac-913b-bc0caf013d59-0')

Чтобы обойти это ограничение, передайте всю историю разговора в модель:

from langchain_core.messages import AIMessage

model.invoke(
[
HumanMessage(content="Привет! Меня зовут Вася"),
AIMessage(
content="Здравствуйте, Вася! Я генеративная языковая модель от Сбера. Готова ответить на ваши вопросы."
),
HumanMessage(content="Как меня зовут?"),
]
)
    AIMessage(content='Вас зовут Вася.', response_metadata={'token_usage': Usage(prompt_tokens=60, completion_tokens=8, total_tokens=68), 'model_name': 'GigaChat-Pro:2.2.25.3', 'finish_reason': 'stop'}, id='run-bc7a1b1d-f6aa-455d-aa5b-f84c65394d07-0')

Теперь модель может гораздо точнее отвечать на дополнительные вопросы.

Работа с историей сообщений позволяет чат-боту вести разговор. Ниже показано, как ее реализовать.

История сообщений

Чтобы модель сохраняла состояние, вы можете обернуть ее в класс Message History. Класс отслеживает входные и выходные данные модели и сохраняет их в хранилище данных. При повторных обращениях сообщения модели загружаются из хранилища и передаются в цепочку, как часть входных данных.

Пример ниже использует хранилище истории сообщений, доступное в пакете gigachain-community, который вы установили в начале.

Импортируйте соответствующие классы и настройте цепочку, которая обернет модель и добавит историю сообщений.

Самой важной частью здесь является функция get_session_history. Она должна принимать строковый идентификатор сессии session_id и возвращать историю разговора в объекте Message History. Параметр session_id используется, чтобы различать разговоры. Он передается как часть конфигурационной переменной при вызове новой цепочки.

from langchain_core.chat_history import (
BaseChatMessageHistory,
InMemoryChatMessageHistory,
)
from langchain_core.runnables.history import RunnableWithMessageHistory

store = {}


def get_session_history(session_id: str) -> BaseChatMessageHistory:
if session_id not in store:
store[session_id] = InMemoryChatMessageHistory()
return store[session_id]


with_message_history = RunnableWithMessageHistory(model, get_session_history)

Создайте переменную config, которая будет содержать дополнительные данные для вызова цепочки. В примере эта переменная содержит идентификатор сессии session_id. Передавайте переменную при каждом вызове runnable.

config = {"configurable": {"session_id": "abc2"}}
response = with_message_history.invoke(
[HumanMessage(content="Привет! Меня зоут Вася")],
config=config,
)

response.content
    'Здравствуйте, Вася! Я генеративная языковая модель от Сбера. Готова ответить на ваши вопросы.'
response = with_message_history.invoke(
[HumanMessage(content="Как меня зовут?")],
config=config,
)

response.content
    'Вас зовут Вася.'

Теперь чат-бот запоминает информацию. Если вы измените переменную config, чтобы сослаться на другую сессию (session_id), то увидите, что разговор начнется заново.

config = {"configurable": {"session_id": "abc3"}}

response = with_message_history.invoke(
[HumanMessage(content="Как меня зовут?")],
config=config,
)

response.content
    'Я не могу знать вашего имени, но готова сгенерировать для вас текст по запросу.'

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

config = {"configurable": {"session_id": "abc2"}}

response = with_message_history.invoke(
[HumanMessage(content="What's my name?")],
config=config,
)

response.content
    'Вас зовут Вася.'

Таким образом, ваш чат-бот сможет разговаривать с разными пользователями.

Примеры ниже показывают, как использовать шаблон промпта, чтобы расширить и персонализировать данные, которые сохраняет чат-бот.

Шаблоны промптов

Шаблоны промптов помогают преобразовать необработанные данные пользователя в формат, с которым может работать LLM. В данном случае необработанные данные — это просто сообщение, которое вы передаете в LLM. Попробуйте развить сообщение.

Сначала добавьте системное сообщение со своими инструкциями (но все еще принимая сообщения в качестве входных данных). А затем добавьте больше входных данных, помимо сообщений.

Для добавления системного сообщения создайте экземпляр ChatPromptTemplate. Чтобы передать все сообщения, используйте MessagesPlaceholder.

from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder

prompt = ChatPromptTemplate.from_messages(
[
(
"system",
"Ты личный помощник. Старайся как можно лучше помочь пользователю.",
),
MessagesPlaceholder(variable_name="messages"),
]
)

chain = prompt | model

В результате тип входных данных изменится — вместо простого списка сообщений вы теперь передаете словарь с ключом messages, который содержит список сообщений.

response = chain.invoke({"messages": [HumanMessage(content="Привет! Меня зовут Вася")]})

response.content
    'Здравствуйте, Вася! Рада знакомству. Как я могу вам помочь?'

Теперь вы можете обернуть полученный код в объект истории сообщений with_message_history, созданный ранее.

with_message_history = RunnableWithMessageHistory(chain, get_session_history)
config = {"configurable": {"session_id": "abc5"}}
response = with_message_history.invoke(
[HumanMessage(content="Привет! Меня зовут Кира")],
config=config,
)

response.content
    'Здравствуйте, Кира! Рада знакомству с вами.'
response = with_message_history.invoke(
[HumanMessage(content="Как меня зовут?")],
config=config,
)

response.content
    'Вас зовут Кира.'

Усложните полученный промпт. Предположим, что шаблон промпта теперь выглядит примерно так:

prompt = ChatPromptTemplate.from_messages(
[
(
"system",
"Ты — личный ассистент. Старайся как можно лучше помочь пользователю. Отвечай на все вопросы пользователя на следующем языке: {language}. Не называй своего имени.",
),
MessagesPlaceholder(variable_name="messages"),
]
)

chain = prompt | model

Выше в промпт добавлена новая переменная language. Теперь вы можете вызвать цепочку и задать язык, на котором должна ответить модель.

response = chain.invoke(
{
"messages": [HumanMessage(content="Привет! Меня зовут Вася")],
"language": "Английский",
}
)

response.content
    'Hello, I am your personal assistant. How can I help you?'

Оберните полученную цепочку в класс with_message_history. Поскольку входные данные содержат несколько ключей, вам нужно указать правильный ключ для сохранения истории чата.

with_message_history = RunnableWithMessageHistory(
chain,
get_session_history,
input_messages_key="messages",
)
config = {"configurable": {"session_id": "abc15"}}
response = with_message_history.invoke(
{
"messages": [HumanMessage(content="Привет! Меня зовут Коля")],
"language": "Английский",
},
config=config,
)

response.content
    'Hello, I am your personal assistant. How can I help you?'

Управление историей разговоров

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

При этом, этот этап должен срабатывать до шаблона промпта, но после загрузки предыдущих сообщений из Message History.

Для этого перед промптом вы можете добавить шаг, который изменяет содержимое messages соответствующим образом, а затем обернуть полученную цепочку в класс Message History.

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

from langchain_core.messages import SystemMessage, trim_messages

trimmer = trim_messages(
max_tokens=35,
strategy="last",
token_counter=model,
include_system=True,
allow_partial=False,
start_on="human",
)

messages = [
SystemMessage(content="Ты — личный ассистент"),
HumanMessage(content="Привет! Меня зовут Вася"),
AIMessage(content="Привет!"),
HumanMessage(content="Я люблю шоколадное мороженое"),
AIMessage(content="Здорово"),
HumanMessage(content="Сколько будет 2 + 2"),
AIMessage(content="4"),
HumanMessage(content="Спасибо"),
AIMessage(content="Пожалуйста!"),
HumanMessage(content="Тебе нравится наш разговор?"),
AIMessage(content="Да!"),
]

trimmer.invoke(messages)
    [SystemMessage(content='Ты — личный ассистент'),
HumanMessage(content='Сколько будет 2 + 2'),
AIMessage(content='4'),
HumanMessage(content='Спасибо'),
AIMessage(content='Пожалуйста!'),
HumanMessage(content='Тебе нравится наш разговор?'),
AIMessage(content='Да!')]

Потоковая передача

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

Для работы с потоковой передачей все цепочки предоставляют метод .stream, в том числе и те, что используют историю сообщений. Используйте его, чтобы получить потоковый ответ.

config = {"configurable": {"session_id": "abc19"}}
for r in with_message_history.stream(
{
"messages": [
HumanMessage(content="Привет! Я Вася. Расскажи историю из 100 слов")
],
"language": "Английский",
},
config=config,
):
print(r.content, end="|")
    Once upon a time, there was a little boy named Jack who lived in a small village with his family. One day, he decided to explore the nearby forest and stumbled upon an old oak tree. Upon closer inspection, he discovered a hidden door leading to a magical underground world filled with fairies and elves. They welcomed him warmly and taught him many things about their world, including how to fly on a magic carpet. When it was time for Jack to return home, they gave him a special gift - a golden amulet that would| protect| him| from| harm|.| From| that| day| forward|,| Jack| always| wore| the| am|u|let| and| never| forgot| his| advent|ure| in| the| mag|ical| under|ground| world|.||

Смотрите также

  • Агенты — чат-боты, которые могут выполнять действия.
ПАО Сбербанк использует cookie для персонализации сервисов и удобства пользователей.
Вы можете запретить сохранение cookie в настройках своего браузера.