Appel de l'outil

L'appel d'outil, également appelé appel de fonction, est un moyen structuré de permettre aux LLM d'envoyer des requêtes à l'application qui les a appelés. Vous définissez les outils que vous souhaitez mettre à la disposition du modèle, et celui-ci envoie des requêtes d'outils à votre application si nécessaire pour répondre aux requêtes que vous lui envoyez.

Les cas d'utilisation de l'appel d'outils se répartissent généralement en quelques thèmes:

Accorder à un LLM l'accès à des informations avec lesquelles il n'a pas été entraîné

  • Informations qui changent fréquemment, comme le cours d'une action ou la météo actuelle
  • Informations spécifiques au domaine de votre application, telles que des informations sur les produits ou des profils utilisateur.

Notez le chevauchement avec la génération augmentée par récupération (RAG), qui est également un moyen de permettre à un LLM d'intégrer des informations factuelles dans ses générations. Le RAG est une solution plus lourde qui est la plus adaptée lorsque vous disposez d'une grande quantité d'informations ou que les informations les plus pertinentes pour une invite sont ambiguës. En revanche, si la récupération des informations dont le LLM a besoin se fait par un simple appel de fonction ou une recherche dans la base de données, l'appel d'outil est plus approprié.

Introduire un degré de déterminisme dans un workflow de LLM

  • Effectuer des calculs que le LLM ne peut pas effectuer de manière fiable.
  • Forcer un LLM à générer un texte textuel dans certaines circonstances, par exemple pour répondre à une question sur les conditions d'utilisation d'une application.

Effectuer une action lorsqu'elle est lancée par un LLM

  • Allumer et éteindre les lumières dans un assistant à domicile basé sur un LLM
  • Réserver une table dans un agent de restaurant optimisé par LLM

Avant de commencer

Si vous souhaitez exécuter les exemples de code de cette page, suivez d'abord les étapes du guide Premiers pas. Tous les exemples partent du principe que vous avez déjà configuré un projet avec les dépendances Genkit installées.

Cette page décrit l'une des fonctionnalités avancées de l'abstraction de modèle Genkit. Avant de vous plonger dans le sujet, vous devez donc vous familiariser avec le contenu de la page Générer du contenu avec des modèles d'IA. Vous devez également connaître le système de Genkit pour définir des schémas d'entrée et de sortie, qui est décrit sur la page Flux.

Présentation de l'appel d'outils

De manière générale, voici à quoi ressemble une interaction d'appel d'outil avec un LLM:

  1. L'application appelante envoie une requête au LLM et inclut également dans l'invite une liste d'outils que le LLM peut utiliser pour générer une réponse.
  2. Le LLM génère une réponse complète ou une requête d'appel d'outil dans un format spécifique.
  3. Si l'appelant reçoit une réponse complète, la requête est traitée et l'interaction se termine. Toutefois, si l'appelant reçoit un appel d'outil, il exécute la logique appropriée et envoie une nouvelle requête au LLM contenant l'invite d'origine ou une variante de celle-ci, ainsi que le résultat de l'appel de l'outil.
  4. Le LLM gère la nouvelle requête comme à l'étape 2.

Pour que cela fonctionne, plusieurs conditions doivent être remplies:

  • Le modèle doit être entraîné pour effectuer des requêtes d'outils lorsqu'il est nécessaire d'exécuter une invite. La plupart des modèles plus volumineux fournis via des API Web, tels que Gemini et Claude, peuvent le faire, mais les modèles plus petits et plus spécialisés ne le peuvent souvent pas. Genkit génère une erreur si vous essayez de fournir des outils à un modèle qui ne les accepte pas.
  • L'application appelante doit fournir des définitions d'outils au modèle au format attendu.
  • L'application appelante doit inviter le modèle à générer des requêtes d'appel d'outil au format attendu par l'application.

Appel d'outils avec Genkit

Genkit fournit une interface unique pour l'appel d'outils avec les modèles compatibles. Chaque plug-in de modèle s'assure que les deux derniers critères ci-dessus sont remplis, et la fonction generate() de l'instance Genkit exécute automatiquement la boucle d'appel d'outil décrite précédemment.

Compatibilité avec les modèles

La compatibilité avec l'appel d'outils dépend du modèle, de l'API du modèle et du plug-in Genkit. Consultez la documentation appropriée pour déterminer si l'appel d'outil est susceptible d'être pris en charge. Autres caractéristiques :

  • Genkit génère une erreur si vous essayez de fournir des outils à un modèle qui ne les accepte pas.
  • Si le plug-in exporte des références de modèle, la propriété info.supports.tools indique s'il est compatible avec l'appel d'outils.

Outils de définition

Utilisez la fonction defineTool() de l'instance Genkit pour écrire des définitions d'outils:

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.';
  }
);

La syntaxe ici ressemble à la syntaxe defineFlow(). Toutefois, les quatre paramètres name, description, inputSchema et outputSchema sont obligatoires. Lorsque vous rédigez une définition d'outil, soyez particulièrement attentif à la formulation et à la description de ces paramètres, car ils sont essentiels pour que le LLM puisse utiliser efficacement les outils disponibles.

Utiliser des outils

Incluez des outils définis dans vos invites pour générer du contenu.

Générer

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' });

Fichier de requête

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

What is the weather in {{location}}?

Vous pouvez ensuite exécuter l'invite dans votre code comme suit:

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

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

Chat

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 gère automatiquement l'appel de l'outil si le LLM doit utiliser l'outil getWeather pour répondre à l'invite.

Gérer explicitement les appels d'outils

Par défaut, Genkit appelle le LLM à plusieurs reprises jusqu'à ce que chaque appel d'outil ait été résolu. Si vous souhaitez mieux contrôler cette boucle d'appel d'outil, par exemple pour appliquer une logique plus complexe, définissez le paramètre returnToolRequests sur true. Il vous incombe maintenant de vous assurer que toutes les requêtes de l'outil sont satisfaites:

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;
}