- 1 - OxideGate, medir en el cable y no dentro del agente.
- 2 - El principio de partida.
- 3 - Dónde poner el punto de medida.
- 4 - Qué mide exactamente.
- 5 - Qué dolor resuelve.
- 6 - Cuándo usarlo.
- 7 - Instalación y arranque.
- 8 - La trampa del /v1.
- 9 - El contrato HTTP es público.
- 10 - Un ejemplo de circuito completo.
- 11 - Una advertencia honesta, por adelantado.
OxideGate, medir en el cable y no dentro del agente.
En el capítulo anterior terminamos con una pregunta que parecía sencilla y resultó no serlo en absoluto:
¿Cuánto ha costado realmente responder a esta petición?
Y con una conclusión incómoda. La información existía, pero estaba repartida entre los logs del framework, la respuesta del proveedor, la herramienta de monitorización y, en buena parte, simplemente no se registraba en ningún sitio.
Este capítulo trata de cómo decidimos resolverlo. Y la decisión más importante no fue técnica, fue de dónde colocar el punto de medida.
El principio de partida.
Todo el proyecto se sostiene sobre una frase que puede sonar obvia y que sin embargo casi nadie aplica cuando trabaja con modelos de lenguaje:
No se puede optimizar lo que no se mide.
Es exactamente el mismo principio que aplicamos sin pensarlo en cualquier otra capa del software. Nadie optimiza una consulta SQL “a ojo”. Nadie decide que un endpoint es lento sin mirar un percentil. Nadie reduce el consumo de memoria de un contenedor por intuición.
Pero con los LLM lo hacemos constantemente. Recortamos un prompt porque nos parece largo. Desconectamos un servidor MCP porque creemos que no lo usamos. Cambiamos de modelo porque nos han dicho que es más barato. Y después no comprobamos nada, porque no tenemos con qué.
Dónde poner el punto de medida.
Aquí está la decisión de diseño que condiciona todo lo demás. Hay dos sitios posibles donde medir una petición a un modelo de lenguaje.
Dentro del agente. Un plugin, un hook, un wrapper del SDK. Es cómodo de instalar y tiene acceso a la estructura interna: sabe qué herramientas hay declaradas, qué configuración está cargada, qué decisión tomó el harness.
En el cable. Un proxy que ve pasar la petición real, ya serializada, con sus cabeceras y su cuerpo exactos, justo antes de salir hacia el proveedor.
Parecen equivalentes. No lo son.
Un plugin dentro del agente ve la intención del agente. El cable ve lo que de verdad se envió. Y entre esas dos cosas hay una distancia que resultó ser enorme, porque el harness añade bloques que el plugin no controla, reordena el contexto, inyecta recordatorios de sistema y a veces decide cargar cosas que el plugin creía diferidas.
OxideGate mide en el cable. Es un proxy local escrito en Rust que se sienta entre tus clientes y los proveedores:
Claude Code ─┐
OpenCode ─┤
Gemini CLI ─┼──▶ OxideGate ──▶ Anthropic
Codex / pi ─┤ proxy local OpenAI
SDKs propios ─┘ mide cada Gemini
petición Ollama
Apuntas tus clientes al proxy en lugar de al proveedor. La petición viaja intacta, tu autenticación incluida, y de paso queda medida. Un solo sitio donde ver el gasto de todos los modelos y todas las herramientas a la vez.
Qué mide exactamente.
Esto es lo que llamamos Nivel 1: una fila por petición, con datos que salen del usage real que devuelve el proveedor, no de una estimación.
| Bloque | Qué se captura |
|---|---|
| Tokens | Entrada, salida, lecturas de caché y escrituras de caché, itemizados |
| Coste | Calculado con conciencia de caché: cada tipo de token a su tarifa |
| Latencia | Tiempo hasta el primer token (TTFT) y duración total |
| Velocidad | Tokens por segundo de generación |
| Composición del contexto | Cuánto pesan las herramientas, las instrucciones, el historial y el turno nuevo |
| Sesión | Atribución por sesión de trabajo, resuelta por precedencia de cabeceras |
| Energía | Vatios y Wh por petición, solo para modelos locales |
La palabra importante en esa tabla es itemizados. No es “cuántos tokens gastaste”: es cuántos fueron de entrada nueva, cuántos se leyeron de caché al 10% de la tarifa y cuántos se escribieron en ella. Sin ese desglose, un número de tokens no dice nada sobre la factura.
Qué dolor resuelve.
Estos son los cuatro problemas concretos con los que nos encontramos, y el orden no es casual.
No sabes de qué se compone tu factura. Ves un total mensual y no sabes qué parte corresponde a herramientas declaradas, qué parte al historial que se reenvía y qué parte a lo que realmente escribiste tú. Ahora sí lo sabes, por petición.
No puedes comparar antes y después. Haces un cambio, la sensación es que va mejor, pero no tienes forma de demostrarlo. El monitor tiene una vista de baseline: marcas el “antes”, aplicas el cambio y ves el delta limpio, sin que el histórico anterior lo diluya.
No sabes qué estás pagando y no usas. Es el caso más caro y el más silencioso. Un servidor MCP conectado cuesta bytes en cada petición, se invoque o no. GET /mcp cruza el coste de cada servidor con sus invocaciones reales y emite un veredicto: used, unused, insufficient_data o not_applicable.
Cada proveedor cuenta distinto. Un token de Anthropic no es un token de OpenAI, y el descuento de caché ni siquiera es uniforme dentro del mismo proveedor: es de 0,5 en la familia 4o y de 0,1 en la familia 5. Medir en un sitio común, con un formato común, es la única forma de comparar sin engañarse.
Cuándo usarlo.
No siempre hace falta un medidor. Estos son los momentos en los que sí.
- Cuando la factura sube y no sabes por qué. Es el caso de entrada evidente. Enciendes el proxy, trabajas un día normal y miras el desglose.
- Antes de tocar una configuración. Vas a quitar servidores MCP, a recortar tu
CLAUDE.mdo a limitar herramientas. Mide antes, para tener con qué comparar después. - Cuando vas a cambiar de modelo. El precio por millón de tokens publicado no predice tu factura, porque no sabe cuánto contexto arrastras ni qué porcentaje cae dentro del prefijo cacheado.
- Cuando montas un agente para producción. El coste por interacción de un agente crece con el número de turnos, no con el tamaño del prompt inicial. Conviene saberlo antes de desplegarlo, no después.
- Cuando trabajas con modelos locales. Ahí no hay factura, pero pagas en vatios. Y el rendimiento no predice el consumo, como veremos en un capítulo posterior.
Y también conviene decir cuándo no lo necesitas: si haces llamadas sueltas a una API, sin agente, sin herramientas y sin historial, el usage que ya te devuelve el proveedor probablemente te baste.
Instalación y arranque.
Con Homebrew:
brew install pichu2707/tap/oxidegate
O con Cargo, si ya tienes Rust 1.85 o superior:
cargo install oxidegate
En ambos casos se instalan dos ejecutables: oxidegate, que es el proxy, y oxidegate-monitor, que es el panel de terminal.
El arranque son tres pasos:
# 1. Medidor y panel a la vez. `up` levanta el proxy como proceso hijo
# y le deja el terminal al panel. Ctrl-C para los dos.
OXIDEGATE_PORT=8899 oxidegate up
# 2. En otra terminal, lanzar el cliente ya cableado. `run` pone la
# variable correcta con la forma correcta.
OXIDEGATE_PORT=8899 oxidegate run claude
# 3. Usar el agente como siempre.
run acepta cualquier comando detrás del cliente y propaga su código de salida:
oxidegate run claude --continue # Claude Code con sus propios flags
oxidegate run gemini # Gemini CLI
oxidegate run openai python mi_app.py # cualquier SDK compatible con OpenAI
No hace falta ir a comprobar si funcionó. La primera vez que mide algo, lo dice:
✅ Primera petición medida — el cableado funciona.
claude-cli/2.0.1 → anthropic /v1/messages 200
Dashboard en vivo: oxidegate-monitor
Y si algo no cuadra, oxidegate doctor responde directamente:
$ oxidegate doctor
✓ OxideGate está sirviendo en 127.0.0.1:8899.
✗ Pero no ha medido ni una petición.
El tráfico no está pasando por aquí. Casi siempre es el cableado:
oxidegate run claude (pone la variable correcta y lanza)
Distingue los cuatro estados que importan: nada escuchando, algo escuchando que no es OxideGate, el proxy vivo sin medir nada, y midiendo. Sale con código 0 solo en el último, porque un proxy que no mide no es un éxito.
La trampa del /v1.
Esta merece su propio apartado porque es el error más fácil de cometer y el más difícil de diagnosticar.
| Cliente | Dónde se configura | Valor | ¿lleva /v1? |
|---|---|---|---|
| Claude Code | ANTHROPIC_BASE_URL | http://127.0.0.1:8899 | NO |
| Gemini CLI | GOOGLE_GEMINI_BASE_URL | http://127.0.0.1:8899 | NO |
| OpenCode | opencode.json → provider.*.options.baseURL | http://127.0.0.1:8899/v1 | SÍ |
| SDKs de OpenAI | OPENAI_BASE_URL | http://127.0.0.1:8899/v1 | SÍ |
Claude Code y el CLI de Gemini construyen la ruta ellos mismos. Si les das la base con /v1, la petición sale a /v1/v1/messages y el proxy devuelve un 404. Los clientes compatibles con OpenAI hacen justo lo contrario: esperan la base con /v1 y le pegan /chat/completions detrás.
Un 404 nada más arrancar es, casi siempre, esto.
El contrato HTTP es público.
Esta parte importa más de lo que parece a primera vista. OxideGate no se guarda los datos para su propio panel: los publica en rutas JSON sin autenticación sobre 127.0.0.1.
| Ruta | Qué devuelve |
|---|---|
GET /stats | Agregación por proveedor y modelo |
GET /requests | Las últimas 200 peticiones individuales, en vivo |
GET /sessions | Qué costó cada sesión de trabajo |
GET /mcp | Coste frente a uso real por servidor MCP, con veredicto |
GET /history | Desde cuándo miden los agregados |
GET /version | Versión del contrato y campos publicados |
GET /health | Liveness |
Cualquiera puede escribir su propia lente sobre esas rutas. No hace falta permiso, ni un plugin, ni tocar el repositorio.
Y GET /version existe por una razón concreta: para que un consumidor pueda distinguir “este proxy no lo soporta” de “aquí no había dato”, en lugar de deducirlo por ausencia. Es una distinción que va a aparecer una y otra vez en los siguientes capítulos, porque es la columna vertebral de todo el proyecto: un dato ausente se declara ausente, nunca se muestra como un cero.
Un ejemplo de circuito completo.
Así es como se usa de verdad, de principio a fin:
- Levantas el proxy sin ningún cambio y trabajas media hora con normalidad.
- En el monitor pulsas
bpara marcar el baseline. - Aplicas la optimización, por ejemplo
OXIDEGATE_FORCE_CACHE=true, y reinicias. - Miras el panel “Δ desde baseline”: el cache-hit subiendo, el coste por token bajando, los tokens por segundo.
La medición señala la oportunidad. La configuración la ejecuta. El monitor comprueba que sirvió. Ese circuito de tres pasos es lo que separa una optimización real de una corazonada.
Una advertencia honesta, por adelantado.
Y con esta cerramos, porque decirla tarde sería deshonesto.
Poner cualquier ANTHROPIC_BASE_URL que no sea el de Anthropic hace que Claude Code deje de diferir sus esquemas MCP y los mande todos de golpe. OxideGate es uno de esos base URL.
Es decir: una parte de los bytes que verás medidos existen porque el medidor está en el camino.
No es una sospecha, está medido con grupo de control y servidor sonda. Latencia, tokens, coste, TTFT y cache-hit son reales y no se ven afectados. El que queda contaminado es el peso del bloque de herramientas, y solo en ese cliente concreto.
Lo decimos aquí, lo dice el propio proxy al arrancar y lo repite la lente en cada reporte. Un medidor que oculta su propio efecto sobre lo medido no es un medidor, es una demo.
En el siguiente capítulo vamos a abrir una petición real de un agente, byte a byte, y a ver dónde va cada uno. El resultado fue bastante peor de lo que esperábamos.
¿Listo para hacer crecer tu negocio?
Analicemos tu proyecto y definamos la estrategia perfecta para alcanzar tus objetivos
Solicitar consultoría gratuita