# Guía completa: Crear un nuevo agente Esta guía documenta todos los pasos para crear, registrar, configurar y poner en marcha un nuevo bot/agente en el sistema. ## Requisitos previos - Go 1.23+ instalado (`/usr/local/go/bin`) - Acceso al homeserver Matrix (`MATRIX_HOMESERVER` y `MATRIX_ADMIN_TOKEN` en `.env`) - Variables de entorno cargadas (`.env` con todos los secretos) ## Paso 1: Crear el scaffold del agente ### Opción A: Script automático ```bash ./dev-scripts/agent/new-agent.sh "Display Name" # Ejemplo: ./dev-scripts/agent/new-agent.sh mi-bot "Mi Bot" ``` Esto crea la estructura base en `agents//`. ### Opción B: Manual Crear la estructura de directorios: ``` agents// ├── agent.go # Reglas puras de decisión ├── config.yaml # Configuración completa del agente ├── prompts/ │ └── system.md # System prompt para el LLM └── data/ # Runtime (auto-generado, en .gitignore) └── crypto/ # Store E2EE ``` ### 1.1 Crear `agents//agent.go` Archivo de reglas puras. El package debe exportar una función `Rules() []decision.Rule`. ```go package mibot import ( "github.com/enmanuel/agents/pkg/decision" ) // Rules returns the decision rules for this agent. func Rules() []decision.Rule { return []decision.Rule{ // !help — comando de ayuda explícito { Name: "help", Match: decision.MatchCommand("help"), Actions: []decision.Action{{ Kind: decision.ActionKindReply, Reply: &decision.ReplyAction{ Content: "Soy mi-bot. Escríbeme lo que necesitas.", }, }}, }, // Catch-all: DMs y menciones → LLM { Name: "llm-all", Match: func(ctx decision.MessageContext) bool { return ctx.IsDirectMsg || ctx.IsMention }, Actions: []decision.Action{{ Kind: decision.ActionKindLLM, LLM: &decision.LLMAction{}, }}, }, } } ``` **Reglas importantes:** - Este archivo es **puro** — sin imports de I/O, sin side effects - Solo usa types de `pkg/decision` - Las reglas se evalúan en orden; la primera que matchea gana ### 1.2 Crear `agents//config.yaml` Configuración completa del agente. Referencia: `internal/config/schema.go`. Secciones principales: | Sección | Descripción | |---------|-------------| | `agent` | Identidad: id, name, version, enabled, description, tags | | `personality` | Tono, verbosidad, idioma, templates, comportamiento | | `llm` | Provider (openai/anthropic), modelo, tokens, temperature, tool_use | | `tools` | SSH, HTTP, scripts, file_ops, MCP — cada uno con su enabled/config | | `matrix` | Homeserver, user_id, token, device_id, encryption, rooms, filters | | `agents` | Peers conocidos, delegación, protocolo inter-agente | | `ssh` | Configuración SSH (solo si aplica) | | `security` | Roles, audit, secrets provider | | `schedules` | Tareas programadas (cron) | | `observability` | Logging, metrics, health, tracing | | `resilience` | Circuit breaker, retry, shutdown, queue | | `storage` | State backend, cache, history | **Campos críticos en `matrix`:** ```yaml matrix: homeserver: "https://matrix-af2f3d.organic-machine.com" user_id: "@:matrix-af2f3d.organic-machine.com" access_token_env: MATRIX_TOKEN_ # nombre de la env var device_id: "" encryption: enabled: true store_path: "./agents//data/crypto/" pickle_key_env: PICKLE_KEY_ trust_mode: tofu ``` **Para habilitar tool-use:** ```yaml llm: tool_use: enabled: true # DEBE ser true max_iterations: 5 parallel_calls: false ``` ### 1.3 Crear `agents//prompts/system.md` System prompt que recibe el LLM. Debe incluir: - Identidad y rol del bot - Capacidades disponibles - Herramientas disponibles (si tool_use está habilitado) - Estilo de respuesta - Limitaciones Usar como referencia: `agents/assistant-bot/prompts/assistant-system.md` o `agents/asistente-2/prompts/system.md`. ## Paso 2: Registrar el agente en el launcher Editar `cmd/launcher/main.go`: 1. Añadir import del package del agente: ```go mibotAgent "github.com/enmanuel/agents/agents/mibot" ``` 2. Añadir entrada en `rulesRegistry`: ```go var rulesRegistry = map[string]func() []decision.Rule{ "assistant-bot": assistantagent.Rules, "mi-bot": mibotAgent.Rules, // ← nuevo } ``` **Nota:** El ID aquí debe coincidir exactamente con `agent.id` en el `config.yaml`. ## Paso 3: Registrar en Matrix ```bash ./dev-scripts/agent/register.sh "Display Name" ``` Este comando: 1. Crea el usuario en Synapse via admin API 2. Genera una contraseña aleatoria 3. Hace login para obtener un access token 4. Guarda `MATRIX_TOKEN_` en `.env` **Guardar la contraseña manualmente** — se necesita para la verificación E2EE: ```bash # Añadir al .env manualmente si no se guardó: MATRIX_PASSWORD_= ``` **Importante:** El script `register.sh` imprime la password en la salida. Copiarla y guardarla. ### Actualizar device_id en config.yaml El registro crea un `device_id` nuevo. Actualizarlo en `agents//config.yaml`: ```yaml matrix: device_id: "" ``` ## Paso 4: Configurar avatar y display name Colocar la imagen del bot en `static/`: ```bash # Subir avatar y sincronizar displayname desde el config ./dev-scripts/agent/avatar.sh static/.jpg ``` Esto hace: 1. Sube la imagen al homeserver Matrix (obtiene una URL `mxc://`) 2. Establece el avatar del usuario bot 3. Sincroniza el displayname desde `agent.name` del `config.yaml` **Formatos soportados:** JPG, PNG. Recomendado: cuadrado, 256x256 o superior. ## Paso 5: Verificación E2EE (cross-signing) Sin este paso, los mensajes del bot mostrarán: **"Encrypted by a device not verified by its owner"**. ```bash ./bin/verify \ --homeserver "$MATRIX_HOMESERVER" \ --username "" \ --password "$MATRIX_PASSWORD_" \ --token "$MATRIX_TOKEN_" \ --store "./agents//data/crypto/" \ --pickle-key "$PICKLE_KEY_" ``` **Qué hace:** 1. Inicializa el crypto helper de mautrix (usando el mismo store y pickle key que el agente) 2. Genera claves de cross-signing (master + self-signing + user-signing) 3. Las sube al homeserver usando UIA con la password del bot 4. Las almacena cifradas en SSSS (Server-Side Secret Storage) en el servidor 5. Imprime un **recovery key** (base58) que permite recuperar las claves privadas ### 5.1 Guardar el recovery key El comando imprime algo como: ``` ─── IMPORTANT: Save the recovery key ─── SSSS_RECOVERY_KEY_MI_BOT=EsXX YYYY ZZZZ ... ``` **Añadir al `.env`** (con comillas, el recovery key tiene espacios): ```bash SSSS_RECOVERY_KEY_MI_BOT="EsXX YYYY ZZZZ ..." ``` ### 5.2 Configurar recovery_key_env en config.yaml ```yaml encryption: enabled: true store_path: "./agents//data/crypto/" pickle_key_env: PICKLE_KEY_ trust_mode: tofu recovery_key_env: SSSS_RECOVERY_KEY_ # ← NUEVO ``` Esto permite que el agente recupere automáticamente las cross-signing private keys desde SSSS cada vez que arranca. Sin esto, las keys solo existen en memoria durante la sesión de verify. **Logs esperados al arrancar con recovery key configurado:** ``` INFO cross-signing private keys fetched from SSSS INFO e2ee ready ``` ### 5.3 Si se cambia la password del bot Cambiar la password (admin API) invalida el token anterior. Hay que: 1. Re-login para obtener nuevo token 2. Actualizar `MATRIX_TOKEN_` y `MATRIX_PASSWORD_` en `.env` 3. Actualizar `device_id` en `config.yaml` 4. Borrar el crypto store viejo (`agents//data/crypto/crypto.db`) 5. Re-ejecutar `cmd/verify` → obtener nuevo recovery key 6. Actualizar `SSSS_RECOVERY_KEY_` en `.env` **Nota:** El pickle key (`PICKLE_KEY_`) NO cambia al rotar el token. Solo se regenera si se pierde. ## Paso 6: Arrancar el agente ```bash ./dev-scripts/server/start.sh ``` Verificar que arrancó correctamente: ```bash # Ver logs tail -f run/.log # Verificar proceso ./dev-scripts/server/ps.sh # Estado general ./dev-scripts/agent/list.sh ``` **Logs esperados al arrancar correctamente:** ``` {"level":"INFO","msg":"initializing e2ee","store":"agents//data/crypto/crypto.db"} {"level":"INFO","msg":"e2ee ready"} {"level":"INFO","msg":"agent starting","id":"","tools":["current_time","matrix_send"]} {"level":"INFO","msg":"starting matrix sync"} ``` ## Paso 7: Verificar funcionamiento 1. Abrir Element u otro cliente Matrix 2. Enviar un DM al bot: `@:matrix-af2f3d.organic-machine.com` 3. Verificar que responde 4. Verificar que no aparece el warning de "device not verified" 5. Si tiene tools, probar que las usa (e.g., preguntar la hora) ## Resumen de comandos (en orden) ```bash # 1. Crear scaffold ./dev-scripts/agent/new-agent.sh "Nombre" # 2. Editar reglas, config, prompt # agents//agent.go # agents//config.yaml # agents//prompts/system.md # 3. Registrar en launcher # Editar cmd/launcher/main.go → import + rulesRegistry # 4. Registrar en Matrix ./dev-scripts/agent/register.sh "Nombre" # 5. Avatar y displayname ./dev-scripts/agent/avatar.sh static/.jpg # 6. Generar pickle key (si no existe) openssl rand -hex 32 # → guardar como PICKLE_KEY_ en .env # 7. Verificación E2EE + recovery key ./bin/verify \ --homeserver "$MATRIX_HOMESERVER" \ --username "" \ --password "$MATRIX_PASSWORD_" \ --token "$MATRIX_TOKEN_" \ --store "./agents//data/crypto/" \ --pickle-key "$PICKLE_KEY_" # → Guardar SSSS_RECOVERY_KEY_ en .env (con comillas) # → Añadir recovery_key_env al config.yaml # 8. Arrancar ./dev-scripts/server/start.sh # 9. Verificar tail -f run/.log ``` ## Control de acceso El sistema de control de acceso permite restringir quién puede interactuar con cada agente. Tiene tres niveles independientes: ### Nivel 1 — Allowlist de usuarios Restringe qué usuarios pueden enviar mensajes al bot. Si la lista está vacía, todos pueden hablar (comportamiento por defecto). ```yaml matrix: filters: allowed_users: - "@admin:matrix-af2f3d.organic-machine.com" - "@enmanuel:matrix-af2f3d.organic-machine.com" unauthorized_response: silent # silent (default) | explicit ``` - `silent`: ignora mensajes de usuarios no autorizados (como si el bot no existiera) - `explicit`: responde con "No tienes permisos para interactuar con este agente" ### Nivel 2 — Invite gating Si `allowed_users` está configurado, el bot solo acepta invites a salas de usuarios en la lista. Invites de usuarios no autorizados se ignoran silenciosamente. ### Nivel 3 — RBAC por acción Conecta los roles de `security.roles` para controlar qué acciones puede ejecutar cada usuario: ```yaml security: roles: admin: users: ["@admin:matrix-af2f3d.organic-machine.com"] actions: ["*"] # acceso total user: users: ["*"] # todos los demás actions: ["ask", "help", "command:help", "command:ping"] ``` **Acciones disponibles:** | Acción | Qué protege | |--------|-------------| | `*` | Todo (wildcard) | | `ask` | Hablar con el LLM (mensajes de texto libre) | | `command:*` | Todos los comandos `!xxx` | | `command:` | Un comando específico (ej: `command:tool`) | | `tool:*` | Todas las tools vía LLM | | `tool:` | Una tool específica (ej: `tool:ssh_command`) | | `help` | Comandos informativos (`!help`, `!info`, `!status`) | **Retrocompatibilidad:** si no se configura `allowed_users` ni `security.roles`, el agente funciona en modo abierto (como siempre). ## Troubleshooting | Problema | Causa | Solución | |----------|-------|----------| | `env var ... is not set` | La regex del `.env` loader no matchea | Verificar que el nombre de la var solo usa `[A-Z0-9_]` | | `M_UNKNOWN_TOKEN` | Token invalidado (password cambiada) | Re-login, actualizar `.env` | | `mismatching device ID` | Crypto store con device viejo | Borrar `agents//data/crypto/crypto.db`, actualizar `device_id` en config | | `olm account not marked as shared` | Crypto store inconsistente | Auto-recovery lo resuelve al reiniciar. Si persiste: borrar crypto.db | | `"Encrypted by device not verified"` | Falta cross-signing | Ejecutar `cmd/verify` con `--store` y `--pickle-key` del agente, guardar recovery key en `.env` | | `cross-signing private keys not available` | Recovery key no configurada | Ejecutar `cmd/verify`, guardar recovery key, configurar `recovery_key_env` | | `verify recovery key: invalid` | Recovery key incorrecta | Re-ejecutar `cmd/verify` para generar nueva recovery key | | Bot no responde | Reglas no matchean | Verificar que hay regla catch-all para DMs/mentions | | `no rules registered for agent` | ID no está en `rulesRegistry` | Añadir en `cmd/launcher/main.go` | | Bot muere al arrancar | Revisar logs | `tail -f run/.log` | ## Compilación Siempre usar la tag `goolm` para soporte E2EE puro (sin CGO): ```bash go build -tags goolm ./cmd/launcher go build -tags goolm ./cmd/verify go run -tags goolm ./cmd/verify --help ```