Genera contenido con modelos de IA

En el centro de la IA generativa, se encuentran los modelos de IA. Los dos ejemplos más destacados de los modelos generativos son los modelos de lenguaje grandes (LLM) y los modelos de generación de imágenes. Estos modelos toman entradas, llamadas instrucciones (por lo general, texto, una imagen o una combinación de ambas), y a partir de ellas producen como salida texto, una imagen o incluso audio o video.

El resultado de estos modelos puede ser sorprendentemente convincente: los LLM generan texto que parece haber sido escrito por un ser humano, y los modelos de generación de imágenes pueden producir imágenes muy cercanas a fotografías reales u obras de arte creadas por humanos.

Además, los LLM demostraron ser capaces de realizar tareas más allá de la generación de texto simple:

  • Escribir programas informáticos.
  • Planificar las subtareas necesarias para completar una tarea más grande.
  • Organizar datos no organizados.
  • Comprender y extraer datos de información de un corpus de texto.
  • Seguir y realizar actividades automatizadas según una descripción de texto de la actividad.

Hay muchos modelos disponibles de varios proveedores. Cada modelo tiene sus propias fortalezas y debilidades, y uno puede destacarse en una tarea, pero no en otras. A menudo, las apps que usan IA generativa pueden beneficiarse del uso de varios modelos diferentes según la tarea en cuestión.

Como desarrollador de apps, por lo general, no interactúas directamente con los modelos de IA generativa, sino a través de servicios disponibles como APIs web. Aunque estos servicios suelen tener una funcionalidad similar, todos los proporcionan a través de APIs diferentes e incompatibles. Si deseas usar varios servicios de modelos, debes usar cada uno de sus SDKs propios, que pueden ser incompatibles entre sí. Además, si quieres actualizar de un modelo al más nuevo y capaz, es posible que debas volver a compilar toda esa integración.

Genkit aborda este desafío proporcionando una única interfaz que abstrae los detalles de acceso a cualquier servicio de modelo de IA generativa, con varias implementaciones precompiladas ya disponibles. Compilar tu app potenciada por IA con Genkit simplifica el proceso de realizar tu primera llamada a la IA generativa y facilita combinar varios modelos o cambiar uno por otro a medida que surgen nuevos modelos.

Antes de comenzar

Si quieres ejecutar los ejemplos de código de esta página, primero completa los pasos de la guía Cómo comenzar. En todos los ejemplos, se da por sentado que ya instalaste Genkit como dependencia en tu proyecto.

Modelos compatibles con Genkit

Genkit está diseñado para ser lo suficientemente flexible como para usar potencialmente cualquier servicio de modelo de IA generativa. Sus bibliotecas principales definen la interfaz común para trabajar con modelos, y los complementos de modelos definen los detalles de la implementación para trabajar con un modelo específico y su API.

El equipo de Genkit mantiene complementos para trabajar con modelos proporcionados por Vertex AI, Google Generative AI y Ollama:

Carga y configura complementos de modelos

Antes de usar Genkit para comenzar a generar contenido, debes cargar y configurar un complemento de modelo. Si vienes de la guía de introducción, ya lo hiciste. De lo contrario, consulta la guía Cómo comenzar o la documentación del complemento individual y sigue los pasos que se indican antes de continuar.

Función genkit.Generate()

En Genkit, la interfaz principal a través de la cual interactúas con los modelos de IA generativa es la función genkit.Generate().

La llamada genkit.Generate() más simple especifica el modelo que deseas usar y una instrucción de texto:

import (
    "context"
    "log"

    "github.com/firebase/genkit/go/ai"
    "github.com/firebase/genkit/go/genkit"
    "github.com/firebase/genkit/go/plugins/googlegenai"
)

func main() {
    ctx := context.Background()

    g, err := genkit.Init(ctx,
        genkit.WithPlugins(&googlegenai.GoogleAI{}),
        genkit.WithDefaultModel("googleai/gemini-2.0-flash"),
    )
    if err != nil {
        log.Fatal(err)
    }

    resp, err := genkit.Generate(ctx, g,
        ai.WithPrompt("Invent a menu item for a pirate themed restaurant."),
    )
    if err != nil {
        log.Fatal(err)
    }

    log.Println(resp.Text())
}

Cuando ejecutes este breve ejemplo, se imprimirá cierta información de depuración, seguida del resultado de la llamada a genkit.Generate(), que suele ser texto Markdown, como en el siguiente ejemplo:

## The Blackheart's Bounty

**A hearty stew of slow-cooked beef, spiced with rum and molasses, served in a
hollowed-out cannonball with a side of crusty bread and a dollop of tangy
pineapple salsa.**

**Description:** This dish is a tribute to the hearty meals enjoyed by pirates
on the high seas. The beef is tender and flavorful, infused with the warm spices
of rum and molasses. The pineapple salsa adds a touch of sweetness and acidity,
balancing the richness of the stew. The cannonball serving vessel adds a fun and
thematic touch, making this dish a perfect choice for any pirate-themed
adventure.

Vuelve a ejecutar la secuencia de comandos y obtendrás un resultado diferente.

La muestra de código anterior envió la solicitud de generación al modelo predeterminado, que especificaste cuando configuraste la instancia de Genkit.

También puedes especificar un modelo para una sola llamada a genkit.Generate():

resp, err := genkit.Generate(ctx, g,
    ai.WithModelName("googleai/gemini-2.5-pro"),
    ai.WithPrompt("Invent a menu item for a pirate themed restaurant."),
)

Un identificador de cadena de modelo se ve como providerid/modelid, en el que el ID del proveedor (en este caso, googleai) identifica el complemento, y el ID del modelo es un identificador de cadena específico del complemento para una versión específica de un modelo.

Estos ejemplos también ilustran un punto importante: cuando usas genkit.Generate() para realizar llamadas a modelos de IA generativa, cambiar el modelo que quieres usar es cuestión de pasar un valor diferente al parámetro del modelo. Si usas genkit.Generate() en lugar de los SDKs de modelos nativos, te brindas la flexibilidad de usar varios modelos diferentes en tu app con mayor facilidad y cambiarlos en el futuro.

Hasta ahora, solo viste ejemplos de las llamadas genkit.Generate() más simples. Sin embargo, genkit.Generate() también proporciona una interfaz para interacciones más avanzadas con modelos generativos, que verás en las secciones que siguen.

Mensajes del sistema

Algunos modelos admiten proporcionar una instrucción del sistema, que le brinda instrucciones al modelo sobre cómo deseas que responda a los mensajes del usuario. Puedes usar la instrucción del sistema para especificar características, como un arquetipo que deseas que adopte el modelo, el tono de sus respuestas y el formato de sus respuestas.

Si el modelo que usas admite instrucciones del sistema, puedes proporcionar una con la opción WithSystem():

resp, err := genkit.Generate(ctx, g,
    ai.WithSystem("You are a food industry marketing consultant."),
    ai.WithPrompt("Invent a menu item for a pirate themed restaurant."),
)

En el caso de los modelos que no admiten instrucciones del sistema, WithSystem() las simula modificando la solicitud para que parezca como una instrucción del sistema.

Parámetros del modelo

La función genkit.Generate() toma una opción WithConfig(), a través de la cual puedes especificar parámetros de configuración opcionales que controlan cómo el modelo genera contenido:

resp, err := genkit.Generate(ctx, g,
    ai.WithModelName("googleai/gemini-2.0-flash"),
    ai.WithPrompt("Invent a menu item for a pirate themed restaurant."),
    ai.WithConfig(&googlegenai.GeminiConfig{
        MaxOutputTokens: 500,
        StopSequences:   ["<end>", "<fin>"],
        Temperature:     0.5,
        TopP:            0.4,
        TopK:            50,
    }),
)

Los parámetros exactos que se admiten dependen del modelo y la API del modelo individual. Sin embargo, los parámetros del ejemplo anterior son comunes a casi todos los modelos. A continuación, se explica cada uno de estos parámetros:

Parámetros que controlan la longitud de la salida

MaxOutputTokens

Los LLM operan en unidades llamadas tokens. Por lo general, un token se asigna a una secuencia específica de caracteres, pero no es necesariamente así. Cuando pasas una instrucción a un modelo, uno de los primeros pasos que realiza es tokenizar la cadena de instrucciones en una secuencia de tokens. Luego, el LLM genera una secuencia de tokens a partir de la entrada tokenizada. Por último, la secuencia de tokens se vuelve a convertir en texto, que es tu resultado.

El parámetro de tokens de salida máximos establece un límite en la cantidad de tokens que se deben generar con el LLM. Cada modelo puede usar un tokenizador diferente, pero una buena regla general es considerar que una sola palabra en inglés consta de 2 a 4 tokens.

Como se indicó anteriormente, es posible que algunos tokens no se asignan a secuencias de caracteres. Un ejemplo de esto es que, a menudo, hay un token que indica el final de la secuencia: cuando un LLM genera este token, deja de generar más. Por lo tanto, es posible y, a menudo, el caso de que un LLM genere menos tokens que el máximo porque generó el token "stop".

StopSequences

Puedes usar este parámetro para establecer los tokens o las secuencias de tokens que, cuando se generan, indican el final de la salida de LLM. Los valores correctos que se deben usar aquí generalmente dependen de cómo se entrenó el modelo y, por lo general, los establece el complemento del modelo. Sin embargo, si le pediste al modelo que genere otra secuencia de detención, puedes especificarla aquí.

Ten en cuenta que estás especificando secuencias de caracteres, no tokens en sí. En la mayoría de los casos, especificarás una secuencia de caracteres que el tokenizador del modelo asignará a un solo token.

Parámetros que controlan la "creatividad"

Los parámetros temperatura, top-p y top-k juntos controlan qué tan “creativo” quieres que sea el modelo. En esta sección, se proporcionan explicaciones muy breves sobre el significado de estos parámetros, pero lo más importante que debes tener en cuenta es que estos parámetros se usan para ajustar el carácter del resultado de un LLM. Los valores óptimos para ellos dependen de tus objetivos y preferencias, y es probable que solo se encuentren a través de la experimentación.

Temperatura

Los LLM son, en esencia, máquinas que predicen tokens. Para una secuencia determinada de tokens (como la instrucción), un LLM predice, para cada token de su vocabulario, la probabilidad de que el token sea el siguiente en la secuencia. La temperatura es un factor de escalamiento por el que se dividen estas predicciones antes de normalizarse a una probabilidad entre 0 y 1.

Los valores de temperatura baja (entre 0.0 y 1.0) amplifican la diferencia en las probabilidades entre los tokens, lo que hace que sea aún menos probable que el modelo produzca un token que ya evaluó como poco probable. Esto a menudo se percibe como un resultado menos creativo. Aunque, técnicamente, 0.0 no es un valor válido, muchos modelos lo consideran como un indicador de que el modelo debe comportarse de manera determinista y solo considerar el token más probable.

Los valores de temperatura alta (aquellos superiores a 1.0) comprimen las diferencias en las probabilidades entre los tokens, lo que aumenta la probabilidad de que el modelo produzca tokens que antes evaluó como poco probables. Esto suele percibirse como un resultado más creativo. Algunas APIs de modelos imponen una temperatura máxima, a menudo de 2.0.

TopP

Top-p es un valor entre 0.0 y 1.0 que controla la cantidad de tokens posibles que deseas que el modelo considere, ya que especifica la probabilidad acumulativa de los tokens. Por ejemplo, un valor de 1.0 significa considerar todos los tokens posibles (pero aún tener en cuenta la probabilidad de cada token). Un valor de 0.4 significa que solo se deben considerar los tokens más probables, cuyas probabilidades suman 0.4, y excluir los tokens restantes.

TopK

Top-K es un valor entero que también controla la cantidad de tokens posibles que quieres que el modelo considere, pero esta vez especificando explícitamente la cantidad máxima de tokens. Especificar un valor de 1 significa que el modelo debe comportarse de manera determinista.

Experimenta con los parámetros del modelo

Puedes experimentar con el efecto de estos parámetros en el resultado que generan diferentes combinaciones de modelos y mensajes con la IU para desarrolladores. Inicia la IU del desarrollador con el comando genkit start y se cargarán automáticamente todos los modelos definidos por los complementos configurados en tu proyecto. Puedes probar rápidamente diferentes instrucciones y valores de configuración sin tener que realizar estos cambios en el código de forma reiterada.

Vincula el modelo con su configuración

Dado que cada proveedor o incluso un modelo específico puede tener su propio esquema de configuración o garantizar ciertos parámetros de configuración, es posible que sea propenso a errores configurar opciones separadas con WithModelName() y WithConfig(), ya que el último no tiene un tipo fuertemente tipificado para el primero.

Para vincular un modelo con su configuración, puedes crear una referencia de modelo que puedes pasar a la llamada de generación en su lugar:

model := googlegenai.GoogleAIModelRef("gemini-2.0-flash", &googlegenai.GeminiConfig{
    MaxOutputTokens: 500,
    StopSequences:   ["<end>", "<fin>"],
    Temperature:     0.5,
    TopP:            0.4,
    TopK:            50,
})

resp, err := genkit.Generate(ctx, g,
    ai.WithModel(model),
    ai.WithPrompt("Invent a menu item for a pirate themed restaurant."),
)
if err != nil {
    log.Fatal(err)
}

El constructor de la referencia del modelo aplicará que se proporcione el tipo de configuración correcto, lo que puede reducir las discrepancias.

Resultados estructurados

Cuando usas la IA generativa como componente en tu aplicación, a menudo quieres que el resultado tenga un formato diferente al texto sin formato. Incluso si solo generas contenido para mostrarlo al usuario, puedes beneficiarte de los resultados estructurados solo con el fin de presentarlo de forma más atractiva al usuario. Sin embargo, para aplicaciones más avanzadas de la IA generativa, como el uso programático del resultado del modelo o la alimentación del resultado de un modelo a otro, el resultado estructurado es imprescindible.

En Genkit, puedes solicitar un resultado estructurado de un modelo especificando un tipo de salida cuando llames a genkit.Generate():

type MenuItem struct {
    Name        string   `json:"name"`
    Description string   `json:"description"`
    Calories    int      `json:"calories"`
    Allergens   []string `json:"allergens"`
}

resp, err := genkit.Generate(ctx, g,
    ai.WithPrompt("Invent a menu item for a pirate themed restaurant."),
    ai.WithOutputType(MenuItem{}),
)
if err != nil {
  log.Fatal(err) // One possible error is that the response does not conform to the type.
}

Los tipos de salida del modelo se especifican como esquema JSON con el paquete invopop/jsonschema. Esto proporciona una verificación de tipos del tiempo de ejecución, que cierra la brecha entre los tipos estáticos de Go y el resultado impredecible de los modelos de IA generativa. Este sistema te permite escribir código que puede depender del hecho de que una llamada de generación correcta siempre mostrará un resultado que se ajusta a tus tipos de Go.

Cuando especificas un tipo de salida en genkit.Generate(), Genkit realiza varias acciones en segundo plano:

  • Aumenta la instrucción con orientación adicional sobre el formato de salida seleccionado. Esto también tiene el efecto secundario de especificarle al modelo qué contenido quieres generar exactamente (por ejemplo, no solo sugerir un elemento de menú, sino también generar una descripción, una lista de alérgenos, etcétera).
  • Verifica que el resultado cumpla con el esquema.
  • Une la salida del modelo en un tipo Go.

Para obtener un resultado estructurado de una llamada de generación correcta, llama a Output() en la respuesta del modelo con un valor vacío del tipo:

var item MenuItem
if err := resp.Output(&item); err != nil {
    log.Fatalf(err)
}

log.Printf("%s (%d calories, %d allergens): %s\n",
    item.Name, item.Calories, len(item.Allergens), item.Description)

Como alternativa, puedes usar genkit.GenerateData() para una llamada más concisa:

item, resp, err := genkit.GenerateData[MenuItem](ctx, g,
    ai.WithPrompt("Invent a menu item for a pirate themed restaurant."),
)
if err != nil {
  log.Fatal(err)
}

log.Printf("%s (%d calories, %d allergens): %s\n",
    item.Name, item.Calories, len(item.Allergens), item.Description)

Esta función requiere el parámetro de tipo de salida, pero establece automáticamente la opción WithOutputType() y llama a resp.Output() antes de mostrar el valor.

Maneja los errores

Ten en cuenta que, en el ejemplo anterior, la llamada a genkit.Generate() puede generar un error. Un posible error puede ocurrir cuando el modelo no genera un resultado que cumpla con el esquema. La mejor estrategia para abordar estos errores dependerá de tu caso de uso exacto, pero aquí tienes algunas sugerencias generales:

  • Prueba con otro modelo. Para que el resultado estructurado tenga éxito, el modelo debe ser capaz de generar resultados en JSON. Los LLM más potentes, como Gemini, son lo suficientemente versátiles como para hacerlo. Sin embargo, es posible que los modelos más pequeños, como algunos de los modelos locales que usarías con Ollama, no puedan generar resultados estructurados de forma confiable, a menos que se hayan entrenado específicamente para hacerlo.

  • Simplifica el esquema. Es posible que los LLM tengan problemas para generar tipos complejos o con anidación profunda. Intenta usar nombres claros, menos campos o una estructura plana si no puedes generar datos estructurados de forma confiable.

  • Reintenta la llamada genkit.Generate(). Si el modelo que elegiste solo raramente falla en la generación de un resultado conforme, puedes tratar el error como lo harías con un error de red y reintentar la solicitud con algún tipo de estrategia de retirada incremental.

Transmisión

Cuando generas grandes cantidades de texto, puedes mejorar la experiencia de los usuarios presentando el resultado a medida que se genera, es decir, transmitiéndolo. Un ejemplo familiar de transmisión en acción se puede ver en la mayoría de las apps de chat de LLM: los usuarios pueden leer la respuesta del modelo a su mensaje a medida que se genera, lo que mejora la capacidad de respuesta percibida de la aplicación y mejora la ilusión de chatear con una contraparte inteligente.

En Genkit, puedes transmitir la salida con la opción WithStreaming():

resp, err := genkit.Generate(ctx, g,
    ai.WithPrompt("Suggest a complete menu for a pirate themed restaurant."),
    ai.WithStreaming(func(ctx context.Context, chunk *ai.ModelResponseChunk) error {
        // Do something with the chunk...
        log.Println(chunk.Text())
        return nil
    }),
)
if err != nil {
    log.Fatal(err)
}

log.Println(resp.Text())

Entrada multimodal

En los ejemplos que viste hasta ahora, se usaron cadenas de texto como instrucciones del modelo. Si bien esta sigue siendo la forma más común de solicitar modelos de IA generativa, muchos modelos también pueden aceptar otros medios como instrucciones. Las instrucciones multimedia se usan con mayor frecuencia junto con instrucciones de texto que le indican al modelo que realice alguna operación en el contenido multimedia, como agregar una leyenda a una imagen o transcribir una grabación de audio.

La capacidad de aceptar entradas multimedia y los tipos de contenido multimedia que puedes usar dependen completamente del modelo y su API. Por ejemplo, la serie de modelos Gemini 2.0 puede aceptar imágenes, video y audio como instrucciones.

Para proporcionar una instrucción multimedia a un modelo que la admita, en lugar de pasar una instrucción de texto simple a genkit.Generate(), pasa un array que consta de una parte multimedia y una parte de texto. En este ejemplo, se especifica una imagen con una URL HTTPS de acceso público.

resp, err := genkit.Generate(ctx, g,
    ai.WithModelName("googleai/gemini-2.0-flash"),
    ai.WithMessages(
        NewUserMessage(
            NewMediaPart("image/jpeg", "https://example.com/photo.jpg"),
            NewTextPart("Compose a poem about this image."),
        ),
    ),
)

También puedes pasar datos multimedia directamente codificándolos como una URL de datos. Por ejemplo:

image, err := ioutil.ReadFile("photo.jpg")
if err != nil {
    log.Fatal(err)
}

resp, err := genkit.Generate(ctx, g,
    ai.WithModelName("googleai/gemini-2.0-flash"),
    ai.WithMessages(
        NewUserMessage(
            NewMediaPart("image/jpeg", "data:image/jpeg;base64," + base64.StdEncoding.EncodeToString(image)),
            NewTextPart("Compose a poem about this image."),
        ),
    ),
)

Todos los modelos que admiten entradas multimedia admiten URLs de datos y URLs HTTPS. Algunos complementos de modelos agregan compatibilidad con otras fuentes de contenido multimedia. Por ejemplo, el complemento de Vertex AI también te permite usar URLs de Cloud Storage (gs://).

Próximos pasos

Más información sobre Genkit

  • Como desarrollador de apps, la forma principal en la que influyes en el resultado de los modelos de IA generativa es a través de las instrucciones. Lee Cómo administrar instrucciones con Dotprompt para descubrir cómo Genkit te ayuda a desarrollar instrucciones eficaces y administrarlas en tu base de código.
  • Aunque genkit.Generate() es el núcleo de cada aplicación potenciada por IA generativa, las aplicaciones del mundo real suelen requerir trabajo adicional antes y después de invocar un modelo de IA generativa. Para reflejar esto, Genkit presenta el concepto de flujos, que se definen como funciones, pero agregan funciones adicionales, como la observabilidad y la implementación simplificada. Para obtener más información, consulta Cómo definir flujos de trabajo de IA.

Uso avanzado de LLM

Existen técnicas que tu app puede usar para aprovechar aún más los LLM.

  • Una forma de mejorar las capacidades de los LLM es proporcionarles una lista de formas en las que pueden solicitarte más información o pedirte que realices alguna acción. Esto se conoce como llamada a herramientas o llamada a función. Los modelos que se entrenan para admitir esta función pueden responder a una instrucción con una respuesta con formato especial, que le indica a la aplicación que realiza la llamada que debe realizar alguna acción y enviar el resultado al LLM junto con la instrucción original. Genkit tiene funciones de biblioteca que automatizan la generación de instrucciones y los elementos del bucle de llamada y respuesta de una implementación de llamada de herramientas. Consulta Llamadas a herramientas para obtener más información.
  • La generación mejorada por recuperación (RAG) es una técnica que se usa para introducir información específica del dominio en el resultado de un modelo. Para ello, se inserta información relevante en una instrucción antes de pasarla al modelo de lenguaje. Una implementación completa de la RAG requiere que combines varias tecnologías: modelos de generación de incorporaciones de texto, bases de datos de vectores y modelos de lenguaje grandes. Consulta Generación mejorada por recuperación (RAG) para aprender cómo Genkit simplifica el proceso de coordinación de estos diversos elementos.