Módulo 2: Vision + LLMs
7. Multi-Image y Context
Descripción
Hasta ahora enviaste una imagen por request. Pero muchos casos de uso reales requieren que el modelo vea múltiples imágenes simultáneamente: comparar un diseño antes y después, analizar las 12 páginas de un contrato, catalogar 20 productos de un inventario, o detectar cambios entre fotos tomadas en diferentes fechas.
Enviar múltiples imágenes no es simplemente repetir N veces un request de una imagen. Cambia la estructura del request, la forma de referenciar cada imagen en el prompt, el consumo de tokens, y las estrategias cuando el conjunto excede el context window. Cada proveedor tiene su propio formato.
En esta cápsula vas a dominar los tres formatos, construir cuatro casos de uso completos, y aprender a gestionar el context window cuando las imágenes se acumulan. Los patrones de la cápsula 06 (descripción, OCR, comparación) se aplican aquí sobre conjuntos de imágenes, y las funciones Base64 de la cápsula 06 del Módulo 1 son prerrequisito.
Multi-Image con OpenAI
Estructura del content array
Para enviar múltiples imágenes con OpenAI, agregas varios objetos image_url al array content del mensaje. El modelo procesa todas las imágenes en orden y puede referenciarlas por posición.
import base64
from pathlib import Path
from openai import OpenAI
client = OpenAI()
MIME_TYPES = {
".jpg": "image/jpeg", ".jpeg": "image/jpeg",
".png": "image/png", ".gif": "image/gif", ".webp": "image/webp",
}
def encode_image(path: str) -> str:
p = Path(path)
mime = MIME_TYPES[p.suffix.lower()]
with open(p, "rb") as f:
b64 = base64.b64encode(f.read()).decode("utf-8")
return f"data:{mime};base64,{b64}"
def analyze_multiple_openai(
image_paths: list[str],
prompt: str,
detail: str = "auto",
model: str = "gpt-4o",
) -> str:
content = [{"type": "text", "text": prompt}]
for path in image_paths:
content.append({
"type": "image_url",
"image_url": {
"url": encode_image(path),
"detail": detail,
},
})
response = client.chat.completions.create(
model=model,
messages=[{"role": "user", "content": content}],
max_tokens=2048,
)
return response.choices[0].message.content
Referenciar imágenes en el prompt
El modelo recibe las imágenes en el orden en que aparecen en el array. Referéncialas explícitamente por posición:
prompt = """Tienes 3 imágenes:
- Imagen 1: Diagrama de arquitectura
- Imagen 2: Captura de código
- Imagen 3: Screenshot del resultado
Describe la relación entre las tres. ¿El código implementa el diagrama?
¿El resultado es consistente con lo esperado?"""
result = analyze_multiple_openai(
["diagrama.png", "codigo.png", "resultado.png"],
prompt,
detail="high",
)
El parámetro detail por imagen
Puedes asignar diferente detail a cada imagen dentro del mismo request. Construye el array content manualmente en lugar de usar la función helper, y asigna "high" a imágenes que necesitan análisis fino (documentos, texto) y "low" a imágenes de contexto (logos, fotos de referencia). Esto optimiza el consumo de tokens sin sacrificar calidad donde importa.
Límites de OpenAI
- Máximo práctico: ~10 imágenes por request (depende de resolución y
detail) - Cada imagen en
highconsume 85 + 170×tiles tokens - El total de tokens (texto + imágenes) no puede exceder el context window de 128K tokens
- En
lowdetail, cada imagen consume solo 85 tokens — puedes enviar más imágenes
Multi-Image con Anthropic
Anthropic estructura multi-image con bloques de tipo image en el array content. Cada imagen va como Base64 con su media_type separado — no acepta URLs.
import anthropic
client_anthropic = anthropic.Anthropic()
def analyze_multiple_anthropic(
image_paths: list[str],
prompt: str,
model: str = "claude-sonnet-4-20250514",
) -> str:
content = []
for i, path in enumerate(image_paths, 1):
p = Path(path)
mime = MIME_TYPES[p.suffix.lower()]
with open(p, "rb") as f:
b64 = base64.b64encode(f.read()).decode("utf-8")
content.append({
"type": "image",
"source": {
"type": "base64",
"media_type": mime,
"data": b64,
},
})
content.append({"type": "text", "text": prompt})
response = client_anthropic.messages.create(
model=model,
max_tokens=2048,
messages=[{"role": "user", "content": content}],
)
return response.content[0].text
result = analyze_multiple_anthropic(
["foto_antes.jpg", "foto_despues.jpg"],
"Compara la Imagen 1 y la Imagen 2. Lista similitudes y diferencias.",
)
Orden de los bloques
En Anthropic, los bloques image aparecen antes del bloque text en el array content. El modelo los procesa en orden secuencial: Imagen 1 es el primer bloque image, Imagen 2 el segundo, etc. Puedes referenciarlas por posición en el prompt igual que con OpenAI.
Límites de Anthropic
- Cada imagen: máximo 5 MB (en Base64, que infla ~33%)
- Archivo original no debe exceder ~3.75 MB para no superar el límite tras codificación
- Context window: 200K tokens (Claude Sonnet)
- Sin límite explícito de número de imágenes, pero el context window es el tope real
Multi-Image con Gemini
Gemini tiene la estructura más simple para multi-image: una lista plana donde mezclas texto e imágenes como elementos secuenciales.
import google.generativeai as genai
from PIL import Image
genai.configure()
gemini_model = genai.GenerativeModel("gemini-2.0-flash")
def analyze_multiple_gemini(
image_paths: list[str],
prompt: str,
) -> str:
contents = [prompt]
for path in image_paths:
contents.append(Image.open(path))
response = gemini_model.generate_content(contents)
return response.text
result = analyze_multiple_gemini(
["dashboard_enero.png", "dashboard_febrero.png", "dashboard_marzo.png"],
"Analiza la evolución de los 3 dashboards mensuales. "
"Imagen 1 = enero, Imagen 2 = febrero, Imagen 3 = marzo. "
"¿Qué métricas mejoraron y cuáles empeoraron?",
)
Ventaja: context window extendido
La ventaja principal de Gemini para multi-image es su context window masivo: 1M tokens en gemini-1.5-pro, hasta 2M en gemini-2.0-flash. Donde OpenAI limita a ~10 imágenes en high, Gemini puede recibir decenas de imágenes en un solo request. Para catálogos grandes, simplemente itera sobre un directorio y agrega cada imagen a la lista contents.
Límites de Gemini
- Context window: hasta 2M tokens (modelo dependiente)
- Imagen individual: máximo 20 MB
- Formatos: PNG, JPEG, GIF, WebP, BMP
- Acepta URLs HTTP,
gs://URIs, y objetos PIL directamente
Comparación de Formatos
| Aspecto | OpenAI | Anthropic | Gemini |
|---|---|---|---|
| Estructura | Array de image_url en content | Array de bloques image + text en content | Lista plana [prompt, img1, img2, ...] |
| Formato de imagen | Data URI o URL | Base64 + media_type separados | Objeto PIL, URL, o gs:// |
| Orden texto/imágenes | Texto primero, luego imágenes | Imágenes primero, texto al final | Orden libre |
| Parámetro de detalle | detail: low, high, auto | No disponible | No disponible |
| URLs directas | Sí | No (solo Base64) | Sí (HTTP y gs://) |
| Context window | 128K tokens | 200K tokens | Hasta 2M tokens |
| Límite por imagen | 20 MB | 5 MB (Base64) | 20 MB |
| Imágenes prácticas/request | ~10 (high) / ~50+ (low) | ~20-30 | ~50-100+ |
Pocas imágenes con alta precisión → OpenAI detail=high o Anthropic. Decenas de imágenes simultáneas → Gemini.
Caso de Uso 1: Comparación Antes/Después
Un patrón frecuente es enviar dos imágenes para generar un reporte de cambios. Aplica a: renovaciones, versiones de diseño, evolución de dashboards, control de calidad.
COMPARISON_PROMPT = """Tienes dos imágenes:
- Imagen 1: Estado ANTES
- Imagen 2: Estado DESPUÉS
Genera un reporte con: Similitudes, Diferencias, los 3 Cambios Principales,
y una Evaluación (dirección: mejora/deterioro/neutro, confianza: alta/media/baja,
resumen en una oración)."""
def compare_before_after(
before_path: str,
after_path: str,
context: str = "",
provider: str = "openai",
) -> str:
prompt = COMPARISON_PROMPT
if context:
prompt += f"\n\nContexto adicional: {context}"
if provider == "openai":
return analyze_multiple_openai([before_path, after_path], prompt, detail="high")
elif provider == "anthropic":
return analyze_multiple_anthropic([before_path, after_path], prompt)
else:
return analyze_multiple_gemini([before_path, after_path], prompt)
report = compare_before_after(
"oficina_2024.jpg",
"oficina_2025.jpg",
context="Remodelación de oficina corporativa",
provider="openai",
)
print(report)
La clave es que el prompt asigne explícitamente roles ("Imagen 1 = antes", "Imagen 2 = después") para que el modelo no confunda el orden.
Caso de Uso 2: Documento Multi-Página
Convertir un PDF en imágenes y enviarlas como multi-image es una de las aplicaciones más prácticas. La decisión clave es todas las páginas a la vez vs. chunking por lotes.
Conversión de PDF a imágenes
from pdf2image import convert_from_path
import tempfile
def pdf_to_images(pdf_path: str, dpi: int = 200) -> list[str]:
images = convert_from_path(pdf_path, dpi=dpi)
paths = []
with tempfile.TemporaryDirectory() as tmpdir:
for i, img in enumerate(images):
path = f"{tmpdir}/page_{i+1:03d}.jpg"
img.save(path, "JPEG", quality=85)
paths.append(path)
return paths
Estrategia 1: Todas las páginas a la vez
Para documentos cortos (≤10 páginas), envía todo en un request:
def analyze_full_document(pdf_path: str, prompt: str) -> str:
page_paths = pdf_to_images(pdf_path)
numbered_prompt = f"Tienes un documento de {len(page_paths)} páginas.\n"
for i in range(len(page_paths)):
numbered_prompt += f"- Imagen {i+1}: Página {i+1}\n"
numbered_prompt += f"\n{prompt}"
return analyze_multiple_openai(page_paths, numbered_prompt, detail="high")
result = analyze_full_document(
"contrato_5_paginas.pdf",
"Extrae todas las cláusulas, montos, y fechas límite del contrato.",
)
Estrategia 2: Chunking por lotes
Para documentos largos, divide en chunks, analiza cada uno, y consolida:
def analyze_document_chunked(
pdf_path: str,
prompt: str,
chunk_size: int = 5,
) -> str:
page_paths = pdf_to_images(pdf_path)
partial_results = []
for start in range(0, len(page_paths), chunk_size):
chunk = page_paths[start:start + chunk_size]
first_page, last_page = start + 1, start + len(chunk)
chunk_prompt = (
f"Páginas {first_page}-{last_page} de {len(page_paths)}.\n\n{prompt}"
)
result = analyze_multiple_openai(chunk, chunk_prompt, detail="high")
partial_results.append(f"### Páginas {first_page}-{last_page}\n{result}")
consolidation = client.chat.completions.create(
model="gpt-4o",
messages=[{
"role": "user",
"content": "Consolida estos análisis parciales en un resumen unificado. "
"Elimina redundancias.\n\n" + "\n\n".join(partial_results),
}],
max_tokens=4096,
)
return consolidation.choices[0].message.content
Estimación de costo para multi-página
Una página típica de documento (1700×2200 a 200 DPI) consume ~1105 tokens en high detail. Para estimar el costo de un documento completo:
| Páginas | Tokens de imagen | Costo gpt-4o | Costo gpt-4o-mini |
|---|---|---|---|
| 5 | ~5,525 | ~$0.019 | ~$0.001 |
| 20 | ~22,100 | ~$0.155 | ~$0.009 |
| 50 | ~55,250 | ~$0.638 | ~$0.038 |
El Ejercicio 3 al final de esta cápsula incluye una función completa para calcular estos costos con dimensiones reales.
Caso de Uso 3: Catálogo de Productos
Analizar un batch de imágenes de productos extrayendo datos estructurados de cada uno. La clave es que el prompt referencie cada imagen por número para que el modelo devuelva datos organizados.
import json
CATALOG_PROMPT = """Tienes {n} imágenes de productos.
{image_list}
Para CADA producto, extrae en JSON: image_number, product_name, category,
visible_price (null si no es visible), color, condition (nuevo/usado/no determinable),
description (una oración).
Responde ÚNICAMENTE con un array JSON válido."""
def analyze_product_catalog(
image_paths: list[str],
provider: str = "openai",
) -> list[dict]:
image_list = "\n".join(
f"- Imagen {i+1}: Producto {i+1}" for i in range(len(image_paths))
)
prompt = CATALOG_PROMPT.format(n=len(image_paths), image_list=image_list)
if provider == "openai":
raw = analyze_multiple_openai(image_paths, prompt, detail="high")
elif provider == "anthropic":
raw = analyze_multiple_anthropic(image_paths, prompt)
else:
raw = analyze_multiple_gemini(image_paths, prompt)
cleaned = raw.strip()
if cleaned.startswith("```"):
cleaned = cleaned.split("\n", 1)[1].rsplit("```", 1)[0]
return json.loads(cleaned)
products = analyze_product_catalog(
["zapato_1.jpg", "zapato_2.jpg", "zapato_3.jpg", "zapato_4.jpg"],
provider="openai",
)
for p in products:
print(f"Imagen {p['image_number']}: {p['product_name']} - {p['category']}")
Para catálogos grandes (>10 productos), procesa en lotes de 5-8 imágenes y concatena los arrays JSON.
Caso de Uso 4: Evolución Temporal
Enviar imágenes cronológicas permite detectar tendencias, progreso de construcción, evolución de métricas, o seguimiento médico.
TEMPORAL_PROMPT = """Tienes {n} imágenes ordenadas cronológicamente:
{timeline}
Analiza la evolución temporal:
1. **Tendencia general**: ¿Mejora, deterioro, estabilidad, o ciclo?
2. **Cambios entre cada par consecutivo**: Lista qué cambió entre imagen N e imagen N+1
3. **Punto de inflexión**: ¿Hay algún momento donde el patrón cambia?
4. **Predicción**: Basándote en la tendencia, ¿qué esperarías en la siguiente observación?"""
def analyze_temporal_evolution(
image_paths: list[str],
labels: list[str],
provider: str = "openai",
) -> str:
timeline = "\n".join(
f"- Imagen {i+1}: {label}" for i, label in enumerate(labels)
)
prompt = TEMPORAL_PROMPT.format(n=len(image_paths), timeline=timeline)
if provider == "openai":
return analyze_multiple_openai(image_paths, prompt, detail="high")
elif provider == "anthropic":
return analyze_multiple_anthropic(image_paths, prompt)
else:
return analyze_multiple_gemini(image_paths, prompt)
report = analyze_temporal_evolution(
["dashboard_q1.png", "dashboard_q2.png", "dashboard_q3.png", "dashboard_q4.png"],
["Q1 2025", "Q2 2025", "Q3 2025", "Q4 2025"],
provider="gemini",
)
print(report)
Gemini es ideal para evolución temporal: 12 meses con una imagen por mes cabe en un solo request, mientras que con OpenAI necesitarías chunking.
Gestión de Context Window
Cómo multi-image afecta los tokens
Cada imagen consume tokens según el proveedor y la resolución. El problema aparece cuando envías muchas imágenes y la suma total excede el context window.
import math
def estimate_multi_image_tokens(
n_images: int, width: int = 1024, height: int = 1024, detail: str = "high",
) -> dict:
if detail == "low":
per_image = 85
else:
w, h = width, height
if max(w, h) > 2048:
scale = 2048 / max(w, h)
w, h = int(w * scale), int(h * scale)
if min(w, h) > 768:
scale = 768 / min(w, h)
w, h = int(w * scale), int(h * scale)
per_image = 85 + 170 * (math.ceil(w / 512) * math.ceil(h / 512))
total = per_image * n_images
limits = {"openai": 128_000, "anthropic": 200_000, "gemini": 1_000_000}
return {
"tokens_per_image": per_image,
"total_tokens": total,
"fits_in": {p: total < lim for p, lim in limits.items()},
}
for n in [5, 10, 20, 50, 100]:
est = estimate_multi_image_tokens(n, detail="high")
ok = [p for p, fits in est["fits_in"].items() if fits]
print(f"{n:>3} imgs: {est['total_tokens']:>7,} tokens → {', '.join(ok)}")
Chunking cuando se excede el contexto
Cuando el total de tokens excede el context window, divide las imágenes en chunks. La función analyze_document_chunked del Caso de Uso 2 implementa este patrón: procesa lotes de N imágenes, recolecta resultados parciales, y consolida con una llamada de texto puro al final.
Estrategia de reducción de tokens
Antes de recurrir a chunking, reduce el consumo de tokens:
- Bajar
detailalow: Reduce de ~765 tokens a 85 tokens por imagen (OpenAI) - Redimensionar imágenes: Una imagen de 512×512 consume menos tiles que 4000×3000
- Usar
gpt-4o-mini: Mismo context window, menor costo - Filtrar imágenes irrelevantes: Envía solo las que necesita el análisis
Troubleshooting
Problema 1: Context window excedido
Síntoma: Error context_length_exceeded o max_tokens al enviar muchas imágenes.
Causa: La suma de tokens de todas las imágenes + prompt + output esperado excede el límite del modelo.
Solución: Usa estimate_multi_image_tokens() antes de enviar. Si excede, aplica chunking o reduce detail a low. Para volúmenes grandes, migra a Gemini (1-2M tokens de contexto).
Problema 2: El modelo confunde el orden de las imágenes
Síntoma: La respuesta atribuye contenido de la Imagen 2 a la Imagen 1, o mezcla información entre imágenes.
Causa: El prompt no asigna roles claros a cada imagen, o el prompt aparece después de muchas imágenes y el modelo pierde la referencia.
Solución: Siempre incluye una lista explícita al inicio del prompt: "Imagen 1 = X, Imagen 2 = Y". Con Anthropic, coloca las imágenes antes del texto para que el modelo las vea en contexto antes de leer las instrucciones.
Problema 3: Una imagen corrupta falla todo el request
Síntoma: Error de parsing o invalid_image al enviar un batch, aunque la mayoría de las imágenes son válidas.
Causa: Una sola imagen con formato incorrecto, tamaño excedido, o codificación Base64 inválida rompe el request completo.
Solución: Valida cada imagen antes de incluirla en el batch. Abre cada archivo con PIL.Image.open().verify(), verifica que el tamaño no exceda el límite del proveedor, y separa las imágenes válidas de las inválidas. Envía solo las válidas y reporta las que fallaron.
Problema 4: Costo inesperadamente alto
Síntoma: La factura es mucho mayor de lo esperado después de implementar multi-image.
Causa: Múltiples imágenes en detail=high × múltiples requests × modelo caro escalan rápido. 100 requests × 5 imágenes × gpt-4o high ≈ $1.50 solo en tokens de imagen.
Solución: Estima el costo antes de ejecutar un batch (ver Ejercicio 3). Usa gpt-4o-mini para filtrado inicial y reserva gpt-4o high solo para análisis que lo requiera.
Ejercicios
Ejercicio 1: Función de comparación con output estructurado (Fácil)
Crea una función compare_two_images(path1, path2) que envíe dos imágenes a OpenAI y devuelva un diccionario con las claves similarities (lista), differences (lista), y verdict (string: "iguales", "similares", o "diferentes").
Ver solución
import json
from openai import OpenAI
client = OpenAI()
def compare_two_images(path1: str, path2: str) -> dict:
prompt = """Compara la Imagen 1 y la Imagen 2.
Responde ÚNICAMENTE con un JSON válido con esta estructura:
{
"similarities": ["similitud 1", "similitud 2", ...],
"differences": ["diferencia 1", "diferencia 2", ...],
"verdict": "iguales | similares | diferentes"
}"""
content = [
{"type": "text", "text": prompt},
{"type": "image_url", "image_url": {"url": encode_image(path1), "detail": "high"}},
{"type": "image_url", "image_url": {"url": encode_image(path2), "detail": "high"}},
]
response = client.chat.completions.create(
model="gpt-4o",
messages=[{"role": "user", "content": content}],
max_tokens=1024,
temperature=0,
)
raw = response.choices[0].message.content.strip()
if raw.startswith("```"):
raw = raw.split("\n", 1)[1].rsplit("```", 1)[0]
return json.loads(raw)
result = compare_two_images("version_a.png", "version_b.png")
print(f"Veredicto: {result['verdict']}")
print(f"Similitudes: {len(result['similarities'])}")
print(f"Diferencias: {len(result['differences'])}")
Explicación: temperature=0 garantiza respuestas determinísticas. El parsing maneja el caso donde el modelo envuelve el JSON en bloques de código markdown.
Ejercicio 2: Analizador de PDF con chunking y consolidación (Medio)
Crea una función analyze_pdf(pdf_path, prompt, chunk_size=5) que convierta un PDF en imágenes, las procese en chunks, y devuelva un resultado consolidado.
Ver solución
from pdf2image import convert_from_path
from openai import OpenAI
import tempfile
client = OpenAI()
def analyze_pdf(pdf_path: str, prompt: str, chunk_size: int = 5) -> str:
with tempfile.TemporaryDirectory() as tmpdir:
images = convert_from_path(pdf_path, dpi=200)
page_paths = []
for i, img in enumerate(images):
path = f"{tmpdir}/page_{i+1:03d}.jpg"
img.save(path, "JPEG", quality=85)
page_paths.append(path)
if len(page_paths) <= chunk_size:
return analyze_multiple_openai(page_paths, prompt, detail="high")
partial_results = []
for start in range(0, len(page_paths), chunk_size):
chunk = page_paths[start:start + chunk_size]
first_page, last_page = start + 1, start + len(chunk)
chunk_prompt = (
f"Páginas {first_page}-{last_page} de {len(page_paths)}.\n\n{prompt}"
)
result = analyze_multiple_openai(chunk, chunk_prompt, detail="high")
partial_results.append(f"[Páginas {first_page}-{last_page}]\n{result}")
consolidation = client.chat.completions.create(
model="gpt-4o",
messages=[{
"role": "user",
"content": "Consolida estos análisis parciales en un resumen unificado. "
"Elimina redundancias.\n\n" + "\n\n---\n\n".join(partial_results),
}],
max_tokens=4096,
)
return consolidation.choices[0].message.content
Explicación: Si el documento cabe en un chunk, envía todo directo. Si no, procesa por lotes y consolida con una llamada de solo texto.
Ejercicio 3: Estimador de costo multi-image (Medio)
Crea una función estimate_batch_cost(image_paths, detail, model) que lea las dimensiones reales de cada imagen, calcule los tokens exactos, y devuelva el costo total estimado antes de hacer cualquier llamada a la API.
Ver solución
import math
from PIL import Image
from pathlib import Path
PRICING = {
"gpt-4o": {"input": 2.50, "output": 10.00},
"gpt-4o-mini": {"input": 0.15, "output": 0.60},
}
def calculate_image_tokens(width: int, height: int, detail: str = "high") -> int:
if detail == "low":
return 85
w, h = width, height
if max(w, h) > 2048:
scale = 2048 / max(w, h)
w, h = int(w * scale), int(h * scale)
if min(w, h) > 768:
scale = 768 / min(w, h)
w, h = int(w * scale), int(h * scale)
tiles = math.ceil(w / 512) * math.ceil(h / 512)
return 85 + 170 * tiles
def estimate_batch_cost(
image_paths: list[str],
detail: str = "high",
model: str = "gpt-4o",
output_tokens_per_image: int = 100,
) -> dict:
total_tokens = 0
per_image = []
for path in image_paths:
with Image.open(path) as img:
w, h = img.size
tokens = calculate_image_tokens(w, h, detail)
total_tokens += tokens
per_image.append({"path": Path(path).name, "size": f"{w}x{h}", "tokens": tokens})
total_output = output_tokens_per_image * len(image_paths)
p = PRICING[model]
cost = (total_tokens / 1e6) * p["input"] + (total_output / 1e6) * p["output"]
return {
"n_images": len(image_paths),
"total_image_tokens": total_tokens,
"total_cost_usd": round(cost, 6),
"per_image": per_image,
}
est = estimate_batch_cost(["product_1.jpg", "product_2.jpg", "product_3.jpg"], model="gpt-4o-mini")
print(f"{est['n_images']} imágenes: {est['total_image_tokens']:,} tokens, ${est['total_cost_usd']:.6f}")
Explicación: Lee las dimensiones reales de cada imagen con Pillow para calcular tokens exactos. Útil para validar presupuesto antes de procesar lotes grandes.
Resumen
- Estructura: OpenAI usa array de
image_url, Anthropic bloquesimagecon Base64, Gemini una lista plana de contenido - Referencia: Siempre numera las imágenes explícitamente en el prompt ("Imagen 1 = X, Imagen 2 = Y")
- Límites prácticos: ~10 imágenes (OpenAI high), ~20-30 (Anthropic), ~50-100+ (Gemini)
- Documentos multi-página: Chunking por lotes + consolidación para documentos que exceden el contexto
- Tokens: Cada imagen en
highconsume ~765 tokens (1024×1024). Multiplica por N imágenes para estimar el total - Costo: Estima siempre antes de ejecutar.
detail=lowreduce 9x el consumo de tokens de imagen - Context window: Si excedes el límite, reduce detail, redimensiona, o aplica chunking. Gemini (1-2M tokens) es la mejor opción para alto volumen
Recursos Adicionales
- OpenAI Vision — Multiple Images — Documentación oficial con ejemplos multi-image
- Anthropic Vision Docs — Formato de múltiples bloques de imagen
- Google Gemini Vision — Multi-image con context extendido
- pdf2image (PyPI) — Conversión PDF a imágenes para análisis multi-página
- OpenAI Token Calculator — Referencia para estimación de tokens