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>
This commit is contained in:
247
docs/eval-strategy.md
Normal file
247
docs/eval-strategy.md
Normal file
@@ -0,0 +1,247 @@
|
||||
# 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`
|
||||
|
||||
```bash
|
||||
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`):
|
||||
```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`):
|
||||
```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`):
|
||||
```yaml
|
||||
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`):
|
||||
```yaml
|
||||
ui:
|
||||
visit: "/insights"
|
||||
expect_text:
|
||||
- "Resueltos: 1"
|
||||
- "Runbook 01"
|
||||
visit: "/"
|
||||
expect_selector: ".ticket-card--RESOLVED"
|
||||
expect_count_selector:
|
||||
selector: ".ticket-card"
|
||||
count: ">=1"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Runner
|
||||
|
||||
```bash
|
||||
./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.
|
||||
|
||||
```bash
|
||||
./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:
|
||||
|
||||
```bash
|
||||
$ ./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.
|
||||
Reference in New Issue
Block a user