Guías
Tutorial: una aplicación detrás de un endpoint HTTP
Evalúe un servicio al que accede por HTTP, sin importar su código: apunte Oloproof a la URL, ejecute la suite, encuentre lo que falla, despliegue un cambio y compare. Un pequeño servicio local ocupa el lugar del suyo, así que todo funciona sin conexión.
Lo que va a construir
Un servicio de recepción de pedidos que lee el mensaje de un cliente y extrae dos campos: un intent (where_is_order, cancel, return u other) y un order_id (cuatro dígitos, o null). Comprobará el formato de cada respuesta, lo que no necesita referencia, y medirá si cada campo es correcto, lo que sí la necesita. Términos como caso, ejecución, métrica y gate se definen en Conceptos.
Requisitos previos
- Python 3.11 o posterior, y Oloproof en un entorno virtual:
python3 -m venv .venv
. .venv/bin/activate
pip install oloproof- El proyecto de ejemplo, que viene con el paquete. Cópielo en un directorio nuevo y trabaje allí (httpx, que importa client.py, se instala con Oloproof):
oloproof init --example http order-intake
cd order-intake- El puerto 8765 libre en esta máquina. Si está ocupado, elija otro y cámbielo tanto en el comando del servidor como en oloproof.yaml.
Nada de esto usa un proveedor, una clave de API ni internet: el servicio escucha en 127.0.0.1.
Los archivos
order-intake/
server.py the stand-in service (Python standard library only)
client.py a callable that calls the service with a token (used near the end)
oloproof.yaml the suite: dataset, HTTP system, evaluators
release.yaml rules for a run
compare.yaml a rule for a comparison
data/messages.jsonl 20 casesEjecute los comandos desde order-intake/. Inicie el servicio en un segundo terminal y déjelo en marcha:
python server.py --port 8765intake service (v1) on http://127.0.0.1:8765/extractEl contrato HTTP
Para cada caso, Oloproof envía una petición: el input del caso como cuerpo JSON, con el método que usted declare (POST por defecto). Lee la respuesta como JSON. Un estado de 400 o superior, un tiempo de espera agotado o una conexión rechazada se registran como un error de ejecución de ese caso, nunca como una respuesta incorrecta.
Petición y respuesta para un caso:
POST /extract
{"message": "Where is order 1042? It has not arrived."}
200 OK
{"result": {"intent": "where_is_order", "order_id": "1042"}, "service": {"rules": "v1"}}output_path: result indica a Oloproof que conserve solo result como salida del caso; sin él, la salida es el cuerpo entero. Una ruta con puntos como data.answer llega más hondo.
version: 1
project: order-intake
dataset: data/messages.jsonl
system:
name: order-intake
http:
url: http://127.0.0.1:8765/extract
method: POST
version: rules-v1
output_path: result
timeout_s: 30
evaluators:
- type: json_schema
criterion: format_valid
field: null
schema:
type: object
required: [intent, order_id]
properties:
intent: {enum: [where_is_order, cancel, return, other]}
order_id: {type: [string, "null"], pattern: '^\d{4}$'}
additionalProperties: false
- type: exact_match
criterion: intent_correct
field: intent
- type: exact_match
criterion: order_id_correct
field: order_idUn sistema HTTP debe declarar una version. Oloproof no puede ver un despliegue: guarda en caché la salida de cada caso bajo la URL, el método, la ruta de salida y esa versión, así que la versión es la forma de decirle que el servicio cambió. Si olvida cambiarla, nunca se llama a un despliegue nuevo.
Las cabeceras y la autenticación todavía no se pueden configurar en system.http. La sección sobre tokens, más abajo, muestra la alternativa.
El conjunto de datos
{"id":"m04","input":{"message":"Has order #5120 shipped yet?"},"expected":{"intent":"where_is_order","order_id":"5120"}}
{"id":"m07","input":{"message":"Do you ship to Canada?"},"expected":{"intent":"other","order_id":null}}
{"id":"m16","input":{"message":"Please refund and take back the lamp from order #1560."},"expected":{"intent":"return","order_id":"1560"}}input es exactamente el cuerpo de la petición. expected contiene la referencia de cada campo; null es un valor de referencia real, que significa "no hay ningún id de pedido en este mensaje".
Elegir los evaluadores
| Criterio | Evaluador | Necesita expected | Mide |
|---|---|---|---|
| format_valid | json_schema sobre la salida | no | formato: un intent conocido y un id bien formado |
| intent_correct | exact_match sobre intent | sí | éxito de la tarea para el primer campo |
| order_id_correct | exact_match sobre order_id | sí | éxito de la tarea para el segundo campo |
El esquema aprobaría {"intent": "other", "order_id": null} para cualquier mensaje: bien formado e inútil. Solo las comprobaciones con referencia dicen si el servicio hizo su trabajo. Puntuar los campos por separado muestra cuál falla, algo que una comprobación combinada ocultaría.
release.yaml no admite ningún fallo de formato y pide que cada campo sea correcto al menos el 70% de las veces, juzgado sobre el intervalo al 95%:
version: 1
confidence_level: 0.95
block_on: [FAIL, INSUFFICIENT_EVIDENCE, MANUAL_REVIEW]
rules:
- id: valid-format
metric: format_valid
kind: observed_count
max_failures: 0
- id: intent-floor
metric: intent_correct
min: 0.70
- id: order-id-floor
metric: order_id_correct
min: 0.70Ejecútelo
oloproof runRun run_01M4... [DECIDED/COMPLETE]
Gate: BLOCK (exit 3)
│ valid-format │ format_valid │ PASS │ observed_failures_within_limit │
│ intent-floor │ intent_correct │ INSUFFICIENT_EVIDENCE │ interval_overlaps_threshold │
│ order-id-floor │ order_id_correct │ INSUFFICIENT_EVIDENCE │ interval_overlaps_threshold │
│ format_valid │ 100.0% │ [83.1%, 100.0%] │ 20 / 20 observed · 0 missing · 0 excluded │
│ intent_correct │ 75.0% │ [50.8%, 91.4%] │ 15 / 20 observed · 0 missing · 0 excluded │
│ order_id_correct │ 75.0% │ [50.8%, 91.4%] │ 15 / 20 observed · 0 missing · 0 excluded │
Cache: execution 0 hit/20 miss; judgment 0 hit/60 missTodas las respuestas están bien formadas. Cada campo es correcto 15 veces de 20, pero 20 casos dejan un intervalo que baja hasta el 50.8%, así que ninguno de los dos mínimos del 70% queda demostrado: INSUFFICIENT_EVIDENCE, y el gate bloquea con salida 3.
Inspeccionar los fallos
oloproof inspect RUN_ID --failures8 of 20 cases failed, errored or did not finish
m04
output: {"intent": "where_is_order", "order_id": null}
order_id_correct: failed
m06
output: {"intent": "other", "order_id": "7011"}
intent_correct: failed
...
m18
output: {"intent": "other", "order_id": null}
intent_correct: failed
order_id_correct: failedLea las entradas al lado (oloproof inspect RUN_ID --case m04) y aparecen dos defectos: el patrón del id solo reconoce "order 1234", no "order #5120", "order no. 8123" ni un "#1673" suelto; y expresiones como "send back", "stop order" y "where's my parcel" no corresponden a ningún intent. Esa es la siguiente acción: ampliar ambas reglas.
Desplegar un cambio
Detenga el servicio e inicie el candidato, que incluye esas correcciones:
python server.py --port 8765 --rules v2Cambie version: rules-v1 por version: rules-v2 en oloproof.yaml, porque la URL no cambió y Oloproof reutilizaría si no las salidas antiguas. Después:
oloproof runGate: ALLOW (exit 0)
│ intent_correct │ 100.0% │ [83.1%, 100.0%] │ 20 / 20 observed · 0 missing · 0 excluded │
│ order_id_correct │ 100.0% │ [83.1%, 100.0%] │ 20 / 20 observed · 0 missing · 0 excluded │Ambos mínimos se cumplen y el comando termina con 0.
Comparar los dos despliegues
compare.yaml pregunta si los intents del candidato son mejores que los de la línea base:
version: 1
confidence_level: 0.95
block_on: [FAIL, INSUFFICIENT_EVIDENCE, MANUAL_REVIEW]
rules:
- id: intent-better
kind: superiority
metric: intent_correctoloproof compare CANDIDATE_RUN_ID BASELINE_RUN_ID --policy compare.yamlformat_valid: +0.0 points [-23.6, +23.6] · 20 paired · 0 missing · 0 excluded
intent_correct: +25.0 points [-8.5, +58.3] · 20 paired · 0 missing · 0 excluded
order_id_correct: +25.0 points [-8.5, +58.3] · 20 paired · 0 missing · 0 excluded
Decisions
intent-better intent_correct superiority INSUFFICIENT_EVIDENCE interval_overlaps_zero
Gate: BLOCK (exit 3)El candidato cumple sus propios mínimos y, sin embargo, la comparación no puede demostrar que sea mejor: cambiaron cinco casos y, sobre 20 casos emparejados, el intervalo de la mejora todavía incluye el cero. Ambas afirmaciones son ciertas a la vez. "Cumple el requisito" y "supera a la línea base" son preguntas distintas, y una suite tan pequeña solo responde a la segunda para efectos grandes. El remedio es tener más mensajes reales.
Un servicio que necesita un token
Inicie el servicio de modo que exija un token bearer:
INTAKE_TOKEN=s3cret python server.py --port 8765 --rules v2 --require-tokensystem.http no envía cabeceras personalizadas, así que con una version nueva (por ejemplo rules-v2-auth) se rechazan todas las llamadas:
Gate: BLOCK (exit 3)
│ valid-format │ format_valid │ INSUFFICIENT_EVIDENCE │ no_observations │
│ intent_correct │ │ [0.0%, 100.0%] │ 0 / 0 observed · 20 missing · 0 excluded │y oloproof inspect RUN_ID --failures muestra execution ERROR: TransientError: system returned HTTP 401 en cada caso. Los errores de ejecución son evidencia que falta, no fallos: no se observó nada, así que cada regla queda en INSUFFICIENT_EVIDENCE.
La alternativa es un callable de Python que haga la petición por sí mismo. client.py añade la cabecera a partir de una variable de entorno, así que el token nunca entra en oloproof.yaml ni en ningún registro almacenado:
URL = os.environ.get("INTAKE_URL", "http://127.0.0.1:8765/extract")
def extract(case: dict[str, Any]) -> dict[str, Any]:
headers = {"Authorization": f"Bearer {os.environ['INTAKE_TOKEN']}"}
response = httpx.post(URL, json=case, headers=headers, timeout=30)
response.raise_for_status()
return response.json()["result"]Sustituya el bloque http: de oloproof.yaml por:
system:
name: order-intake
version: rules-v2
callable: client:extracty ejecute con el token en el entorno:
INTAKE_TOKEN=s3cret oloproof runLos 20 casos vuelven a observarse y el gate permite la publicación. La caché del callable sigue su propio código fuente y su version, no el servicio que hay detrás, así que se aplica la misma regla: cambie version cuando despliegue.
Solución de problemas
| Síntoma | Causa y solución |
|---|---|
| system connection failed (ConnectError) en todos los casos | El servicio no está en marcha, o escucha en otro puerto. |
| system connection failed (RemoteProtocolError) | Otra cosa responde en ese puerto. Elija uno libre. |
| KeyError: "missing output path 'results'" | output_path nombra un campo que la respuesta no tiene. |
| system returned HTTP 401 o 403 | El endpoint necesita credenciales: use la alternativa del callable. |
| Desplegó un cambio y la línea de caché indica todo aciertos | La version no cambió, así que se reutilizaron las salidas almacenadas. |
| an HTTP system needs a declared version | Añada version bajo system o system.http. |
Limitaciones
- Sin cabeceras personalizadas, autenticación, parámetros de consulta ni plantillas de petición en system.http: el input del caso es el cuerpo JSON tal cual. Use un callable para cualquier otra cosa.
- Las respuestas deben ser JSON. Las respuestas en streaming no se leen como un flujo.
- Oloproof no puede detectar un despliegue; la version declarada es toda la identidad de lo que respondió.
- Su servicio es dueño de su estado: Oloproof envía peticiones y registra respuestas; no restablece, aísla ni revierte nada de lo que una petición cambie.