Files
agents_and_robots/dev/issues/0030-robot-vs-agent.md
T
egutierrez 8f6958f856 chore: agregar issues 0026-0032 y worktrees a gitignore
Registra los nuevos issues pendientes en el indice y excluye
la carpeta worktrees/ del control de versiones.

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

5.6 KiB

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:
    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):
    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

# 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
// 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