merge: issue/0030-robot-vs-agent — tipo Robot ligero para bots de comandos
This commit is contained in:
@@ -0,0 +1,157 @@
|
||||
# 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
|
||||
Reference in New Issue
Block a user