Course EN
← back to chapter

Clasificar a JSON

Suelta un mensaje de soporte y recibe de vuelta un objeto sobre el que puedes

Showcase — Clasificar a JSON

Suelta un mensaje de soporte y recibe de vuelta un objeto sobre el que puedes ramificar sin nunca parsear texto: un label de un conjunto fijo, un score de confianza y una lista corta de razones. La semana 2 sacaba un label como string y esperaba que el formato se mantuviera. Aquí el schema lo convierte en garantía.

Córrelo

bash bootstrap-secrets.sh              # reads ../../../../.env, writes secrets/
docker compose up --build              # default: PROVIDER=openai

Abre http://localhost:3000.

Para correrlo contra Gemini:

PROVIDER=gemini docker compose up --build

Qué hay aquí

  • backend/ai_openai.py — el schema Classification (Literal label + float + list) y la llamada a OpenAI
  • backend/ai_gemini.py — mismo schema, llamada a Gemini
  • backend/main.py — loader de FastAPI idéntico; lee PROVIDER y despacha
  • frontend/app/page.tsx — textarea + resultado (renderizado como JSON)
  • docker-compose.yml — dos servicios, secrets montados desde ./secrets/

run(input: str) -> str regresa el objeto validado como JSON legible, manteniendo el contrato idéntico entre los showcases de la semana.

Detenlo

docker compose down

Ejecútalo en tu máquina

Descarga el proyecto como ZIP y córrelo con Docker. Levanta un backend FastAPI y un frontend Next.js en localhost:3000.

Descargar classify-json.zip

unzip classify-json.zip
cd classify-json
bash bootstrap-secrets.sh   # one-time: pulls API keys into ./secrets
docker compose up --build   # default provider: openai
# or:  PROVIDER=gemini docker compose up --build

Escribe algo, elige un proveedor y ejecuta el mismo código de Código contra la API real. Requiere iniciar sesión.


  

Los mismos módulos que ejecuta el botón Run. El proyecto completo (frontend, Dockerfile, compose) está en el ZIP, pestaña README.

backend/ai_openai.py

"""Week 4 - Showcase 2 (OpenAI): classify into a guaranteed-parseable object.

Week 2's few-shot showcase coaxed a label out as text and hoped it stayed
on format. This does the same job, but the schema makes the format a
guarantee: a label from a fixed set, a confidence float, and the reasons.
No parsing, no regex, no praying.
"""
import json
from typing import Literal

from openai import OpenAI
from pydantic import BaseModel

_client = OpenAI()


class Classification(BaseModel):
    label: Literal["billing", "bug", "feature_request", "praise", "other"]
    confidence: float
    reasons: list[str]


def run(text: str) -> str:
    response = _client.responses.parse(
        model="gpt-5.4-nano",
        input="Classify this support message. Give the label, your confidence "
              f"from 0 to 1, and a short reason or two.\n\n{text.strip()}",
        text_format=Classification,
    )
    return json.dumps(response.output_parsed.model_dump(), indent=2)

backend/ai_gemini.py

"""Week 4 - Showcase 2 (Gemini): classify into a guaranteed-parseable object."""
import json
import os
from typing import Literal

from google import genai
from google.genai import types
from pydantic import BaseModel

_client = genai.Client(api_key=os.environ["GEMINI_API_KEY"])


class Classification(BaseModel):
    label: Literal["billing", "bug", "feature_request", "praise", "other"]
    confidence: float
    reasons: list[str]


def run(text: str) -> str:
    response = _client.models.generate_content(
        model="gemini-3.1-flash-lite",
        contents="Classify this support message. Give the label, your confidence "
                 f"from 0 to 1, and a short reason or two.\n\n{text.strip()}",
        config=types.GenerateContentConfig(
            response_mime_type="application/json",
            response_schema=Classification,
        ),
    )
    return json.dumps(response.parsed.model_dump(), indent=2)

Archivos del proyecto

  • .gitignore
  • README.es.md
  • README.md
  • backend/Dockerfile
  • backend/ai_gemini.py
  • backend/ai_openai.py
  • backend/main.py
  • backend/requirements.txt
  • bootstrap-secrets.sh
  • docker-compose.yml
  • frontend/Dockerfile
  • frontend/app/layout.tsx
  • frontend/app/page.tsx
  • frontend/next.config.ts
  • frontend/package.json
  • frontend/tsconfig.json