קריאה לכלי

קריאה לכלי, שנקראת גם קריאה לפונקציה, היא דרך מובנית לתת ל-LLMs את היכולת לשלוח בקשות חזרה לאפליקציה שהפעילה אותם. אתם מגדירים את הכלים שרוצים להפוך לזמינים למודל, והמודל ישלח לאפליקציה בקשות לשימוש בכלים לפי הצורך כדי לבצע את ההנחיות שתספקו לו.

תרחישים לדוגמה של קריאה לכלים מתחלקים בדרך כלל לכמה נושאים:

מתן גישה של LLM למידע שלא אומן עליו

  • מידע שמשתנה לעיתים קרובות, כמו מחיר של מניה או מזג האוויר הנוכחי.
  • מידע ספציפי לדומיין של האפליקציה, כמו פרטי מוצרים או פרופילים של משתמשים.

שימו לב לחפיפה עם יצירה משופרת באמצעות אחזור (RAG), שהיא גם דרך לאפשר ל-LLM לשלב מידע עובדתי ביצירה שלו. RAG הוא פתרון כבד יותר שמתאים במיוחד כשיש כמות גדולה של מידע או כשהמידע הרלוונטי ביותר להנחיה הוא לא ברור. לעומת זאת, אם אחזור המידע שדרוש ל-LLM הוא קריאה פשוטה לפונקציה או חיפוש במסד נתונים, קריאה לכלי מתאימה יותר.

הוספת מידה מסוימת של דטרמיניזם לתהליך עבודה של LLM

  • ביצוע חישובים ש-LLM לא יכול להשלים בעצמו באופן מהימן.
  • איך מאלצים LLM ליצור טקסט מילה במילה בנסיבות מסוימות, למשל כשעונים על שאלה לגבי התנאים וההגבלות של אפליקציה.

ביצוע פעולה כשהיא מופעלת על ידי LLM

  • הפעלה והשבתה של אורות בעזרת עוזרת בית מבוססת-LLM
  • הזמנת שולחן בסוכנות מסעדות שמבוססת על LLM

לפני שמתחילים

כדי להריץ את דוגמאות הקוד בדף הזה, צריך לבצע קודם את השלבים שמפורטים במדריך תחילת העבודה. כל הדוגמאות מבוססות על ההנחה שכבר הגדרתם פרויקט עם יחסי התלות של Genkit מותקנים.

בדף הזה מוסבר על אחת מהתכונות המתקדמות של הפשטת המודלים ב-Genkit, לכן לפני שמתעמקים יותר מדי, כדאי להכיר את התוכן בדף יצירת תוכן באמצעות מודלים של AI. כדאי גם להכיר את המערכת של Genkit להגדרת סכימות קלט ופלט. המערכת הזו מפורטת בדף תהליכים.

סקירה כללית על קריאה לכלי

באופן כללי, כך נראית אינטראקציה טיפוסית של קריאה לכלי עם LLM:

  1. אפליקציית הקריאה שולחת בקשה ל-LLM, ומצרפת להנחיה גם רשימה של כלים שבהם ה-LLM יכול להשתמש כדי ליצור תשובה.
  2. ה-LLM יוצר תשובה מלאה או בקשה לקריאה לכלי בפורמט ספציפי.
  3. אם מבצע הקריאה מקבל תשובה מלאה, הבקשה מתמלאת והאינטראקציה מסתיימת. אבל אם מבצע הקריאה מקבל קריאה לכלי, הוא מבצע את הלוגיקה המתאימה ושולח בקשה חדשה ל-LLM שמכילה את ההנחיה המקורית או וריאציה כלשהי שלה, וגם את התוצאה של הקריאה לכלי.
  4. ה-LLM מטפל בהנחיה החדשה כמו בשלב 2.

כדי שהתכונה הזו תפעל, צריך לעמוד בכמה דרישות:

  • צריך לאמן את המודל לבקש את הכלים הנדרשים כדי להשלים את ההנחיה. רוב המודלים הגדולים יותר שזמינים דרך ממשקי API לאינטרנט, כמו Gemini ו-Claude, יכולים לעשות זאת, אבל לרוב מודלים קטנים יותר ומודלים מיוחדים יותר לא יכולים. אם תנסו לספק כלים לדגם שלא תומך בהם, תופיע הודעת שגיאה ב-Genkit.
  • האפליקציה הקוראת חייבת לספק הגדרות של כלים למודל בפורמט שהוא מצפה לו.
  • האפליקציה הקוראת צריכה להנחות את המודל ליצור בקשות לקריאה לכלי בפורמט שהאפליקציה מצפה לו.

שימוש בכלי ליצירת שיחות עם Genkit

Genkit מספק ממשק יחיד לקריאה לכלי עם מודלים שתומכים בו. כל פלאגין של מודל מוודא שהשניים האחרונים מהקריטריונים שלמעלה מתקיימים, והפונקציה generate() של מופע Genkit מבצעת באופן אוטומטי את לולאת הקריאה לכלי שתיארנו קודם.

תמיכה במודלים

התמיכה בקריאה לכלי תלויה בדגם, ב-API של הדגם ובפלאגין של Genkit. כדאי לעיין במסמכי התיעוד הרלוונטיים כדי לבדוק אם יש סיכוי שהכלי יתמוך בקריאה. בנוסף:

  • אם תנסו לספק כלים למודל שלא תומך בהם, תופיע הודעת שגיאה ב-Genkit.
  • אם הפלאגין מייצא הפניות למודלים, המאפיין info.supports.tools יציין אם הוא תומך בקריאה לכלי.

הגדרת כלים

משתמשים בפונקציה defineTool() של המכונה של Genkit כדי לכתוב הגדרות של כלים:

import { genkit, z } from 'genkit';
import { googleAI, gemini15Flash } from '@genkitai/google-ai';

const ai = genkit({
  plugins: [googleAI()],
  model: gemini15Flash,
});

const getWeather = ai.defineTool(
  {
    name: 'getWeather',
    description: 'Gets the current weather in a given location',
    inputSchema: z.object({ 
      location: z.string().describe('The location to get the current weather for')
    }),
    outputSchema: z.string(),
  },
  async (input) => {
    // Here, we would typically make an API call or database query. For this
    // example, we just return a fixed value.
    return 'The current weather in ${input.location} is 63°F and sunny.';
  }
);

התחביר כאן נראה בדיוק כמו התחביר של defineFlow(), אבל צריך להשתמש בפרמטרים name,‏ description ו-inputSchema. כשכותבים הגדרה של כלי, חשוב להקפיד במיוחד על הניסוח והתיאור של הפרמטרים האלה. הם חיוניים כדי ש-LLM יוכל להשתמש ביעילות בכלים הזמינים.

שימוש בכלים

כדאי לכלול כלים מוגדרים בהנחיות ליצירת תוכן.

יצירה

const response = await ai.generate({
  prompt: 'What is the weather in Baltimore?',
  tools: [getWeather],
});

definePrompt

const weatherPrompt = ai.definePrompt(
  {
    name: 'weatherPrompt',
    tools: [getWeather],
  },
  'What is the weather in {{location}}?'
);

const response = await weatherPrompt({ location: 'Baltimore' });

קובץ ההנחיה

---
system: "Answer questions using the tools you have."
tools: [getWeather]
input:
  schema:
    location: string
---

What is the weather in {{location}}?

לאחר מכן תוכלו להריץ את ההנחיה בקוד באופן הבא:

// assuming prompt file is named weatherPrompt.prompt
const weatherPrompt = ai.prompt('weatherPrompt');

const response = await weatherPrompt({ location: 'Baltimore' });

צ'אט

const chat = ai.chat({
  system: 'Answer questions using the tools you have.',
  tools: [getWeather],
});

const response = await chat.send('What is the weather in Baltimore?');

// Or, specify tools that are message-specific 
const response = await chat.send({
  prompt: 'What is the weather in Baltimore?',
  tools: [getWeather],
});

Genkit יטפל באופן אוטומטי בקריאה לכלי אם ה-LLM צריך להשתמש בכלי getWeather כדי לענות להנחיה.

השהיה של לולאת הכלי באמצעות הפסקות

כברירת מחדל, Genkit מבצע קריאות חוזרות ל-LLM עד שכל הקריאות לכלי יפתרו. אפשר להשהות את הביצוע באופן מותנה במצבים שבהם רוצים לעשות זאת, לדוגמה:

  • שואלים את המשתמש שאלה או מציגים ממשק משתמש.
  • מאשרים עם המשתמש פעולה שעלולה להיות מסוכנת.
  • בקשה לאישור מחוץ לפורום לביצוע פעולה.

הפסקות הן כלים מיוחדים שיכולים לעצור את הלולאה ולהחזיר את השליטה לקוד כדי שתוכלו לטפל בתרחישים מתקדמים יותר. במדריך הזה מוסבר איך משתמשים בהם.

טיפול מפורש בקריאות לכלי

אם רוצים לשלוט באופן מלא בלולאת הקריאה לכלי, למשל כדי להחיל לוגיקה מורכבת יותר, מגדירים את הפרמטר returnToolRequests לערך true. עכשיו באחריותכם לוודא שכל הבקשות לשימוש בכלים ימולאו:

const getWeather = ai.defineTool(
  {
    // ... tool definition ...
  },
  async ({ location }) => {
    // ... tool implementation ...
  },
);

const generateOptions: GenerateOptions = {
  prompt: "What's the weather like in Baltimore?",
  tools: [getWeather],
  returnToolRequests: true,
};

let llmResponse;
while (true) {
  llmResponse = await ai.generate(generateOptions);
  const toolRequests = llmResponse.toolRequests;
  if (toolRequests.length < 1) {
    break;
  }
  const toolResponses: ToolResponsePart[] = await Promise.all(
    toolRequests.map(async (part) => {
      switch (part.toolRequest.name) {
        case 'specialTool':
          return {
            toolResponse: {
              name: part.toolRequest.name,
              ref: part.toolRequest.ref,
              output: await getWeather(part.toolRequest.input),
            },
          };
        default:
          throw Error('Tool not found');
      }
    })
  );
  generateOptions.messages = llmResponse.messages;
  generateOptions.prompt = toolResponses;
}