Creazione di sessioni di chat permanenti

Molti dei tuoi utenti avranno interagito con i modelli linguistici di grandi dimensioni per la prima volta tramite i chatbot. Sebbene gli LLM siano in grado di fare molto di più che simulare conversazioni, rimangono uno stile di interazione familiare e utile. Anche se i tuoi utenti non interagiranno direttamente con il modello in questo modo, lo stile di prompt conversazionale è un modo efficace per influenzare l'output generato da un modello di IA.

Per supportare questo stile di interazione, Genkit fornisce un insieme di interfacce e abstazioni che semplificano la creazione di applicazioni LLM basate su chat.

Prima di iniziare

Prima di leggere questa pagina, devi conoscere i contenuti trattati nella pagina Generare contenuti con modelli di IA.

Se vuoi eseguire gli esempi di codice in questa pagina, completa prima i passaggi descritti nella guida Introduzione. Tutti gli esempi presuppongono che tu abbia già installato Genkit come dipendenza nel tuo progetto.

Nozioni di base sulle sessioni di chat

Ecco un'applicazione di chatbot minima basata su 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);
  }
})();

Una sessione di chat con questo programma è simile al seguente esempio:

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?

Come puoi vedere da questa breve interazione, quando invii un messaggio a una sessione di chat, il modello può utilizzare la sessione fino a quel momento nelle sue risposte. Questo è possibile perché Genkit esegue alcune operazioni dietro le quinte:

  • Recupera la cronologia chat, se esistente, dallo spazio di archiviazione (maggiori dettagli su persistenza e spazio di archiviazione più avanti)
  • Invia la richiesta al modello, come per generate(), ma include automaticamente la cronologia della chat
  • Salva la risposta del modello nella cronologia chat

Configurazione modello

Il metodo chat() accetta la maggior parte delle opzioni di configurazione di generate(). Per passare le opzioni di configurazione al modello:

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

Sessioni di chat stateful

Oltre a mantenere la cronologia dei messaggi di una sessione di chat, puoi anche mantenere qualsiasi oggetto JavaScript arbitrario. In questo modo, puoi gestire lo stato in modo più strutturato, anziché basarti solo sulle informazioni nella cronologia dei messaggi.

Per includere lo stato in una sessione, devi creare una sessione in modo esplicito:

interface MyState {
  userName: string;
}

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

Puoi quindi avviare una chat all'interno della sessione:

const chat = session.chat();

Per modificare lo stato della sessione in base allo svolgimento della chat, definisci gli strumenti e includili nelle tue richieste:

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

Sessioni multithread

Una singola sessione può contenere più thread di chat. Ogni thread ha la propria cronologia dei messaggi, ma condividono un unico stato della sessione.

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

Persistenza della sessione (Sperimentale)

Quando inizializi una nuova chat o sessione, per impostazione predefinita viene configurata per memorizzare la sessione solo in memoria. Questo è sufficiente quando la sessione deve essere persistente solo per la durata di una singola chiamata del programma, come nel chatbot di esempio all'inizio di questa pagina. Tuttavia, quando integri la chat LLM in un'applicazione, in genere esegui il deployment della logica di generazione dei contenuti come endpoint API web senza stato. Affinché le chat permanenti funzionino in questa configurazione, dovrai implementare un qualche tipo di spazio di archiviazione della sessione che possa mantenere attivo lo stato tra le invocazioni dei tuoi endpoint.

Per aggiungere la persistenza a una sessione di chat, devi implementare l'interfaccia SessionStore di Genkit. Ecco un esempio di implementazione che salva lo stato della sessione in singoli file 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' });
  }
}

Questa implementazione probabilmente non è adeguata per i deployment pratici, ma illustra che un'implementazione dello spazio di archiviazione delle sessioni deve svolgere solo due attività:

  • Recuperare un oggetto sessione dallo spazio di archiviazione utilizzando il relativo ID sessione
  • Salvare un determinato oggetto sessione, indicizzato dal relativo ID sessione

Dopo aver implementato l'interfaccia per il backend di archiviazione, passa un'istanza della tua implementazione ai costruttori della sessione:

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