Como criar sessões de chat persistentes

Muitos dos seus usuários vão interagir com modelos de linguagem grandes pela primeira vez usando chatbots. Embora os LLMs sejam capazes de muito mais do que simular conversas, eles continuam sendo um estilo de interação conhecido e útil. Mesmo que os usuários não interajam diretamente com o modelo dessa forma, o estilo de comando de conversa é uma maneira eficaz de influenciar a saída gerada por um modelo de IA.

Para oferecer suporte a esse estilo de interação, o Genkit fornece um conjunto de interfaces e abstrações que facilitam a criação de aplicativos de LLM baseados em chat.

Antes de começar

Antes de ler esta página, você precisa conhecer o conteúdo abordado na página Como gerar conteúdo com modelos de IA.

Se você quiser executar os exemplos de código nesta página, primeiro conclua as etapas do guia Primeiros passos. Todos os exemplos assumem que você já instalou o Genkit como uma dependência no seu projeto.

Noções básicas sobre a sessão de chat

Confira um aplicativo de chatbot mínimo baseado no console:

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);
  }
})();

Uma sessão de chat com esse programa é semelhante ao exemplo abaixo:

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?

Como você pode ver nesta breve interação, quando você envia uma mensagem para uma sessão de chat, o modelo pode usar a sessão até o momento nas respostas. Isso é possível porque o Genkit faz algumas coisas nos bastidores:

  • Recupera o histórico de chat, se houver, do armazenamento. Mais informações sobre persistência e armazenamento mais tarde
  • Envia a solicitação para o modelo, como generate(), mas inclui automaticamente o histórico de bate-papo
  • Salva a resposta do modelo no histórico de chat

Configuração do modelo

O método chat() aceita a maioria das mesmas opções de configuração que generate(). Para transmitir opções de configuração ao modelo:

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,
  },
});

Sessões de chat com estado

Além de manter o histórico de mensagens de uma sessão de chat, você também pode manter qualquer objeto JavaScript arbitrário. Isso permite que você gerencie o estado de maneira mais estruturada, dependendo apenas das informações no histórico de mensagens.

Para incluir o estado em uma sessão, você precisa instanciar uma sessão explicitamente:

interface MyState {
  userName: string;
}

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

Você pode iniciar um chat na sessão:

const chat = session.chat();

Para modificar o estado da sessão com base no desenvolvimento do chat, defina ferramentas e as inclua nas suas solicitações:

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');

Sessões com várias linhas de execução

Uma única sessão pode conter várias linhas de chat. Cada linha de execução tem seu próprio histórico de mensagens, mas elas compartilham um único estado de sessão.

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

Persistência de sessão (EXPERIMENTAL)

Quando você inicializa uma nova sessão ou chat, ela é configurada por padrão para armazenar a sessão apenas na memória. Isso é adequado quando a sessão precisa persistir apenas durante a duração de uma única invocação do programa, como no exemplo de chatbot do início desta página. No entanto, ao integrar o chat do LLM a um aplicativo, você geralmente implanta a lógica de geração de conteúdo como endpoints de API da Web sem estado. Para que as conversas persistentes funcionem com essa configuração, você precisa implementar algum tipo de armazenamento de sessão que possa manter o estado em todas as invocações dos endpoints.

Para adicionar persistência a uma sessão de chat, é necessário implementar a interface SessionStore do Genkit. Confira um exemplo de implementação que salva o estado da sessão em arquivos JSON individuais:

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' });
  }
}

Essa implementação provavelmente não é adequada para implantações práticas, mas ilustra que uma implementação de armazenamento de sessão só precisa realizar duas tarefas:

  • Receber um objeto de sessão do armazenamento usando o ID da sessão
  • Salvar um determinado objeto de sessão, indexado pelo ID da sessão

Depois de implementar a interface do back-end de armazenamento, transmita uma instância da implementação aos construtores de sessão:

// 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(),
});