קריאה לכלי

קריאה לכלי, שנקראת גם קריאה לפונקציה, היא דרך מובנית לתת ל-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 '@genkit-ai/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 ו-outputSchema. כשכותבים הגדרה של כלי, חשוב לשים לב במיוחד לניסוח ולתיאור של הפרמטרים האלה, כי הם חיוניים כדי ש-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;
}