רבים מהמשתמשים ייחשפו למודלים גדולים של שפה בפעם הראשונה דרך צ'אט בוטים. מודלים מסוג 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',
});
שמירת נתוני סשן (ניסיוני)
כשמאתחלים צ'אט או סשן חדשים, הם מוגדרים כברירת מחדל לאחסון הסשן בזיכרון בלבד. האפשרות הזו מתאימה כשהסשן צריך להישאר רק למשך קריאה יחידה של התוכנית, כמו ב-chatbot לדוגמה שמופיע בתחילת הדף הזה. עם זאת, כשמשלבים צ'אט של LLM באפליקציה, בדרך כלל פורסים את הלוגיקה ליצירת תוכן כנקודות קצה ללא מצב של ממשק API לאינטרנט. כדי שהצ'אטים הקבועים יפעלו בהגדרה הזו, תצטרכו להטמיע סוג כלשהו של אחסון סשנים שיכול לשמור את המצב בין הפעלות של נקודות הקצה.
כדי להוסיף עמידות לסשן צ'אט, צריך להטמיע את הממשק SessionStore
של Genkit. דוגמה להטמעה שמשמרת את מצב הסשן בקובצי 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' });
}
}
סביר להניח שההטמעה הזו לא מתאימה לפריסות מעשיות, אבל היא ממחישה שצריך לבצע רק שתי משימות כדי להטמיע אחסון של סשנים:
- אחזור אובייקט סשן מהאחסון באמצעות מזהה הסשן שלו
- שמירת אובייקט סשן נתון, הוספה לאינדקס לפי מזהה הסשן שלו
אחרי שמטמיעים את הממשק לקצה העורפי של האחסון, מעבירים מופע של ההטמעה לבוררים של הסשנים:
// 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(),
});