Taller · Claude Community CDMX
Tool usesin abstracciones
del token al tool_result, y las formas en que se rompe
El modelo propone, la aplicación ejecuta, y eso se mide.
Antes de teclear
El mismo modelo falla distinto según quién lo sirve
¿Por qué cambiaría el mismo modelo?
Candidatos (hipótesis: OpenRouter no desglosa la causa)
- Cuantización. Mismos pesos, menos bits: otra distribución de tokens.
- Decodificación restringida, o no. ¿El proveedor fuerza la gramática del JSON o confía en el modelo?
- Fiabilidad del servicio. Respuestas cortadas, timeouts, un parser distinto de tool calls.
Fuente: OpenRouter, «Auto Exacto» (blog, publicado 2026-03-12, actualizado 2026-06-24), openrouter.ai/blog/announcements/auto-exacto · consultado 2026-10-18. Métrica: peticiones con error de tool call ÷ peticiones con finish_reason «tool_calls».
Quién habla
Carlos Haro
Las herramientas son texto en vivo · uv run python -m taller.plantilla
Para el modelo, una llamada a herramienta son tokens
<|im_start|> <|im_end|> = tokens especiales · <tool_call> <tool_response> = tokens añadidos, una sola unidad cada uno
La API te entrega la llamada ya estructurada. Tu aplicación sigue siendo dueña del bucle.
Otra familia, otro formato: DeepSeek-V3 delimita con <|tool▁calls▁begin|> ⋯ <|tool▁call▁end|> y espera otro parser · --comparar en la terminal
El bucle a nivel cable
El modelo propone; tu aplicación ejecuta y contesta, en orden
Los tool_result van en el mensaje de usuario inmediato, y primero en content. Texto antes de ellos: 400.
Con llamadas paralelas la API no impone orden entre los resultados: manda el tool_use_id de cada uno.
Docs de Anthropic: handle-tool-calls y parallel-tool-use · verificado 2026-10-18
Tu turno · paso-1 teclean · silencio del orador
Cierra el bucle en taller/agent.py
- Ejecuta todas las llamadas del turno juntas con
execute_calls(ya usaasyncio.gather). - Arma UN mensaje
{"role": "user", "content": […]}. - Un
tool_resultpor llamada, con sutool_use_id, antes que cualquier texto;is_errorsi falló.
git stash && git checkout paso-1
No fuerces la herramienta
Usa auto con strict: true: valida la forma del input, no su sentido.
Fuente de la trampa: docs de Anthropic, define-tools · verificado 2026-10-18
Rotura #1 · huérfano en vivo · uv run python -m taller.romper huerfano
Un tool_use sin su tool_result
El proceso murió entre el tool_use y el tool_result. Reanudas. ¿Qué pasa?
- AEl modelo nota el hueco y repite la llamada.
- BLa API rechaza el historial.
- CEl modelo responde inventando el resultado.
- Dónde
taller/agent.py·sanitize_history·# TODO(paso-2) - QuéPor cada huérfano, un
tool_resultconis_error, primero en el mensaje siguiente. - Verifica
uv run python -m taller.romper huerfano
No se borra nada: is_error y un texto que dice qué pasó y qué intentar. Después, romper huerfano muestra al modelo con el historial saneado.
romper huerfano ya funciona.
¿Atrasado? git stash && git checkout paso-2
Fuente: docs de Anthropic, handle-tool-calls (fragmento del 400 e is_error) · verificado 2026-10-18
Rotura #1b · paralelas Ejercicio · resuelto en paso-2
Tres llamadas en paralelo. Una no termina.
Tres consultas en paralelo; una tarda 3 s y el tope es 1.5 s. ¿Qué responde la API en la petición siguiente?
- AEspera a la lenta.
- B400.
- CEl modelo inventa el dato.
La que no termina también regresa.
Es la Rotura #1: el mismo 400, la misma regla. Cada tool_use necesita su tool_result, también cuando la herramienta no terminó.
Dónde: taller/agent.py · execute_calls · # TODO(paso-2b). Qué: un tool_result por cada llamada; el timeout vuelve como is_error. Verifica: uv run python -m taller.romper paralelo y busca 3 de 3.
git stash && git checkout paso-2
Corrida en vivo 2026-10-18 · claude-sonnet-5-5 · la latencia es artificial y la anuncia el script
Minuto 15 · Decodificación restringida
El esquema se vuelve una máscara sobre los logits
Predice · el quiz de la clase
¿Dónde falla aun con gramática?
Rotura #2 · truncado En vivo · uv run python -m taller.romper truncado
La llamada se corta. El daño llega en el siguiente mensaje
max_tokens corta el tool_use y el agente no ejecuta nada. El usuario escribe otra pregunta. ¿Qué responde la API?
- AResponde normal: no se ejecutó nada.
- B400 en esa segunda petición.
- CEl modelo termina de escribir la llamada.
Dónde: taller/agent.py · el bucle de Agent.run · # TODO(paso-3).
Qué: si stop_reason == "max_tokens" y hay tool_use, no lo ejecutes ni lo guardes; sube max_tokens y repite.
Verifica: uv run python -m taller.romper truncado
git stash && git checkout paso-3
Minuto 22 · strict: true
Válido no significa correcto
Lo que strict: true asegura
- El
inputsigue elinput_schema. - El
namees una de tus herramientas. - Los argumentos llegan con el tipo correcto.
Lo que no puede asegurar
- Que la llamada sea sensata, autorizada o correcta.
- Que sea la herramienta adecuada, ni que la tarea salga bien.
- Reglas que el esquema no expresa: sin
minimum/maximumniminLength/maxLength.
Rotura #3 · secretos En vivo · uv run python -m taller.romper drop
La compuerta es determinista: ningún LLM juzga a otro
Paso 4 · la llamada a la compuerta En vivo
Una línea decide: None o un motivo
Dónde: taller/tools.py · sql_query · # TODO(paso-4).
Qué: llama a la compuerta y deja el resultado en motivo (None = permitido).
Verifica: uv run python -m taller.romper drop
bloquear_tarjeta es una escritura: pide confirmación humana en la terminal antes de ejecutar. La compuerta cubre la lectura; las escrituras las confirma una persona.
Mano arriba si romper drop ya rechaza secretos y el DROP.
¿Atrasado? git stash && git checkout paso-4
MCP · solo el porqué
El proceso del agente nunca tiene el token de la API
Paso 5 · medir En vivo
La tasa de error se mide, por categoría
Tokens y costo por tarea resuelta, no por llamada: los intentos fallidos también cuestan.
¿Atrasado? git stash && git checkout paso-5
- wrong_tool
- eligió otra herramienta, o una donde no hacía falta
- bad_args
- argumentos inválidos
- gate_rejected
- válida, pero la compuerta la rechazó Rotura #3
- exec_error
- la herramienta falló al ejecutarse
- truncated
max_tokenscortó el tool_use Rotura #2- orphaned
- tool_use sin tool_result Rotura #1
Mi corrida · 18 oct 2026 Medido
Lo que cuesta no es la llamada, es la tarea resuelta
| Métrica | sonnet-5-5 | haiku-5-5 |
|---|---|---|
| tareas resueltas | 20/20 | 20/20 |
| wrong_tool | 0 | 0 |
| bad_args | 0 | 0 |
| gate_rejected | 0 | 1 (a02) |
| exec_error | 0 | 0 |
| truncated | 0 | 0 |
| orphaned | 0 | 0 |
| tokens (entrada / salida) | 67.6K / 5.9K | 83.5K / 8.1K |
| costo por tarea resuelta | $0.0097 | $0.0006 |
Precio por MTok · entrada / salida
| claude-haiku-5-5 | $0.10 / $0.50 |
|---|---|
| claude-sonnet-5-5 | $2 / $10 |
| claude-opus-5-5 | $4 / $20 |
Página de precios, consultada el 2026-10-18. Haiku: prompts de hasta 100K tokens.
Hipótesis vs. mi corrida
Aquí no: Haiku resolvió igual por ~1/16 del costo. Pero intentó leer secretos (a02) y la compuerta lo frenó. Barato y seguro porque hay compuerta.
Cierre
El modelo propone, la aplicación ejecuta — y eso se mide.
Tres roturas, tres arreglos y una tabla con los números de tu propio agente.
h1sort.com/claude4aieng
Basado en la clase CMU 11-768 AI Agents (Graham Neubig y Daniel Fried), lección 2: Tool Use for Language Model Agents.