Files
fit-boilerplate-wox/docs/eval-strategy.md
Felipe Arentsen b089c2ef18
Some checks failed
CI — Lint + Evals / lint (push) Has been cancelled
CI — Lint + Evals / smoke (push) Has been cancelled
feat: v1 — boilerplate WxO + web
Boilerplate completo para construir soluciones agénticas sobre IBM
watsonx Orchestrate (ADK 2.x) con una capa web encima.

Destilado de cotemar-poc-n1 y dun-casos-prueba. Incluye:

- 11 docs (best practices, architecture patterns, ADK cheatsheet,
  observability, tool authoring, deployment, RUNBOOK, DEPLOY_TO_NEW_WOX,
  known-issues con 16 errores reales y fix, eval strategy, INDEX)
- 13 templates WxO (orchestrator/specialist/single-meta agents,
  3 connections, KB + runbook, observable_tool decorator, coercion
  helpers, Python tools, OpenAPI spec, backend filter endpoint, webhook
  validator HMAC, MCP connection)
- 6 scripts (deploy idempotente con fallback ADK, undeploy, reset,
  check-adk-version, new-specialist scaffold, eval-agents runner)
- 4 eval pieces (linter de best practices, runner, smoke-test, direct
  backend probe) + scenario templates
- 8 subagentes Claude (.claude/agents/) — wxo-architect, wxo-agent-author,
  wxo-tool-author, runbook-author, mock-builder, backend-tool-builder,
  eval-author, web-layer-builder
- Skill bundle fit-wxo-bootstrap/ (SKILL.md + 3 templates) listo para
  copiar a ~/.claude/skills/
- Web layer default FastAPI+HTMX con vista timeline observable + endpoint
  receptor de trazas
- Docker compose Coolify-ready (healthcheck wget -qO-, sin labels Traefik,
  network split internal:true)
- CI Gitea workflow con lint + smoke

Best practices enforced por evals/lint_wxo_yaml.py:
- A1: máx 10 tools por agente
- A3: orchestrator sin tools de remediación
- A6: agente sin propósito (react sin tools ni KB)
- T1: @observable_tool obligatorio, no @tool directo
- T3: _compat shim inline en tools.py
- D1: docker-compose sin wget --spider
- I-005: OpenAPI con description per-operation
- I-009: sin labels Traefik manuales

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-16 14:59:44 +00:00

6.4 KiB

Eval Strategy

Cómo validar que tu solución hace lo que debe. Cuatro capas, cada una respondiendo una pregunta distinta.


Capa 1 — Native (ADK)

Pregunta: ¿El agente responde algo razonable al input?

Herramienta: orchestrate agents test

orchestrate agents test mi_agente --input-file evals/scenarios/test1.json

Output:

  • La respuesta del agente
  • La traza de tool calls
  • El timing

Cobertura:

  • Happy path
  • Casos límite (input ambiguo, datos faltantes)
  • Casos de error (sistema externo caído, datos inválidos)

Limitación: no compara contra expected. Solo te muestra qué pasó.

Fixture format (evals/scenarios/test1.json):

{
  "messages": [
    { "role": "user", "content": "Necesito resetear la contraseña de juan.perez@cotemar.com" }
  ]
}

Capa 2 — Agent behavior (custom)

Pregunta: ¿El agente llamó las tools correctas en el orden correcto?

Herramienta: evals/runner.py que consume trazas observables.

Fixture format (evals/scenarios/scenario_reset.yaml):

name: reset_password_happy_path
input: "Necesito resetear la contraseña de juan.perez@cotemar.com"
expect:
  agent_response_contains: ["TKT-"]
  tool_calls_in_order:
    - tool: lookup_user
      inputs.username: "juan.perez@cotemar.com"
    - tool: reset_password
    - tool: create_ticket
      inputs.status: "RESOLVED"
  no_tool_calls:
    - escalate_to_n2

Cómo corre:

  1. Manda el input al agente vía WxO Runs API
  2. Espera la respuesta
  3. Lee las trazas emitidas durante el run (capa observability)
  4. Compara contra expect
  5. Pass/fail

Por qué importa: un agente puede dar la respuesta correcta llamando las tools incorrectas (ej: dijo RESOLVED sin llamar reset_password). Esto detecta esos casos.


Capa 3 — Runbook compliance (custom)

Pregunta: ¿El estado final del sistema coincide con lo que el runbook dice que debe pasar?

Herramienta: evals/runner.py con asserts sobre la DB del backend.

Fixture format (evals/scenarios/scenario_reset.yaml — sección final_state):

final_state:
  query: "SELECT * FROM tickets WHERE created_at > $START_TIME"
  expect_count: 1
  expect_fields:
    status: "RESOLVED"
    category: "password_reset"
    assigned_group: "Capital Humano"
    extra.runbook: "01"

Cómo corre:

  1. Captura timestamp inicial
  2. Ejecuta el scenario (manda input al agente)
  3. Query a la DB para ver qué se creó/actualizó
  4. Asserts contra final_state

Por qué importa: la traza puede decir "todo bien" pero el ticket final puede tener el grupo equivocado, falta de metadata, etc.


Capa 4 — Web layer (custom, opcional)

Pregunta: ¿La UI muestra correctamente el resultado?

Herramienta: Playwright o requests + parsing HTML.

Cuándo aplica: si la solución incluye un control plane / kanban / dashboard que el usuario consume directamente.

Fixture format (evals/scenarios/scenario_reset.yaml — sección ui):

ui:
  visit: "/insights"
  expect_text:
    - "Resueltos: 1"
    - "Runbook 01"
  visit: "/"
  expect_selector: ".ticket-card--RESOLVED"
  expect_count_selector:
    selector: ".ticket-card"
    count: ">=1"

Runner

./evals/eval-agents.sh

Hace en orden:

  1. lint_wxo_yaml.py — best practices estáticas (issue check antes de runtime)
  2. Native eval por cada evals/scenarios/*.json
  3. Behavior eval por cada evals/scenarios/*.yaml
  4. Final state eval (parte del mismo YAML)
  5. UI eval si existe sección ui

Reporta:

[lint]    PASS
[native]  PASS  (5/5)
[behavior] PASS (4/5)  — scenario "edge_case_inactive_user" FAILED
[final_state] PASS  (4/5)
[ui]      SKIP  (no fixtures)

Exit code 0 si todo pasa, 1 si alguno falla.


Smoke test (CI nightly)

evals/smoke-test.sh es el subset mínimo que debe pasar siempre:

  1. Health endpoint responde 200
  2. Un caso simple end-to-end (login → create → execute → poll → assert)
  3. El audit log tiene las entries esperadas
  4. La UI muestra el resultado

Si falla, algo de la infraestructura está mal (deploy, DNS, healthcheck, auth, etc.). NO mira el agente en profundidad — para eso están las capas 2/3.

./evals/smoke-test.sh

Direct backend probe (diagnóstico)

evals/direct-backend-probe.sh golpea el backend directamente con curl + el token de WxO. Para aislar problemas:

$ ./evals/direct-backend-probe.sh
[1/3] Healthcheck         OK (200)
[2/3] Direct tool call    OK (200) — got expected "case not found"
[3/3] Auth test           OK (200 con token, 401 sin)
✓ Backend OK from WxO side. Si la solución falla, es el agente o el LLM.

Si los 3 pasan pero el agente igual falla, el problema está en:

  • El prompt del agente
  • El modelo (issue I-002)
  • El recursion_limit (issue I-001)
  • Los schemas (issue I-003)

Estrategia recomendada por fase del proyecto

Fase 1 — Desarrollo

  • Capa 1 (native) frecuente, manualmente
  • Lint antes de cada commit (pre-commit hook)

Fase 2 — Estabilización

  • Agregar Capa 2 (behavior) para cada scenario
  • Agregar Capa 3 (final_state) para cada scenario
  • CI corre las 3 en cada PR

Fase 3 — Demo / handoff

  • Agregar Capa 4 (UI) para los 2-3 paths críticos
  • Smoke test nightly en CI

Fase 4 — Producción

  • Smoke test cada 15 min (cron)
  • Direct backend probe en healthcheck
  • Behavior eval suite semanal

Archivos a crear por scenario

evals/scenarios/
├── scenario_reset_password.yaml      ← Capas 2, 3, 4 unified
├── scenario_reset_password.input.json ← Capa 1 (input para `agents test`)
├── scenario_restart_service.yaml
├── scenario_restart_service.input.json
└── ...

El subagente eval-author (en .claude/agents/eval-author.md) genera estos pares automáticamente cuando le das un runbook o un agente nuevo.


Cuando una eval falla

  1. Mirá evals/last_run.log — qué fixture, qué assert
  2. Si es Capa 1: el agente respondió mal o no respondió. Ver prompt.
  3. Si es Capa 2: el agente respondió bien pero llamó las tools mal. Ver instructions del agente.
  4. Si es Capa 3: tools se llamaron pero el resultado final está incorrecto. Ver el handler del backend.
  5. Si es Capa 4: backend OK pero UI no muestra. Ver el view / HTMX template.

Repará, corré la eval específica (./evals/eval-agents.sh -s scenario_x), commit.