Cómo generar JSON estructurado con la API de Gemini: guía para desarrolladores

Del JSON por prompt y responseMimeType a responseSchema / JSON Schema: salidas estructuradas de Gemini, ejemplos Python y REST, Function Calling y validación antes de publicar.

El articulo anteriorPor qué los Agent de IA no pueden vivir sin JSONSe rastrearon Tool Calling y MCP salto a salto. Éste mira elrespuesta finalel modelo le brinda a un usuario o a un programa posterior: cómo hacer que Gemini emita JSON que puede analizar, validar y almacenar, no prosa que simplemente se parezca a JSON.

Esos son resultados estructurados (generación controlada) en los documentos Gemini. Comparte ideas de esquema con Function Calling pero un objetivo diferente: el primero restringe elcarga útil final; este último limitaherramienta argumentos. Manténgalos separados para que un Agente no trate “extraer una factura” y “llamar al pago API” como el mismo tipo de llamada.

Tres enfoques, cada uno más estricto

Los equipos suelen probar tres formas de "hacer que Gemini genere JSON". La confiabilidad difiere en un orden de magnitud:

Acercarselo que controlascuando sea suficiente
Solo mensaje: "por favor envíe JSON"Restricción suave; Todavía aparecen vallas de rebajas y comentarios finales.Exploración, guiones únicos
responseMimeType: application/jsonLa salida debe ser un texto JSON válidoLa forma varía; sólo necesitas parse() para tener éxito
MIME + responseSchema / responseJsonSchemaLos campos, tipos, enumeraciones y claves requeridas están restringidosExtracción de producción, formularios, cargas útiles de agente a agente.

Everyone has seen the first failure mode: a ```json fence, an extra paragraph, single quotes, a trailing comma. The second layer parses, but price may be a string and items may be missing. The third layer is this tutorial: hand JSON Schema to the API so the decoder avoids illegal paths at each token.

Decodificación restringida: por qué Schema supera un mensaje

A prompt only raises the odds that the model wants to comply. Structured Output compiles the Schema into generation: if the next token would break JSON syntax or leave the Schema (for example starting an undeclared field), its probability is suppressed. So response.text is usually a parseable object — no regex to strip fences.

Since 2025 the Gemini API complements the OpenAPI 3.0-style responseSchema with standard JSON Schema (often responseJsonSchema on the wire). Pydantic model_json_schema() and Zod exports can be sent almost as-is. Gemini 2.5 and later also tend to preserve property order from the Schema, which helps CSV columns and tables downstream.

Classification has a side path: responseMimeType: text/x.enum emits only the enum string (for example Keyboard), with no braces. Use application/json when you need an object; use the enum MIME when you need a single label.

Python: un ejemplo completo de google-genai

Prefer the current SDK google-genai (from google import genai). Do not mix it with the legacy google-generativeai package. With GEMINI_API_KEY set:

from google import genai
from pydantic import BaseModel, Field


class LineItem(BaseModel):
    name: str = Field(description="Product name")
    qty: int = Field(description="Quantity, positive integer")
    unit_price_cents: int = Field(description="Unit price in cents")


class Invoice(BaseModel):
    vendor: str
    currency: str = Field(description="ISO 4217, e.g. CNY")
    items: list[LineItem]
    total_cents: int


client = genai.Client()
response = client.models.generate_content(
    model="gemini-2.5-flash",
    contents="Extract an invoice from: Acme sold 2 keyboards at 199 CNY each.",
    config={
        "response_mime_type": "application/json",
        "response_schema": Invoice,
    },
)

print(response.text)      # JSON string
invoice = response.parsed  # Invoice instance (Pydantic path)
print(invoice.total_cents)

response.parsed is meaningful when response_schema is a Pydantic or SDK type. If you pass a raw JSON Schema dict (next section), json.loads(response.text) and validate yourself.

For many records use list[Invoice] or wrap invoices: list[Invoice] in an object. An array at the root is less stable on some models than always returning an object; production code usually does the latter.

response_schema vs JSON Esquema

No adivines qué clave de configuración usar:

  • esquema_respuesta: un modelo Pydantic, Python Enum o un objeto de esquema SDK. El SDK lo asigna al subconjunto OpenAPI conectado.
  • response_json_schema: a JSON Schema object (dict). Use it for Invoice.model_json_schema(), Zod toJSONSchema(), and richer keywords such as additionalProperties, minimum / maximum, and prefixItems.
schema = {
  "type": "object",
  "properties": {
    "vendor": { "type": "string" },
    "currency": { "type": "string", "enum": ["CNY", "USD", "EUR"] },
    "items": {
      "type": "array",
      "items": {
        "type": "object",
        "properties": {
          "name": { "type": "string" },
          "qty": { "type": "integer", "minimum": 1 },
          "unit_price_cents": { "type": "integer", "minimum": 0 }
        },
        "required": ["name", "qty", "unit_price_cents"],
        "additionalProperties": False
      }
    },
    "total_cents": { "type": "integer" }
  },
  "required": ["vendor", "currency", "items", "total_cents"],
  "additionalProperties": False
}

response = client.models.generate_content(
    model="gemini-2.5-flash",
    contents="Extract the invoice: …",
    config={
        "response_mime_type": "application/json",
        "response_json_schema": schema,
    },
)

Older REST docs use uppercase types in responseSchema (OBJECT, STRING, ARRAY, INTEGER). The JSON Schema path uses lowercase object / string. Do not mix the two keyword sets. Put field meaning in description: it enters the model context and decides whether qty is pieces or cases. Types alone cannot.

Cómo se ve la solicitud REST

On the Gemini Developer API, generateContent puts structured output under generationConfig. The key goes in x-goog-api-key or a query param — never in a frontend repo.

POST https://generativelanguage.googleapis.com/v1beta/models/gemini-2.5-flash:generateContent

{
  "contents": [
    {
      "role": "user",
      "parts": [{ "text": "Extract an invoice from the text: …" }]
    }
  ],
  "generationConfig": {
    "responseMimeType": "application/json",
    "responseJsonSchema": {
      "type": "object",
      "properties": {
        "vendor": { "type": "string" },
        "total_cents": { "type": "integer" }
      },
      "required": ["vendor", "total_cents"]
    }
  }
}

On success the candidate text is candidates[0].content.parts[0].text — a JSON string. Vertex AI uses the same field names; only the endpoint and GCP auth change. Images and PDFs can be inputs: the Schema constrains output, not multimodal input.

Cómo dividir el trabajo con llamadas a funciones

Ambos usan Schema para gobernar JSON, pero se asientan en saltos diferentes:

Salida estructuradaLlamadas a funciones / Llamadas a herramientas
que esta restringidoRespuesta final JSONArgumento de herramienta JSON
¿Quién corre efectos secundarios?Nadie; son solo datosHost / MCP Servidor
Configuración típicaresponseMimeType + Esquemaherramientas[].parameters / inputSchema
En caso de fracasoReintentar o recurrir a un humanoEscribe el error como mensaje de herramienta y vuelve a preguntar.

Extracción de facturas, etiquetas de moderación, conversión de notas en una lista de tareas: Salida estructurada. Búsqueda de inventario, creación de un ticket, lectura de un archivo de repositorio: herramientas — consulteel artículo sobre flujo de datosyJSON Esquema y MCP evolución. No utilice la salida estructurada para simular que un pago API ya se ejecutó; el modelo no lo llamó.

Validación en tiempo de ejecución y errores comunes

La decodificación restringida no es corrección empresarial. Mantenga dos puertas:

  1. Syntax and Schema: after json.loads, validate again with the same JSON Schema (required, enum, minimum).
  2. Business invariants: for example sum(item.qty * item.unit_price_cents) == total_cents. Schema cannot express that; you write it.

Errores comunes:

  • Palabras clave no admitidas: deshacerse de un esquema borrador 2020-12 completo puede ignorar silenciosamente algunas palabras clave. Comience con tipo / propiedades / requerido / enumeración / elementos, luego agregue additionalProperties y min/max.
  • Array at the root: { "items": [ ... ] } as an object root is often more reliable.
  • Mezclando comentarios Markdown con JSON: una vez que JSON MIME esté activado, no solicite "explicar primero, luego JSON".
  • Truncamiento: aumente maxOutputTokens o divida "la lista primero, luego complete cada fila".
  • Claves en el frontend: demostraciones de salida estructurada en las claves API del navegador. El esquema puede ser público; la clave permanece en el servidor.

Durante el desarrollo, mantenga el esquema y dos o tres muestras positivas/negativas en git. Inspeccione la estructura y las diferencias localmente en JSON Toolbox: el mismo hábito de prueba de contrato que REST, con Gemini como consumidor.

Preguntas frecuentes

¿Cuál es la diferencia entre el tipo JSON MIME solo y también enviar un esquema?

Con solo responseMimeType application/json, el modelo intenta emitir JSON válido, pero los nombres de campo, los tipos y las claves requeridas no están restringidos. Agregar responseSchema o responseJsonSchema restringe los tokens durante la decodificación, por lo que la forma es lo suficientemente estable como para persistir o pasar al siguiente agente.

¿Cómo elijo response_schema frente a response_json_schema?

Utilice response_schema con un modelo Pydantic o un esquema SDK; el SDK puede exponer la respuesta.parsed. Utilice response_json_schema para un objeto JSON Schema completo (additionalProperties, min/max, prefixItems) o cuando envíe Pydantic/Zod model_json_schema() tal cual. Ambos requieren response_mime_type=application/json.

¿Puede la salida estructurada reemplazar las llamadas a funciones?

No. La salida estructurada restringe el JSON final que ve el usuario o el código posterior. llamada a función/llamada a herramienta restringe el argumento de herramienta JSON y aún requiere que el host ejecute la herramienta. Utilice resultados estructurados para extracción, clasificación y llenado de formularios; use herramientas para el clima, archivos y MCP. Las canalizaciones Agent suelen utilizar ambos.

¿El modelo garantiza el 100% de cumplimiento del esquema?

La decodificación restringida reduce los errores de sintaxis y la deriva tipográfica, pero aún ocurren alucinaciones semánticas, truncamientos y palabras clave no compatibles ignoradas. En producción, ejecute el mismo esquema a través de un validador y vuelva a intentarlo o degradéelo en caso de falla.

¿Se admiten objetos, matrices y enumeraciones anidados?

Sí. Objetos, matrices y enumeraciones de cadenas son la combinación habitual. Para la clasificación, puede configurar MIME en text/x.enum para que el modelo emita solo el valor de enumeración, no un objeto JSON. Se pueden rechazar anidamientos muy profundos o referencias cíclicas: aplanar el esquema.

¿Cómo valido el esquema y la salida de muestra localmente?

Guarde responseJsonSchema y algunos ejemplos de resultados de modelos como archivos JSON. Verifique la sintaxis y la estructura localmente en JSON Toolbox en el navegador; no se carga nada. Utilice el mismo esquema nuevamente en tiempo de ejecución después del envío.

Resumen

To get structured JSON from Gemini, the order is: Schema first, JSON MIME second, prompt last. The prompt owns meaning (what to extract); the Schema owns shape (what fields look like). Pydantic / Zod are author-friendly fronts; on the wire you send response_schema or response_json_schema.

Run one real invoice or a support transcript: write the Schema → call generateContent once → paste response.text into a validator. Only then wire a database or the next agent. Tool arguments still go through Function Calling / MCP — do not collapse them into one API.