Claude Agent SDK vs OpenAI Agents SDK vs Google ADK: Un agente, tres versiones
Claude Agent SDK vs OpenAI Agents SDK vs Google ADK: un agente para etiquetas nutricionales creado tres veces, mostrando qué maneja cada kit y qué aún debes escribir tú mismo.
Todos los grandes proveedores de IA ahora incluyen un SDK para agentes, y su promoción es prácticamente idéntica: danos tus herramientas y una instrucción, y nosotros gestionaremos todo el proceso: llamadas a herramientas, salida estructurada, sesiones y seguimiento incluidos. Si la publicidad es tan similar, ¿qué es lo que realmente diferencia a estos kits, y en cuál deberías basarte?
La forma de averiguarlo fue crear el mismo agente tres veces: con el Claude Agent SDK de Anthropic, con el OpenAI Agents SDK y con el Agent Development Kit (ADK) de Google. Este agente no es un juguete; es un investigador de etiquetas nutricionales que analiza una foto, utiliza varias herramientas para razonar, emite un veredicto y mejora a medida que ve más etiquetas. Con un mismo agente y tres SDK diferentes, cualquier diferencia en los resultados se debe al SDK y no al problema en sí.
En resumen: los tres enfoques coinciden casi por completo en la parte más difícil, el ciclo de razonamiento, y difieren en todo lo demás relacionado con él. Se diferencian en cómo se declara una herramienta, cómo se introduce una imagen y si se obtiene algún resultado escrito. Lo más revelador es que difieren en lo que ninguno de ellos hace por usted. Al final sabrá qué kit es adecuado para cada tarea y cuánto trabajo le quedará después de elegir uno.
Hay otro hilo común en las tres versiones. El agente fue diseñado para aprender de la experiencia. Recuerda los productos que ya ha examinado y los nombres de los aditivos que ha logrado resolver, y guarda esa información en el disco, de modo que cada ejecución posterior depende un poco menos del modelo, termina más rápido y se comporta de manera más determinista. Los pesos del modelo nunca cambian. Es el agente quien acumula habilidades. Ninguno de los SDKs te ofrece esto de forma predeterminada, lo que lo convierte en una herramienta útil para identificar dónde termina el framework y comienza tu propio trabajo de ingeniería.
Primero, ¿qué se considera un agente?
Un modelo de lenguaje sencillo no es un agente. Considéralo como una función que toma texto e imágenes y devuelve texto. Nada se mantiene entre llamadas: no puede ejecutar código ni cargar una página, e incluso es incapaz de llamarse a sí mismo nuevamente. Si se le da una etiqueta nutricional, le indicará cómo procedería para investigarla, pero la investigación nunca tiene lugar.
Un agente es un modelo que funciona en un bucle con herramientas. El patrón nunca cambia. Se le da al modelo un objetivo y una lista de herramientas que puede utilizar. Responde ya sea con un resultado final o con una solicitud para usar una herramienta, como “buscar E211”. Si solicita una herramienta, algo la ejecuta y devuelve el resultado, y el modelo vuelve a intentarlo. El ciclo se repite hasta que aparece una respuesta final.
Ese ciclo es toda la idea: el modelo toma una decisión, se ejecuta una herramienta, llega el resultado, el modelo vuelve a decidir. Nuestro investigador repite el proceso muchas veces, una vez para buscar el producto, una vez por cada aditivo, luego para verificar la opción más saludable, determinar la calificación y realizar una síntesis final. En cada paso, el modelo elige su siguiente acción basándose en lo que acaba de aprender. Eso es lo que hace que el sistema sea autónomo, y es por eso que una sola llamada a la API envuelta en un bucle for no cumple con los requisitos.
Observe lo que emite realmente el modelo: solo “Me gustaría llamar a check_sfa_additive con estos argumentos”. Algo más tiene que convertir ese deseo en una llamada de función real y mantener todo en funcionamiento. Eso es el harness, y contiene más componentes técnicos poco llamativos de lo que la mayoría de las personas imagina. Se divide en tres capas: una capa de Desarrollo donde se construye el agente, una capa en Tiempo de Ejecución donde se opera uno en producción, y una Plano de Control donde se gestiona toda una flota. Es la capa de Desarrollo la que hace que el ciclo funcione, y es allí donde se encuentran estos SDKs.
Comience con la capa de Desarrollo, donde se construye un agente individual. Esta capa consta del bucle en sí (llamar al modelo, detectar solicitudes de uso de herramientas, repetir hasta completar la tarea y salir de forma segura al alcanzar un límite de turnos o presupuesto) y de la personalidad, es decir, el prompt del sistema que indica al modelo quién es y cómo actuar. Alrededor de estos elementos se encuentran los esquemas de herramientas, que describen cada herramienta al modelo en el formato que este espera, y la ejecución de herramientas, que actualmente ejecuta su función, ya sea a través de un servidor MCP o de una función integrada como la lectura de archivos, capturando el resultado. Un cliente MCP permite que el agente acceda a servidores de terceros. La gestión de contexto mantiene el transcripción en curso, maneja las imágenes y las elimina cuando se vuelven demasiado largas. La configuración del proveedor abarca la autenticación, los puntos de conexión y los formatos de solicitud para un proveedor específico. Además, existen las salidas estructuradas, que imponen y verifican que la respuesta final se ajuste a una forma fija, y los flujos de trabajo multiagente que conectan a los agentes en secuencia, en paralelo o en ciclos.
Los SDK facilitan todo eso. Al pasar de ejecutar un script a operar un servicio, aparece la capa Runtime, junto con una lista más extensa de requisitos. El agente necesita una ejecución alojada, es decir, un punto de conexión con aislamiento de sesiones al que otros sistemas puedan acceder. También requiere una pasarela de herramientas, así como mecanismos de reintentos, tiempos de espera y manejo de credenciales que garanticen la fiabilidad de las llamadas a las herramientas cuando la API upstream falla, además de memoria gestionada, de modo que las sesiones y el estado a largo plazo permanezcan almacenados en un lugar que sobreviva al proceso y pueda ser compartido entre instancias. Necesita una identidad de carga de trabajo para presentarla al llamar a las herramientas, evitando así que esas llamadas se realicen con la clave personal de alguien, y también capacidad de observabilidad, con trazas y segmentos que muestren qué herramientas se activaron, con qué y por qué. Añada un entorno de pruebas para el código propio del agente o las herramientas del navegador, bucles resistentes que continúen desde el último paso completado tras una caída, y escalabilidad mediante concurrencia y autoscaling detrás del endpoint.
Debajo de esto se encuentra el Control Plane, que gestiona a muchos agentes al mismo tiempo. Un registro permite que los agentes se publiquen y se descubran en tiempo de ejecución en lugar de estar conectados entre sí mediante código. Cada agente necesita una identidad como entidad propia y atribuible en el directorio, además de un alcance de permisos que se aplique de forma estricta y no solo se asuma. Posteriormente, se necesita un registro de auditoría y cumplimiento que muestre qué hizo la flota, una gobernanza de datos sobre qué datos pueden almacenarse y revelarse en la memoria compartida, así como controles de costos y cuotas con presupuestos y límites de velocidad. Los agentes pueden comunicarse entre sí mediante un protocolo abierto como A2A. Dado que la salida de las herramientas fluye directamente de vuelta al modelo, son importantes las medidas de control y la defensa contra inyecciones, y la evaluación permite saber si las respuestas son correctas y no solo si la ejecución finalizó. Un registro para herramientas y servidores MCP se encarga de la catalogación, la versionación, el descubrimiento, la agrupación y la autenticación por servidor, y la versionación rápida permite fijar, comparar y revertir prompts para que las ejecuciones sigan siendo reproducibles. En resumen, cada uno de estos kits es un conjunto listo para usar. Los de Claude, OpenAI y Google proporcionan el ciclo de trabajo, la infraestructura de herramientas, el manejo de contexto y la conexión con los proveedores, dejándote escribir las partes interesantes: tus herramientas, tu prompt y la estructura de tu salida. ADK es el nombre excepcional, aunque realiza la misma función, por lo que “los SDKs” mencionados a continuación abarcan de forma general a los tres.
Se diferencian en qué piezas del arnés proporcionan, cuán específicas son cada una de esas piezas y dónde termina exactamente el arnés para que su código asuma el control. La forma más clara de concretar esto es crear un agente en cada uno de ellos. Una vez que existan los tres, se podrá diseñar el arnés completo y marcar qué aporta cada kit y qué deja a su cargo.
El agente en pruebas: un investigador de etiquetas nutricionales
Una prueba rigurosa debe hacer que estos kits realicen la tarea para la cual fueron creados: un ciclo de razonamiento que involucra varias herramientas, donde cada paso siguiente depende de los resultados del anterior. Una sola llamada a un modelo sin utilizar el marco adecuado no cuenta. Es el investigador quien realiza el trabajo real; basta mostrarle una foto de una etiqueta nutricional para que ejecute cinco tareas distintas.
- Lee la etiqueta mediante la visión propia del modelo, extrayendo el nombre del producto, la lista completa de ingredientes y aditivos, la cantidad de azúcar y grasa saturada por 100 ml, y si el producto es una bebida. La foto es la única fuente fiable, ya que no existe una base de datos de productos a la que recurrir.
- Para cada aditivo que encuentra, ya sea que la etiqueta indique
E471o “Lecitina de soja”, consulta la lista de aditivos permitidos por la SFA de Singapur y reporta su estado así como cualquier advertencia para el consumidor. - Interroga el conjunto de datos HPB en data.gov.sg para determinar si el producto lleva el Símbolo de Elección Más Saludable.
- En el caso de las bebidas, calcula la clasificación Nutri-Grade A/B/C/D según el contenido de azúcar y grasa saturada.
- Reúne toda la información en un veredicto jerárquico: VERDE, ÁMBAR o ROJO, según cada hallazgo, indicando siempre la herramienta o etiqueta que lo generó, además de exponer de antemano el razonamiento del modelo.
Tampoco comienza desde cero cada vez. Recuerda lo que ya ha visto y aprende a medida que avanza, por lo que tanto un producto examinado ayer como el nombre de un aditivo que alguna vez le costó identificar estarán disponibles la próxima vez. Hablaremos más de esto más adelante.
Así, el entorno de pruebas ofrece un ciclo de herramientas de longitud variable, una fuente de datos real de Singapur, entrada de imágenes, un resultado que se puede verificar en los pasillos de un supermercado y un agente que mejora un poco en cada ejecución.
Mantener una comparación justa
Cualquier elemento que no fuera parte del SDK tenía que mantenerse idéntico. Las tres demostraciones utilizan un proxy LiteLLM, una pasarela compatible con OpenAI que también sirve como punto de acceso /v1/messages de Anthropic para el SDK de Claude. Un único archivo .env raíz contiene la clave, la URL base y el nombre del modelo para cada una de ellas; los modelos son Claude Sonnet 4.6, GPT-5.4 y Gemini 3.5 Flash. La forma en que cada SDK se dirige a ese proxy revela mucha información.
Cada demo existe en su propio proyecto uv, por lo que se puede ejecutar una sin instalar las dependencias de las otras. La tabla de aditivos SFA se creó una sola vez a partir del PDF “Permitted-Additives” publicado por la agencia alimentaria de Singapur, generando sfa_additives.json (aproximadamente 540 entradas, de las cuales unas 270 tienen números E), y luego se copió sin cambios a cada proyecto. Las imágenes de prueba también eran idénticas: dos productos reales de Singapur, un sobre de Milo 3in1 y un envase de leche Marigold HL.
Esto deja solo lo que realmente es una decisión del SDK: cómo se define una herramienta, cómo se incluye una imagen, cómo se conecta el modelo, si existe una salida tipada y cómo se puede ver qué ocurrió.
Un aviso antes de mostrar el código. Los fragmentos a continuación contienen una herramienta recall_product y un argumento e_number_hint. Estos son los responsables de la función de memoria y auto-mejora que se explicará más adelante, así que ignórenlos por ahora.
El Claude Agent SDK de Anthropic
El Claude Agent SDK se basa directamente en Claude Code, el agente que muchas personas ya utilizan en una terminal, y esa decisión determina todo el resto. No se trata de armar un bucle a partir de elementos básicos; se trata de utilizar un agente completo y probado en el campo, al que se le proporcionan las herramientas y los comandos necesarios. Las herramientas integradas (Read, Write, Edit, Bash, WebFetch) ya están disponibles, el modelo de permisos y la gestión de contexto ya están resueltos, y las herramientas personalizadas se conectan a través de un servidor MCP que funciona dentro de su propio proceso. Todo se ejecuta de forma asíncrona mediante anyio.
Lo que está comprando es un verdadero agente capaz de utilizar una computadora, listo para usarlo de inmediato. Cualquier herramienta que necesite leer archivos, ejecutar comandos de shell, editar código fuente o navegar por la red llega en una versión de calidad profesional desde el primer día, ya que se trata de la misma infraestructura que impulsa a Claude Code. MCP goza de un estatus privilegiado, por lo que una herramienta personalizada que escriba funcionará sin cambios en Claude Code, en Claude Desktop y en su propia aplicación. De los tres, este agente cuenta con el entorno de ejecución más maduro.
El costo se refleja en las herramientas, donde debe escribir manualmente el nombre, la descripción y el esquema:
@tool(
"check_sfa_additive",
"Check whether a food additive is permitted by the Singapore Food Agency, "
"using the full SFA permitted-additives list parsed from the official PDF. "
"Accepts an E-number ('E211') OR a name ('Sodium Benzoate', 'Soy Lecithin'). "
"If you pass a name and also know its E/INS number, set e_number_hint — an "
"unrecognised name that the hint resolves is REMEMBERED for next time.",
{"additive": str, "e_number_hint": str},
)
async def check_sfa_additive(args: dict) -> dict:
raw = args["additive"].strip()
hint = args.get("e_number_hint", "").strip()
entry = _resolve_additive(raw)
if not entry and hint:
entry = _resolve_additive(hint)
if entry: # learn: this label name -> this number, persisted to disk
num = entry["e_number"] or entry.get("ins") or ""
_MEMORY["learned_aliases"][_norm(raw)] = re.sub(r"\(.*\)", "", num).lower()
_save_memory(_MEMORY)
return _ok(_format_entry(entry, raw))
Ese diccionario {arg: type} requiere más tecleo que los otros dos kits, pero no deja espacio para ambigüedades. Las herramientas se exponen a través de un servidor MCP en el mismo proceso, y el modelo las referencia con un prefijo mcp__<server>__<tool>; debes recordarlo al completar allowed_tools:
server = create_sdk_mcp_server(
name="sg-nutrition-tools",
version="1.0.0",
tools=[recall_product, check_sfa_additive, check_hcs, calculate_nutri_grade],
)
options = ClaudeAgentOptions(
system_prompt=SYSTEM_PROMPT,
mcp_servers={"sg": server},
allowed_tools=[
"mcp__sg__recall_product",
"mcp__sg__check_sfa_additive",
"mcp__sg__check_hcs",
"mcp__sg__calculate_nutri_grade",
],
model=CLAUDE_MODEL,
permission_mode="bypassPermissions",
max_turns=20,
# Point the bundled CLI at the LiteLLM proxy's Anthropic endpoint.
env={
**os.environ,
"ANTHROPIC_BASE_URL": BASE_URL,
"ANTHROPIC_AUTH_TOKEN": API_KEY,
},
)
Las imágenes se insertan de la misma manera manual. No existe el argumento image=. query() acepta un iterable asíncrono de diccionarios de mensajes, por lo que debes crear tú mismo los bloques de contenido nativos de Claude, un bloque de texto junto con un bloque de imagen:
async def message_stream():
yield {
"type": "user",
"message": {"role": "user", "content": [
{"type": "text", "text": "Investigate this nutrition label."},
{"type": "image", "source": {"type": "base64", "media_type": mime, "data": b64}},
]},
}
async with ClaudeSDKClient(options=options) as client:
await client.query(message_stream())
async for message in client.receive_response():
... # stream ToolUseBlock / TextBlock as they arrive
El borde afilado corresponde al cableado del modelo. El SDK hace funcionar la CLI de Claude Code incluida, la cual utiliza el formato de mensajes de Anthropic; por lo tanto, se redirige hacia el proxy a través de variables de entorno (ANTHROPIC_BASE_URL y ANTHROPIC_AUTH_TOKEN). No hay un objeto de cliente que pasar. La ventaja es que existe un ciclo completamente visible: se imprime cada ToolUseBlock a medida que llega, permitiendo observar en tiempo real cómo se desarrolla cada ciclo aditivo. Aquí hay un ejemplo de ejecución con el sobre de Milo:
sausheong claude-demo % uv run python main.py ../milo.jpeg
Investigating image: ../milo.jpeg
────────────────────────────────────────────────────────────
I'll start by reading the label carefully, then run all checks in parallel.
**Reading from the label:**
- **Product:** Nestlé MILO 3in1
- **Net weight:** 27g powder sachet (add hot water to prepare — this is a **powder, not a ready-to-drink beverage**)
- **Ingredients:** Sugar, PROTOMAL® Malt Extract (Contains Barley), Skimmed Milk (Cow's Milk), Cocoa, Palm Oil, Minerals (Dicalcium Phosphate, Disodium Phosphate, Ferric Pyrophosphate), Resistant Dextrin, Flavourings, Vitamins (C, B3, D, B6, B2, B12)
- **Identifiable additives:** Dicalcium Phosphate (E341), Disodium Phosphate (E339), Ferric Pyrophosphate; Flavourings (generic — cannot identify specific ones)
Kicking off recall + HCS check + all additive checks simultaneously:
→ tool: recall_product({'product_name': 'Nestlé MILO 3in1'})
→ tool: check_hcs({'product_name': 'MILO 3in1'})
→ tool: check_sfa_additive({'additive': 'Dicalcium Phosphate', 'e_number_hint': 'E341'})
→ tool: check_sfa_additive({'additive': 'Disodium Phosphate', 'e_number_hint': 'E339'})
→ tool: check_sfa_additive({'additive': 'Ferric Pyrophosphate', 'e_number_hint': 'E450'})
All checks returned. Note: this is a **powder**, not a ready-to-drink beverage, so Nutri-Grade (which requires per-100ml liquid values) does **not apply** — I will skip `calculate_nutri_grade`.
---
## Reasoning
**Step 1 — Product recall.** This product has been seen before. The prior verdict is consistent with what I read now — I'll treat this fresh investigation as the authoritative one and use the prior as a cross-check.
**Step 2 — Label reading.**
- MILO 3in1 is a **27g powder sachet** — you add hot water to make a drink. It is classified as a "Tonic Food Drink" on the label. Because it is sold as a dry powder (not a pre-packaged ready-to-drink liquid), Nutri-Grade per-100ml calculation does not apply.
- Nutritional values per serving (27g): Energy 110 kcal, Total Fat ~2.4g, Saturated Fat ~1.2g, Total Sugars ~13.8g (of which Added Sugars ~10g), Sodium ~38mg.
- **Sugar is the #1 ingredient** — it appears first in the ingredient list, before the malt extract.
**Step 3 — Additives.**
- **Dicalcium Phosphate (E341):** SFA-permitted ✅ — used as a mineral/calcium source.
- **Disodium Phosphate (E339):** SFA-permitted ✅ — used as an acidity regulator/mineral.
- **Ferric Pyrophosphate:** SFA-permitted ✅ — used as an iron supplement/mineral source.
- **Resistant Dextrin:** A dietary fibre ingredient (prebiotic fibre), not a regulated additive — no E number, no SFA check needed.
- **Flavourings:** Generic label — cannot identify specific flavouring compounds; SFA check not possible.
- **Vitamins (C, B3, D, B6, B2, B12):** Added as nutritional fortification, not food additives in the regulatory sense. Riboflavin (B2) carries E101 when used as a colourant, but here it's explicitly declared as a vitamin supplement — standard and expected.
**Step 4 — Healthier Choice Symbol.** HCS certified ✅ (MILO 3in1 Activ-Go appears multiple times in the HPB dataset — `check_hcs`).
**Step 5 — Sugar content reality check.** Each 27g sachet contains ~13.8g total sugars (~10g added sugars). That's more than **half the sachet's weight is sugar**, and exceeds the WHO daily added-sugar guideline of 25g in less than one cup. This is label-read, not a tool finding.
---
## Verdict
### 🟢 GREEN — No Concern
- **All three declared additives are SFA-permitted** (Dicalcium Phosphate E341, Disodium Phosphate E339, Ferric Pyrophosphate) — `check_sfa_additive` ×3.
- **Carries the Healthier Choice Symbol** — HPB has certified MILO 3in1 Activ-Go — `check_hcs`.
- **Vitamins and minerals fortified** — B2, B3, B6, B12, C, D, plus calcium and iron — label.
- **Contains barley malt extract** — a whole-grain source of energy — label.
- **Resistant Dextrin** (prebiotic dietary fibre) is a functional ingredient, not a concern — label.
### 🟡 AMBER — Worth Knowing
- **Palm Oil present** — a saturated fat source. Saturated fat is 1.2g per sachet (~4.4g per 100g of powder). Not alarming at one serving, but worth knowing if you have multiple cups a day — label.
- **"Flavourings" are not specified** — cannot determine whether natural or artificial. No SFA check was possible — label.
- **Nutri-Grade does not apply here** — this is sold as a powder. If you reconstitute it and compare the drink to ready-to-drink alternatives, it would likely land in Grade C or D territory based on sugar content, but no regulatory grade is required on the packet.
### 🔴 RED — Genuinely Concerning
- **Sugar is the #1 ingredient** — it appears first in the list, meaning it is the dominant ingredient by weight. Each sachet delivers **~13.8g total sugars / ~10g added sugars** — label. That's 40% of the WHO's recommended 25g/day added-sugar limit in a single cup. If you're drinking 2–3 sachets a day (as Nestlé's own label suggests for daily energy), you are consuming **20–30g of added sugar from MILO alone**, likely exceeding the daily limit.
- **Marketing framing vs. nutritional reality:** The packet prominently says "Nourishing Energy" and shows vitamins, but the sugar load is the dominant nutritional feature. The fortification (vitamins, minerals) does not offset this — label.
**Bottom line for a Singapore shopper:** MILO 3in1 is safe from an additives and regulatory standpoint — everything checks out with SFA and HPB. But it is a **sugar-heavy product** dressed up in health language. Treat it as an occasional treat rather than a daily nutritious breakfast drink, especially for children.
Dónde hay limitaciones. No existe una salida estructurada nativa, por lo que se obtiene texto que hay que analizar manualmente o se debe especificar con precisión su formato, lo cual representa la mayor diferencia cotidiana en comparación con el SDK de OpenAI. Los esquemas son manuales, por lo que hay que escribir más contenido y mantenerlo sincronizado con cada función. La conexión de los modelos se realiza a través del entorno, y el proxy al que te diriges debe ofrecer un endpoint en formato Anthropic en /v1/messages, ya que un endpoint compatible con OpenAI por sí solo no es suficiente. Considera la lista allowed_tools como una aprobación previa, nunca como un entorno aislado: significa “omite la solicitud de confirmación para estos”, no “el agente solo puede ver estos”, y quienes lo interpreten como una lista blanca se llevarán una sorpresa. Olvídate del prefijo mcp__sg__; en ese caso la herramienta simplemente quedará sin aprobación, sin generar error alguno. La huella de recursos también es mayor, ya que se ejecuta el entorno de Claude Code en segundo plano y no se realiza una llamada a la API directa.
Elige esta opción cuando el agente deba operar una máquina. Eso incluye leer y escribir archivos, ejecutar comandos de shell, editar un código o navegar por la web; cualquier tarea en la que las herramientas integradas de Claude Code te ahorren la necesidad de crear un entorno aislado. También es la opción natural si ya utilizas MCP o si estás creando algo que debe sentirse como Claude Code. El bucle de transmisión en tiempo real es fácil de seguir, lo cual es útil para agentes que funcionan durante mucho tiempo.
Elige esta opción cuando necesites un bucle de llamada a funciones compacto y estructurado que devuelva un objeto organizado, ya que ahí es donde brilla el SDK de OpenAI. Elige la otra opción si no puedes proporcionar un endpoint en formato Anthropic o si es importante tener un menor número de dependencias. Una tarea sencilla como “llamar a tres herramientas y devolver un objeto Pydantic” requiere mucho menos complejidad.
SDK de OpenAI Agents
El SDK de OpenAI es un wrapper ligero y deliberadamente orientado a Python alrededor del bucle, y se adhiere plenamente al propio lenguaje. Las sugerencias de tipo se convierten en esquemas de herramientas, las cadenas de documentación en descripciones de herramientas y los modelos Pydantic en el contrato de salida. Nada prescribe un layout para el proyecto, no existe un entorno de ejecución separado ni un desvío mediante MCP. Escribes funciones y modelos, construyes un Agent y se lo pasas a Runner.run. El objetivo es lograr la distancia más corta posible entre una función de Python y un agente funcional.
Su mejor truco es el viaje de ida y vuelta con formato estructurado. Si especificas output_type=YourModel, lo que se devuelve es un objeto Python validado y no un bloque de datos que esperes que sea JSON; la SDK escribe la solicitud, analiza la respuesta, la valida y vuelve al modelo cuando la estructura no es correcta. Los esquemas que se obtienen gratuitamente a partir de las indicaciones de tipo, sumados a un panel de seguimiento sin costo en el propio backend de OpenAI, convierten esto en la forma más rápida de obtener un agente estructurado y observable. Cuando la tarea es “toma esta entrada y dame un resultado con formato estructurado”, ningún otro kit es tan directo.
Las herramientas no requieren en absoluto la creación de esquemas:
@function_tool
async def check_sfa_additive(additive: str, e_number_hint: str = "") -> str:
"""Check whether a food additive is permitted by the Singapore Food Agency.
Looks the additive up in the full SFA permitted-additives list (parsed from
the official SFA PDF). Accepts either an E-number or a plain-English name as
printed on a Singapore label.
Args:
additive: An E-number ('E211', 'e211', 'en:e211') OR an additive name
('Sodium Benzoate', 'Soy Lecithin', 'MSG').
e_number_hint: Optional. If you pass a name and also know its E/INS
number, supply it here. If the name isn't recognised but the number
is, the mapping is REMEMBERED so the name resolves directly next time. """
...
El decorador lee las indicaciones de tipo y la sección Args de la documentación para crear el esquema JSON, por lo que no hay nada que mantener a mano.
Donde realmente da sus frutos es en el resultado. El veredicto se declara como un modelo Pydantic:
class NutritionVerdict(BaseModel):
reasoning: str # declared first: the model works out loud before committing
product_name: str
summary: str
green: list[Finding]
amber: list[Finding]
red: list[Finding]
recommendation: str
Con output_type=NutritionVerdict, la ejecución devuelve un objeto validado, sin necesidad de ningún paso de análisis ni de arriesgarse al azar. Es una buena técnica incluir primero reasoning: la salida estructurada funciona también como una zona de trabajo temporal, de modo que el modelo reflexiona en voz alta antes de llegar a conclusiones, y así se mantienen juntos el objeto organizado y la lógica detrás de él.
La conexión de los modelos y la entrada de imágenes son las más ordenadas de las tres:
client = AsyncOpenAI(base_url=BASE_URL, api_key=API_KEY)
model = OpenAIChatCompletionsModel(model=OPENAI_MODEL, openai_client=client)
agent = Agent(name="SG Nutrition Investigator", model=model,
instructions=INSTRUCTIONS, output_type=NutritionVerdict,
tools=[recall_product, check_sfa_additive, check_hcs, calculate_nutri_grade])
user_input = [{"role": "user", "content": [
{"type": "input_text", "text": "Investigate this nutrition label."},
{"type": "input_image", "image_url": _data_url(image_path)}, # base64 data URL
]}]
result = await Runner.run(agent, input=user_input)
verdict: NutritionVerdict = result.final_output
Se trata de un cliente AsyncOpenAI dirigido al proxy, envuelto en OpenAIChatCompletionsModel, con un elemento input_image que contiene la foto. Sin complicaciones.
Puntos a tener en cuenta. Por defecto, las llamadas a las herramientas se ejecutan en paralelo, por lo que el modelo inicia varias llamadas check_sfa_additive al mismo tiempo sin problemas. Las nuestras son asíncronas y no tienen efectos secundarios, lo cual es una ventaja. Una herramienta que modifique el estado compartido, mantenga una conexión no reentrante o dependa del orden de ejecución tendrá un comportamiento incorrecto, y los fallos aparecerán de forma intermitente y dependerán del momento, lo cual es lo peor. O bien se deben hacer las herramientas seguras para la concurrencia o se debe restringir al modelo.
El rastreo tiene sus propios inconvenientes. El panel de control es excelente, pero las trazas se envían al endpoint de ingestión propio de OpenAI, y detrás de un proxy de terceros la clave emitida por dicho proxy no puede autenticarse allí. Por lo tanto, al ejecutar set_tracing_disabled(True) tu mejor herramienta de observabilidad desaparece con una sola línea de código. Lejos de api.openai.com, debes recurrir a OpenTelemetry o a sistemas de registro desarrollados internamente. Hay otros problemas menores: detrás de un proxy se necesita OpenAIChatCompletionsModel, ya que la ruta predeterminada de la API Responses apunta a los endpoints de OpenAI y elegir la opción incorrecta genera errores confusos. El esquema de output_type debe cumplir con las reglas del modo estricto, por lo que los modelos con anidamiento profundo o muchas uniones pueden provocar intentos repetidos de validación; por esa razón Finding y NutritionVerdict permanecen sin estructura anidada. Y no vienen herramientas para usar la computadora en la caja, por lo que no hay sistema de archivos, shell ni navegación a menos que usted las agregue.
He aquí el resultado para el sobre de Milo:
sausheong openai-demo % uv run python main.py ../milo.jpeg
Investigating image: ../milo.jpeg
────────────────────────────────────────────────────────────
Product: Nestlé MILO 3 in 1 Activ-Go
Reasoning: I read the sachet as **Nestlé MILO 3 in 1 Activ-Go**. The front says “goodness of malt, milk & cocoa” and the back says “MILO Ionic Food Drink,” so this is a **powdered drink mix**, not a ready-to-drink beverage. Because it is not sold as a beverage with per-100ml nutrition for the prepared drink as consumed, I did **not** calculate Nutri-Grade; that tool is only for beverages.
I first checked whether this product had been investigated before. A prior record existed, and it broadly matched what I can read here: a sweetened powdered malt-cocoa-milk drink sachet with permitted phosphate additives, relatively high in sugar for a single sachet, and not found in the HCS database. I used that only as background and then re-read this label fresh.
From the nutrition panel, I can read:
- Serving size: **1 sachet (27g)**
- Servings per package: **1**
- Per 100g / per serving shown side by side
- **Total sugars: 50.1g per 100g; 13.8g per serving**
- **Saturated fat: 0.5g per 100g; 1.2g per serving**
This is a powder, so the relevant direct reading is per 100g, not per 100ml.
I can read the ingredient list as:
**Sugar, Protomalt™ Malt Extract (contains barley), skimmed milk/cow’s milk, cocoa, palm oil, minerals (dicalcium phosphate, disodium phosphate), fibre (pyrophosphate), resistant dextrin, flavourings, vitamins (C, B3, D, B6, B2, B12).**
For additives, I checked each identifiable additive individually where possible:
- **Dicalcium phosphate**: checked and found **permitted by SFA**. That lands in GREEN.
- **Disodium phosphate**: checked and found **permitted by SFA**. That also lands in GREEN.
- The label also contains the broader terms **“phosphates”** and **“pyrophosphate”/“fibre (pyrophosphate)”** in wording around ingredients, but the image does not clearly identify a specific additive code for the pyrophosphate entry, and the generic umbrella term “phosphates” is not reliable enough on its own. I therefore do not treat the generic term as confirmed permitted just from the broad word alone. I only rely on the specifically named phosphates I could check.
- **Flavourings** is too generic to identify a specific additive, so I could not check it individually.
- The vitamins and minerals are nutrients rather than red-flag food additives in the SFA additive sense.
I also checked for the **Healthier Choice Symbol** using the product name. It was **not found** in the HCS database.
Main nutritional takeaways from the label:
- Sugar is the **first ingredient**, which usually means it is the largest ingredient by weight.
- The product contains **50.1g sugar per 100g** powder, and **13.8g sugar per 27g sachet**. That is quite a lot for one small sachet drink.
- Saturated fat is **1.2g per sachet**, which is not extreme, but the bigger concern here is sugar.
So the overall picture is: the identifiable phosphate additives appear permitted, but nutritionally this is still a **sweetened convenience drink mix** rather than a lower-sugar everyday option.
Summary: This is a powdered MILO drink sachet, not a ready-to-drink beverage. The clearly identifiable phosphate additives on the label are permitted by SFA, but sugar is the first ingredient and the product contains 50.1g sugar per 100g, or 13.8g per 27g sachet. It was not found in the Healthier Choice Symbol database.
🟢 Dicalcium phosphate is permitted by SFA. [check_sfa_additive]
🟢 Disodium phosphate is permitted by SFA. [check_sfa_additive]
🟢 This is a powdered drink mix rather than a ready-to-drink beverage, so Nutri-Grade was not applicable here. [label]
🟡 Generic terms such as “flavourings” and the unclear “fibre (pyrophosphate)” wording are not specific enough to confirm exact additive identities from the image alone. [label]
🟡 The product was not found in the Healthier Choice Symbol database. [check_hcs]
🟡 Saturated fat is 1.2g per 27g sachet (0.5g per 100g listed on the label panel image). [label]
🔴 Sugar is the first ingredient on the label. [label]
🔴 Total sugars are 50.1g per 100g and 13.8g per 27g sachet, which is high for a small single-serve drink mix. [label]
Recommendation: Fine as an occasional sachet drink if convenience matters, but not the best everyday choice if you are trying to reduce sugar. If you drink MILO often, consider using plain MILO powder with your own milk and less sugar, or choose a lower-sugar version.
Elige esta opción cuando la tarea esté estructurada en pasos definidos, como la clasificación, extracción, puntuación o emisión de veredictos, en cualquier situación en la que un objeto estructurado sea más útil que el texto libre y desees utilizar la menor cantidad posible de código en Python. Los equipos a los que les gustan las indicaciones de tipo lo apreciarán, y gracias al backend de OpenAI, el seguimiento gratuito facilita la depuración de agentes con múltiples pasos.
Omite esta opción cuando tus herramientas no puedan ser seguras desde el punto de vista de la concurrencia y prefieras no lidiar con el funcionamiento por defecto en paralelo, cuando necesites capacidad de observabilidad pero estés detrás de una pasarela que no sea de OpenAI (el seguimiento desaparece), o cuando el agente deba utilizar herramientas del sistema de archivos y de la shell de forma inmediata, lo cual es el ámbito de Claude. Para varios agentes que cooperan bajo un flujo de control explícito, ADK es más adecuado, y ese es el tercer kit.
Google's Agent Development Kit (ADK)
De los tres, ADK es el que realmente funciona como un marco de trabajo, con una filosofía definida, mientras que los otros son más ligeros. Incluye agentes, sesiones, un Runner, un SessionService y flujos de trabajo multiagente explícitos a través de SequentialAgent, ParallelAgent y agentes en bucle. Un diseño de proyecto predefinido permite que sus herramientas localicen el agente correspondiente, y adk web abre una interfaz de chat local con un inspector de eventos. Las herramientas son funciones simples de Python documentadas con strings de documentación al estilo de Google, sin decoradores ni diccionarios de esquema, y el wrapper LiteLlm lo convierte en la opción más independiente del proveedor.
La filosofía es la arquitectura para sistemas y no para scripts. Cuando “el agente” en realidad se refiere a varios agentes, como un planificador que transfiere tareas a trabajadores, un flujo secuencial o una distribución paralela, ADK los ofrece como componentes componibles de primera clase, ahorrándote la necesidad de crear flujos de control manualmente. Las sesiones están integradas, la interfaz de depuración es la mejor del grupo y la capa de modelo independiente del proveedor evita que te ates a un solo vendedor. Está diseñado para evolucionar hasta convertirse en una aplicación compleja, con estado y multiagente.
Las definiciones de herramientas son las más sencillas aquí:
async def check_sfa_additive(additive: str, e_number_hint: str = "") -> dict:
"""Check whether a food additive is permitted under Singapore Food Agency rules.
Looks the additive up in the full SFA permitted-additives list (parsed from
the official SFA PDF). Accepts either an E-number or a plain-English name as
printed on a Singapore label.
Args:
additive: An E-number ('E211', 'e211', 'en:e211') OR an additive name
('Sodium Benzoate', 'Soy Lecithin', 'MSG').
e_number_hint: Optional E/INS number for when you pass a name. If the
name isn't recognised but the number is, the mapping is REMEMBERED
so the name resolves directly next time.
Returns:
dict with the input, resolved e_number/name, permitted status, schedule
and any consumer note. found=False if it is not in the SFA list.
"""
...
El esquema se construye a partir de esa documentación en estilo Google de Args y Returns, lo que significa que su calidad tiene una gran importancia. Una documentación vaga perjudica directamente la selección de herramientas del modelo, por lo que la documentación constituye el contrato real.
Para que adk web encuentre su root_agent, el proyecto necesita una estructura de paquetes:
adk-demo/
sg_nutrition_investigator/
__init__.py # from . import agent
agent.py # tools + root_agent
main.py # standalone Runner + InMemorySessionService
root_agent = LlmAgent(
name="sg_nutrition_investigator",
model=LiteLlm(model="openai/" + GEMINI_MODEL, api_base=BASE_URL, api_key=API_KEY),
instruction=INSTRUCTION,
tools=[FunctionTool(recall_product), FunctionTool(check_sfa_additive),
FunctionTool(check_hcs), FunctionTool(calculate_nutri_grade)],
)
# image input — the cleanest of the three:
message = types.Content(role="user", parts=[
types.Part(text="Investigate this nutrition label."),
types.Part.from_bytes(data=Path(image_path).read_bytes(), mime_type=mime),
])
El modelo se enruta a través del wrapper LiteLlm de ADK, donde el prefijo openai/ hace que litellm utilice el protocolo compatible con OpenAI para comunicarse con el proxy. Pasar imágenes mediante types.Part.from_bytes es la opción más adecuada de las tres, y adk web ofrece la mejor experiencia de desarrollo integrada en esta comparación.
Puntos a tener en cuenta. La mayor preocupación es el peso. Fuera de adk web, es necesario conectar un Runner, un SessionService además de IDs explícitos para la aplicación, el usuario y la sesión antes de que se envíe el primer mensaje, lo que implica muchos pasos adicionales frente a las pocas líneas que requieren los demás. El diseño también tiene gran importancia. La línea from . import agent en __init__.py es la forma en que adk web descubre a root_agent, y cualquier desviación de esta estructura interrumpe el proceso de detección. litellm genera advertencias por backends no utilizados (advertencias inofensivas como “bedrock” y “sagemaker: no module named botocore”), mientras que ADK añade una advertencia de esquema [EXPERIMENTAL] encima, lo que hace necesario silenciar ambas al importarlos. La salida estructurada también es más complicada, ya que response_schema de Gemini es más delicado al estar detrás de un proxy que output_type de OpenAI, por lo que esta demostración utiliza un veredicto basado en texto generado a partir de una instrucción. Justamente, eso depende tanto del proxy como de ADK. Un último punto: en la ruta LiteLlm más Gemini-flash, el agente narró su recuperación de información de forma menos detallada que los demás con la misma instrucción, lo que demuestra que el comportamiento depende del modelo y de la configuración, no solo del SDK.
La salida para el paquete Milo:
sausheong adk-demo % uv run python main.py ../milo.jpeg
Investigating image: ../milo.jpeg
────────────────────────────────────────────────────────────
→ tool: recall_product({'product_name': 'Nestle MILO 3 in 1'})
→ tool: check_sfa_additive({'e_number_hint': 'E341', 'additive': 'Dicalcium Phosphate'})
→ tool: check_sfa_additive({'additive': 'Disodium Phosphate', 'e_number_hint': 'E339'})
→ tool: check_sfa_additive({'additive': 'Ferric Pyrophosphate'})
→ tool: check_hcs({'product_name': 'Nestle MILO 3 in 1'})
→ tool: calculate_nutri_grade({'saturated_fat_per_100ml': 0.5, 'sugar_per_100ml': 6.1})
## Reasoning
We investigated the label of **Nestle MILO 3 in 1 (with Activ-Go)**. Here is our step-by-step analysis:
1. **Product Categorisation & Reconstitution**:
* This is a dry beverage powder sold in single-serving 27g sachets.
* The nutritional information panel provides two columns: "Per Serving 27g" (the dry sachet) and "Per 100ml*" (as reconstituted by adding 200ml of hot water).
* For the **Singapore Nutri-Grade** system, the grading is calculated based on the beverage *as reconstituted* according to the directions.
2. **Sugar & Saturated Fat Analysis (Per 100ml prepared)**:
* **Total Sugars**: **6.1g per 100ml** (of which 4.8g is Added Sugar).
* **Saturated Fat**: **0.5g per 100ml**.
* Feeding these values into the Nutri-Grade calculator yields **Nutri-Grade C** (due to the sugar content being > 5g/100ml). Grade C beverages are classified as "less healthy" and must display the Nutri-Grade label at retail.
3. **Ingredients & Additives Analysis**:
* **Sugar is the #1 Ingredient**: Refined sugar is listed first on the ingredients list, meaning it is the largest ingredient by weight in this packet—even ahead of malt extract and milk powder.
* **Additives**:
* **Dicalcium Phosphate** (E341(ii)) and **Disodium Phosphate** (E339(ii)) are SFA-approved food additives used as stabilizers and emulsifiers.
* **Ferric Pyrophosphate** is an SFA-approved mineral nutrient used to fortify the drink with Iron.
* **Healthier Choice Symbol (HCS)**: The packet displays the red HCS logo. This is because it is formulated to be "25% lower in sugar" *compared to regular malted beverages*. However, in absolute terms, it still carries enough sugar to land in Nutri-Grade C.
---
## Verdict
### 🟢 Green Findings
* **Fortified with Vitamins & Minerals** `[label]`: Contains B-vitamins (B2, B3, B6, B12), Vitamin C, Vitamin D, Calcium, and Iron (via *Ferric Pyrophosphate* `[check_sfa_additive]`).
* **Contains Soluble Fibre** `[label]`: Features *Resistant Dextrin*, a prebiotic soluble dietary fibre (1.1g per serving) that supports gut health.
* **SFA-Approved Additives** `[check_sfa_additive]`: Phosphates used for stabilizing and emulsifying (*Dicalcium Phosphate* and *Disodium Phosphate*) are fully permitted.
* **Healthier Choice Certified** `[check_hcs]`: Carries Singapore's HCS logo for having at least 25% less sugar than standard malted chocolate drinks.
### 🟡 Amber Findings
* **Nutri-Grade C** `[calculate_nutri_grade]`: When prepared as instructed, it gets a **Grade C** because of its **6.1g of sugar per 100ml**. Grade C drinks are in the "less healthy" bracket.
* **Contains Palm Oil** `[label]`: Palm oil is added to give a rich mouthfeel, contributing **1.2g of saturated fat per serving** (0.5g/100ml).
### 🔴 Red Findings
* **Sugar is the Main Ingredient** `[label]`: Sugar is listed as the very first ingredient on the label, which means there is more plain sugar by weight than malt extract (*PROTOMALT*) or milk powder.
* **High Sugar Load per Cup** `[label]`: A single small 27g sachet contains **13.8g of total sugar** (equivalent to about **3 teaspoons of sugar**), of which **10.8g** (approx. 2.5 teaspoons) is added refined sugar. Drinking multiple cups a day can quickly max out your recommended daily limit for added sugars.
Elige ADK cuando estés construyendo un sistema: múltiples agentes que cooperan, flujos de trabajo secuenciales, paralelos o en bucle de forma explícita, un estado de sesión real y la posibilidad de intercambiar modelos entre proveedores. El inspector adk web se paga por sí mismo durante el desarrollo, y la arquitectura compensa su costo inicial a medida que el proyecto crece.
Omite ADK cuando haya un único agente sencillo. El código base para ejecutar y gestionar sesiones, junto con el diseño prescrito, representan un gasto adicional del que nunca obtendrás compensación. Para obtener el resultado más rápido al escribir código, utiliza el SDK de OpenAI; y para un agente que utilice una computadora con herramientas integradas, elige el de Claude. ADK justifica su complejidad a gran escala, pero fuera de ese contexto es más un framework del necesario.
Ejecutar las demostraciones
Una vez creados los tres agentes, aquí está cómo ejecutarlos. Cada uno es un proyecto uv, y cada uno lee el archivo .env raíz que contiene la clave del proxy, la URL base y los nombres de los tres modelos:
cd openai-demo # or claude-demo, or adk-demo
uv run python main.py ../milo.jpeg
uv run python main.py ../hl.jpeg
Para regenerar la tabla SFA a partir del PDF, ejecute uv run --with pdfplumber python build_additives.py. Proporcione su propia foto de etiqueta indicando la ruta correspondiente. Las dos etiquetas incluidas son productos reales de Singapur y obtienen las calificaciones esperadas: la leche recibe la categoría Nutri-Grade B, mientras que Milo, en su forma en polvo, obtiene la categoría C una vez preparado.
Más allá de una sola prueba
Una demostración que realiza una sola pasada oculta las partes interesantes. Tres adiciones, idénticas en forma en todos los SDK, hacen que los agentes sean más realistas, y cada una de ellas revela algo sobre el SDK subyacente.
Datos reales. La verificación de aditivos no es un diccionario de juguete con siete entradas. build_additives.py analiza el PDF oficial de SFA, cuyas tres tablas utilizan tres diseños de columnas diferentes (una aventura en sí misma), para obtener aproximadamente 540 aditivos. Al cargarse, cada demostración crea un índice de nombres normalizado de modo que la etiqueta “monosodio glutamato” coincida con la forma formal del PDF, “Monosodium L-glutamate”. Un índice de números E/INS basado en el número principal permite que “500” siga relacionándose con “500(ii)”. Un mapa breve maneja términos coloquiales reales que faltan en el PDF, como “MSG” y “vitamina C”. Todo lo que realmente no está en la lista se muestra como “no está en la lista” y nunca como un falso “permitido”.
Memoria y aprendizaje. Estos dos aspectos se entienden mejor juntos:
- Explicar. Cada agente muestra su razonamiento, ya sea a través de un campo
reasoningescrito a mano en el caso de OpenAI o mediante una sección inicial## Reasoningen Claude y ADK. - Recordar. Primero se ejecuta una herramienta
recall_productque muestra cualquier veredicto anterior sobre el mismo producto, y cada ejecución almacena el suyo propio. Si se escanea una etiqueta dos veces, la segunda pasada la compara con la conclusión anterior. - Aprender. Si una etiqueta muestra un nombre que la tabla no reconoce y el modelo proporciona el número correcto a través de
e_number_hint, la asociación nombre-número se guarda en un pequeño archivoagent_memory.json. En la siguiente ejecución, ese nombre se identifica sin necesidad de consultar al modelo.
Eso es aprender a nivel del sistema. Las ponderaciones nunca cambian, pero el agente perfecciona sus propios datos y herramientas a partir de la experiencia. Se trata de un tipo de aprendizaje práctico y al alcance, a diferencia del ajuste fino, que ninguno de estos kits realiza en tiempo de ejecución. Cada uno incluye una capa de sesión nativa que se utilizaría para la memoria de producción (el SQLiteSession de OpenAI, el almacén de sesiones de Claude, el SessionService de ADK), y el archivo JSON simplemente hace que la demostración sea portátil y fácil de inspeccionar.
Cuanto más etiquetas lee, mejor se vuelve. Una foto cuyos nombres de ingredientes el modelo convierte en números le enseña al sistema de búsqueda un alias nuevo, y cada producto analizado se convierte en algo que la siguiente ejecución puede recordar. Cuando aparece por primera vez “fosfato disódico”, la ejecución depende del modelo para convertirlo en E339; después de eso, la tabla determinística ya lo sabe, por lo que es más rápida, económica y reproducible sin necesidad de llamar al modelo. A lo largo de una estantería llena de productos, agent_memory.json se convierte en un diccionario de alias y en un historial de conclusiones que el agente mismo ha compilado. Si se dirige una flota a un único archivo de memoria compartida, un nombre aprendido por un agente se vuelve conocido por todos. El modelo permanece inmutable, pero el agente no.
Un último detalle de las pruebas reales: al pedirles que asignaran un número E junto con su nombre, los tres modelos cumplieron de manera fiable, convirtiendo “Fosfato de Disonio” en E339 sin necesidad de ayuda. Eso es el bucle haciendo su trabajo, con el conocimiento general del modelo cubriendo lo que la búsqueda determinística omite y el sistema recordando el resultado.
Quién proporciona qué
Una vez creados, probados y ampliados los tres agentes, se puede organizar todo el sistema indicando qué proporcionó realmente cada SDK en comparación con lo que tuvo que construirse a mano. Imagine una cuadrícula donde el verde indica que el SDK lo proporciona, el ámbar significa que es parcial y el rojo indica que se construye por uno mismo. Los componentes se dividen en las mismas tres capas: Desarrollo para crear el agente, Tiempo de ejecución para operar uno y Plano de control para gestionar una flota. Los SDKs se encuentran en la primera capa.
Si observas la capa de Desarrollo, casi todo aparece en color verde; los kits coinciden casi por completo en cuanto a la creación de un agente. Pero si miras las dos capas inferiores, el patrón cambia: unas pocas celdas verdes o ámbar en los bordes (los flujos de trabajo y la evaluación de ADK, y el seguimiento de OpenAI) se encuentran sobre un mar rojo compartido por los tres. Ese contraste es el hallazgo real. En cuanto a la creación del agente, están de acuerdo, pero la ejecución y gestión de este queda a tu cargo.
Las carencias en los SDK
La capa de Desarrollo es aquello que los SDK facilitan. La capa de Ejecución y la de Control prácticamente faltan en ellos, y, en realidad, eso no es lo que los SDK están diseñados para ofrecer.
Comience con las deficiencias que surgieron únicamente durante la compilación de prueba. Una de ellas es la puerta de enlace de herramientas, que la capa de ejecución distingue de una simple invocación de herramienta. El mecanismo de integración llama a su herramienta de manera fiable, pero no hace nada para garantizar su fiabilidad. Por ejemplo, la API HCS de data.gov.sg a veces responde con 503 junto con un cuerpo HTML, y un uso ingenuo de response.json() hacía que toda la ejecución fallara (una lección aprendida a costa de dificultades). Un helper _safe_json, tiempos de espera de diez segundos y soluciones alternativas como “no está en la lista” solucionaron el problema, de modo que un servicio inestable se convirtió en un problema detectable y ya no detenía al agente. Las reintentos, los retrasos y la gestión de errores dentro de las herramientas son responsabilidad suya, tareas precisamente para las cuales existe una puerta de enlace.
Otra diferencia es la memoria gestionada. Las tres incluyen una capa de sesión, que representa el estado de la conversación en un nodo, pero no es lo mismo que una aplicación que almacena datos a los que pueda acceder todo el conjunto de agentes. El archivo agent_memory.json generado aquí (los alias aprendidos, el historial de recall_product, las actualizaciones que mejoran al agente con cada etiqueta) representa la lógica específica del dominio, y el hecho de que “cuanto más se ejecute, mejor será” no es algo que proporcione un framework. Lo mismo ocurre con el uso de datos reales: el análisis del PDF de SFA para obtener más de 540 elementos estructurados, la creación del índice normalizado y la búsqueda por número base: nada de esto forma parte de las funciones de un SDK, y proporcionar datos reales a un agente es un proceso que se configura manualmente, no un botón que se pulsa.
La seguridad en la concurrencia para las herramientas es peor que simplemente no estar gestionada, ya que el propio framework genera el riesgo. Por defecto, el SDK de OpenAI ejecuta llamadas a herramientas independientes en paralelo, y corresponderá a usted hacer que sus herramientas sean seguras en esas condiciones. Otro aspecto cotidiano: la generación del veredicto y su guardado en cualquier lugar son tareas de código de aplicación habitual al que ningún SDK interviene.
Al analizar la capa de tiempo de ejecución, el agente primero necesita ejecución alojada, una forma de ser invocado desde algo distinto a una sesión de terminal, con múltiples ejecuciones en curso y capacidad de escalado en segundo plano. Necesita también una identidad de carga de trabajo, un credencial que se presenta a las herramientas para que las llamadas se realicen como una carga de trabajo con nombre y no a través de una clave API oculta en una variable de entorno. Además, requiere bucles duraderos y reanudables, de modo que una interrupción a mitad de proceso continúe en lugar de reiniciar toda la ejecución. Ninguno de estos elementos existe en un script en una computadora portátil, ni es algo que el SDK afirme ofrecer. El tiempo de ejecución también debe escalar los agentes a medida que aumenta la carga.
El plano de control es donde los tres casi no hacen nada. Evaluación es el primer caso: “se ejecutó sin errores” no significa “estuvo correcto”, y medir adecuadamente la calidad del agente depende en gran medida de usted. ADK es la excepción parcial gracias a su marco de evaluación, mientras que los otros dos dejan la cuestión abierta.
A continuación, límites de seguridad y defensa contra inyección de comandos. Cada resultado de la herramienta es una entrada no confiable que vuelve al modelo, y una foto de etiqueta o una respuesta de API podría contener una instrucción como “ignora tus reglas”. La cobertura es desigual y nada es automático, por lo que el modelo de amenazas le corresponde a usted.
Luego está la identidad del agente con delimitación de permisos. Los kits permiten establecer una personaaje, pero no indican cuál es el estatus del agente como principal del directorio ni qué puede acceder, por lo que corresponderá a usted vincularlo a un propietario real y limitar su alcance.
Conectarse a un servidor MCP es otra historia, ya que los tres ofrecen un cliente para ello. Sin embargo, un registro de herramientas y MCP, que cataloga y versiona las herramientas y gestiona un conjunto de servidores MCP con funciones de detección, agrupamiento, credenciales por servidor y verificaciones de estado, nuevamente queda a su cargo.
El mismo problema aparece un nivel más alto en el registro de agentes, donde los agentes se publican para que otros puedan descubrirlos y llamarlos en tiempo de ejecución, en lugar de mediante conexiones predefinidas. ADK es una excepción parcial, ya que soporta comunicación A2A y tarjetas de agente, mientras que los otros dos dejan esto en sus manos.
Finalmente está la gobernanza de costos y cuotas, un registro de auditoría que demuestra qué hizo la flota, la gobernanza del almacén de datos compartido y una versiónación oportuna. El prompt del sistema es simplemente una cadena de texto de tu propiedad y el modelo es estocástico, por lo que fijar, comparar y revertir prompts es una disciplina que debes aplicar tú mismo.
Nada de esto es una crítica. Detenerse en el bucle del agente y en los componentes relacionados es un punto razonable para establecer un límite. Sin embargo, esto significa que elegir un SDK resuelve quizás la mitad de los requisitos de un agente en producción.
¿Entonces, qué SDK deberías usar?
Volviendo a los kits en sí, aquí está una descripción de cada uno en una sola oración. Elige el Claude Agent SDK cuando el agente deba operar una computadora, manejar archivos, la shell, código y navegar por Internet; es el entorno de ejecución más maduro, se paga mediante esquemas manuales, no permite salida tipada ni conexión mediante variables de entorno. Elige el OpenAI Agents SDK para entradas y salidas estructuradas con un uso mínimo de Python; ofrece el tiempo de respuesta más rápido con datos tipados, siempre y cuando aceptes la configuración predeterminada de herramientas en paralelo y la pérdida de seguimiento detrás de un proxy. Elige Google ADK cuando varios agentes deben cooperar a través de flujos de trabajo explícitos respaldados por sesiones reales; su interfaz de desarrollo es la más robusta y su selección de modelos la más amplia, aunque un agente individual sencillo lo encontrará excesivamente complejo.
A continuación, se trata de herramientas diferentes y no de tres ejecutores en una misma escala. Se puede implementar el mismo agente en todas ellas, ya que el núcleo fundamental, el ciclo de razonamiento sobre las herramientas, es común a todas. Las diferencias radican en la estructura adicional que rodea ese núcleo, y esa es la que debe determinar su elección.
Lo que reveló construirlo tres veces
Construir el agente tres veces hizo evidente una conclusión: el ciclo del agente es un problema ya resuelto y estándar, y los tres kits lo implementan de manera eficaz. Las verdaderas diferencias se encuentran en la estructura adicional, como los esquemas escritos a mano frente a los inferidos, el texto libre frente a la salida tipada, y un entorno de ejecución integrado frente a un cliente ligero o a un framework completo. Estas son cuestiones de adecuación y no de calidad. Adecúe el SDK a la tarea, ya sea Claude para operar una computadora, OpenAI para obtener resultados tipados o ADK para sistemas multiagente; cualquiera de los tres le servirá bien.
La lección fundamental es la delimitación, y las tres capas muestran dónde se encuentra. Los kits poseen la capa de Desarrollo y te la entregan en una tarde. La capa de Ejecución y la Plana de Control, que implica operar un agente en tiempo real y gestionar una flota, son tuyas, y ahí se concentra el mayor esfuerzo de ingeniería. El investigador solo resultó útil después de añadir elementos que ningún SDK proporciona: la tabla SFA analizada como punto de referencia, la herramienta para manejar fallos que normalmente estaría a cargo de una pasarela y la memoria que la perfecciona con cada etiqueta. Por lo tanto, elige un SDK según su adecuación para la capa de Desarrollo, y luego presupuesta para las dos capas siguientes.
Detrás de esta historia se esconde otra más amplia, ubicada en esas capas inferiores. Ahora que la creación de agentes individuales se ha convertido en algo común, la competencia se ha desplazado a quién gestiona a esos agentes y quién los regula. Allí están surgiendo nuevos productos: entornos de ejecución como AWS Bedrock AgentCore y el Gemini Enterprise Agent Runtime de Google, que pueden albergar cualquier cosa que generen los tres kits mencionados, además de planes de control como Agent 365 de Microsoft y Google Enterprise, que registran, identifican y supervisan flotas completas. Prácticamente, venden línea por línea las componentes esenciales de esa estructura. El problema es que todas las nubes desean contar con las tres capas, por lo que el SDK que elijas hoy te inclinará hacia el entorno de ejecución y los sistemas de gobierno de un proveedor en particular mañana.
Leer más
- Construyendo agentes autocorregibles con LangGraph: Cinco consejos prácticos — Crea un agente de búsqueda de LangGraph que intente nuevamente en caso de fallo, con explicaciones sobre estado, aristas condicionales, puntos de control, aprobación humana y nodos pequeños, todo ello mediante código funcional.
- 10 frameworks de agentes de IA que debes conocer en 2026: LangGraph, CrewAI y más — Diez frameworks de agentes clasificados según cómo coordinan a los agentes, con sus ventajas y desventajas, usos óptimos y una guía para elegir uno adecuado para tu equipo.