Files
agents_and_robots/dev/issues/completed/0030-robot-vs-agent.md
T
egutierrez a5d038913f docs: cerrar issue 0030 — separacion Robot vs Agent
Mover issue a completed/ y actualizar estado en README.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-09 00:21:52 +00:00

158 lines
5.6 KiB
Markdown

# 0030 — Separacion Robot vs Agente
## Objetivo
Crear un tipo `Robot` como runtime ligero para bots que solo responden comandos, sin LLM, reglas, memoria ni tools. Distinguir en config entre `type: robot` y `type: agent` para que el launcher sepa que runtime instanciar.
## Contexto
- Actualmente todos los bots usan el mismo `Agent` struct (1,182 lineas) con 25+ subsistemas
- Un bot de comandos simples (ej: `!deploy prod`, `!status`) no necesita LLM, memoria, knowledge, skills, sanitizacion, ni tool-use
- Si `llm.primary.provider` esta vacio, `runtime.go` loguea "running as command-only bot" pero sigue inicializando todo el subsistema
- No hay forma idiomatica de crear un bot simple sin arrastrar toda la complejidad
- Ejemplos de robots: bot de deploys, bot de health checks, bot de notificaciones, bot de CI/CD
## Arquitectura
```
agents/robot.go NEW → Robot struct (~150 lineas): Matrix + Commands
agents/robot_test.go NEW → tests del runtime minimo
agents/types.go NEW → interfaz comun Runner { Run(ctx), Stop(), RegisterCommand() }
cmd/launcher/main.go → detectar type: robot y crear Robot en vez de Agent
internal/config/schema.go → añadir campo Agent.Type ("robot" | "agent")
```
### Patron pure core / impure shell
- `pkg/` — sin cambios (el Robot no usa decision engine ni reglas)
- `shell/matrix/` — sin cambios (el Robot reutiliza el mismo cliente Matrix)
- `agents/robot.go` — impuro (tiene Matrix client), pero minimo
- `agents/runtime.go` — sin cambios (Agent sigue igual)
## Tareas
### Fase 1: Definir interfaz comun
- [ ] **1.1** Crear `agents/types.go` con interfaz `Runner`:
```go
type Runner interface {
Run(ctx context.Context) error
Stop()
RegisterCommand(spec command.Spec, handler CommandHandler)
}
```
- [ ] **1.2** Verificar que `Agent` ya satisface `Runner` (o adaptar)
### Fase 2: Implementar Robot
- [ ] **2.1** Crear `agents/robot.go` con struct `Robot`:
- `matrix *matrix.Client`
- `commands *command.Registry` (built-ins + custom)
- `logger *slog.Logger`
- `config config.AgentConfig`
- [ ] **2.2** Implementar `NewRobot(cfg, logger)` — solo inicializa Matrix + commands
- [ ] **2.3** Implementar `Run()` — sync loop que solo despacha comandos
- [ ] **2.4** Implementar `Stop()` — cierra Matrix client
- [ ] **2.5** Implementar `RegisterCommand()` — registra comando custom
- [ ] **2.6** En `handleEvent()`: si no es comando, ignorar silenciosamente (no hay LLM)
### Fase 3: Config y launcher
- [ ] **3.1** Añadir campo `Type string` a `AgentCfg` en schema.go (default: "agent")
- [ ] **3.2** En launcher: si `cfg.Agent.Type == "robot"`, crear `NewRobot()` en vez de `New()`
- [ ] **3.3** El launcher usa la interfaz `Runner` para manejar ambos tipos uniformemente
### Fase 4: Template y scaffolding
- [ ] **4.1** Crear `agents/_template_robot/` con config minimo para robots
- [ ] **4.2** Config de robot ejemplo (~20 lineas):
```yaml
agent:
id: deploy-bot
type: robot
description: "Bot de deploys"
personality:
prefix: "🤖"
matrix:
threads:
enabled: true
```
- [ ] **4.3** Actualizar `dev-scripts/agent/create-full.sh` para aceptar flag `--robot`
### Fase 5: Tests
- [ ] **5.1** Test: Robot responde a `!help` con lista de comandos
- [ ] **5.2** Test: Robot responde a `!ping` con pong
- [ ] **5.3** Test: Robot ignora mensajes normales (no es error, simplemente no responde)
- [ ] **5.4** Test: Robot con comando custom registrado lo ejecuta
- [ ] **5.5** Test: `Runner` interfaz es satisfecha por ambos `Agent` y `Robot`
### Fase 6: Documentacion
- [ ] **6.1** Actualizar `CLAUDE.md` con seccion Robot vs Agent
- [ ] **6.2** Actualizar `.claude/rules/create_agent.md` mencionando la opcion robot
- [ ] **6.3** Añadir tabla comparativa en docs
---
## Ejemplo de uso
```yaml
# agents/deploy-bot/config.yaml
agent:
id: deploy-bot
type: robot
description: "Bot de deploys con comandos directos"
personality:
prefix: "🚀"
matrix:
homeserver: ${MATRIX_HOMESERVER}
user_id: "@deploy-bot:matrix-af2f3d.organic-machine.com"
access_token_env: MATRIX_TOKEN_DEPLOY_BOT
```
```go
// agents/deploy-bot/commands.go
package deploy
func Commands() []agents.CommandEntry {
return []agents.CommandEntry{
{
Spec: command.Spec{Name: "deploy", Usage: "!deploy <env>"},
Handler: func(ctx context.Context, msg decision.MessageContext) string {
return fmt.Sprintf("Deploying to %s...", msg.Args[0])
},
},
}
}
```
Interaccion en Element:
```
Usuario: !deploy staging
Bot: 🚀 Deploying to staging...
Usuario: hola bot
Bot: (silencio — no tiene LLM)
```
## Decisiones de diseno
- **Interfaz `Runner`**: permite al launcher tratar robots y agentes uniformemente sin type switches
- **Robot NO tiene reglas**: las reglas son para routing inteligente. Un robot solo hace dispatch de comandos
- **Robot NO tiene memory/knowledge/skills**: mantener el runtime lo mas ligero posible
- **Config minimo**: un robot funcional se define en ~20 lineas de YAML
- **Silencio ante mensajes normales**: un robot no responde "no entiendo", simplemente ignora. Los comandos tienen `!help` para descubrirse
## Prerequisitos
- Ninguno (puede hacerse independiente)
- Se beneficia de 0026 (split runtime) pero no lo requiere
## Riesgos
- **Duplicacion**: Robot y Agent comparten logica de Matrix, commands, lifecycle. Mitigacion: reutilizar `shell/matrix/` y `pkg/command/` sin duplicar
- **Scope creep**: tentacion de añadir "un poquito de LLM" al Robot. Mitigacion: la linea es clara — si necesita LLM, es un Agent