Añadir sección de control de acceso con los tres niveles: - Nivel 1: allowlist de usuarios (allowed_users) - Nivel 2: invite gating (auto-join restringido) - Nivel 3: RBAC por acción (security.roles) Incluye tabla de acciones disponibles y nota de retrocompatibilidad. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
13 KiB
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_HOMESERVERyMATRIX_ADMIN_TOKENen.env) - Variables de entorno cargadas (
.envcon todos los secretos)
Paso 1: Crear el scaffold del agente
Opción A: Script automático
./dev-scripts/agent/new-agent.sh <agent-id> "Display Name"
# Ejemplo: ./dev-scripts/agent/new-agent.sh mi-bot "Mi Bot"
Esto crea la estructura base en agents/<agent-id>/.
Opción B: Manual
Crear la estructura de directorios:
agents/<agent-id>/
├── 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-id>/agent.go
Archivo de reglas puras. El package debe exportar una función Rules() []decision.Rule.
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/<agent-id>/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:
matrix:
homeserver: "https://matrix-af2f3d.organic-machine.com"
user_id: "@<agent-id>:matrix-af2f3d.organic-machine.com"
access_token_env: MATRIX_TOKEN_<AGENT_UPPER> # nombre de la env var
device_id: "<se obtiene al registrar>"
encryption:
enabled: true
store_path: "./agents/<agent-id>/data/crypto/"
pickle_key_env: PICKLE_KEY_<AGENT_UPPER>
trust_mode: tofu
Para habilitar tool-use:
llm:
tool_use:
enabled: true # DEBE ser true
max_iterations: 5
parallel_calls: false
1.3 Crear agents/<agent-id>/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:
- Añadir import del package del agente:
mibotAgent "github.com/enmanuel/agents/agents/mibot"
- Añadir entrada en
rulesRegistry:
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
./dev-scripts/agent/register.sh <agent-id> "Display Name"
Este comando:
- Crea el usuario en Synapse via admin API
- Genera una contraseña aleatoria
- Hace login para obtener un access token
- Guarda
MATRIX_TOKEN_<AGENT>en.env
Guardar la contraseña manualmente — se necesita para la verificación E2EE:
# Añadir al .env manualmente si no se guardó:
MATRIX_PASSWORD_<AGENT>=<password_generada>
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/<agent-id>/config.yaml:
matrix:
device_id: "<DEVICE_ID del output de register>"
Paso 4: Configurar avatar y display name
Colocar la imagen del bot en static/:
# Subir avatar y sincronizar displayname desde el config
./dev-scripts/agent/avatar.sh <agent-id> static/<imagen>.jpg
Esto hace:
- Sube la imagen al homeserver Matrix (obtiene una URL
mxc://) - Establece el avatar del usuario bot
- Sincroniza el displayname desde
agent.namedelconfig.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".
./bin/verify \
--homeserver "$MATRIX_HOMESERVER" \
--username "<agent-id>" \
--password "$MATRIX_PASSWORD_<AGENT>" \
--token "$MATRIX_TOKEN_<AGENT>" \
--store "./agents/<agent-id>/data/crypto/" \
--pickle-key "$PICKLE_KEY_<AGENT>"
Qué hace:
- Inicializa el crypto helper de mautrix (usando el mismo store y pickle key que el agente)
- Genera claves de cross-signing (master + self-signing + user-signing)
- Las sube al homeserver usando UIA con la password del bot
- Las almacena cifradas en SSSS (Server-Side Secret Storage) en el servidor
- 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):
SSSS_RECOVERY_KEY_MI_BOT="EsXX YYYY ZZZZ ..."
5.2 Configurar recovery_key_env en config.yaml
encryption:
enabled: true
store_path: "./agents/<agent-id>/data/crypto/"
pickle_key_env: PICKLE_KEY_<AGENT>
trust_mode: tofu
recovery_key_env: SSSS_RECOVERY_KEY_<AGENT> # ← 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:
- Re-login para obtener nuevo token
- Actualizar
MATRIX_TOKEN_<AGENT>yMATRIX_PASSWORD_<AGENT>en.env - Actualizar
device_idenconfig.yaml - Borrar el crypto store viejo (
agents/<id>/data/crypto/crypto.db) - Re-ejecutar
cmd/verify→ obtener nuevo recovery key - Actualizar
SSSS_RECOVERY_KEY_<AGENT>en.env
Nota: El pickle key (PICKLE_KEY_<AGENT>) NO cambia al rotar el token. Solo se regenera si se pierde.
Paso 6: Arrancar el agente
./dev-scripts/server/start.sh <agent-id>
Verificar que arrancó correctamente:
# Ver logs
tail -f run/<agent-id>.log
# Verificar proceso
./dev-scripts/server/ps.sh <agent-id>
# Estado general
./dev-scripts/agent/list.sh
Logs esperados al arrancar correctamente:
{"level":"INFO","msg":"initializing e2ee","store":"agents/<id>/data/crypto/crypto.db"}
{"level":"INFO","msg":"e2ee ready"}
{"level":"INFO","msg":"agent starting","id":"<agent-id>","tools":["current_time","matrix_send"]}
{"level":"INFO","msg":"starting matrix sync"}
Paso 7: Verificar funcionamiento
- Abrir Element u otro cliente Matrix
- Enviar un DM al bot:
@<agent-id>:matrix-af2f3d.organic-machine.com - Verificar que responde
- Verificar que no aparece el warning de "device not verified"
- Si tiene tools, probar que las usa (e.g., preguntar la hora)
Resumen de comandos (en orden)
# 1. Crear scaffold
./dev-scripts/agent/new-agent.sh <id> "Nombre"
# 2. Editar reglas, config, prompt
# agents/<id>/agent.go
# agents/<id>/config.yaml
# agents/<id>/prompts/system.md
# 3. Registrar en launcher
# Editar cmd/launcher/main.go → import + rulesRegistry
# 4. Registrar en Matrix
./dev-scripts/agent/register.sh <id> "Nombre"
# 5. Avatar y displayname
./dev-scripts/agent/avatar.sh <id> static/<imagen>.jpg
# 6. Generar pickle key (si no existe)
openssl rand -hex 32 # → guardar como PICKLE_KEY_<AGENT> en .env
# 7. Verificación E2EE + recovery key
./bin/verify \
--homeserver "$MATRIX_HOMESERVER" \
--username "<id>" \
--password "$MATRIX_PASSWORD_<AGENT>" \
--token "$MATRIX_TOKEN_<AGENT>" \
--store "./agents/<id>/data/crypto/" \
--pickle-key "$PICKLE_KEY_<AGENT>"
# → Guardar SSSS_RECOVERY_KEY_<AGENT> en .env (con comillas)
# → Añadir recovery_key_env al config.yaml
# 8. Arrancar
./dev-scripts/server/start.sh <id>
# 9. Verificar
tail -f run/<id>.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).
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:
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:<name> |
Un comando específico (ej: command:tool) |
tool:* |
Todas las tools vía LLM |
tool:<name> |
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/<id>/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/<id>.log |
Compilación
Siempre usar la tag goolm para soporte E2EE puro (sin CGO):
go build -tags goolm ./cmd/launcher
go build -tags goolm ./cmd/verify
go run -tags goolm ./cmd/verify --help