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:
192
docs/architecture-patterns.md
Normal file
192
docs/architecture-patterns.md
Normal file
@@ -0,0 +1,192 @@
|
||||
# Architecture Patterns
|
||||
|
||||
Árbol de decisión que usa el subagente `wxo-architect` cuando arrancás un
|
||||
caso nuevo. Te llevo pregunta por pregunta.
|
||||
|
||||
---
|
||||
|
||||
## Pregunta 1 — ¿Cuántos dominios distintos toca el caso?
|
||||
|
||||
Un "dominio" = una superficie funcional bien delimitada (identidad,
|
||||
operaciones, RRHH, finanzas, CRM, etc.).
|
||||
|
||||
- **0–1 dominio** → 1 agente solo. Salí del árbol con la **Topología Single**.
|
||||
- **2–5 dominios** → N specialists + 1 orchestrator. **Topología Multi-Specialist** (estilo Cotemar).
|
||||
- **6+ dominios** → 2 capas: sub-orchestrators temáticos. **Topología Multi-Capa**.
|
||||
|
||||
---
|
||||
|
||||
## Pregunta 2 — ¿El flujo es lineal o ramificado?
|
||||
|
||||
- **Lineal y conocido** (caso entra → A → B → C → D → resultado, siempre el mismo orden): aplicá **Patrón Meta-Tool** (estilo Dun). Una sola tool `run_full_case` que el backend orquesta internamente. El agente invoca esa tool y listo.
|
||||
- **Ramificado** (el agente decide qué hacer según el input): N tools individuales, agente razona. Cuidado con `recursion_limit=30` (ver `known-issues.md`).
|
||||
- **Mixto**: parte lineal en meta-tool, parte ramificada en tools individuales. Es lo más común en casos reales.
|
||||
|
||||
---
|
||||
|
||||
## Pregunta 3 — ¿Cada agente necesita razonar sobre procedimientos escritos?
|
||||
|
||||
Para cada agente, preguntate: "¿el LLM necesita citar un runbook para
|
||||
decidir bien?"
|
||||
|
||||
- **Sí** (procedural): este agente tiene **KB con runbooks**. Estilo
|
||||
Cotemar AD specialist.
|
||||
- **No** (API-driven): este agente **no tiene KB**. El system prompt
|
||||
describe qué hace, las tools describen las acciones disponibles.
|
||||
Estilo Dun QA Studio.
|
||||
- **Mixto**: KB para el "qué" + tools para el "cómo". Solo cuando los
|
||||
procedimientos son largos y variables.
|
||||
|
||||
---
|
||||
|
||||
## Pregunta 4 — ¿Qué tipo de tools va a tener cada agente?
|
||||
|
||||
Por cada dominio, elegí el tipo de tool:
|
||||
|
||||
### Opción A — OpenAPI (preferido si tenés backend propio)
|
||||
|
||||
**Cuándo:** tu solución incluye un backend (FastAPI, Express, lo que sea)
|
||||
que ya define endpoints REST.
|
||||
|
||||
**Pros:**
|
||||
- Una sola fuente de verdad (el spec)
|
||||
- ADK importa todo en un comando
|
||||
- Cambios al backend = re-import del spec, no toques YAML de tool
|
||||
|
||||
**Pattern del template (estilo Dun):**
|
||||
- Backend expone `/orchestrate-tools-spec.json` (endpoint filtrado)
|
||||
- Filtra el openapi.json a una allowlist `PUBLIC_TOOLS`
|
||||
- Fuerza `description` y `security` per-operation
|
||||
- Patch del `servers[0].url` en deploy time según env
|
||||
|
||||
Ver `wxo/tools/openapi/_backend_filter_endpoint.py` para el template.
|
||||
|
||||
### Opción B — Python `@tool` (preferido para mocks/PoC)
|
||||
|
||||
**Cuándo:** estás prototipando, mocks externos, lógica simple por tool,
|
||||
sin backend que valga la pena mantener.
|
||||
|
||||
**Pros:**
|
||||
- Rápido de escribir, una función = una tool
|
||||
- Fácil de testear con pytest
|
||||
- Bajo overhead
|
||||
|
||||
**Pattern del template (estilo Cotemar):**
|
||||
- Una función `@observable_tool(name="x")` por endpoint mockeado
|
||||
- Lee `BASE_URL` del environment de su connection
|
||||
- `requests.post(...)` y devuelve dict
|
||||
- `_compat.py` inline al inicio del archivo
|
||||
|
||||
Ver `wxo/tools/python/_template_tools.py`.
|
||||
|
||||
### Opción C — MCP (preferido si el sistema lo expone)
|
||||
|
||||
**Cuándo:** el sistema externo (HubSpot, Outline, una DB con MCP server,
|
||||
GitHub vía MCP, etc.) ya expone un MCP server.
|
||||
|
||||
**Pros:**
|
||||
- Trae types + permisos del sistema
|
||||
- Mantenido por el vendor
|
||||
- Una sola connection MCP da acceso a todas las tools del server
|
||||
|
||||
**Pattern del template:**
|
||||
- Una `connection.yaml` de tipo MCP apuntando al server
|
||||
- ADK descubre las tools y las hace disponibles al agente
|
||||
- Ver `wxo/tools/mcp/_template_mcp_connection.yaml`
|
||||
|
||||
### Decisión
|
||||
|
||||
| Situación | Recomendación |
|
||||
|---|---|
|
||||
| Es PoC, mocks externos | Python @tool |
|
||||
| Tengo backend propio (FastAPI/Express) | OpenAPI |
|
||||
| Conecto a SaaS con MCP server | MCP |
|
||||
| Conecto a SaaS sin MCP server | OpenAPI (escribís el spec a mano) o Python |
|
||||
| Mezcla | Cada agente puede usar más de uno |
|
||||
|
||||
---
|
||||
|
||||
## Pregunta 5 — ¿Web layer: cuál stack?
|
||||
|
||||
Dos happy-paths probados:
|
||||
|
||||
### Stack A — FastAPI + HTMX + Jinja (default)
|
||||
**Cuándo:** demos, PoCs, control planes, kanbans, paneles operativos
|
||||
**Pros:** SSR, sin build step, polling con `hx-trigger` es trivial,
|
||||
una sola persona la mantiene
|
||||
**Contras:** menos interactivo, menos componibilidad
|
||||
**Origen:** estilo Cotemar (UNUS Kanban + Control Plane)
|
||||
|
||||
### Stack B — FastAPI + React + Vite + Tailwind + zustand
|
||||
**Cuándo:** app productiva con varios usuarios, mucha interacción, UI rica
|
||||
**Pros:** ecosistema React, lazy loading, state management decente
|
||||
**Contras:** build step, más superficie de mantenimiento
|
||||
**Origen:** estilo Dun (QA Studio)
|
||||
|
||||
### Decisión
|
||||
- **Demo / interno / PoC** → Stack A
|
||||
- **Producción / multi-usuario / app rica** → Stack B
|
||||
- **Híbrido** → Stack A para dashboards, Stack B para la app principal,
|
||||
unidos por reverse proxy
|
||||
|
||||
El template trae Stack A en `web/_default_fastapi_htmx/`. Stack B viene
|
||||
documentado pero no implementado (el subagente Claude `web-layer-builder`
|
||||
te lo arma según el caso).
|
||||
|
||||
---
|
||||
|
||||
## Pregunta 6 — ¿Deploy: dónde corre esto?
|
||||
|
||||
Tres opciones soportadas:
|
||||
|
||||
| Target | Para qué | Ver |
|
||||
|---|---|---|
|
||||
| **Coolify** (FIT default) | Demos + PoCs productivos en `*.fitlabs.dev` | `docs/deployment-guide.md` |
|
||||
| **Docker compose local** | Dev / testing | `docs/INSTRUCCIONES.md` |
|
||||
| **K8s / Cloud Run / otro** | Producción del cliente | `docs/deployment-guide.md` § "Other targets" |
|
||||
|
||||
---
|
||||
|
||||
## Topologías resultantes
|
||||
|
||||
### Topología Single (1 agente)
|
||||
```
|
||||
[user] → [agente único] ──tools──→ [sistema externo]
|
||||
└──KB──→ [opcional]
|
||||
```
|
||||
|
||||
### Topología Multi-Specialist (Cotemar pattern)
|
||||
```
|
||||
┌──→ ad_specialist ──tools──→ AD
|
||||
[user] → [N1] ────┼──→ ops_specialist ──tools──→ Ops
|
||||
(orch) └──→ rrhh_specialist ──tools──→ HR
|
||||
│
|
||||
└─escalate→ runbook 03 (KB propia)
|
||||
```
|
||||
|
||||
### Topología Meta-Tool (Dun pattern)
|
||||
```
|
||||
[user] → [agente único] ──run_full_case(id)──→ [backend orquesta:
|
||||
step 1, step 2, ...
|
||||
write_audit cada paso]
|
||||
```
|
||||
|
||||
### Topología Multi-Capa (>5 dominios)
|
||||
```
|
||||
┌─→ [sub-orch identidad] ─→ ad, hr, vendors
|
||||
[user] → [super-orch] ────┤
|
||||
├─→ [sub-orch ops] ─→ dynatrace, k8s, db
|
||||
└─→ [sub-orch finanzas] ─→ sap, billing
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Cuando dudes
|
||||
|
||||
- **¿1 agente o varios?** → Si los dominios son disjuntos, varios. Si todo es del mismo dominio, uno.
|
||||
- **¿KB o no?** → Si hay procedimiento escrito que el LLM debe seguir literal, sí. Si no, no.
|
||||
- **¿OpenAPI o Python?** → ¿Tenés backend? OpenAPI. ¿Mocks? Python.
|
||||
- **¿Meta-tool o tools sueltas?** → ¿El flujo es lineal y conocido? Meta-tool. ¿El agente decide? Sueltas.
|
||||
|
||||
Cuando dudes, **arrancá con menos**. Es más fácil splittear un agente en
|
||||
dos que mergear dos en uno.
|
||||
Reference in New Issue
Block a user