建立持續即時通訊工作階段

許多使用者會透過聊天機器人,首次與大型語言模型互動。雖然大型語言模型的功能遠不止於模擬對話,但仍是一種熟悉且實用的互動方式。即使使用者不會以這種方式直接與模型互動,對話式提示仍是影響 AI 模型產生輸出的強大方法。

為了支援這種互動方式,Genkit 提供一組介面和抽象概念,讓您更輕鬆地建構以即時通訊為主的 LLM 應用程式。

事前準備

閱讀本頁之前,請先熟悉「使用 AI 模型產生內容」頁面所涵蓋的內容。

如果您想執行本頁的程式碼範例,請先完成「開始使用」指南中的步驟。所有範例都假設您已將 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 聊天整合至應用程式時,通常會將內容產生邏輯部署為無狀態 Web API 端點。如要讓持續性即時通訊在這個設定下運作,您必須實作某種會在端點呼叫期間持續保留狀態的工作階段儲存空間。

如要在聊天工作階段中新增持久性,您必須實作 Genkit 的 SessionStore 介面。以下是將工作階段狀態儲存至個別 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' });
  }
}

這項實作可能不適合實際部署作業,但它說明瞭工作階段儲存空間實作作業只需完成兩項工作:

  • 使用工作階段 ID 從儲存空間取得工作階段物件
  • 儲存指定的工作階段物件,並以工作階段 ID 建立索引

為儲存空間後端實作介面後,請將實作項目的例項傳遞至工作階段建構函式:

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