许多用户将首次通过聊天机器人与大语言模型互动。虽然 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(),
});