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>
177 lines
8.2 KiB
Bash
Executable File
177 lines
8.2 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
# ─────────────────────────────────────────────────────────────────────────────
|
|
# Deploy completo de la solución WxO. Idempotente, re-runnable.
|
|
#
|
|
# Hace en orden:
|
|
# 1. Connections (draft Y live — issue I-004)
|
|
# 2. Tools (Python + OpenAPI según haya)
|
|
# 3. KBs
|
|
# 4. Agents (specialists primero — issue A4)
|
|
# 5. Deploy a live de todos los agents
|
|
# 6. Channel webchat (si hay landing)
|
|
#
|
|
# Variables de control:
|
|
# PUBLIC_HOST — host público donde corre tu stack
|
|
# WXO_ENV_NAME — env del ADK a usar (default: el activo)
|
|
# SKIP_TOOLS — saltar tools
|
|
# SKIP_KB — saltar KBs
|
|
# SKIP_CHANNEL — saltar canal webchat
|
|
# AGENTS_GLOB — patrón para detectar agents (default: wxo/agents/*.agent.yaml)
|
|
# ─────────────────────────────────────────────────────────────────────────────
|
|
|
|
set -euo pipefail
|
|
|
|
ROOT="$(cd "$(dirname "$0")/.." && pwd)"
|
|
cd "$ROOT"
|
|
|
|
# ─── Config (con defaults razonables) ───────────────────────────────────────
|
|
PUBLIC_HOST="${PUBLIC_HOST:-mi-cliente.fitlabs.dev}"
|
|
PUBLIC_BASE="https://${PUBLIC_HOST}"
|
|
SKIP_TOOLS="${SKIP_TOOLS:-false}"
|
|
SKIP_KB="${SKIP_KB:-false}"
|
|
SKIP_CHANNEL="${SKIP_CHANNEL:-false}"
|
|
AGENTS_GLOB="${AGENTS_GLOB:-wxo/agents/*.agent.yaml}"
|
|
|
|
echo "════════════════════════════════════════════════════════════════"
|
|
echo " WxO deploy — $PUBLIC_BASE"
|
|
echo " env activo : $(orchestrate env list 2>/dev/null | grep ACTIVE || echo '???')"
|
|
echo "════════════════════════════════════════════════════════════════"
|
|
|
|
# ─── 1. CONNECTIONS ─────────────────────────────────────────────────────────
|
|
# Auto-detect: cada wxo/connections/*.yaml define un app_id.
|
|
# Mapping del BASE_URL se hace por convención:
|
|
# nombre del archivo (sin extensión) → /<basename>
|
|
# Override por archivo en `scripts/deploy-wxo.conf`.
|
|
|
|
echo "→ [1/6] Connections"
|
|
for f in wxo/connections/*.yaml; do
|
|
[ -f "$f" ] || continue
|
|
app_id=$(grep -E '^name:' "$f" | head -1 | awk '{print $2}')
|
|
[ -z "$app_id" ] && { echo " skip $f (no name)"; continue; }
|
|
|
|
# Convención: BASE_URL = $PUBLIC_BASE/<basename_sin_connection>
|
|
basename="${f##*/}"
|
|
basename="${basename%_connection.yaml}"
|
|
basename="${basename%.yaml}"
|
|
base_url="${PUBLIC_BASE}/${basename}"
|
|
|
|
echo " • $app_id → $base_url"
|
|
|
|
orchestrate connections add -a "$app_id" 2>/dev/null || true
|
|
|
|
for ENV in draft live; do
|
|
# Detectar kind (key_value | api_key | mcp) del YAML
|
|
kind=$(grep -E '^auth_type:' "$f" | awk '{print $2}')
|
|
[ -z "$kind" ] && kind="key_value"
|
|
|
|
orchestrate connections configure -a "$app_id" --env "$ENV" --type team --kind "$kind" 2>/dev/null || true
|
|
orchestrate connections set-credentials -a "$app_id" --env "$ENV" -e "BASE_URL=${base_url}" 2>/dev/null || true
|
|
done
|
|
done
|
|
|
|
# ─── 2. TOOLS PYTHON ────────────────────────────────────────────────────────
|
|
if [ "$SKIP_TOOLS" != "true" ]; then
|
|
echo "→ [2/6] Tools (Python)"
|
|
if [ -f wxo/tools/python/requirements.txt ]; then
|
|
for f in wxo/tools/python/*.py; do
|
|
[ -f "$f" ] || continue
|
|
# Saltar los archivos de soporte (empiezan con _)
|
|
base="${f##*/}"
|
|
case "$base" in _*) continue ;; esac
|
|
|
|
# Derivar app_id del nombre (convención: <app>_tools.py → app_demo o app)
|
|
app_id="${base%_tools.py}_demo"
|
|
echo " • $f → app-id $app_id"
|
|
orchestrate tools import -k python \
|
|
-f "$f" \
|
|
-r wxo/tools/python/requirements.txt \
|
|
--app-id "$app_id" || echo " (already imported, skipping)"
|
|
done
|
|
fi
|
|
|
|
# ─── 3. TOOLS OPENAPI ─────────────────────────────────────────────────────
|
|
echo "→ [3/6] Tools (OpenAPI)"
|
|
for f in wxo/tools/openapi/*.yaml wxo/tools/openapi/*.json; do
|
|
[ -f "$f" ] || continue
|
|
base="${f##*/}"
|
|
case "$base" in _*) continue ;; esac
|
|
|
|
# Derivar app_id del nombre
|
|
app_id="${base%.yaml}"
|
|
app_id="${app_id%.json}"
|
|
echo " • $f → app-id $app_id"
|
|
orchestrate tools import -k openapi -f "$f" --app-id "$app_id" || echo " (already imported, skipping)"
|
|
done
|
|
fi
|
|
|
|
# ─── 4. KNOWLEDGE BASES ─────────────────────────────────────────────────────
|
|
if [ "$SKIP_KB" != "true" ]; then
|
|
echo "→ [4/6] Knowledge bases"
|
|
for f in wxo/knowledge_base/*.kb.yaml; do
|
|
[ -f "$f" ] || continue
|
|
base="${f##*/}"
|
|
case "$base" in _*) continue ;; esac
|
|
echo " • $f"
|
|
orchestrate knowledge-bases import -f "$f" || echo " (already imported, skipping)"
|
|
done
|
|
fi
|
|
|
|
# ─── 5. AGENTS ──────────────────────────────────────────────────────────────
|
|
# IMPORTANTE: specialists antes que orchestrators (issue A4).
|
|
# Convención: si el archivo se llama *orchestrator* o *n1_* va último.
|
|
|
|
echo "→ [5/6] Agents"
|
|
SPECIALIST_FILES=()
|
|
ORCHESTRATOR_FILES=()
|
|
for f in $AGENTS_GLOB; do
|
|
[ -f "$f" ] || continue
|
|
base="${f##*/}"
|
|
case "$base" in _*) continue ;; esac
|
|
case "$base" in
|
|
*orchestrator*|*n1_*) ORCHESTRATOR_FILES+=("$f") ;;
|
|
*) SPECIALIST_FILES+=("$f") ;;
|
|
esac
|
|
done
|
|
|
|
for f in "${SPECIALIST_FILES[@]}"; do
|
|
echo " • specialist $f"
|
|
orchestrate agents import -f "$f"
|
|
done
|
|
|
|
for f in "${ORCHESTRATOR_FILES[@]}"; do
|
|
echo " • orchestrator $f"
|
|
orchestrate agents import -f "$f"
|
|
done
|
|
|
|
# Deploy a live de todos
|
|
echo "→ [5b/6] Deploying agents to LIVE"
|
|
for f in "${SPECIALIST_FILES[@]}" "${ORCHESTRATOR_FILES[@]}"; do
|
|
name=$(grep -E '^name:' "$f" | head -1 | awk '{print $2}')
|
|
[ -z "$name" ] && continue
|
|
echo " • $name → live"
|
|
orchestrate agents deploy --name "$name" --env live || echo " (deploy failed, see logs)"
|
|
done
|
|
|
|
# ─── 6. CHANNEL WEBCHAT ─────────────────────────────────────────────────────
|
|
if [ "$SKIP_CHANNEL" != "true" ] && [ ${#ORCHESTRATOR_FILES[@]} -gt 0 ]; then
|
|
echo "→ [6/6] Channel webchat (bound to orchestrator)"
|
|
for f in "${ORCHESTRATOR_FILES[@]}"; do
|
|
name=$(grep -E '^name:' "$f" | head -1 | awk '{print $2}')
|
|
[ -z "$name" ] && continue
|
|
orchestrate channels create webchat \
|
|
--agent "$name" \
|
|
--name "${name} - Web" 2>/dev/null || echo " (channel for $name already exists)"
|
|
done
|
|
fi
|
|
|
|
# ─── DONE ───────────────────────────────────────────────────────────────────
|
|
echo "════════════════════════════════════════════════════════════════"
|
|
echo " ✓ Deploy completado."
|
|
echo ""
|
|
echo " Próximos pasos manuales:"
|
|
echo " 1. orchestrate agents list → capturar agentId y agentEnvironmentId del orchestrator"
|
|
echo " 2. Pegarlos en tu landing HTML y env vars"
|
|
echo " 3. WxO Console → Settings → Embed Security → OFF (sin esto, embed falla)"
|
|
echo " 4. ./evals/smoke-test.sh → validar end-to-end"
|
|
echo "════════════════════════════════════════════════════════════════"
|