# Cómo crear un nuevo agente Guía para LLMs que asisten en la creación de agentes en este proyecto. ## Flujo completo automatizado ```bash # 1. Scaffold — crea config.yaml (E2EE habilitado), agent.go, prompts/, data/ ./dev-scripts/new-agent.sh "Display Name" # 2. Registrar en Matrix — genera y guarda en .env: # MATRIX_TOKEN_, MATRIX_PASSWORD_, PICKLE_KEY_ ./dev-scripts/register.sh "Display Name" # 3. Verificar E2EE — genera cross-signing keys, firma el device, # guarda SSSS_RECOVERY_KEY_ en .env ./dev-scripts/verify.sh # 4. Arrancar — ya verificado desde el primer arranque ./dev-scripts/start.sh ``` Los scripts automatizan todo. Solo intervenir manualmente si un paso falla. ## Convención de nombres de env vars — REGLA CRÍTICA **Normalización**: agent ID → mayúsculas, guiones → underscores. **Sin eliminar sufijos.** Función canónica en `dev-scripts/_common.sh`: ```bash normalize_id() { echo "$1" | tr '[:lower:]-' '[:upper:]_'; } ``` | Agent ID | Sufijo normalizado | Env vars | |---|---|---| | `assistant-bot` | `ASSISTANT_BOT` | `MATRIX_TOKEN_ASSISTANT_BOT`, `MATRIX_PASSWORD_ASSISTANT_BOT`, `PICKLE_KEY_ASSISTANT_BOT`, `SSSS_RECOVERY_KEY_ASSISTANT_BOT` | | `asistente-2` | `ASISTENTE_2` | `MATRIX_TOKEN_ASISTENTE_2`, `MATRIX_PASSWORD_ASISTENTE_2`, `PICKLE_KEY_ASISTENTE_2`, `SSSS_RECOVERY_KEY_ASISTENTE_2` | | `monitor-bot` | `MONITOR_BOT` | `MATRIX_TOKEN_MONITOR_BOT`, ... | **NUNCA** usar `sed 's/_BOT$//'` ni transformaciones que eliminen partes del ID. ## Estructura requerida Cada agente vive en `agents//` con esta estructura: ``` agents// ├── agent.go # Package propio, exporta Rules() []decision.Rule ├── config.yaml # Configuración completa (ver schema en internal/config/schema.go) ├── data/ # Runtime data (crypto, logs) — en .gitignore │ └── crypto/ # Crypto store E2EE — NUNCA compartir entre agentes └── prompts/ └── system.md # System prompt del LLM ``` ## Archivos a crear ### 1. `agents//agent.go` — Reglas puras ```go package import "github.com/enmanuel/agents/pkg/decision" func Rules() []decision.Rule { return []decision.Rule{ // Regla help explícita { Name: "help", Match: decision.MatchCommand("help"), Actions: []decision.Action{{ Kind: decision.ActionKindReply, Reply: &decision.ReplyAction{Content: "Descripción de capacidades del bot."}, }}, }, // Catch-all → 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 del archivo de reglas:** - **PURO**: sin imports de I/O, sin side effects, solo `pkg/decision` - El package name debe ser Go-valid (sin guiones): `agents/mi-bot/` → `package mibot` - Las reglas se evalúan en orden — poner las específicas antes del catch-all - El catch-all debe cubrir `ctx.IsDirectMsg || ctx.IsMention` como mínimo - `ActionKindReply` para respuestas estáticas, `ActionKindLLM` para respuestas dinámicas ### 2. `agents//config.yaml` — Configuración `new-agent.sh` genera esto automáticamente. Campos que hay que personalizar: ```yaml agent: id: # DEBE coincidir con el directorio y rulesRegistry name: "Display Name" description: "Qué hace este agente" llm: primary: provider: openai # o anthropic model: gpt-4o # o claude-sonnet-4-20250514 api_key_env: OPENAI_API_KEY # o ANTHROPIC_API_KEY tool_use: enabled: true/false # true si el agente usa herramientas matrix: user_id: "@:matrix-af2f3d.organic-machine.com" access_token_env: MATRIX_TOKEN_ device_id: "" # se resuelve automáticamente via whoami ``` ### Sección E2EE en config.yaml (generada por new-agent.sh) ```yaml encryption: enabled: true # SIEMPRE true para agentes nuevos store_path: "./agents//data/crypto/" # SIEMPRE por agente, nunca compartida pickle_key_env: PICKLE_KEY_ # generada por register.sh trust_mode: tofu recovery_key_env: SSSS_RECOVERY_KEY_ # generada por verify.sh ``` ### 3. `agents//prompts/system.md` — System prompt Debe incluir: - Identidad del bot (quién es, qué hace) - Capacidades y limitaciones - Herramientas disponibles (si `tool_use.enabled: true`) - Estilo de respuesta (idioma, tono, formato) - Instrucciones de uso de herramientas (cuándo y cómo usarlas) ## Archivos a modificar ### 4. `cmd/launcher/main.go` — Registro en el launcher `new-agent.sh` hace esto automáticamente. Dos cambios: **Import:** ```go agent "github.com/enmanuel/agents/agents/" ``` **rulesRegistry:** ```go var rulesRegistry = map[string]func() []decision.Rule{ // ... agentes existentes ... "": agent.Rules, // ← nuevo } ``` **El ID en rulesRegistry DEBE coincidir exactamente con `agent.id` del config.yaml.** ### 5. `agents/runtime.go` — Registro de herramientas (solo si hay tools nuevas) Si el agente necesita una herramienta nueva (no existente), ver la policy `create_tool.md`. Las herramientas "siempre disponibles" (`current_time`, `matrix_send`) ya están registradas para todos los agentes. ## E2EE — Cómo funciona la verificación ### Flujo al arrancar (agents/runtime.go) ``` InitCrypto → crea/carga el device y Olm account del crypto store FetchCrossSigningKeys → obtiene private keys de SSSS usando recovery key SignOwnDevice → fetch device keys del servidor + firma con self-signing key ``` ### Qué hace cada script | Script | Qué genera | Dónde se guarda | |---|---|---| | `register.sh` | Token, password, pickle key (32 bytes hex) | `.env` | | `verify.sh` | Cross-signing keys (master, self-signing, user-signing) + recovery key | Server (keys) + `.env` (recovery key) | ### Por qué cada credential importa | Credential | Para qué | Si falta | |---|---|---| | `MATRIX_TOKEN_*` | Autenticación del bot con el homeserver | Bot no arranca | | `MATRIX_PASSWORD_*` | UIA al subir cross-signing keys (verify.sh) | verify.sh intenta dummy auth (MSC3967) | | `PICKLE_KEY_*` | Cifrar el crypto store en disco | Usa sha256(token) como fallback — inseguro | | `SSSS_RECOVERY_KEY_*` | Recuperar private keys de cross-signing al arrancar | Device no se firma → "not verified by its owner" | ### Problemas comunes y soluciones **"Encrypted by a device not verified by its owner"** → Ejecutar `./dev-scripts/verify.sh ` y reiniciar **"self-signing private key not in cache"** → La recovery key en `.env` no corresponde a las cross-signing keys actuales. Re-ejecutar verify.sh. **"received update for device with different signing key"** → Bug resuelto: `SignOwnDevice` ahora hace `FetchKeys` antes de firmar. Si reaparece, recompilar el launcher: `go build -tags goolm -o bin/launcher ./cmd/launcher` **"data is not encrypted for given key ID"** → Las cross-signing keys fueron regeneradas pero la recovery key en `.env` es la vieja. Re-ejecutar verify.sh (actualiza .env automáticamente). **Recovery key sin comillas en .env** → Causan `command not found` al hacer `source .env`. Las recovery keys tienen espacios y DEBEN ir entre comillas: `SSSS_RECOVERY_KEY_*="EsXX YYYY ZZZZ ..."` ## Después de crear los archivos Verificar compilación: ```bash go build -tags goolm ./... ``` Luego seguir con registro, verificación y arranque usando los dev-scripts. ## Reglas generales - **Nunca** poner side effects en `agent.go` — es código puro - **Siempre** verificar que `agent.id` coincide entre config.yaml, rulesRegistry y el directorio - **Siempre** compilar con `-tags goolm` para soporte E2EE - **Siempre** usar `normalize_id()` de `_common.sh` para nombres de env vars - **Idioma**: español en configs, prompts y descripciones de dominio; inglés en código Go - **No** crear archivos `data/` — se generan automáticamente al arrancar - **No** commitear tokens ni passwords — solo van en `.env` - **No** compartir crypto stores entre agentes — cada uno tiene su `store_path` - Si el agente usa tool_use, asegurarse de que `llm.tool_use.enabled: true` en el config - Usar `agents/asistente-2/` como referencia completa de un agente con tools habilitadas