702ebcca26
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>
425 lines
13 KiB
Markdown
425 lines
13 KiB
Markdown
# 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 <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`.
|
|
|
|
```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/<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`:**
|
|
|
|
```yaml
|
|
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:**
|
|
|
|
```yaml
|
|
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`:
|
|
|
|
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 <agent-id> "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_<AGENT>` 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_<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`:
|
|
|
|
```yaml
|
|
matrix:
|
|
device_id: "<DEVICE_ID del output de register>"
|
|
```
|
|
|
|
## 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 <agent-id> static/<imagen>.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 "<agent-id>" \
|
|
--password "$MATRIX_PASSWORD_<AGENT>" \
|
|
--token "$MATRIX_TOKEN_<AGENT>" \
|
|
--store "./agents/<agent-id>/data/crypto/" \
|
|
--pickle-key "$PICKLE_KEY_<AGENT>"
|
|
```
|
|
|
|
**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/<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:
|
|
1. Re-login para obtener nuevo token
|
|
2. Actualizar `MATRIX_TOKEN_<AGENT>` y `MATRIX_PASSWORD_<AGENT>` en `.env`
|
|
3. Actualizar `device_id` en `config.yaml`
|
|
4. Borrar el crypto store viejo (`agents/<id>/data/crypto/crypto.db`)
|
|
5. Re-ejecutar `cmd/verify` → obtener nuevo recovery key
|
|
6. 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
|
|
|
|
```bash
|
|
./dev-scripts/server/start.sh <agent-id>
|
|
```
|
|
|
|
Verificar que arrancó correctamente:
|
|
|
|
```bash
|
|
# 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
|
|
|
|
1. Abrir Element u otro cliente Matrix
|
|
2. Enviar un DM al bot: `@<agent-id>: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 <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).
|
|
|
|
```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:<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):
|
|
|
|
```bash
|
|
go build -tags goolm ./cmd/launcher
|
|
go build -tags goolm ./cmd/verify
|
|
go run -tags goolm ./cmd/verify --help
|
|
```
|