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>
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:
- Manda el input al agente vía WxO Runs API
- Espera la respuesta
- Lee las trazas emitidas durante el run (capa observability)
- Compara contra
expect - 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:
- Captura timestamp inicial
- Ejecuta el scenario (manda input al agente)
- Query a la DB para ver qué se creó/actualizó
- 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:
lint_wxo_yaml.py— best practices estáticas (issue check antes de runtime)- Native eval por cada
evals/scenarios/*.json - Behavior eval por cada
evals/scenarios/*.yaml - Final state eval (parte del mismo YAML)
- 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:
- Health endpoint responde 200
- Un caso simple end-to-end (login → create → execute → poll → assert)
- El audit log tiene las entries esperadas
- 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
- Mirá
evals/last_run.log— qué fixture, qué assert - Si es Capa 1: el agente respondió mal o no respondió. Ver prompt.
- Si es Capa 2: el agente respondió bien pero llamó las tools mal. Ver instructions del agente.
- Si es Capa 3: tools se llamaron pero el resultado final está incorrecto. Ver el handler del backend.
- 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.