Remove outdated plans and implement new features for bot tools, memory, interaction, avatar management, and cron scheduling
- Deleted old plan files for bot memory, interaction, avatar, and cron. - Added new task files for bot tools, memory, interaction, avatar, and cron scheduling with detailed designs and implementation notes. - Updated `agents/runtime.go` to support memory clearing and message deletion from SQLite. - Created necessary shell and package files for implementing bot tools and cron scheduling functionalities.
This commit is contained in:
@@ -0,0 +1,52 @@
|
||||
# Plan: Herramientas para los bots
|
||||
|
||||
## Objetivo
|
||||
Permitir que los bots ejecuten herramientas reales (funciones Go) como respuesta a
|
||||
decisiones del LLM — patrón function calling / tool use.
|
||||
|
||||
## Estado: COMPLETADO
|
||||
|
||||
---
|
||||
|
||||
## Diseño
|
||||
|
||||
### Capa pura (`pkg/tools/`)
|
||||
- Definir `ToolSpec` con nombre, descripción y esquema JSON de parámetros
|
||||
- Definir `ToolCallAction` en `pkg/decision/` — acción pura que contiene
|
||||
`ToolName string` y `Args map[string]any`
|
||||
- El motor de reglas puede emitir `ToolCallAction` como cualquier otra acción
|
||||
|
||||
### Capa shell (`shell/tools/`)
|
||||
- `Executor` que mapea nombre → función Go real
|
||||
- Ejecuta la herramienta y devuelve `ToolResult{Output string, Err error}`
|
||||
- El Runner de `shell/effects/` llama al Executor cuando recibe `ToolCallAction`
|
||||
|
||||
### Integración LLM
|
||||
- `shell/llm/anthropic.go` y `openai.go` ya soportan tool_use / function_calling
|
||||
- Mapear `[]ToolSpec` al formato nativo de cada proveedor
|
||||
- Parsear la respuesta del LLM para extraer llamadas a herramientas
|
||||
|
||||
### Herramientas iniciales a implementar
|
||||
| Herramienta | Descripción | Shell |
|
||||
|-----------------|-------------------------------------|-------------------|
|
||||
| `http_get` | GET a una URL, devuelve body | `shell/tools/` |
|
||||
| `http_post` | POST JSON a una URL | `shell/tools/` |
|
||||
| `ssh_command` | Ejecutar comando remoto por SSH | `shell/ssh/` |
|
||||
| `read_file` | Leer archivo local | `shell/tools/` |
|
||||
| `matrix_send` | Enviar mensaje a una sala Matrix | `shell/matrix/` |
|
||||
|
||||
---
|
||||
|
||||
## Archivos a crear/modificar
|
||||
- `pkg/tools/spec.go` — ToolSpec, ToolResult
|
||||
- `pkg/decision/actions.go` — añadir ToolCallAction
|
||||
- `shell/tools/executor.go` — registro y ejecución de herramientas
|
||||
- `shell/effects/runner.go` — manejar ToolCallAction
|
||||
- `shell/llm/anthropic.go` — emitir tools en el request, parsear tool_use blocks
|
||||
- `shell/llm/openai.go` — idem para function_calling
|
||||
- `agents/<id>/agent.go` — registrar herramientas por agente
|
||||
|
||||
## Notas
|
||||
- Las herramientas se declaran en `pkg/` (pure spec) pero se implementan en `shell/`
|
||||
- Un agente solo tiene acceso a las herramientas declaradas en su config
|
||||
- Respetar `security.allowed_tools` del config YAML
|
||||
@@ -0,0 +1,95 @@
|
||||
# Plan: Memoria para los bots
|
||||
|
||||
## Objetivo
|
||||
Que cada bot recuerde conversaciones anteriores, hechos importantes sobre usuarios
|
||||
y contexto de salas. Memoria a corto plazo (ventana de conversación) y largo plazo
|
||||
(SQLite persistente).
|
||||
|
||||
## Estado: completado ✓
|
||||
|
||||
---
|
||||
|
||||
## Tipos de memoria
|
||||
|
||||
### 1. Memoria de conversación (corto plazo)
|
||||
- Ventana deslizante de `N` mensajes por room
|
||||
- Se pasa como historial al LLM en cada llamada
|
||||
- Vive en RAM; se pierde al reiniciar (aceptable)
|
||||
|
||||
### 2. Memoria episódica (largo plazo)
|
||||
- Hechos extraídos de conversaciones: nombre del usuario, preferencias, eventos
|
||||
- Guardados en SQLite (`agents/<id>/data/memory.db`)
|
||||
- El LLM puede leer y escribir hechos mediante herramientas (`remember`, `recall`)
|
||||
|
||||
---
|
||||
|
||||
## Diseño capa pura (`pkg/memory/`)
|
||||
|
||||
```go
|
||||
// Tipos puros — sin I/O
|
||||
type Message struct {
|
||||
Role string // "user" | "assistant"
|
||||
Content string
|
||||
At time.Time
|
||||
}
|
||||
|
||||
type Fact struct {
|
||||
Subject string
|
||||
Key string
|
||||
Value string
|
||||
At time.Time
|
||||
}
|
||||
|
||||
// Ventana de conversación
|
||||
type Window struct {
|
||||
RoomID string
|
||||
Messages []Message
|
||||
MaxSize int
|
||||
}
|
||||
|
||||
func (w Window) Append(m Message) Window { ... } // pura
|
||||
func (w Window) ToLLMMessages() []llm.Message { ... } // pura
|
||||
```
|
||||
|
||||
## Diseño capa shell (`shell/memory/`)
|
||||
|
||||
```go
|
||||
// Acceso a SQLite — impuro
|
||||
type Store interface {
|
||||
SaveFact(ctx, agentID, fact) error
|
||||
GetFacts(ctx, agentID, subject) ([]Fact, error)
|
||||
GetHistory(ctx, agentID, roomID, limit) ([]Message, error)
|
||||
AppendMessage(ctx, agentID, roomID, msg) error
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Herramientas LLM para memoria
|
||||
- `remember(subject, key, value)` — guardar un hecho
|
||||
- `recall(subject, key)` — recuperar hechos sobre alguien/algo
|
||||
- `forget(subject, key)` — borrar un hecho
|
||||
|
||||
---
|
||||
|
||||
## Integración con el flujo actual
|
||||
1. `agents/runtime.go` mantiene un `map[roomID]memory.Window` en RAM
|
||||
2. Antes de llamar al LLM, inyectar historial de la ventana al request
|
||||
3. Después de la respuesta, hacer `Append` con el mensaje del bot
|
||||
4. Las herramientas `remember`/`recall` van al `Store` SQLite
|
||||
|
||||
---
|
||||
|
||||
## Archivos a crear/modificar
|
||||
- `pkg/memory/types.go` — Message, Fact, Window (puros)
|
||||
- `pkg/memory/window.go` — operaciones sobre Window (puras)
|
||||
- `shell/memory/sqlite_store.go` — Store SQLite
|
||||
- `shell/memory/migrations/001_init.sql` — schema
|
||||
- `agents/runtime.go` — inyectar historial antes del LLM call
|
||||
- `agents/<id>/agent.go` — registrar herramientas remember/recall
|
||||
|
||||
## Notas
|
||||
- Schema SQLite: tabla `facts(agent_id, subject, key, value, updated_at)`,
|
||||
tabla `messages(agent_id, room_id, role, content, created_at)`
|
||||
- El tamaño de la ventana se configura en `storage.max_context_messages`
|
||||
(añadir al schema de config)
|
||||
@@ -0,0 +1,275 @@
|
||||
# Plan: Multi-bot Orchestration — Middleware invisible
|
||||
|
||||
## Objetivo
|
||||
Cuando hay más de un bot en una sala, un **orquestador invisible** (sin identidad
|
||||
Matrix) coordina quién responde y cuándo. Opera como middleware en el proceso del
|
||||
launcher — los humanos solo ven a los bots especializados respondiendo.
|
||||
|
||||
## Estado: pendiente
|
||||
|
||||
---
|
||||
|
||||
## Arquitectura: `agents/specials/`
|
||||
|
||||
Los **special agents** son componentes de sistema sin identidad Matrix. Viven en
|
||||
`agents/specials/<id>/` y el launcher los instancia de forma diferente a los bots
|
||||
normales: sin token, sin listener propio, sin `user_id`.
|
||||
|
||||
```
|
||||
agents/
|
||||
assistant/ → bot normal (Matrix user, token, listener)
|
||||
specials/ → componentes de sistema, sin identidad Matrix
|
||||
orchestrator/ → middleware de coordinación multi-bot
|
||||
scheduler/ → (futuro) cron runner
|
||||
memory/ → (futuro) gestor de historial cross-bot
|
||||
```
|
||||
|
||||
### Diferencias vs bot normal
|
||||
|
||||
| | Bot normal | Special agent |
|
||||
|---|---|---|
|
||||
| Matrix user | ✓ (@bot:server) | ✗ |
|
||||
| Token propio | ✓ | ✗ |
|
||||
| Listener Matrix | ✓ | ✗ |
|
||||
| LLM propio | opcional | ✓ (para decisiones) |
|
||||
| Instanciado por | launcher vía rulesRegistry | launcher vía specialsRegistry |
|
||||
| Visible en salas | ✓ | ✗ nunca |
|
||||
|
||||
---
|
||||
|
||||
## Config del orquestador
|
||||
|
||||
```yaml
|
||||
# agents/specials/orchestrator/config.yaml
|
||||
|
||||
special:
|
||||
id: orchestrator
|
||||
type: orchestrator # clave para que el launcher sepa cómo instanciarlo
|
||||
enabled: true
|
||||
description: "Middleware de coordinación multi-bot. Sin identidad Matrix."
|
||||
|
||||
llm:
|
||||
primary:
|
||||
provider: anthropic
|
||||
model: claude-sonnet-4-6
|
||||
api_key_env: ANTHROPIC_API_KEY
|
||||
max_tokens: 512 # respuestas cortas: solo IDs de bots y scores
|
||||
temperature: 0.2 # determinista para routing
|
||||
|
||||
orchestration:
|
||||
max_iterations: 3 # máximo de bots que responden por pregunta
|
||||
quality_threshold: 0.8 # score mínimo para cortar el pipeline (0.0–1.0)
|
||||
silent: true # no emite mensajes Matrix propios
|
||||
delegation_timeout: 30s # tiempo máximo esperando respuesta de un bot
|
||||
|
||||
rooms:
|
||||
- room_id: "${MATRIX_ROOM_SHARED}"
|
||||
participants: # bots que participan en esta sala
|
||||
- id: assistant-bot
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Flujo de eventos
|
||||
|
||||
```
|
||||
Matrix event (room compartida)
|
||||
│
|
||||
▼
|
||||
Launcher (event router)
|
||||
│
|
||||
├─► ¿hay orquestador activo para este room? ──No──► dispatch normal
|
||||
│
|
||||
▼ Sí
|
||||
Orchestrator.Route(event, participants)
|
||||
│
|
||||
│ LLM Call 1: "¿Qué bot responde primero?"
|
||||
▼
|
||||
Bus.Dispatch(taskEvent → bot-A)
|
||||
│
|
||||
▼
|
||||
bot-A.Handle(task) → SendMessage(room, respuesta)
|
||||
│
|
||||
▼
|
||||
Orchestrator.Evaluate(pregunta, respuesta-A)
|
||||
│ LLM Call 2: score + continue?
|
||||
│
|
||||
├─► score >= threshold ──► fin del pipeline
|
||||
│
|
||||
▼ continuar
|
||||
Bus.Dispatch(taskEvent → bot-B) # bot-B ≠ bot-A (exclusión del último)
|
||||
(taskEvent incluye pregunta + respuesta-A como contexto)
|
||||
│
|
||||
▼
|
||||
bot-B.Handle(task) → SendMessage(room, respuesta mejorada)
|
||||
│
|
||||
▼
|
||||
Orchestrator.Evaluate(...) # repite hasta max_iterations o threshold
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Protocolo interno: TaskEvent
|
||||
|
||||
El orquestador no usa Matrix para comunicarse con los bots — usa el bus interno
|
||||
(`shell/bus`). Todos los bots corren en el mismo proceso del launcher.
|
||||
|
||||
```go
|
||||
// pkg/orchestration/task.go
|
||||
type TaskEvent struct {
|
||||
TargetBotID string
|
||||
TargetRoomID string
|
||||
OriginalQuestion string
|
||||
Iteration int
|
||||
PreviousResponses []BotResponse // vacío en primera iteración
|
||||
}
|
||||
|
||||
type BotResponse struct {
|
||||
BotID string
|
||||
Text string
|
||||
}
|
||||
|
||||
type QualityScore struct {
|
||||
Score float64 // 0.0–1.0
|
||||
Continue bool
|
||||
Reason string
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## LLM calls del orquestador
|
||||
|
||||
### Call 1: Routing inicial
|
||||
```
|
||||
System (prompts/routing.md):
|
||||
Eres un coordinador de agentes. Disponibles:
|
||||
- assistant-bot: Asistente general, preguntas, resúmenes, redacción
|
||||
Responde SOLO con el ID del bot más adecuado.
|
||||
|
||||
User: [pregunta del humano]
|
||||
```
|
||||
|
||||
### Call 2: Evaluación de calidad
|
||||
```
|
||||
System (prompts/quality.md):
|
||||
Evalúa si la respuesta resuelve completamente la pregunta.
|
||||
Responde en JSON: {"score": 0.0-1.0, "continue": bool, "reason": "..."}
|
||||
|
||||
User:
|
||||
Pregunta: [...]
|
||||
Respuesta de [bot-X]: [...]
|
||||
```
|
||||
|
||||
### Call 3: Routing de refinamiento (si continue=true)
|
||||
```
|
||||
System:
|
||||
La respuesta necesita mejora. Bots disponibles (excluido [último]):
|
||||
- [lista sin el último respondedor]
|
||||
Responde SOLO con el ID del bot.
|
||||
|
||||
User:
|
||||
Pregunta: [...] | Respuesta actual: [...]
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Comportamiento de los bots en sala orquestada
|
||||
|
||||
Los bots **no saben** que están siendo orquestados. El launcher simplemente no
|
||||
les entrega el evento Matrix directamente. En su lugar reciben un `TaskEvent`
|
||||
via bus con el contexto correcto.
|
||||
|
||||
Un bot en sala orquestada responde al `TaskEvent` igual que responde a un
|
||||
mensaje normal: genera texto y llama a `SendMessage(targetRoomID, text)`.
|
||||
La diferencia la gestiona el launcher, no el bot.
|
||||
|
||||
Esto preserva el principio **pure core / impure shell** — los bots siguen siendo
|
||||
puros, el orquestador es shell.
|
||||
|
||||
---
|
||||
|
||||
## Launcher: registro de specials
|
||||
|
||||
```go
|
||||
// cmd/launcher/main.go — nuevo registro análogo a rulesRegistry
|
||||
var specialsRegistry = map[string]special.Factory{
|
||||
"orchestrator": orchestration.New,
|
||||
// "scheduler": scheduler.New, // futuro
|
||||
// "memory": memory.New, // futuro
|
||||
}
|
||||
```
|
||||
|
||||
El launcher escanea `agents/specials/*/config.yaml`, lee el campo `special.type`,
|
||||
busca en `specialsRegistry` y lo instancia. Los specials se arrancan antes que
|
||||
los bots normales (son infraestructura).
|
||||
|
||||
---
|
||||
|
||||
## Anti-bucle: garantías
|
||||
|
||||
| Escenario | Mitigación |
|
||||
|-----------|-----------|
|
||||
| Bot responde sin ser delegado | El launcher no entrega eventos Matrix en salas orquestadas directamente |
|
||||
| Loop de refinamiento infinito | `max_iterations` hard limit |
|
||||
| Orquestador elige el mismo bot dos veces seguidas | Exclusión explícita del último respondedor en Call 3 |
|
||||
| Bot no responde (timeout) | `delegation_timeout` → orquestador corta o elige otro bot |
|
||||
| Sala con 1 solo bot | El orquestador detecta `len(participants)==1` y hace dispatch directo sin LLM |
|
||||
|
||||
---
|
||||
|
||||
## Archivos a crear
|
||||
|
||||
```
|
||||
agents/specials/orchestrator/
|
||||
config.yaml → config del orquestador (LLM + rooms)
|
||||
prompts/routing.md → system prompt para routing inicial
|
||||
prompts/quality.md → system prompt para evaluación de calidad
|
||||
prompts/refinement.md → system prompt para routing de refinamiento
|
||||
|
||||
pkg/orchestration/
|
||||
task.go → TaskEvent, BotResponse, QualityScore (tipos puros)
|
||||
protocol.go → serialización/deserialización de TaskEvent
|
||||
|
||||
shell/orchestration/
|
||||
orchestrator.go → Orchestrator struct, Route(), Evaluate()
|
||||
runner.go → loop de coordinación, gestión de timeouts
|
||||
|
||||
internal/config/
|
||||
schema.go → SpecialCfg, OrchestrationCfg (nuevas secciones)
|
||||
loader.go → LoadSpecial() análogo a Load()
|
||||
|
||||
cmd/launcher/
|
||||
main.go → specialsRegistry + arranque de specials
|
||||
specials.go → scanSpecials(), instanciación
|
||||
```
|
||||
|
||||
### Modificados
|
||||
```
|
||||
agents/runtime.go → aceptar TaskEvent además de eventos Matrix
|
||||
shell/bus/bus.go → soporte para TaskEvent routing
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Fases de implementación
|
||||
|
||||
### Fase 1 — Scaffold + protocolo básico
|
||||
- Estructura `agents/specials/` y scanner en launcher
|
||||
- `pkg/orchestration/task.go` con tipos puros
|
||||
- Dispatch via bus sin LLM (keyword matching simple)
|
||||
- Un bot responde, sin refinamiento
|
||||
|
||||
### Fase 2 — LLM routing
|
||||
- Call 1 y Call 3 con LLM real
|
||||
- Exclusión del último respondedor
|
||||
- `max_iterations` funcional
|
||||
|
||||
### Fase 3 — Quality evaluation
|
||||
- Call 2 con score de calidad
|
||||
- `quality_threshold` para corte automático
|
||||
- Logs de orquestación en `run/orchestrator.log`
|
||||
|
||||
### Fase 4 — Observabilidad
|
||||
- Topic del room refleja estado del pipeline en curso
|
||||
- `"[2/3] bot respondió · evaluando..."` → topic actualizado en tiempo real
|
||||
@@ -0,0 +1,69 @@
|
||||
# Plan: Editar fotos de perfil de los bots
|
||||
|
||||
## Objetivo
|
||||
Poder actualizar el avatar (foto de perfil) y el display name de cada bot en Matrix
|
||||
desde la CLI (`agentctl`) o desde un dev-script.
|
||||
|
||||
## Estado: COMPLETADO
|
||||
|
||||
---
|
||||
|
||||
## Cómo funciona en Matrix
|
||||
- Endpoint: `PUT /_matrix/client/v3/profile/{userId}/avatar_url`
|
||||
- Body: `{ "avatar_url": "mxc://..." }` — URI de contenido subido al Media repo
|
||||
- Para subir una imagen: `POST /_matrix/media/v3/upload` con el body binario
|
||||
y `Content-Type` de la imagen
|
||||
- También se puede cambiar el display name:
|
||||
`PUT /_matrix/client/v3/profile/{userId}/displayname`
|
||||
|
||||
La secuencia es:
|
||||
1. Subir imagen → obtener `mxc://server/mediaID`
|
||||
2. Establecer `avatar_url` en el perfil con esa URI
|
||||
|
||||
---
|
||||
|
||||
## Diseño
|
||||
|
||||
### CLI: `agentctl avatar <agent-id> <image-path>`
|
||||
Nuevo subcomando en `cmd/agentctl/`:
|
||||
```
|
||||
agentctl avatar assistant-bot /path/to/photo.png
|
||||
agentctl displayname assistant-bot "Assistant Bot"
|
||||
```
|
||||
|
||||
### Shell: `shell/matrix/profile.go`
|
||||
```go
|
||||
// UploadMedia sube un archivo y devuelve la mxc:// URI
|
||||
func UploadMedia(ctx, client, filePath string) (mxcURI string, err error)
|
||||
|
||||
// SetAvatar establece avatar_url en el perfil del bot
|
||||
func SetAvatar(ctx, client, mxcURI string) error
|
||||
|
||||
// SetDisplayName cambia el displayname
|
||||
func SetDisplayName(ctx, client, name string) error
|
||||
```
|
||||
|
||||
Usa el cliente `mautrix.Client` ya existente en `shell/matrix/client.go`.
|
||||
|
||||
### Dev-script: `dev-scripts/avatar.sh`
|
||||
```bash
|
||||
#!/usr/bin/env bash
|
||||
# Uso: ./dev-scripts/avatar.sh <agent-id> <image-path>
|
||||
./bin/agentctl avatar "$1" "$2"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Archivos a crear/modificar
|
||||
- `shell/matrix/profile.go` — UploadMedia, SetAvatar, SetDisplayName
|
||||
- `cmd/agentctl/avatar.go` — subcomando `avatar` y `displayname`
|
||||
- `cmd/agentctl/main.go` — registrar los nuevos subcomandos en Cobra
|
||||
- `dev-scripts/avatar.sh` — wrapper convenience
|
||||
|
||||
## Notas
|
||||
- El token del bot necesita permiso de escritura en su propio perfil (normal por defecto)
|
||||
- Formatos soportados: PNG, JPG, WebP — Matrix los acepta todos
|
||||
- mautrix-go tiene métodos `client.UploadMedia()` y `client.SetAvatarURL()`;
|
||||
usar esos directamente para evitar HTTP manual
|
||||
- El comando debe cargar el token del bot desde las env vars (`MATRIX_TOKEN_<BOT>`)
|
||||
igual que hace `cmd/launcher/`
|
||||
@@ -0,0 +1,108 @@
|
||||
# Plan: Cron scheduler para actividad autónoma de los bots
|
||||
|
||||
## Objetivo
|
||||
Que los bots puedan publicar mensajes, ejecutar tareas o interactuar en salas
|
||||
de forma autónoma según un horario — sin que el usuario tenga que escribirles.
|
||||
|
||||
## Estado: pendiente
|
||||
|
||||
---
|
||||
|
||||
## Casos de uso
|
||||
- Bot saluda "buenos días" en una sala a las 9:00
|
||||
- Devops-bot hace healthcheck de servidores cada hora y reporta
|
||||
- Assistant-bot publica un resumen diario a las 18:00
|
||||
- Bots conversan entre sí a horas fijas para simular actividad
|
||||
|
||||
---
|
||||
|
||||
## Diseño
|
||||
|
||||
### Config YAML — `schedules` (ya existe en el schema)
|
||||
```yaml
|
||||
schedules:
|
||||
- cron: "0 9 * * *" # cada día a las 9:00
|
||||
action: send_message
|
||||
room: "!roomid:server.com"
|
||||
template: "prompts/good-morning.md" # se envía como mensaje o como prompt al LLM
|
||||
|
||||
- cron: "0 * * * *" # cada hora
|
||||
action: run_tool
|
||||
tool: ssh_command
|
||||
args:
|
||||
host: "prod-server"
|
||||
command: "systemctl is-active myapp"
|
||||
|
||||
- cron: "0 18 * * *"
|
||||
action: llm_prompt
|
||||
room: "!roomid:server.com"
|
||||
prompt: "Genera un resumen del día de hoy para el equipo."
|
||||
```
|
||||
|
||||
### Tipos de acción de cron
|
||||
| Tipo | Descripción |
|
||||
|-----------------|-------------------------------------------------------|
|
||||
| `send_message` | Envía un mensaje literal o desde plantilla a una sala |
|
||||
| `run_tool` | Ejecuta una herramienta (SSH, HTTP, etc.) |
|
||||
| `llm_prompt` | Llama al LLM con un prompt y publica la respuesta |
|
||||
|
||||
---
|
||||
|
||||
## Implementación: `shell/cron/`
|
||||
|
||||
```go
|
||||
// Scheduler lanza goroutines para cada schedule configurado
|
||||
type Scheduler struct {
|
||||
agent *agents.Agent
|
||||
cfg []config.ScheduleCfg
|
||||
effects *effects.Runner
|
||||
}
|
||||
|
||||
func (s *Scheduler) Start(ctx context.Context)
|
||||
func (s *Scheduler) Stop()
|
||||
```
|
||||
|
||||
Usa `time.AfterFunc` o una librería cron mínima.
|
||||
|
||||
### Librería cron recomendada
|
||||
`github.com/robfig/cron/v3` — ligera, soporta sintaxis cron estándar y `@every 1h`.
|
||||
Sin dependencias de CGO.
|
||||
|
||||
### Integración en `agents/runtime.go`
|
||||
```go
|
||||
type Agent struct {
|
||||
...
|
||||
scheduler *cron.Scheduler // nil si no hay schedules
|
||||
}
|
||||
|
||||
func (a *Agent) Start(ctx) error {
|
||||
...
|
||||
if len(a.cfg.Schedules) > 0 {
|
||||
a.scheduler = cron.New(a, a.cfg.Schedules, a.runner)
|
||||
a.scheduler.Start(ctx)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Flujo para `llm_prompt`
|
||||
1. El cron dispara
|
||||
2. Construir `CompletionRequest` con el prompt del schedule
|
||||
3. Llamar al LLM (usando `shell/llm/`)
|
||||
4. Emitir `SendMessageAction` con la respuesta
|
||||
5. El Runner lo envía a la sala Matrix configurada
|
||||
|
||||
---
|
||||
|
||||
## Archivos a crear/modificar
|
||||
- `shell/cron/scheduler.go` — Scheduler, parseador de ScheduleCfg
|
||||
- `shell/cron/actions.go` — ejecutores de cada tipo de acción de cron
|
||||
- `internal/config/schema.go` — revisar/completar `ScheduleCfg` (ya tiene campos)
|
||||
- `agents/runtime.go` — instanciar y arrancar el Scheduler
|
||||
- `go.mod` — añadir `github.com/robfig/cron/v3`
|
||||
|
||||
## Notas
|
||||
- El Scheduler corre en goroutines separadas; respetar el `ctx` de shutdown
|
||||
- Los prompts de los schedules pueden ser strings inline o rutas a archivos `.md`
|
||||
- Fase 1: solo `send_message` y `llm_prompt`
|
||||
- Fase 2: `run_tool` con resultado incluido en el mensaje
|
||||
- Fase 3: schedules de interacción entre bots (bot-A pide a bot-B que haga algo)
|
||||
@@ -0,0 +1,22 @@
|
||||
# Plans — Extensiones pendientes
|
||||
|
||||
Cada archivo describe un feature a implementar con su diseño técnico, archivos
|
||||
afectados y notas de implementación.
|
||||
|
||||
| # | Feature | Archivo | Estado |
|
||||
|---|----------------------------|----------------------------|-----------|
|
||||
| 1 | Herramientas para los bots | [01-bot-tools.md](01-bot-tools.md) | pendiente |
|
||||
| 2 | Memoria para los bots | [02-bot-memory.md](02-bot-memory.md) | pendiente |
|
||||
| 3 | Interacción entre bots | [03-bot-interaction.md](03-bot-interaction.md) | pendiente |
|
||||
| 4 | Fotos de perfil | [04-bot-avatar.md](04-bot-avatar.md) | pendiente |
|
||||
| 5 | Cron scheduler | [05-bot-cron.md](05-bot-cron.md) | pendiente |
|
||||
|
||||
## Orden de implementación sugerido
|
||||
|
||||
```
|
||||
04-avatar → independiente, más simple, buen punto de partida
|
||||
01-tools → base necesaria para los demás
|
||||
02-memory → depende de 01-tools (herramientas remember/recall)
|
||||
05-cron → depende de 01-tools (run_tool) y 02-memory (contexto)
|
||||
03-interact → depende de todos los anteriores para ser útil
|
||||
```
|
||||
Reference in New Issue
Block a user