Llamadas a herramientas

Las llamadas a herramientas, también conocidas como llamadas a función, son una forma estructurada de brindar a los LLM la capacidad de realizar solicitudes a la aplicación que los llamó. Tú define las herramientas que deseas poner a disposición del modelo, y este hará solicitudes de herramientas a tu app según sea necesario para completar las instrucciones que le des.

Los casos de uso de las llamadas a herramientas suelen incluir algunos temas:

Dar acceso a un LLM a información con la que no se entrenó

  • Información que cambia con frecuencia, como el precio de una acción o el clima actual
  • Información específica del dominio de tu app, como la información del producto o los perfiles de usuario

Ten en cuenta la superposición con la generación mejorada por recuperación (RAG), que también es una forma de permitir que un LLM integre información fáctica en sus generaciones. La RAG es una solución más pesada que es más adecuada cuando tienes una gran cantidad de información o cuando la información más relevante para una instrucción es ambigua. Por otro lado, si la recuperación de la información que necesita el LLM es una llamada a función simple o una búsqueda en la base de datos, las llamadas a herramientas son más apropiadas.

Cómo introducir un grado de determinismo en un flujo de trabajo de LLM

  • Realizar cálculos que el LLM no puede completar de forma confiable.
  • Forzar a un LLM a generar texto literal en ciertas circunstancias, como cuando se responde una pregunta sobre las condiciones del servicio de una app

Cómo realizar una acción cuando la inicia un LLM

  • Encender y apagar luces en un asistente de casa potenciado por LLM
  • Cómo reservar mesas en un agente de restaurantes con LLM

Antes de comenzar

Si quieres ejecutar los ejemplos de código de esta página, primero completa los pasos de la guía de Cómo comenzar. En todos los ejemplos, se da por sentado que ya configuraste un proyecto con las dependencias de Genkit instaladas.

En esta página, se analiza una de las funciones avanzadas de la abstracción de modelos de Genkit, por lo que antes de profundizar demasiado, debes familiarizarte con el contenido de la página Cómo generar contenido con modelos de IA. También debes estar familiarizado con el sistema de Genkit para definir esquemas de entrada y salida, que se explica en la página Flujo.

Descripción general de las llamadas a herramientas

En términos generales, así se ve una interacción típica de llamada a herramientas con un LLM:

  1. La aplicación que realiza la llamada le envía una solicitud al LLM y también incluye en la instrucción una lista de herramientas que el LLM puede usar para generar una respuesta.
  2. El LLM genera una respuesta completa o una solicitud de llamada a la herramienta en un formato específico.
  3. Si el llamador recibe una respuesta completa, se entrega la solicitud y finaliza la interacción. Sin embargo, si el llamador recibe una llamada a la herramienta, realiza la lógica adecuada y envía una solicitud nueva al LLM que contiene la instrucción original o alguna variación de ella, así como el resultado de la llamada a la herramienta.
  4. El LLM controla la nueva instrucción como en el paso 2.

Para que esto funcione, se deben cumplir varios requisitos:

  • El modelo debe entrenarse para realizar solicitudes de herramientas cuando sea necesario para completar una instrucción. La mayoría de los modelos más grandes que se proporcionan a través de las APIs web, como Gemini y Claude, pueden hacerlo, pero los modelos más pequeños y especializados a menudo no pueden. Genkit arrojará un error si intentas proporcionar herramientas a un modelo que no las admite.
  • La aplicación que realiza la llamada debe proporcionar definiciones de herramientas al modelo en el formato que espera.
  • La aplicación que realiza la llamada debe solicitarle al modelo que genere solicitudes de llamadas a la herramienta en el formato que espera la aplicación.

Llamadas de herramientas con Genkit

Genkit proporciona una sola interfaz para las llamadas a herramientas con modelos compatibles. Cada complemento de modelo garantiza que se cumplan los últimos dos de los criterios anteriores, y la función generate() de la instancia de Genkit realiza automáticamente el bucle de llamadas de herramientas descrito anteriormente.

Compatibilidad con modelos

La compatibilidad con las llamadas a herramientas depende del modelo, la API del modelo y el complemento de Genkit. Consulta la documentación relevante para determinar si es probable que se admita la llamada a herramientas. Además, tenga en cuenta lo siguiente:

  • Genkit arrojará un error si intentas proporcionar herramientas a un modelo que no las admite.
  • Si el complemento exporta referencias de modelos, la propiedad info.supports.tools indicará si admite llamadas a herramientas.

Definición de herramientas

Usa la función defineTool() de la instancia de Genkit para escribir definiciones de herramientas:

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 sintaxis aquí se ve igual que la sintaxis de defineFlow(). Sin embargo, se requieren los cuatro parámetros name, description, inputSchema y outputSchema. Cuando escribas una definición de herramienta, ten especial cuidado con el lenguaje y la descriptividad de estos parámetros, ya que son vitales para que el LLM use de manera eficaz las herramientas disponibles.

Cómo usar herramientas

Incluye herramientas definidas en tus instrucciones para generar contenido.

Generar

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

Archivo de la instrucción

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

What is the weather in {{location}}?

Luego, puedes ejecutar la instrucción en tu código de la siguiente manera:

// 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 controlará automáticamente la llamada a la herramienta si el LLM necesita usar la herramienta getWeather para responder la instrucción.

Controla de forma explícita las llamadas a herramientas

De forma predeterminada, Genkit llama al LLM de forma reiterada hasta que se resuelve cada llamada a la herramienta. Si deseas tener más control sobre este bucle de llamadas de herramientas, por ejemplo, para aplicar una lógica más complicada, establece el parámetro returnToolRequests en true. Ahora es tu responsabilidad garantizar que se cumplan todas las solicitudes de herramientas:

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