Создание постоянных сеансов чата

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

Для поддержки этого стиля взаимодействия Genkit предоставляет набор интерфейсов и абстракций, которые упрощают создание приложений LLM на основе чата.

Прежде чем начать

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

Если вы хотите запустить примеры кода на этой странице, сначала выполните действия, описанные в руководстве по началу работы . Во всех примерах предполагается, что вы уже установили Genkit в качестве зависимости в своем проекте.

Основы сеанса чата

Вот минимальное консольное приложение чат-бота:

import { genkit } from "genkit";
import { googleAI, gemini15Flash } from "@genkit-ai/googleai";

import { createInterface } from "node:readline/promises";

const ai = genkit({
  plugins: [googleAI()],
  model: gemini15Flash,
});

(async () => {
  const chat = ai.chat();
  console.log("You're chatting with Gemini. Ctrl-C to quit.\n");
  const readline = createInterface(process.stdin, process.stdout);
  while (true) {
    const userInput = await readline.question("> ");
    const { text } = await chat.send(userInput);
    console.log(text);
  }
})();

Сеанс чата с этой программой выглядит примерно так:

You're chatting with Gemini. Ctrl-C to quit.

> hi
Hi there! How can I help you today? 

> my name is pavel
Nice to meet you, Pavel! What can I do for you today? 

> what's my name?
Your name is Pavel! I remembered it from our previous interaction. 

Is there anything else I can help you with?

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

  • Извлекает историю чата, если она существует, из хранилища (подробнее о сохранении и хранении позже).
  • Отправляет запрос модели, как и в случае generate() , но автоматически включает историю чата.
  • Сохраняет ответ модели в историю чата.

Конфигурация модели

Метод chat() принимает большинство тех же параметров конфигурации, что и generate() . Чтобы передать параметры конфигурации в модель:

const chat = ai.chat({
  model: gemini15Pro,
  system:
    "You're a pirate first mate. Address the user as Captain and assist " +
    "them however you can.",
  config: {
    temperature: 1.3,
  },
});

Сеансы чата с сохранением состояния

Помимо сохранения истории сообщений сеанса чата, вы также можете сохранить любой произвольный объект JavaScript. Это позволит вам управлять состоянием более структурированным образом, чем полагаться только на информацию из истории сообщений.

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

interface MyState {
  userName: string;
}

const session = ai.createSession<MyState>({
  initialState: {
    userName: 'Pavel',
  },
});

Затем вы можете начать чат в рамках сеанса:

const chat = session.chat();

Чтобы изменить состояние сеанса в зависимости от того, как разворачивается чат, определите инструменты и включите их в свои запросы:

const changeUserName = ai.defineTool(
  {
    name: 'changeUserName',
    description: 'can be used to change user name',
    inputSchema: z.object({
      newUserName: z.string(),
    }),
  },
  async (input) => {
    await ai.currentSession<MyState>().updateState({
      userName: input.newUserName,
    });
    return 'changed username to ${input.newUserName}';
  }
);
const chat = session.chat({
  model: gemini15Pro,
  tools: [changeUserName],
});
await chat.send('change user name to Kevin');

Многопоточные сеансы

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

const lawyerChat = session.chat('lawyerThread', {
  system: 'talk like a lawyer',
});
const pirateChat = session.chat('pirateThread', {
  system: 'talk like a pirate',
});

Сохранение сеанса (ЭКСПЕРИМЕНТАЛЬНО)

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

Чтобы добавить сохранение в сеанс чата, вам необходимо реализовать интерфейс SessionStore Genkit. Вот пример реализации, которая сохраняет состояние сеанса в отдельные файлы JSON:

class JsonSessionStore<S = any> implements SessionStore<S> {
  async get(sessionId: string): Promise<SessionData<S> | undefined> {
    try {
      const s = await readFile(`${sessionId}.json`, { encoding: 'utf8' });
      const data = JSON.parse(s);
      return data;
    } catch {
      return undefined;
    }
  }

  async save(sessionId: string, sessionData: SessionData<S>): Promise<void> {
    const s = JSON.stringify(sessionData);
    await writeFile(`${sessionId}.json`, s, { encoding: 'utf8' });
  }
}

Эта реализация, вероятно, не подходит для практического развертывания, но она показывает, что реализация хранилища сеансов должна выполнять только две задачи:

  • Получить объект сеанса из хранилища, используя его идентификатор сеанса.
  • Сохраните данный объект сеанса, проиндексированный его идентификатором сеанса.

После того как вы реализовали интерфейс для своего хранилища, передайте экземпляр своей реализации конструкторам сеанса:

// To create a new session:
const session = ai.createSession({
  store: new JsonSessionStore(),
});

// Save session.id so you can restore the session the next time the
// user makes a request.
// If the user has a session ID saved, load the session instead of creating
// a new one:
const session = await ai.loadSession(sessionId, {
    store: new JsonSessionStore(),
});