创建永久性聊天会话

许多用户将首次通过聊天机器人与大语言模型互动。虽然 LLM 的功能远不止于模拟对话,但对话仍然是一种熟悉且实用的互动方式。即使用户不会以这种方式直接与模型互动,对话式提示也是影响 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(),
});