Chiamata di strumenti

Le chiamate di strumenti, note anche come chiamate di funzioni, sono un modo strutturato per consentire agli LLM di inviare richieste all'applicazione che li ha chiamati. Tu definisci gli strumenti che vuoi rendere disponibili al modello, che farà richieste di strumenti alla tua app in base alle necessità per soddisfare i prompt che gli fornisci.

I casi d'uso delle chiamate allo strumento rientrano generalmente in alcuni temi:

Fornire a un modello LLM l'accesso a informazioni con cui non è stato addestrato

  • Informazioni che cambiano di frequente, ad esempio il prezzo di un'azione o il meteo corrente.
  • Informazioni specifiche per il dominio della tua app, ad esempio informazioni sui prodotti o profili degli utenti.

Tieni presente la sovrapposizione con la retrieval augmented generation (RAG), che è anche un modo per consentire a un LLM di integrare informazioni oggettive nelle sue generazioni. La RAG è una soluzione più complessa, adatta quando hai una grande quantità di informazioni o quando le informazioni più pertinenti per un prompt sono ambigue. D'altra parte, se il recupero delle informazioni di cui ha bisogno l'LLM è una semplice chiamata di funzione o una ricerca nel database, la chiamata dello strumento è più appropriata.

Introduzione di un grado di determinismo in un flusso di lavoro LLM

  • Eseguire calcoli che l'LLM non è in grado di completare in modo affidabile.
  • Forzare un LLM a generare un testo letterale in determinate circostanze, ad esempio quando si risponde a una domanda sui termini di servizio di un'app.

Eseguire un'azione quando viene avviata da un modello LLM

  • Accendere e spegnere le luci in un'assistente domestica basata su LLM
  • Prenotazione di tavoli in un agente di ristoranti basato su LLM

Prima di iniziare

Se vuoi eseguire gli esempi di codice in questa pagina, completa prima i passaggi descritti nella guida Introduzione. Tutti gli esempi presuppongono che tu abbia già configurato un progetto con le dipendenze di Genkit installate.

Questa pagina illustra una delle funzionalità avanzate dell'astrazione del modello Genkit, quindi prima di addentrarti troppo in profondità, devi conoscere i contenuti della pagina Generare contenuti con i modelli di IA. Inoltre, devi conoscere il sistema di Genkit per la definizione degli schemi di input e output, descritto nella pagina Fluidi.

Panoramica delle chiamate allo strumento

A livello generale, un'interazione di chiamata dello strumento con un LLM è simile alla seguente:

  1. L'applicazione chiamante presenta all'LLM una richiesta e include anche nel prompt un elenco di strumenti che l'LLM può utilizzare per generare una risposta.
  2. L'LLM genera una risposta completa o una richiesta di chiamata allo strumento in un formato specifico.
  3. Se chi chiama riceve una risposta completa, la richiesta viene soddisfatta e l'interazione termina. Se invece chi chiama riceve una chiamata dello strumento, esegue la logica appropriata e invia una nuova richiesta all'LLM contenente il prompt originale o una sua variante, nonché il risultato della chiamata dello strumento.
  4. L'LLM gestisce il nuovo prompt come indicato nel passaggio 2.

Affinché questa operazione funzioni, devono essere soddisfatti diversi requisiti:

  • Il modello deve essere addestrato a effettuare richieste di strumenti quando è necessario per completare un prompt. La maggior parte dei modelli più grandi forniti tramite API web, come Gemini e Claude, può farlo, ma spesso i modelli più piccoli e specializzati non possono. Genkit genera un errore se provi a fornire strumenti a un modello che non li supporta.
  • L'applicazione chiamante deve fornire al modello le definizioni degli strumenti nel formato che prevede.
  • L'applicazione chiamante deve chiedere al modello di generare richieste di chiamata dello strumento nel formato previsto dall'applicazione.

Chiamate di strumenti con Genkit

Genkit fornisce un'unica interfaccia per le chiamate allo strumento con i modelli che lo supportano. Ogni plug-in del modello garantisce che vengano soddisfatti gli ultimi due dei criteri sopra indicati e la funzione generate() dell'istanza Genkit esegue automaticamente il loop di chiamata dello strumento descritto in precedenza.

Supporto dei modelli

Il supporto delle chiamate allo strumento dipende dal modello, dall'API del modello e dal plug-in Genkit. Consulta la documentazione pertinente per stabilire se è probabile che le chiamate allo strumento siano supportate. Inoltre:

  • Genkit genera un errore se provi a fornire strumenti a un modello che non li supporta.
  • Se il plug-in esporta i riferimenti ai modelli, la proprietà info.supports.tools indicarà se supporta le chiamate allo strumento.

Strumenti di definizione

Utilizza la funzione defineTool() dell'istanza Genkit per scrivere le definizioni degli strumenti:

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 sintassi è simile a quella di defineFlow(), ma sono obbligatori tutti e quattro i parametri name, description, inputSchema e outputSchema. Quando scrivi una definizione di uno strumento, presta particolare attenzione alla formulazione e alla descrittività di questi parametri, in quanto sono fondamentali per l'utilizzo efficace degli strumenti disponibili da parte dell'LLM.

Utilizzare gli strumenti

Includi strumenti definiti nei prompt per generare contenuti.

Genera

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

File dei prompt

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

What is the weather in {{location}}?

Poi puoi eseguire il prompt nel codice come segue:

// 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 gestirà automaticamente la chiamata allo strumento se l'LLM deve utilizzare lo strumentogetWeather per rispondere al prompt.

Gestire esplicitamente le chiamate allo strumento

Per impostazione predefinita, Genkit chiama ripetutamente l'LLM finché ogni chiamata allo strumento non è stata risolta. Se vuoi un maggiore controllo sul loop di chiamata di questo strumento, ad esempio per applicare una logica più complessa, imposta il parametro returnToolRequests su true. Ora è tua responsabilità assicurarti che tutte le richieste di strumenti vengano soddisfatte:

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