docs: nuevas task specs 015-018 y actualización de 014
Añade especificaciones de tareas para: multi-plataforma Telegram (015), sistema de skills (016), cliente MCP tools (017), shared knowledge (018). Actualiza task 014 para incluir sistema de personalidades y nuevas capacidades del proyecto (skills, shared-knowledge, cron jobs) en el template de agentes. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -1,104 +1,464 @@
|
||||
# 014 — Agente plantilla + estandarización de config.yaml
|
||||
# 014 — Agente plantilla + sistema de personalidades + estandarizacion
|
||||
|
||||
## Objetivo
|
||||
Crear un agente plantilla (no lanzable) que sirva como referencia canónica para la configuración de todos los agentes. Enriquecer `!info` para mostrar metadata completa. Estandarizar los config.yaml existentes.
|
||||
|
||||
Crear un agente plantilla (no lanzable) que sirva como referencia canonica para la configuracion de todos los agentes. Incluir un sistema de personalidades rico que permita definir agentes con caracteres distintos. Enriquecer `!info` para mostrar metadata completa. Estandarizar los config.yaml existentes integrando las nuevas capacidades del proyecto: skills, shared-knowledge, cron jobs.
|
||||
|
||||
## Contexto
|
||||
- El launcher descubre agentes via `agents/*/config.yaml` (glob en cmd/launcher/main.go:55)
|
||||
- `!info` existe como built-in en `pkg/command/builtins.go` pero solo muestra: nombre, ID, versión, descripción
|
||||
|
||||
- El launcher descubre agentes via `agents/*/config.yaml` (glob en cmd/launcher/main.go)
|
||||
- `!info` existe como built-in en `agents/commands.go` pero solo muestra: nombre, ID, version, descripcion
|
||||
- No hay herencia de configs ni template base — cada config.yaml es autocontenido
|
||||
- Agentes actuales: assistant-bot, asistente-2, meteorologo
|
||||
- Agentes actuales: assistant-bot, asistente-2
|
||||
- La seccion `personality` actual es basica: tone, verbosity, emoji_style, templates, behavior
|
||||
- Nuevas capacidades en desarrollo: skills (016), shared-knowledge (018), cron jobs (005)
|
||||
|
||||
---
|
||||
|
||||
## Tareas
|
||||
|
||||
### Fase 1: Agente plantilla
|
||||
### Fase 1: Sistema de personalidades enriquecido
|
||||
|
||||
- [ ] **1.1** Decidir mecanismo de exclusión del launcher. Opciones:
|
||||
- (A) Campo `agent.template: true` en config.yaml → launcher lo ignora
|
||||
- (B) Directorio especial fuera de `agents/` (ej: `agents/_template/`)
|
||||
- (C) Convención de nombre con prefijo `_` que el glob excluya
|
||||
- **Recomendación**: opción (A) es la más explícita y extensible. Añadir campo `Template bool` a `AgentMeta` en schema.go y filtrar en launcher.
|
||||
El sistema actual (`pkg/personality/traits.go` + `PersonalityCfg` en schema.go) define tone, verbosity, emoji, error_style, templates y behavior. Esto es funcional pero plano — todos los agentes terminan sonando igual con variaciones menores.
|
||||
|
||||
- [ ] **1.2** Añadir campo `template: true` a `internal/config/schema.go` → `AgentMeta`
|
||||
El objetivo es ampliar la personalidad para que cada agente tenga un **caracter unico** que se refleje en como habla, piensa y actua.
|
||||
|
||||
- [ ] **1.3** Filtrar agentes template en `cmd/launcher/main.go` — skip si `cfg.Agent.Template == true`
|
||||
- [ ] **1.1** Ampliar `PersonalityCfg` en `internal/config/schema.go` con nuevos campos:
|
||||
|
||||
- [ ] **1.4** Crear `agents/_template/config.yaml` con TODAS las secciones documentadas y valores por defecto comentados. Este archivo es la referencia canónica. Incluir:
|
||||
- `agent:` — id, name, description, version, tags, template: true
|
||||
- `personality:` — valores estándar (tone, verbosity, language, emoji_style, prefix, templates, behavior)
|
||||
- `llm:` — primary con placeholders, tool_use config, fallback
|
||||
- `tools:` — todas las subsecciones (memory, knowledge, ssh, http, scripts, file_ops, mcp)
|
||||
- `matrix:` — homeserver, user_id, encryption, rooms, filters
|
||||
- `agents:` — peers, delegation
|
||||
- `security:` — roles, audit
|
||||
- `schedules:` — ejemplo de cron
|
||||
- `observability:` — logging, metrics, health
|
||||
- `resilience:` — circuit breaker, retry, shutdown
|
||||
- `storage:` — state, cache, history
|
||||
- `memory:` — window config
|
||||
```go
|
||||
type PersonalityCfg struct {
|
||||
// --- campos existentes (sin cambios) ---
|
||||
Tone string `yaml:"tone"`
|
||||
Verbosity string `yaml:"verbosity"`
|
||||
Language string `yaml:"language"`
|
||||
LanguagesSupported []string `yaml:"languages_supported"`
|
||||
EmojiStyle string `yaml:"emoji_style"`
|
||||
Prefix string `yaml:"prefix"`
|
||||
ErrorStyle string `yaml:"error_style"`
|
||||
Templates TemplatesCfg `yaml:"templates"`
|
||||
Behavior BehaviorCfg `yaml:"behavior"`
|
||||
|
||||
- [ ] **1.5** Crear `agents/_template/agent.go` mínimo con `Rules()` que retorna slice vacío (para que compile, aunque nunca se lance)
|
||||
// --- NUEVOS campos ---
|
||||
// Identidad narrativa
|
||||
Role string `yaml:"role"` // rol principal: "asistente general", "devops engineer", "analista de datos"
|
||||
Backstory string `yaml:"backstory"` // breve historia/contexto del personaje (1-3 frases)
|
||||
Expertise []string `yaml:"expertise"` // areas de experiencia: ["linux", "docker", "monitoring"]
|
||||
Limitations []string `yaml:"limitations"` // que NO sabe o no debe intentar
|
||||
|
||||
- [ ] **1.6** Actualizar `dev-scripts/agent/new-agent.sh` para copiar desde `_template/` en lugar de generar inline
|
||||
// Estilo de comunicacion
|
||||
Communication CommunicationCfg `yaml:"communication"`
|
||||
|
||||
### Fase 2: Enriquecer `!info`
|
||||
// Directivas de comportamiento en texto libre
|
||||
CustomDirectives []string `yaml:"custom_directives"` // instrucciones adicionales para el system prompt
|
||||
}
|
||||
|
||||
- [ ] **2.1** Modificar el handler de `!info` en `agents/commands.go` para que devuelva:
|
||||
- Nombre (`agent.name`)
|
||||
- Descripción (`agent.description`)
|
||||
- Versión (`agent.version`)
|
||||
- Personalidad: tone, verbosity, language, emoji_style
|
||||
- LLM: provider + modelo del primary
|
||||
- Nº de tools registradas (del toolRegistry)
|
||||
- Memoria habilitada (sí/no + window size)
|
||||
- Knowledge habilitado (sí/no)
|
||||
- Nº de docs en knowledge (si habilitado, contar archivos en knowledge dir)
|
||||
// CommunicationCfg define como se expresa el agente mas alla del tone basico.
|
||||
type CommunicationCfg struct {
|
||||
Formality string `yaml:"formality"` // formal | semiformal | casual | coloquial
|
||||
Humor string `yaml:"humor"` // none | subtle | moderate | frequent
|
||||
Personality string `yaml:"personality"` // analytical | creative | pragmatic | empathetic | assertive
|
||||
ResponseStyle string `yaml:"response_style"` // structured | conversational | bullet_points | narrative
|
||||
Quirks []string `yaml:"quirks"` // rasgos unicos: ["usa analogias de cocina", "cita a Linus Torvalds"]
|
||||
AvoidTopics []string `yaml:"avoid_topics"` // temas que evita o redirige
|
||||
Catchphrases []string `yaml:"catchphrases"` // frases tipicas que usa ocasionalmente
|
||||
}
|
||||
```
|
||||
|
||||
- [ ] **1.2** Ampliar tipos puros en `pkg/personality/traits.go`:
|
||||
|
||||
```go
|
||||
type Formality string
|
||||
const (
|
||||
FormalityFormal Formality = "formal"
|
||||
FormalitySemiformal Formality = "semiformal"
|
||||
FormalityCasual Formality = "casual"
|
||||
FormalityColoquial Formality = "coloquial"
|
||||
)
|
||||
|
||||
type Humor string
|
||||
const (
|
||||
HumorNone Humor = "none"
|
||||
HumorSubtle Humor = "subtle"
|
||||
HumorModerate Humor = "moderate"
|
||||
HumorFrequent Humor = "frequent"
|
||||
)
|
||||
|
||||
type PersonalityType string
|
||||
const (
|
||||
PersonalityAnalytical PersonalityType = "analytical"
|
||||
PersonalityCreative PersonalityType = "creative"
|
||||
PersonalityPragmatic PersonalityType = "pragmatic"
|
||||
PersonalityEmpathetic PersonalityType = "empathetic"
|
||||
PersonalityAssertive PersonalityType = "assertive"
|
||||
)
|
||||
|
||||
type ResponseStyle string
|
||||
const (
|
||||
ResponseStructured ResponseStyle = "structured"
|
||||
ResponseConversational ResponseStyle = "conversational"
|
||||
ResponseBulletPoints ResponseStyle = "bullet_points"
|
||||
ResponseNarrative ResponseStyle = "narrative"
|
||||
)
|
||||
```
|
||||
|
||||
Ampliar el struct `Personality` con los nuevos campos correspondientes.
|
||||
|
||||
- [ ] **1.3** Crear funcion `BuildPersonalityPrompt(cfg PersonalityCfg) string` en `pkg/personality/` que genere un bloque de system prompt a partir de la config de personalidad. Esta funcion es **pura** — recibe config, devuelve string. El runtime la usa para inyectar personalidad en el prompt del LLM.
|
||||
|
||||
El prompt generado debe incluir:
|
||||
- Rol y backstory
|
||||
- Expertise y limitaciones
|
||||
- Estilo de comunicacion (formality, humor, personality, response_style)
|
||||
- Quirks y catchphrases
|
||||
- Custom directives
|
||||
- Todo redactado como instrucciones naturales para el LLM
|
||||
|
||||
Ejemplo de output:
|
||||
```
|
||||
## Tu personalidad
|
||||
|
||||
Eres un ingeniero DevOps senior con 10 anos de experiencia en Linux y containers.
|
||||
|
||||
**Rol**: DevOps engineer especializado en infraestructura y monitoring.
|
||||
**Expertise**: Linux, Docker, Kubernetes, Prometheus, bash scripting.
|
||||
**Limitaciones**: No das consejos de frontend ni diseno UI.
|
||||
|
||||
**Como te comunicas**:
|
||||
- Tono semiformal, directo pero amable
|
||||
- Humor sutil — algun comentario ironico cuando algo falla de forma obvia
|
||||
- Estilo pragmatico — siempre priorizas la solucion sobre la teoria
|
||||
- Respuestas estructuradas con comandos claros
|
||||
- A veces citas a Linus Torvalds o usas analogias mecanicas
|
||||
|
||||
**Directivas especiales**:
|
||||
- Siempre sugiere verificar con un dry-run antes de ejecutar cambios destructivos
|
||||
- Cuando algo falla, muestra el log relevante antes de diagnosticar
|
||||
```
|
||||
|
||||
- [ ] **1.4** Integrar `BuildPersonalityPrompt` en `agents/runtime.go` — concatenar el bloque de personalidad al system prompt leido del archivo. El orden debe ser: system prompt del archivo + bloque de personalidad generado.
|
||||
|
||||
### Fase 2: Agente plantilla con personalidades de ejemplo
|
||||
|
||||
- [ ] **2.1** Anadir campo `Template bool` a `AgentMeta` en `internal/config/schema.go`
|
||||
|
||||
- [ ] **2.2** Filtrar agentes template en `cmd/launcher/main.go` — skip si `cfg.Agent.Template == true`
|
||||
|
||||
- [ ] **2.3** Crear `agents/_template/config.yaml` — referencia canonica con TODAS las secciones. Incluir:
|
||||
|
||||
**Identidad**:
|
||||
```yaml
|
||||
agent:
|
||||
id: "_template"
|
||||
name: "Template Agent"
|
||||
version: "0.0.0"
|
||||
enabled: true
|
||||
template: true # el launcher ignora este agente
|
||||
description: "Agente plantilla. No se lanza. Sirve como referencia para crear nuevos agentes."
|
||||
tags: [template]
|
||||
```
|
||||
|
||||
**Personalidad completa** (con todos los campos nuevos documentados):
|
||||
```yaml
|
||||
personality:
|
||||
# --- Identidad narrativa ---
|
||||
role: "asistente general"
|
||||
backstory: "Un asistente amigable creado para ayudar con tareas cotidianas."
|
||||
expertise: [general]
|
||||
limitations: []
|
||||
|
||||
# --- Estilo basico ---
|
||||
tone: friendly # direct | friendly | formal | casual | technical
|
||||
verbosity: concise # minimal | concise | detailed | verbose
|
||||
language: es
|
||||
languages_supported: [es, en]
|
||||
emoji_style: minimal # none | minimal | moderate | heavy
|
||||
prefix: ""
|
||||
error_style: helpful # terse | helpful | detailed
|
||||
|
||||
# --- Comunicacion avanzada ---
|
||||
communication:
|
||||
formality: semiformal # formal | semiformal | casual | coloquial
|
||||
humor: none # none | subtle | moderate | frequent
|
||||
personality: pragmatic # analytical | creative | pragmatic | empathetic | assertive
|
||||
response_style: structured # structured | conversational | bullet_points | narrative
|
||||
quirks: [] # rasgos unicos del personaje
|
||||
avoid_topics: [] # temas a evitar
|
||||
catchphrases: [] # frases tipicas
|
||||
|
||||
# --- Directivas libres ---
|
||||
custom_directives: [] # instrucciones extra para el system prompt
|
||||
|
||||
# --- Templates de respuesta ---
|
||||
templates:
|
||||
greeting: "Hola, soy {name}. En que puedo ayudarte?"
|
||||
unknown_command: "No entiendo ese comando. Usa !help."
|
||||
permission_denied: "No tienes permiso para eso."
|
||||
error: "Algo salio mal: {{.Error}}"
|
||||
success: "{{.Summary}}"
|
||||
busy: "Estoy procesando otra solicitud, un momento..."
|
||||
|
||||
# --- Comportamiento ---
|
||||
behavior:
|
||||
proactive: false
|
||||
ask_confirmation: false
|
||||
show_reasoning: false
|
||||
thread_replies: true
|
||||
typing_indicator: true
|
||||
acknowledge_receipt: false
|
||||
```
|
||||
|
||||
**Skills** (nueva seccion):
|
||||
```yaml
|
||||
skills:
|
||||
enabled: false
|
||||
path: "skills/" # ruta base de skills (relativa al proyecto)
|
||||
categories: [] # vacio = todas las categorias | ["devops", "system"] = filtradas
|
||||
```
|
||||
|
||||
**Shared knowledge** (nueva seccion):
|
||||
```yaml
|
||||
# Dentro de tools:
|
||||
tools:
|
||||
# ... tools existentes ...
|
||||
|
||||
shared_knowledge:
|
||||
enabled: false
|
||||
dir: "knowledges" # directorio compartido
|
||||
db_path: "knowledges/data/knowledge.db"
|
||||
```
|
||||
|
||||
**Schedules con ejemplos**:
|
||||
```yaml
|
||||
schedules:
|
||||
# - name: "buenos-dias"
|
||||
# cron: "0 9 * * 1-5"
|
||||
# action:
|
||||
# kind: llm_prompt
|
||||
# target: "Buenos dias equipo. Dame un resumen rapido del estado de los servicios."
|
||||
# output_room: "!roomid:server.com"
|
||||
# on_failure:
|
||||
# notify_room: ""
|
||||
# escalate_to: ""
|
||||
```
|
||||
|
||||
Incluir TODAS las demas secciones (llm, matrix, agents, ssh, security, observability, resilience, storage, memory) con valores por defecto documentados.
|
||||
|
||||
- [ ] **2.4** Crear `agents/_template/agent.go` minimo con `Rules()` retornando slice vacio
|
||||
|
||||
- [ ] **2.5** Crear `agents/_template/prompts/system.md` con un system prompt plantilla que muestre donde va cada seccion (instrucciones base, personalidad inyectada automaticamente, tools disponibles, etc.)
|
||||
|
||||
- [ ] **2.6** Actualizar `dev-scripts/agent/new-agent.sh` para copiar desde `_template/` en lugar de generar inline
|
||||
|
||||
### Fase 3: Ejemplos de personalidades distintas
|
||||
|
||||
Para demostrar que el sistema funciona, definir perfiles de personalidad que se puedan usar como punto de partida. Estos van como comentarios/documentacion en el template, NO como agentes reales.
|
||||
|
||||
- [ ] **3.1** Documentar en `agents/_template/PERSONALITIES.md` al menos 4 perfiles de ejemplo:
|
||||
|
||||
**Perfil: DevOps pragmatico**
|
||||
```yaml
|
||||
personality:
|
||||
role: "ingeniero DevOps senior"
|
||||
backstory: "Veterano de infraestructura con cicatrices de guerra de incidentes en produccion."
|
||||
expertise: [linux, docker, kubernetes, monitoring, bash, networking]
|
||||
limitations: ["no da consejos de frontend", "no hace diseno UI"]
|
||||
tone: direct
|
||||
verbosity: concise
|
||||
communication:
|
||||
formality: semiformal
|
||||
humor: subtle
|
||||
personality: pragmatic
|
||||
response_style: structured
|
||||
quirks: ["usa analogias mecanicas", "siempre pide ver los logs primero"]
|
||||
catchphrases: ["primero los logs, despues las teorias", "en produccion no se experimenta"]
|
||||
custom_directives:
|
||||
- "Siempre sugiere dry-run antes de cambios destructivos"
|
||||
- "Incluye el comando exacto, no solo la descripcion"
|
||||
```
|
||||
|
||||
**Perfil: Analista meticuloso**
|
||||
```yaml
|
||||
personality:
|
||||
role: "analista de datos"
|
||||
backstory: "Obsesionado con los patrones y las anomalias. Nada escapa a su atencion."
|
||||
expertise: [analisis de logs, metricas, estadistica, patrones de errores]
|
||||
limitations: ["no ejecuta cambios en produccion", "no toma decisiones operativas"]
|
||||
tone: technical
|
||||
verbosity: detailed
|
||||
communication:
|
||||
formality: formal
|
||||
humor: none
|
||||
personality: analytical
|
||||
response_style: structured
|
||||
quirks: ["siempre cuantifica", "pide rango de fechas antes de analizar"]
|
||||
catchphrases: ["los datos no mienten", "correlacion no implica causalidad"]
|
||||
```
|
||||
|
||||
**Perfil: Asistente amigable**
|
||||
```yaml
|
||||
personality:
|
||||
role: "asistente personal"
|
||||
backstory: "Siempre dispuesto a ayudar, paciente y claro en sus explicaciones."
|
||||
expertise: [tareas generales, redaccion, organizacion, resumen]
|
||||
limitations: ["no tiene acceso a servidores", "no ejecuta codigo"]
|
||||
tone: friendly
|
||||
verbosity: concise
|
||||
communication:
|
||||
formality: casual
|
||||
humor: subtle
|
||||
personality: empathetic
|
||||
response_style: conversational
|
||||
quirks: ["pregunta si quieres mas detalle", "celebra cuando termina una tarea"]
|
||||
catchphrases: ["listo!", "algo mas en lo que pueda ayudar?"]
|
||||
```
|
||||
|
||||
**Perfil: Guardian de seguridad**
|
||||
```yaml
|
||||
personality:
|
||||
role: "especialista en seguridad"
|
||||
backstory: "Paranoico profesional. Asume que todo esta comprometido hasta demostrar lo contrario."
|
||||
expertise: [seguridad, auditoria, permisos, CVEs, hardening]
|
||||
limitations: ["no implementa features", "no optimiza performance"]
|
||||
tone: formal
|
||||
verbosity: detailed
|
||||
communication:
|
||||
formality: formal
|
||||
humor: none
|
||||
personality: assertive
|
||||
response_style: bullet_points
|
||||
quirks: ["siempre menciona el principio de minimo privilegio", "pide MFA para todo"]
|
||||
catchphrases: ["confiar pero verificar", "eso necesita un CVE review"]
|
||||
custom_directives:
|
||||
- "Nunca sugieras deshabilitar firewalls o SELinux como solucion"
|
||||
- "Siempre recomienda rotar credenciales despues de un incidente"
|
||||
```
|
||||
|
||||
### Fase 4: Enriquecer `!info`
|
||||
|
||||
- [ ] **4.1** Modificar el handler de `!info` en `agents/commands.go` para que devuelva:
|
||||
- Nombre, ID, version, descripcion
|
||||
- Personalidad: role, tone, formality, personality type, humor
|
||||
- LLM: provider + modelo
|
||||
- Tools habilitadas (lista de nombres)
|
||||
- Skills habilitadas (si/no + categorias + cantidad)
|
||||
- Knowledge: privado (si/no), compartido (si/no)
|
||||
- Memoria: si/no + window size
|
||||
- Schedules: cantidad de cron jobs configurados
|
||||
- Uptime del agente
|
||||
|
||||
- [ ] **2.2** Formatear la respuesta como bloque legible (markdown con secciones o tabla)
|
||||
- [ ] **4.2** Formatear como markdown legible con secciones
|
||||
|
||||
- [ ] **2.3** Asegurar que `!info` no exponga datos sensibles (tokens, API keys, paths internos)
|
||||
- [ ] **4.3** No exponer datos sensibles (tokens, API keys, paths internos, passwords)
|
||||
|
||||
### Fase 3: Estandarizar configs existentes
|
||||
### Fase 5: Estandarizar configs existentes
|
||||
|
||||
- [ ] **3.1** Definir convenciones estándar obligatorias para todo config.yaml:
|
||||
- [ ] **5.1** Definir convenciones estandar obligatorias para todo config.yaml:
|
||||
- `agent.version` siempre presente (semver)
|
||||
- `agent.tags` siempre presente (al menos un tag de categoría)
|
||||
- `personality.language` y `personality.languages_supported` siempre explícitos
|
||||
- `personality.behavior` siempre con las 6 claves (proactive, ask_confirmation, show_reasoning, thread_replies, typing_indicator, acknowledge_receipt)
|
||||
- `llm.tool_use` siempre explícito (enabled true/false, max_iterations)
|
||||
- `agent.tags` siempre presente (al menos un tag)
|
||||
- `personality.role` siempre presente
|
||||
- `personality.language` y `personality.languages_supported` siempre explicitos
|
||||
- `personality.communication` siempre presente (al menos formality y personality)
|
||||
- `personality.behavior` siempre con las 6 claves
|
||||
- `llm.tool_use` siempre explicito (enabled true/false, max_iterations)
|
||||
- `tools.memory` y `tools.knowledge` siempre presentes (enabled true/false)
|
||||
- `matrix.homeserver` y `matrix.encryption` siempre presentes
|
||||
- `observability.logging.level` siempre explícito
|
||||
- `observability.logging.level` siempre explicito
|
||||
- Si `skills.enabled: true`, al menos `skills.path` definido
|
||||
- Si `schedules` tiene entradas, cada una con `name` y `cron` validos
|
||||
|
||||
- [ ] **3.2** Actualizar `agents/assistant-bot/config.yaml` según convenciones
|
||||
- [ ] **5.2** Actualizar `agents/assistant-bot/config.yaml` — anadir personalidad rica:
|
||||
```yaml
|
||||
personality:
|
||||
role: "asistente general"
|
||||
backstory: "Asistente polivalente, siempre listo para ayudar con cualquier tarea."
|
||||
expertise: [general, redaccion, resumen, consultas]
|
||||
limitations: []
|
||||
communication:
|
||||
formality: semiformal
|
||||
humor: subtle
|
||||
personality: empathetic
|
||||
response_style: conversational
|
||||
quirks: []
|
||||
avoid_topics: []
|
||||
catchphrases: []
|
||||
custom_directives: []
|
||||
# ... mas secciones nuevas (skills, shared_knowledge, etc.)
|
||||
```
|
||||
|
||||
- [ ] **3.3** Actualizar `agents/asistente-2/config.yaml` según convenciones
|
||||
- [ ] **5.3** Actualizar `agents/asistente-2/config.yaml` — idem, personalidad diferenciada
|
||||
|
||||
- [ ] **3.4** Actualizar `agents/meteorologo/config.yaml` según convenciones
|
||||
- [ ] **5.4** Validar que ambos agentes arrancan correctamente tras los cambios
|
||||
|
||||
- [ ] **3.5** Validar que los tres agentes arrancan correctamente tras los cambios
|
||||
### Fase 6: Integracion con nuevas capacidades en config
|
||||
|
||||
### Fase 4: Documentación y tooling
|
||||
Las tasks 005 (cron), 016 (skills) y 018 (shared-knowledge) definen nuevos sistemas. El template debe incluir sus secciones de config para que nuevos agentes ya las tengan disponibles.
|
||||
|
||||
- [ ] **4.1** Añadir validación en `internal/config/loader.go` que emita warnings si faltan secciones recomendadas (no bloquear, solo log)
|
||||
- [ ] **6.1** Anadir `SkillsCfg` al `AgentConfig` en schema.go (si no lo hizo la task 016):
|
||||
```go
|
||||
type SkillsCfg struct {
|
||||
Enabled bool `yaml:"enabled"`
|
||||
Path string `yaml:"path"` // default: "skills/"
|
||||
Categories []string `yaml:"categories"` // filtro de categorias
|
||||
}
|
||||
```
|
||||
|
||||
- [ ] **4.2** Actualizar `.claude/policies/create_agent.md` para referenciar el template
|
||||
- [ ] **6.2** Anadir `SharedKnowledgeCfg` al `ToolsCfg` en schema.go (si no lo hizo la task 018):
|
||||
```go
|
||||
type SharedKnowledgeCfg struct {
|
||||
Enabled bool `yaml:"enabled"`
|
||||
Dir string `yaml:"dir"` // default: "knowledges"
|
||||
DBPath string `yaml:"db_path"` // default: "knowledges/data/knowledge.db"
|
||||
}
|
||||
```
|
||||
|
||||
- [ ] **4.3** Actualizar `docs/creating-agents.md` con la nueva referencia del template
|
||||
- [ ] **6.3** Verificar que `ScheduleCfg` soporta los 3 tipos de accion (send_message, run_tool, llm_prompt) — el schema actual ya los tiene pero validar completitud
|
||||
|
||||
- [ ] **6.4** Actualizar el template con las secciones de skills, shared_knowledge y schedules de ejemplo
|
||||
|
||||
### Fase 7: Documentacion y tooling
|
||||
|
||||
- [ ] **7.1** Anadir validacion en `internal/config/loader.go` que emita warnings si faltan secciones recomendadas (no bloquear, solo log):
|
||||
- personality.role vacio
|
||||
- personality.communication sin definir
|
||||
- skills.enabled true pero sin path
|
||||
- schedules con entradas sin name
|
||||
|
||||
- [ ] **7.2** Actualizar `.claude/policies/create_agent.md` para:
|
||||
- Referenciar el template como punto de partida
|
||||
- Incluir paso de definir personalidad rica
|
||||
- Incluir paso de decidir skills y shared-knowledge
|
||||
|
||||
- [ ] **7.3** Actualizar `docs/creating-agents.md` con la seccion de personalidades
|
||||
|
||||
- [ ] **7.4** Actualizar `CLAUDE.md` — agregar `SkillsCfg` y `SharedKnowledgeCfg` a la descripcion del schema
|
||||
|
||||
---
|
||||
|
||||
## Orden de ejecución recomendado
|
||||
1. Fase 1 (template) → base para todo lo demás
|
||||
2. Fase 2 (info) → independiente, puede hacerse en paralelo
|
||||
3. Fase 3 (estandarizar) → requiere Fase 1 como referencia
|
||||
4. Fase 4 (docs) → última, cuando todo esté estable
|
||||
## Orden de ejecucion recomendado
|
||||
|
||||
## Decisiones de diseño pendientes
|
||||
- ¿El template debería ser un config.yaml real parseable o solo documentación con comentarios?
|
||||
→ Recomendación: real parseable con `template: true`, así sirve como test de que el schema está completo
|
||||
- ¿Añadir un comando `agentctl validate <config.yaml>` para verificar conformidad?
|
||||
→ Nice-to-have, se puede añadir en una fase posterior
|
||||
1. **Fase 1** (sistema de personalidades) — tipos puros + BuildPersonalityPrompt + integracion runtime
|
||||
2. **Fase 2** (template) — config.yaml canonica con todo documentado
|
||||
3. **Fase 3** (ejemplos de personalidades) — PERSONALITIES.md como referencia
|
||||
4. **Fase 5** (estandarizar configs) — aplicar nuevos campos a agentes existentes
|
||||
5. **Fase 4** (info) — mostrar la metadata enriquecida
|
||||
6. **Fase 6** (nuevas capacidades) — integrar skills/knowledge/cron en schema si no existen
|
||||
7. **Fase 7** (docs) — cuando todo este estable
|
||||
|
||||
## Dependencias con otras tasks
|
||||
|
||||
| Task | Relacion |
|
||||
|------|----------|
|
||||
| 005 (cron) | El template incluye schedules de ejemplo. Si 005 no esta implementado, los schedules son solo config sin efecto. |
|
||||
| 016 (skills) | El template incluye `skills:` config. Si 016 no esta implementado, el runtime ignora la seccion. |
|
||||
| 018 (shared-knowledge) | El template incluye `shared_knowledge:` config. Si 018 no esta implementado, el runtime la ignora. |
|
||||
|
||||
Esta task puede ejecutarse **antes** que 005/016/018 — solo define el schema y template. Las otras tasks implementan la funcionalidad real.
|
||||
|
||||
## Decisiones de diseno
|
||||
|
||||
- **Personalidad en config, no en codigo**: la personalidad se define 100% en YAML y se transforma a prompt via `BuildPersonalityPrompt`. Cero logica de personalidad en Go.
|
||||
- **BuildPersonalityPrompt es pura**: vive en `pkg/personality/`, recibe datos, devuelve string. Sin side effects.
|
||||
- **Personalidad se concatena al system prompt**: no reemplaza el archivo `prompts/system.md`, se anade despues. El archivo define instrucciones base, la personalidad anade caracter.
|
||||
- **Template parseable**: el config.yaml del template es YAML valido con `template: true`. Sirve como test de que el schema esta completo.
|
||||
- **Backwards compatible**: los campos nuevos son opcionales. Agentes existentes sin `communication` o `role` siguen funcionando — `BuildPersonalityPrompt` genera un bloque vacio/minimo si no hay datos.
|
||||
- **PERSONALITIES.md como catalogo**: no son agentes reales, son perfiles de referencia. Al crear un agente nuevo, se copia un perfil y se ajusta.
|
||||
|
||||
@@ -0,0 +1,211 @@
|
||||
# 015 — Soporte multi-plataforma: Telegram como segunda plataforma
|
||||
|
||||
## Objetivo
|
||||
|
||||
Desacoplar el runtime de agentes de Matrix e introducir abstracciones de plataforma que permitan conectar un mismo agente a multiples servicios de mensajeria. Implementar Telegram como primera plataforma adicional para validar el diseno.
|
||||
|
||||
## Contexto
|
||||
|
||||
- Actualmente `agents/runtime.go` depende directamente de `*matrix.Client` y `*matrix.Listener`
|
||||
- `shell/effects/runner.go` ya define `MatrixSender` como interfaz, pero con nombre acoplado
|
||||
- `decision.MessageContext` es **generico** — no tiene nada de Matrix
|
||||
- Las reglas, LLM, tools y memoria son independientes de la plataforma
|
||||
- El acoplamiento esta en: runtime.go, effects/runner.go, listener, y algunos tools (matrix_send)
|
||||
|
||||
## Prerequisitos
|
||||
|
||||
- Ninguno estricto. Se puede hacer de forma incremental sin romper Matrix.
|
||||
|
||||
---
|
||||
|
||||
## Tareas
|
||||
|
||||
### Fase 1: Abstracciones de plataforma en `pkg/platform/`
|
||||
|
||||
- [ ] **1.1** Crear `pkg/platform/types.go` con las interfaces puras:
|
||||
```go
|
||||
// Messenger envía mensajes a una plataforma de chat.
|
||||
type Messenger interface {
|
||||
SendText(ctx context.Context, roomID, text string) error
|
||||
SendMarkdown(ctx context.Context, roomID, markdown string) error
|
||||
SendReplyMarkdown(ctx context.Context, roomID, inReplyTo, markdown string) error
|
||||
SendTyping(ctx context.Context, roomID string, typing bool) error
|
||||
}
|
||||
|
||||
// EventSource escucha eventos de una plataforma y los entrega como MessageContext.
|
||||
type EventSource interface {
|
||||
Run(ctx context.Context) error
|
||||
}
|
||||
|
||||
// Platform agrupa Messenger + EventSource para una plataforma concreta.
|
||||
type Platform interface {
|
||||
Messenger
|
||||
EventSource
|
||||
Name() string // "matrix", "telegram", "slack", etc.
|
||||
}
|
||||
```
|
||||
Nota: estas interfaces van en `pkg/` porque son tipos puros (no ejecutan I/O, solo los definen).
|
||||
|
||||
- [ ] **1.2** Definir `PlatformID` como prefijo para room IDs multi-plataforma:
|
||||
- Formato: `matrix:!abc123:server.com`, `telegram:chat_456`
|
||||
- Crear helpers `PrefixRoomID(platform, rawID) string` y `ParseRoomID(prefixed) (platform, rawID)`
|
||||
- Esto permite que la memoria y windows no mezclen contextos entre plataformas
|
||||
|
||||
### Fase 2: Adaptar shell/matrix/ a las interfaces
|
||||
|
||||
- [ ] **2.1** Verificar que `shell/matrix/Client` ya satisface `platform.Messenger` (deberia, con los metodos actuales). Anadir metodo `Name() string` que retorne `"matrix"`.
|
||||
|
||||
- [ ] **2.2** Refactorizar `shell/matrix/Listener` para que implemente `platform.EventSource`:
|
||||
- El `EventHandler` callback ya recibe `decision.MessageContext` — solo necesita ajustar la firma de `Run(ctx)` si difiere
|
||||
- Internamente sigue usando mautrix syncer, pero externamente expone la interfaz generica
|
||||
|
||||
- [ ] **2.3** Crear wrapper `shell/matrix/Platform` que componga Client + Listener e implemente `platform.Platform`
|
||||
|
||||
### Fase 3: Desacoplar runtime.go
|
||||
|
||||
- [ ] **3.1** Cambiar el campo `matrix *matrix.Client` en `Agent` struct por `messenger platform.Messenger`
|
||||
|
||||
- [ ] **3.2** Cambiar `listener *matrix.Listener` por `sources []platform.EventSource`
|
||||
|
||||
- [ ] **3.3** Actualizar `Run()` para arrancar multiples EventSources en goroutines:
|
||||
```go
|
||||
for _, src := range a.sources {
|
||||
go src.Run(ctx)
|
||||
}
|
||||
```
|
||||
|
||||
- [ ] **3.4** Actualizar `handleEvent` para que no reciba `*event.Event` — actualmente solo usa `evt.RoomID` que ya esta en `MessageContext.RoomID`. Eliminar la dependencia de `mautrix/event`.
|
||||
|
||||
- [ ] **3.5** Actualizar todas las llamadas directas a `a.matrix.SendXxx()` y `a.matrix.SendTyping()` para usar `a.messenger.SendXxx()`. Puntos clave:
|
||||
- `handleEvent` — typing indicator, command replies, unknown command
|
||||
- `executeActions` — ya pasa por el runner, OK
|
||||
- `handleTaskEvent` — typing indicator, send reply
|
||||
- `runLLM` — tool call notices
|
||||
|
||||
- [ ] **3.6** Actualizar `shell/effects/runner.go`:
|
||||
- Renombrar interfaz `MatrixSender` a `Messenger` (o importar `platform.Messenger`)
|
||||
- El Runner ya recibe la interfaz, solo cambia el nombre
|
||||
|
||||
- [ ] **3.7** Actualizar `New()` constructor para recibir `[]platform.Platform` en vez de construir matrix.Client internamente. Mover la creacion de clientes de plataforma al launcher.
|
||||
|
||||
### Fase 4: Implementar shell/telegram/
|
||||
|
||||
- [ ] **4.1** Elegir libreria de Telegram Bot API para Go. Opciones:
|
||||
- (A) `github.com/go-telegram-bot-api/telegram-bot-api/v5` — la mas popular, estable
|
||||
- (B) `github.com/gotd/td` — cliente completo (MTProto), mas complejo
|
||||
- (C) HTTP directo contra Bot API — minimo, sin dependencias extra
|
||||
- **Recomendacion**: opcion (A) por madurez y simplicidad
|
||||
|
||||
- [ ] **4.2** Crear `shell/telegram/client.go`:
|
||||
- Struct `Client` con el bot API client interno
|
||||
- Constructor `New(cfg config.TelegramCfg) (*Client, error)`
|
||||
- Implementar `platform.Messenger`:
|
||||
- `SendText` — `tgbotapi.NewMessage(chatID, text)`
|
||||
- `SendMarkdown` — `tgbotapi.NewMessage` con `ParseMode: "MarkdownV2"`
|
||||
- `SendReplyMarkdown` — `ReplyToMessageID` en el message config
|
||||
- `SendTyping` — `tgbotapi.NewChatAction(chatID, "typing")`
|
||||
|
||||
- [ ] **4.3** Crear `shell/telegram/listener.go`:
|
||||
- Implementar `platform.EventSource`
|
||||
- Modo long-polling con `GetUpdatesChan()` (webhook es mas complejo y requiere dominio publico)
|
||||
- Convertir cada `tgbotapi.Update` a `decision.MessageContext`:
|
||||
- `SenderID` = user ID de Telegram (string)
|
||||
- `SenderName` = username o first_name
|
||||
- `RoomID` = `telegram:<chat_id>` (con prefijo de plataforma)
|
||||
- `Content` = texto del mensaje
|
||||
- `IsDirectMsg` = true si chat.Type == "private"
|
||||
- `IsMention` = true si el mensaje contiene @botname
|
||||
- `Command` = parsear si empieza con `!` (o `/` que es la convencion Telegram)
|
||||
- Llamar al mismo `handleEvent(ctx, msgCtx)` del Agent
|
||||
|
||||
- [ ] **4.4** Crear `shell/telegram/platform.go` que componga Client + Listener e implemente `platform.Platform`
|
||||
|
||||
### Fase 5: Configuracion
|
||||
|
||||
- [ ] **5.1** Anadir tipos de config en `internal/config/schema.go`:
|
||||
```yaml
|
||||
telegram:
|
||||
enabled: false
|
||||
bot_token_env: "TELEGRAM_TOKEN_BOT"
|
||||
allowed_chats: [] # lista de chat IDs permitidos (vacio = todos)
|
||||
command_prefix: "/" # convencion Telegram, ademas de "!"
|
||||
```
|
||||
Struct: `TelegramCfg` con campos `Enabled`, `BotTokenEnv`, `AllowedChats`, `CommandPrefix`
|
||||
|
||||
- [ ] **5.2** Anadir `TelegramCfg` al config principal del agente (junto a `MatrixCfg`)
|
||||
|
||||
- [ ] **5.3** Actualizar `internal/config/loader.go` para parsear la nueva seccion
|
||||
|
||||
- [ ] **5.4** Actualizar `cmd/launcher/main.go` para instanciar plataformas segun config:
|
||||
```go
|
||||
var platforms []platform.Platform
|
||||
if cfg.Matrix.Enabled { platforms = append(platforms, matrixPlatform) }
|
||||
if cfg.Telegram.Enabled { platforms = append(platforms, telegramPlatform) }
|
||||
```
|
||||
|
||||
- [ ] **5.5** Anadir `TELEGRAM_TOKEN_<BOT>` a `.env.example`
|
||||
|
||||
### Fase 6: Tool matrix_send → platform_send
|
||||
|
||||
- [ ] **6.1** Evaluar si `matrix_send` tool debe ser generico o especifico:
|
||||
- Opcion A: renombrar a `send_message` con parametro `platform` — mas flexible
|
||||
- Opcion B: mantener `matrix_send` y anadir `telegram_send` — mas simple
|
||||
- **Recomendacion**: opcion A a largo plazo, pero para esta task basta con que el LLM
|
||||
responda por la misma plataforma que recibio el mensaje (ya lo hace via `handleEvent` → runner)
|
||||
- `matrix_send` como tool explícita solo se usa para enviar a rooms arbitrarios; si no se necesita eso en Telegram, no hace falta `telegram_send` ahora
|
||||
|
||||
- [ ] **6.2** Si se opta por generalizar: crear `tools/send.go` con `NewPlatformSend(messenger platform.Messenger)` que el LLM pueda usar para enviar a cualquier plataforma
|
||||
|
||||
### Fase 7: Tests
|
||||
|
||||
- [ ] **7.1** Unit tests para `pkg/platform/types.go` — verificar que las interfaces compilan y los helpers de PlatformID funcionan
|
||||
|
||||
- [ ] **7.2** Unit tests para `shell/telegram/client.go` — mock del bot API, verificar conversion de mensajes
|
||||
|
||||
- [ ] **7.3** Unit tests para `shell/telegram/listener.go` — mock de updates, verificar conversion a MessageContext
|
||||
|
||||
- [ ] **7.4** Integration test: verificar que un Agent con dos plataformas (matrix mock + telegram mock) recibe y responde correctamente por ambas
|
||||
|
||||
- [ ] **7.5** Verificar que todos los agentes existentes siguen funcionando solo con Matrix (backward compat)
|
||||
|
||||
### Fase 8: Documentacion
|
||||
|
||||
- [ ] **8.1** Actualizar `CLAUDE.md` — anadir `shell/telegram/` a la estructura de directorios, actualizar diagrama de flujo
|
||||
|
||||
- [ ] **8.2** Actualizar `docs/creating-agents.md` con la seccion de configuracion multi-plataforma
|
||||
|
||||
- [ ] **8.3** Actualizar `.claude/policies/create_agent.md` para mencionar la seccion `telegram:` en config
|
||||
|
||||
- [ ] **8.4** Anadir a `README.md` la seccion de soporte Telegram
|
||||
|
||||
---
|
||||
|
||||
## Orden de ejecucion recomendado
|
||||
|
||||
1. **Fase 1** (interfaces) — base para todo lo demas
|
||||
2. **Fase 2** (adaptar matrix) — asegurar que Matrix sigue funcionando con las nuevas interfaces
|
||||
3. **Fase 3** (desacoplar runtime) — el refactor central; debe compilar y pasar tests con solo Matrix
|
||||
4. **Fase 5** (config) — preparar el config antes de implementar Telegram
|
||||
5. **Fase 4** (implementar telegram) — el codigo nuevo
|
||||
6. **Fase 6** (tools) — ajustar si es necesario
|
||||
7. **Fase 7** (tests) — validar todo
|
||||
8. **Fase 8** (docs) — ultima, cuando todo este estable
|
||||
|
||||
## Decisiones de diseno pendientes
|
||||
|
||||
- **Memoria compartida vs separada**: Si un usuario habla por Matrix y por Telegram, son windows separadas (por el prefijo de plataforma en roomID). Podria unificarse en el futuro con un "user identity" cross-platform, pero no es necesario ahora.
|
||||
- **Comandos `/` vs `!`**: Telegram usa `/` como convencion para comandos de bot. Soportar ambos prefijos (`/` y `!`) en el parser de comandos, configurable por plataforma.
|
||||
- **Webhooks vs long-polling**: Empezar con long-polling por simplicidad. Webhook requiere HTTPS publico y es una optimizacion posterior.
|
||||
- **Presence**: Matrix tiene presence (online/offline). Telegram no tiene equivalente nativo para bots. Abstraer como opcional en la interfaz.
|
||||
- **Reactions**: Matrix tiene reactions (`m.reaction`). Telegram tiene limitaciones. No incluir en `Messenger` por ahora; dejarlo como extension especifica de plataforma.
|
||||
|
||||
## Dependencias nuevas
|
||||
|
||||
```
|
||||
github.com/go-telegram-bot-api/telegram-bot-api/v5 # Telegram Bot API client
|
||||
```
|
||||
|
||||
## Riesgos
|
||||
|
||||
- El refactor de runtime.go (Fase 3) es el mas delicado — cambia el corazon del sistema. Hacer commits atomicos despues de cada sub-tarea.
|
||||
- Asegurar backward compatibility: un agente sin `telegram:` en su config debe funcionar exactamente como antes.
|
||||
@@ -0,0 +1,255 @@
|
||||
# 016 — Sistema de Skills para agentes
|
||||
|
||||
## Objetivo
|
||||
|
||||
Crear un sistema de skills reutilizables que los agentes puedan cargar y ejecutar. Las skills son paquetes de instrucciones, scripts y recursos que amplian las capacidades de un agente mas alla de las tools de function calling. Mientras las tools son funciones atomicas (clock, http_get, ssh_command), las skills son flujos completos de trabajo (deploy a produccion, analizar logs, generar reportes).
|
||||
|
||||
## Contexto
|
||||
|
||||
- Las **tools** (`tools/`) son funciones atomicas: reciben args, ejecutan, devuelven resultado. El LLM las invoca via function calling.
|
||||
- Las **skills** son paquetes de instrucciones + recursos que guian al agente para completar tareas complejas multi-paso. Son como "recetas" que el agente sigue.
|
||||
- Ejemplo: una tool es `ssh_command`. Una skill es "deploy-service" que usa ssh_command, http_get, y logica condicional para hacer un deploy completo.
|
||||
|
||||
## Prerequisitos
|
||||
|
||||
- Ninguno estricto. El sistema de tools existente sigue funcionando igual.
|
||||
|
||||
---
|
||||
|
||||
## Estructura de una skill
|
||||
|
||||
```
|
||||
skills/<categoria>/<skill-name>/
|
||||
├── SKILL.md ← obligatorio (frontmatter YAML + instrucciones markdown)
|
||||
├── LICENSE.txt ← opcional
|
||||
├── scripts/ ← opcional, codigo ejecutable (bash, python, etc.)
|
||||
├── references/ ← opcional, docs de referencia
|
||||
├── templates/ ← opcional, plantillas/assets
|
||||
└── assets/ ← opcional, fuentes, iconos, etc.
|
||||
```
|
||||
|
||||
### SKILL.md — formato
|
||||
|
||||
```yaml
|
||||
---
|
||||
name: skill-name
|
||||
description: >
|
||||
Descripcion clara de que hace la skill y cuando debe activarse.
|
||||
Esta descripcion es el mecanismo principal de triggering.
|
||||
---
|
||||
|
||||
# Instrucciones
|
||||
|
||||
Cuerpo markdown con las instrucciones completas.
|
||||
Idealmente < 500 lineas.
|
||||
```
|
||||
|
||||
### Carga progresiva (3 niveles)
|
||||
|
||||
1. **Metadata** (name + description) — siempre en contexto (~100 palabras). El agente la lee para decidir si activar la skill.
|
||||
2. **Cuerpo del SKILL.md** — se carga cuando la skill se activa. Instrucciones principales.
|
||||
3. **Recursos bundled** (scripts/, references/, etc.) — se cargan bajo demanda. El SKILL.md indica cuando leer cada archivo.
|
||||
|
||||
### Carpetas opcionales
|
||||
|
||||
| Carpeta | Proposito |
|
||||
|---------|-----------|
|
||||
| `scripts/` | Codigo ejecutable que el agente corre (bash, python). Puede ejecutarlos sin cargarlos en contexto. |
|
||||
| `references/` | Documentacion extensa, leida solo cuando es relevante. Si > 300 lineas, agregar TOC al inicio. |
|
||||
| `templates/` | Plantillas que la skill usa como base para generar outputs. |
|
||||
| `assets/` | Archivos estaticos (fuentes, iconos, imagenes). |
|
||||
|
||||
---
|
||||
|
||||
## Tareas
|
||||
|
||||
### Fase 1: Estructura de directorios y skills iniciales
|
||||
|
||||
- [ ] **1.1** Crear la carpeta `skills/` en la raiz del proyecto con subcategorias:
|
||||
```
|
||||
skills/
|
||||
├── README.md ← documentacion del sistema de skills
|
||||
├── devops/ ← skills de operaciones y deploy
|
||||
├── analysis/ ← skills de analisis de datos/logs
|
||||
├── communication/ ← skills de comunicacion y notificaciones
|
||||
├── coding/ ← skills de desarrollo y code review
|
||||
└── system/ ← skills de administracion del sistema
|
||||
```
|
||||
|
||||
- [ ] **1.2** Crear skills iniciales de ejemplo:
|
||||
- `skills/devops/deploy-service/SKILL.md` — deploy de un servicio via SSH
|
||||
- `skills/analysis/log-analyzer/SKILL.md` — analisis de logs con patrones
|
||||
- `skills/communication/daily-report/SKILL.md` — generar y enviar reporte diario
|
||||
- `skills/system/health-check/SKILL.md` — verificar salud de servicios
|
||||
|
||||
### Fase 2: Tipos puros en `pkg/skills/`
|
||||
|
||||
- [ ] **2.1** Crear `pkg/skills/types.go` con los tipos puros:
|
||||
```go
|
||||
// SkillMeta es la metadata extraida del frontmatter YAML del SKILL.md.
|
||||
type SkillMeta struct {
|
||||
Name string `yaml:"name"`
|
||||
Description string `yaml:"description"`
|
||||
Category string // derivado de la ruta del directorio
|
||||
}
|
||||
|
||||
// Skill es la representacion completa de una skill cargada.
|
||||
type Skill struct {
|
||||
Meta SkillMeta
|
||||
Instructions string // cuerpo markdown del SKILL.md
|
||||
BasePath string // ruta al directorio de la skill
|
||||
Scripts []string // rutas relativas a scripts/
|
||||
References []string // rutas relativas a references/
|
||||
Templates []string // rutas relativas a templates/
|
||||
}
|
||||
|
||||
// SkillMatch indica si una skill es relevante para un contexto dado.
|
||||
type SkillMatch struct {
|
||||
Skill SkillMeta
|
||||
Confidence float64 // 0.0 - 1.0
|
||||
}
|
||||
```
|
||||
|
||||
- [ ] **2.2** Crear `pkg/skills/match.go` — funcion pura que dado un mensaje y una lista de `SkillMeta`, retorna las skills mas relevantes:
|
||||
```go
|
||||
func Match(query string, skills []SkillMeta) []SkillMatch
|
||||
```
|
||||
Implementacion inicial: keyword matching simple contra name + description.
|
||||
|
||||
### Fase 3: Loader en `shell/skills/`
|
||||
|
||||
- [ ] **3.1** Crear `shell/skills/loader.go` — carga skills desde el filesystem:
|
||||
```go
|
||||
// Loader descubre y carga skills desde un directorio base.
|
||||
type Loader struct {
|
||||
basePath string
|
||||
}
|
||||
|
||||
func NewLoader(basePath string) *Loader
|
||||
func (l *Loader) LoadAll() ([]skills.Skill, error) // carga todas las skills
|
||||
func (l *Loader) LoadMeta() ([]skills.SkillMeta, error) // solo metadata (nivel 1)
|
||||
func (l *Loader) LoadSkill(name string) (*skills.Skill, error) // skill completa (nivel 2)
|
||||
func (l *Loader) ReadResource(skill, path string) (string, error) // recurso (nivel 3)
|
||||
```
|
||||
|
||||
- [ ] **3.2** Implementar parsing del SKILL.md:
|
||||
- Extraer frontmatter YAML entre `---`
|
||||
- Extraer cuerpo markdown
|
||||
- Listar archivos en subcarpetas opcionales
|
||||
|
||||
### Fase 4: Integracion con el runtime
|
||||
|
||||
- [ ] **4.1** Anadir `skillLoader *shellskills.Loader` al struct `Agent` en `agents/runtime.go`
|
||||
|
||||
- [ ] **4.2** Crear una tool `skill_search` en `tools/skills/` que permita al LLM buscar skills relevantes:
|
||||
```go
|
||||
// Def: name="skill_search", params=[{name: "query", type: "string"}]
|
||||
// Exec: usa el loader para buscar skills por relevancia
|
||||
```
|
||||
|
||||
- [ ] **4.3** Crear una tool `skill_load` en `tools/skills/` que cargue el contenido completo de una skill:
|
||||
```go
|
||||
// Def: name="skill_load", params=[{name: "skill_name", type: "string"}]
|
||||
// Exec: retorna las instrucciones completas del SKILL.md
|
||||
```
|
||||
|
||||
- [ ] **4.4** Crear una tool `skill_read_resource` para cargar recursos bajo demanda:
|
||||
```go
|
||||
// Def: name="skill_read_resource", params=[{name: "skill_name"}, {name: "path"}]
|
||||
// Exec: lee un archivo de scripts/, references/, templates/, o assets/
|
||||
```
|
||||
|
||||
- [ ] **4.5** Registrar las tools de skills en el builder de tools de `runtime.go`
|
||||
|
||||
- [ ] **4.6** Inyectar la lista de skills disponibles (nivel 1: metadata) en el system prompt del agente, para que sepa que skills tiene a disposicion.
|
||||
|
||||
### Fase 5: Configuracion
|
||||
|
||||
- [ ] **5.1** Anadir seccion `skills:` al config schema en `internal/config/schema.go`:
|
||||
```go
|
||||
type SkillsCfg struct {
|
||||
Enabled bool `yaml:"enabled"`
|
||||
SkillsPath string `yaml:"path"` // default: "skills/"
|
||||
Categories []string `yaml:"categories"` // filtro opcional de categorias
|
||||
}
|
||||
```
|
||||
|
||||
- [ ] **5.2** Anadir `SkillsCfg` al `AgentConfig` principal
|
||||
|
||||
- [ ] **5.3** Respetar el filtro de categorias: si un agente solo tiene `categories: [devops, system]`, no carga skills de `analysis/` o `communication/`
|
||||
|
||||
### Fase 6: Ejecucion de scripts
|
||||
|
||||
- [ ] **6.1** Evaluar como ejecutar scripts de skills de forma segura:
|
||||
- Los scripts viven en `skills/<cat>/<name>/scripts/`
|
||||
- El agente necesita permisos para ejecutarlos (similar a ssh_command)
|
||||
- Opcion A: ejecutar via `os/exec` con sandbox basico (allowlist de interpreters)
|
||||
- Opcion B: ejecutar via SSH contra localhost (reutiliza infra existente)
|
||||
- Opcion C: solo permitir bash scripts con validacion previa
|
||||
- **Recomendacion**: opcion A con allowlist configurable de interpreters
|
||||
|
||||
- [ ] **6.2** Crear `shell/skills/executor.go` para ejecutar scripts:
|
||||
```go
|
||||
type Executor struct {
|
||||
allowedInterpreters []string // ["bash", "python3", "sh"]
|
||||
timeout time.Duration
|
||||
}
|
||||
func (e *Executor) Run(ctx context.Context, scriptPath string, args []string) (string, error)
|
||||
```
|
||||
|
||||
- [ ] **6.3** Crear tool `skill_run_script` en `tools/skills/`:
|
||||
```go
|
||||
// Def: name="skill_run_script", params=[{name: "skill_name"}, {name: "script"}, {name: "args"}]
|
||||
// Exec: ejecuta un script de la skill con el executor
|
||||
```
|
||||
|
||||
### Fase 7: Tests
|
||||
|
||||
- [ ] **7.1** Unit tests para `pkg/skills/types.go` — verificar parsing de metadata
|
||||
- [ ] **7.2** Unit tests para `pkg/skills/match.go` — verificar matching de skills
|
||||
- [ ] **7.3** Unit tests para `shell/skills/loader.go` — verificar carga desde filesystem (con directorio temporal)
|
||||
- [ ] **7.4** Unit tests para `shell/skills/executor.go` — verificar ejecucion de scripts
|
||||
- [ ] **7.5** Integration test: un agente con skills habilitadas puede buscar, cargar y ejecutar una skill
|
||||
|
||||
### Fase 8: Documentacion
|
||||
|
||||
- [ ] **8.1** Crear `skills/README.md` con la guia completa del sistema de skills
|
||||
- [ ] **8.2** Actualizar `CLAUDE.md` — anadir `skills/`, `pkg/skills/`, `shell/skills/` a la estructura
|
||||
- [ ] **8.3** Crear `.claude/policies/create_skill.md` — policy para crear nuevas skills
|
||||
- [ ] **8.4** Actualizar `docs/creating-agents.md` con la seccion de skills
|
||||
|
||||
---
|
||||
|
||||
## Orden de ejecucion recomendado
|
||||
|
||||
1. **Fase 1** (estructura + skills de ejemplo) — valida el formato antes de escribir codigo
|
||||
2. **Fase 2** (tipos puros) — base para el loader y matching
|
||||
3. **Fase 3** (loader) — carga skills desde disco
|
||||
4. **Fase 5** (config) — permite habilitar/configurar skills por agente
|
||||
5. **Fase 4** (integracion runtime) — conecta skills al agente via tools
|
||||
6. **Fase 6** (ejecucion scripts) — opcional, solo si hay scripts
|
||||
7. **Fase 7** (tests) — validar todo
|
||||
8. **Fase 8** (docs) — cuando todo este estable
|
||||
|
||||
## Decisiones de diseno
|
||||
|
||||
- **Skills vs Tools**: las tools son atomicas (function calling). Las skills son flujos multi-paso que el agente sigue como instrucciones. Las skills USAN tools internamente.
|
||||
- **Carga progresiva**: no cargar todo en contexto — solo metadata siempre, instrucciones cuando se activa, recursos bajo demanda.
|
||||
- **Skills como carpeta en raiz**: viven en `skills/` (no en `pkg/` ni `shell/`) porque son contenido declarativo, no codigo Go. Similar a como `agents/` tiene configs y prompts.
|
||||
- **Subcategorias**: organizadas por dominio (devops, analysis, etc.) como los tools por funcion (clock, http, ssh, etc.).
|
||||
- **Seguridad de scripts**: los scripts de skills deben tener las mismas restricciones que ssh_command — allowlist de interpreters, timeout, sin acceso a secretos directos.
|
||||
|
||||
## Analogia con el patron del proyecto
|
||||
|
||||
```
|
||||
pkg/skills/ → PURE: tipos SkillMeta, Skill, SkillMatch + matching puro
|
||||
shell/skills/ → IMPURE: Loader (filesystem), Executor (os/exec)
|
||||
tools/skills/ → tools de function calling para que el LLM interactue con skills
|
||||
skills/ → contenido declarativo (SKILL.md + recursos)
|
||||
```
|
||||
|
||||
## Riesgos
|
||||
|
||||
- Inflar el contexto del LLM si se cargan muchas skills de golpe — mitigado por carga progresiva
|
||||
- Ejecucion de scripts arbitrarios — mitigado por allowlist de interpreters y timeout
|
||||
- Complejidad innecesaria si los agentes actuales no necesitan skills — empezar con 2-3 skills simples y validar
|
||||
@@ -0,0 +1,241 @@
|
||||
# 017 — MCP Client: consumir servidores MCP como tools del agente
|
||||
|
||||
## Objetivo
|
||||
|
||||
Permitir que los agentes se conecten a servidores MCP externos y expongan las tools de esos servidores como tools normales en su registry. Desde el punto de vista del LLM, una tool MCP es indistinguible de una tool nativa (ssh_command, http_get, etc.) — aparece en el function calling con su nombre, descripcion y parametros.
|
||||
|
||||
## Contexto
|
||||
|
||||
- Ya existe `shell/protocols/mcp.go` que **expone** tools del agente como MCP server (server-side). Falta el **cliente** que consume tools de servidores MCP externos.
|
||||
- La dependencia `github.com/mark3labs/mcp-go v0.44.1` ya esta en go.mod. Incluye paquetes `client` y `mcp` con soporte para stdio y SSE/HTTP.
|
||||
- El config ya tiene `MCPToolCfg` con `Servers []MCPServerCfg` en `internal/config/schema.go`, pero solo soporta `url` — hay que extender para soportar transporte stdio (command + args).
|
||||
- El tool registry (`tools/Registry`) ya soporta registrar cualquier `tools.Tool` (Def + Exec).
|
||||
- El runtime (`agents/runtime.go:buildToolRegistry`) ya tiene el patron para registrar tools condicionalmente.
|
||||
|
||||
## Prerequisitos
|
||||
|
||||
- Ninguno estricto. La infraestructura de tools y config ya existe.
|
||||
|
||||
---
|
||||
|
||||
## Arquitectura
|
||||
|
||||
```
|
||||
config.yaml (tools.mcp.servers)
|
||||
↓
|
||||
shell/mcp/client.go ← conecta a servidores MCP, descubre tools
|
||||
↓
|
||||
tools/mcptools/mcp.go ← wrappea cada tool MCP como tools.Tool
|
||||
↓
|
||||
agents/runtime.go ← registra en el Registry como cualquier otra tool
|
||||
↓
|
||||
LLM ve las tools MCP en function calling, las invoca normalmente
|
||||
```
|
||||
|
||||
### Patron pure core / impure shell
|
||||
|
||||
```
|
||||
pkg/ (nada nuevo) → no se necesitan tipos puros nuevos; tools.Def ya cubre
|
||||
shell/mcp/ → IMPURE: cliente MCP real (I/O, subprocesos, red)
|
||||
tools/mcptools/ → bridge: convierte MCP tool → tools.Tool
|
||||
```
|
||||
|
||||
## Transportes MCP soportados
|
||||
|
||||
| Transporte | Config | Descripcion |
|
||||
|-----------|--------|-------------|
|
||||
| **stdio** | `command` + `args` | Lanza un subproceso y se comunica via stdin/stdout. El mas comun (Claude Desktop, npx servers). |
|
||||
| **SSE/HTTP** | `url` | Se conecta a un servidor MCP remoto via HTTP con Server-Sent Events. |
|
||||
|
||||
## Tareas
|
||||
|
||||
### Fase 1: Extender config para stdio transport
|
||||
|
||||
- [ ] **1.1** Modificar `MCPServerCfg` en `internal/config/schema.go` para soportar ambos transportes:
|
||||
```go
|
||||
type MCPServerCfg struct {
|
||||
Name string `yaml:"name"` // nombre logico del servidor
|
||||
Transport string `yaml:"transport"` // "stdio" | "sse" (default: auto-detect)
|
||||
Command string `yaml:"command"` // stdio: comando a ejecutar
|
||||
Args []string `yaml:"args"` // stdio: argumentos del comando
|
||||
Env map[string]string `yaml:"env"` // stdio: variables de entorno extra
|
||||
URL string `yaml:"url"` // sse: URL del servidor
|
||||
Headers map[string]string `yaml:"headers"` // sse: headers HTTP extra (auth, etc.)
|
||||
Tools []string `yaml:"tools"` // filtro: solo exponer estas tools (vacio = todas)
|
||||
Prefix string `yaml:"prefix"` // prefijo para nombres de tools (evitar colisiones)
|
||||
Timeout time.Duration `yaml:"timeout"` // timeout por llamada (default: 30s)
|
||||
}
|
||||
```
|
||||
|
||||
- [ ] **1.2** Validar que `Command` o `URL` este presente (al menos uno).
|
||||
|
||||
### Fase 2: MCP Client en `shell/mcp/`
|
||||
|
||||
- [ ] **2.1** Crear `shell/mcp/client.go` — wrapper sobre `mcp-go/client`:
|
||||
```go
|
||||
// Client conecta a un servidor MCP y descubre sus tools.
|
||||
type Client struct {
|
||||
name string
|
||||
mcpClient *client.StdioMCPClient // o SSEMCPClient
|
||||
tools []mcp.Tool // tools descubiertas
|
||||
logger *slog.Logger
|
||||
}
|
||||
|
||||
func NewStdioClient(name, command string, args []string, env map[string]string, logger *slog.Logger) (*Client, error)
|
||||
func NewSSEClient(name, url string, headers map[string]string, logger *slog.Logger) (*Client, error)
|
||||
func (c *Client) Tools() []mcp.Tool // tools descubiertas
|
||||
func (c *Client) CallTool(ctx context.Context, name string, args map[string]any) (*mcp.CallToolResult, error)
|
||||
func (c *Client) Close() error
|
||||
```
|
||||
|
||||
- [ ] **2.2** Implementar `NewStdioClient`:
|
||||
- Crear `client.NewStdioMCPClient(command, env, args...)` (ver API de mcp-go)
|
||||
- Llamar `Initialize()` con info del agente
|
||||
- Llamar `ListTools()` para descubrir tools disponibles
|
||||
- Guardar la lista de tools
|
||||
|
||||
- [ ] **2.3** Implementar `NewSSEClient`:
|
||||
- Crear `client.NewSSEMCPClient(url, options...)`
|
||||
- Initialize + ListTools igual que stdio
|
||||
|
||||
- [ ] **2.4** Implementar `CallTool`:
|
||||
- Delegar a `mcpClient.CallTool(ctx, mcp.CallToolRequest{...})`
|
||||
- Extraer texto del resultado (manejar text y error results)
|
||||
|
||||
- [ ] **2.5** Implementar `Close`:
|
||||
- Cerrar el cliente MCP (mata el subproceso en stdio, cierra conexion en SSE)
|
||||
|
||||
### Fase 3: Bridge MCP → tools.Tool en `tools/mcptools/`
|
||||
|
||||
- [ ] **3.1** Crear `tools/mcptools/mcp.go` — convierte tools de un MCP server en `[]tools.Tool`:
|
||||
```go
|
||||
// FromMCPServer toma un shell/mcp.Client y genera tools.Tool para cada tool MCP.
|
||||
// prefix se antepone al nombre de la tool (ej: "brave_" → "brave_web_search").
|
||||
// filter limita que tools exponer (vacio = todas).
|
||||
func FromMCPServer(mcpClient *shellmcp.Client, prefix string, filter []string, timeout time.Duration) []tools.Tool
|
||||
```
|
||||
|
||||
- [ ] **3.2** Implementar conversion de `mcp.Tool` → `tools.Def`:
|
||||
- `Name` = prefix + tool.Name
|
||||
- `Description` = tool.Description
|
||||
- `Parameters` = convertir `tool.InputSchema` (JSON Schema) → `[]tools.Param`
|
||||
- JSON Schema properties → Param con name, type, description
|
||||
- JSON Schema required → Param.Required = true
|
||||
|
||||
- [ ] **3.3** Implementar el `ToolFunc` wrapper:
|
||||
- Recibe `args map[string]any`
|
||||
- Llama a `mcpClient.CallTool(ctx, originalName, args)` (sin prefix)
|
||||
- Convierte el resultado MCP a `tools.Result`
|
||||
|
||||
### Fase 4: Integracion en runtime
|
||||
|
||||
- [ ] **4.1** Crear `shell/mcp/manager.go` — gestiona multiples clientes MCP:
|
||||
```go
|
||||
// Manager inicializa y gestiona conexiones a multiples servidores MCP.
|
||||
type Manager struct {
|
||||
clients map[string]*Client // name → client
|
||||
logger *slog.Logger
|
||||
}
|
||||
|
||||
func NewManager(servers []config.MCPServerCfg, logger *slog.Logger) (*Manager, error)
|
||||
func (m *Manager) AllTools(reg *tools.Registry) // registra todas las tools en el registry
|
||||
func (m *Manager) Close() error // cierra todos los clientes
|
||||
```
|
||||
|
||||
- [ ] **4.2** Integrar en `agents/runtime.go`:
|
||||
- En `New()`: si `cfg.Tools.MCP.Enabled && len(cfg.Tools.MCP.Servers) > 0`, crear `mcp.NewManager(...)`
|
||||
- Llamar `manager.AllTools(toolReg)` para registrar las tools MCP en el registry
|
||||
- Guardar manager en `Agent` struct para cerrar en `Run()` defer
|
||||
- Las tools MCP aparecen automaticamente en el function calling del LLM
|
||||
|
||||
- [ ] **4.3** Anadir campo `mcpManager` al struct `Agent` y cerrar en `Run()`:
|
||||
```go
|
||||
type Agent struct {
|
||||
// ...existing fields...
|
||||
mcpManager *shellmcp.Manager // nil when MCP client is disabled
|
||||
}
|
||||
```
|
||||
|
||||
### Fase 5: Ejemplo de configuracion
|
||||
|
||||
- [ ] **5.1** Documentar ejemplo con servidor MCP stdio (ej: brave-search, filesystem):
|
||||
```yaml
|
||||
tools:
|
||||
mcp:
|
||||
enabled: true
|
||||
servers:
|
||||
- name: brave-search
|
||||
command: npx
|
||||
args: ["-y", "@anthropic/mcp-server-brave-search"]
|
||||
env:
|
||||
BRAVE_API_KEY: "${BRAVE_API_KEY}"
|
||||
prefix: "brave_"
|
||||
|
||||
- name: filesystem
|
||||
command: npx
|
||||
args: ["-y", "@anthropic/mcp-server-filesystem", "/home/data"]
|
||||
prefix: "fs_"
|
||||
|
||||
- name: remote-tools
|
||||
url: "http://localhost:8080/mcp"
|
||||
tools: ["search", "summarize"] # solo estas tools
|
||||
prefix: "remote_"
|
||||
```
|
||||
|
||||
- [ ] **5.2** Probar con al menos un servidor MCP real (brave-search o filesystem) en un agente de prueba.
|
||||
|
||||
### Fase 6: Tests
|
||||
|
||||
- [ ] **6.1** Unit tests para `tools/mcptools/mcp.go` — verificar conversion de schema MCP → tools.Def
|
||||
- [ ] **6.2** Unit tests para `shell/mcp/client.go` — mock del protocolo MCP (o test con echo server)
|
||||
- [ ] **6.3** Integration test: un agente con MCP habilitado lista tools MCP en su registry
|
||||
|
||||
### Fase 7: Cleanup y docs
|
||||
|
||||
- [ ] **7.1** Actualizar `CLAUDE.md` — anadir `shell/mcp/`, `tools/mcptools/` a la estructura
|
||||
- [ ] **7.2** Actualizar `.claude/policies/create_tool.md` si es necesario — mencionar que tools MCP se auto-registran
|
||||
- [ ] **7.3** Mover o refactorizar `shell/protocols/mcp.go` (MCP server) a `shell/mcp/server.go` para colocarlo junto al client
|
||||
|
||||
---
|
||||
|
||||
## Ejemplo de flujo completo
|
||||
|
||||
```
|
||||
1. Agente arranca, config tiene tools.mcp.servers con brave-search (stdio)
|
||||
|
||||
2. runtime.go → mcp.NewManager() → lanza `npx -y @anthropic/mcp-server-brave-search`
|
||||
→ Initialize → ListTools → descubre: web_search, local_search
|
||||
|
||||
3. mcptools.FromMCPServer() convierte:
|
||||
- mcp.Tool{name: "web_search", ...} → tools.Tool{Def: {Name: "brave_web_search", ...}, Exec: wrapper}
|
||||
- mcp.Tool{name: "local_search", ...} → tools.Tool{Def: {Name: "brave_local_search", ...}, Exec: wrapper}
|
||||
|
||||
4. Se registran en el toolReg → aparecen en ToLLMSpecs()
|
||||
|
||||
5. Usuario pregunta: "busca noticias sobre Go 1.23"
|
||||
→ LLM ve brave_web_search en sus tools → genera tool_call
|
||||
→ runtime ejecuta → wrapper llama mcpClient.CallTool("web_search", args)
|
||||
→ resultado vuelve al LLM → genera respuesta final
|
||||
```
|
||||
|
||||
## Decisiones de diseno
|
||||
|
||||
- **Prefix por servidor**: evita colisiones de nombres entre servidores MCP que tengan tools con el mismo nombre. Configurable por servidor.
|
||||
- **Filter de tools**: permite exponer solo un subset de tools de un servidor MCP (seguridad + reducir contexto del LLM).
|
||||
- **Manager pattern**: centraliza lifecycle de multiples clientes MCP. Similar a como el bus manager gestiona multiples agentes.
|
||||
- **Stdio como transporte principal**: es el estandar de facto en MCP. Los servidores mas populares (brave, filesystem, github, etc.) usan stdio.
|
||||
- **Auto-discovery**: las tools se descubren automaticamente via `ListTools()`. No hace falta declararlas manualmente.
|
||||
- **Sin tipos puros nuevos**: `tools.Def` y `tools.Param` ya cubren la especificacion de una tool. No se necesita nada en `pkg/`.
|
||||
|
||||
## Riesgos
|
||||
|
||||
- **Subprocesos zombie**: si el agente crashea, los procesos MCP stdio pueden quedar huerfanos. Mitigar con process groups y cleanup en `Close()`.
|
||||
- **Latencia de inicio**: `npx -y` descarga paquetes la primera vez. Puede tardar. Considerar cache o pre-instalacion.
|
||||
- **Schema complejo**: algunos MCP servers tienen input schemas con nested objects/arrays. La conversion a `tools.Param` debe manejar esto (al menos `object` y `array` como tipos).
|
||||
- **Seguridad**: un servidor MCP malicioso podria exponer tools daninas. El filtro de tools y el prefix ayudan, pero la confianza es del operador.
|
||||
- **Timeout**: llamadas a MCP servers externos pueden ser lentas. Timeout configurable por servidor.
|
||||
|
||||
## Dependencias
|
||||
|
||||
- `github.com/mark3labs/mcp-go v0.44.1` — ya en go.mod, incluye `client` package
|
||||
- No se necesitan dependencias nuevas
|
||||
@@ -0,0 +1,161 @@
|
||||
# 018 — Shared Knowledge: base de conocimiento compartida entre agentes
|
||||
|
||||
## Objetivo
|
||||
|
||||
Crear un sistema de conocimiento compartido (`knowledges/` en la raiz del proyecto) donde multiples agentes pueden leer, escribir y buscar documentos en comun. Esto permite colaboracion entre agentes: uno puede registrar informacion que otros consultan.
|
||||
|
||||
## Contexto
|
||||
|
||||
- Cada agente ya tiene su **knowledge privado** en `agents/<id>/knowledge/` con SQLite FTS5 index (`shell/knowledge/store.go`).
|
||||
- Los tipos puros ya existen: `pkg/knowledge.Document`, `SearchResult`, `Store` interface.
|
||||
- Las tools de knowledge ya existen: `tools/knowledgetools/` (search, read, write, list).
|
||||
- El `FileStore` en `shell/knowledge/` ya implementa todo el CRUD + FTS5.
|
||||
- Lo que falta es una **instancia compartida** de `FileStore` apuntando a `knowledges/` con tools dedicadas que multiples agentes puedan usar.
|
||||
|
||||
## Arquitectura
|
||||
|
||||
```
|
||||
knowledges/ ← carpeta raiz, documentos .md compartidos
|
||||
knowledges/data/knowledge.db ← SQLite FTS5 index compartido (en .gitignore)
|
||||
|
||||
pkg/knowledge/ ← sin cambios, los tipos puros ya cubren
|
||||
shell/knowledge/store.go ← sin cambios, FileStore ya es reutilizable
|
||||
tools/knowledgetools/shared.go ← NEW: tools prefijadas shared_knowledge_*
|
||||
agents/runtime.go ← instanciar shared store + registrar tools
|
||||
internal/config/schema.go ← config para habilitar shared knowledge
|
||||
```
|
||||
|
||||
### Patron pure core / impure shell
|
||||
|
||||
- `pkg/` — sin cambios, `knowledge.Store` interface ya sirve
|
||||
- `shell/knowledge/` — sin cambios, `FileStore` ya funciona con cualquier directorio
|
||||
- `tools/knowledgetools/` — nuevas tools que wrappean el store compartido
|
||||
- `agents/runtime.go` — composicion: crea shared store y registra tools
|
||||
|
||||
## Tareas
|
||||
|
||||
### Fase 1: Config
|
||||
|
||||
- [ ] **1.1** Agregar seccion `shared_knowledge` al config en `internal/config/schema.go`:
|
||||
```go
|
||||
type SharedKnowledgeCfg struct {
|
||||
Enabled bool `yaml:"enabled"` // default false
|
||||
Dir string `yaml:"dir"` // default "knowledges"
|
||||
DBPath string `yaml:"db_path"` // default "knowledges/data/knowledge.db"
|
||||
}
|
||||
```
|
||||
- [ ] **1.2** Agregar campo `SharedKnowledge SharedKnowledgeCfg` al `ToolsCfg` (o al `AgentConfig` directamente).
|
||||
|
||||
### Fase 2: Tools compartidas en `tools/knowledgetools/`
|
||||
|
||||
- [ ] **2.1** Crear `tools/knowledgetools/shared.go` con tools prefijadas `shared_knowledge_*`:
|
||||
- `shared_knowledge_search` — buscar en la base compartida
|
||||
- `shared_knowledge_read` — leer un documento compartido por slug
|
||||
- `shared_knowledge_write` — crear/actualizar un documento compartido
|
||||
- `shared_knowledge_list` — listar todos los documentos compartidos
|
||||
- Reutilizar `KnowledgeStore` interface y la misma logica de las tools privadas pero con nombres y descripciones que indican "shared across all agents"
|
||||
|
||||
- [ ] **2.2** Cada tool debe incluir en su descripcion que es conocimiento **compartido** entre agentes:
|
||||
```
|
||||
"Search the shared knowledge base accessible by all agents. Use this to find information other agents have recorded."
|
||||
```
|
||||
|
||||
- [ ] **2.3** Funcion constructora:
|
||||
```go
|
||||
// NewSharedKnowledgeTools creates all shared knowledge tools backed by the given store.
|
||||
func NewSharedKnowledgeTools(store KnowledgeStore) []tools.Tool
|
||||
```
|
||||
|
||||
### Fase 3: Integracion en runtime
|
||||
|
||||
- [ ] **3.1** En `agents/runtime.go`, si `cfg.Tools.SharedKnowledge.Enabled` (o donde se ponga en config):
|
||||
- Crear un `shellknowledge.New(dir, dbPath, logger)` con la ruta compartida
|
||||
- Llamar `Sync(ctx)` al arrancar
|
||||
- Registrar las tools de `NewSharedKnowledgeTools(sharedStore)` en el registry
|
||||
- Guardar referencia para cerrar en defer
|
||||
|
||||
- [ ] **3.2** El shared store debe ser **una instancia por agente** (cada proceso abre su propia conexion SQLite al mismo archivo DB). SQLite soporta lecturas concurrentes y escrituras serializadas con WAL mode.
|
||||
|
||||
- [ ] **3.3** Habilitar WAL mode en el shared store para mejor concurrencia entre procesos:
|
||||
```go
|
||||
db.Exec("PRAGMA journal_mode=WAL")
|
||||
```
|
||||
Esto puede ir en `shell/knowledge/store.go` `New()` para beneficiar tambien al store privado.
|
||||
|
||||
### Fase 4: Carpeta `knowledges/`
|
||||
|
||||
- [ ] **4.1** Crear `knowledges/` en la raiz del proyecto con un `README.md` explicando su proposito.
|
||||
- [ ] **4.2** Agregar `knowledges/data/` a `.gitignore` (la DB no se commitea, los .md si).
|
||||
|
||||
### Fase 5: Coexistencia con knowledge privado
|
||||
|
||||
- [ ] **5.1** Un agente puede tener **ambos** habilitados: knowledge privado (`agents/<id>/knowledge/`) y shared (`knowledges/`). Las tools se distinguen por nombre:
|
||||
- `knowledge_search` / `knowledge_read` / `knowledge_write` / `knowledge_list` → privado
|
||||
- `shared_knowledge_search` / `shared_knowledge_read` / `shared_knowledge_write` / `shared_knowledge_list` → compartido
|
||||
|
||||
- [ ] **5.2** Documentar en el system prompt de los agentes la diferencia:
|
||||
- Knowledge privado: "tu base de conocimiento personal, solo tu puedes ver"
|
||||
- Knowledge compartido: "base compartida entre todos los agentes, usa para colaborar"
|
||||
|
||||
### Fase 6: Tests
|
||||
|
||||
- [ ] **6.1** Test de `NewSharedKnowledgeTools` — verificar que genera 4 tools con nombres `shared_knowledge_*`.
|
||||
- [ ] **6.2** Test de integracion: dos stores apuntando al mismo directorio pueden leer lo que el otro escribe (simula dos agentes).
|
||||
- [ ] **6.3** Test de concurrencia basico con WAL mode.
|
||||
|
||||
### Fase 7: Cleanup y docs
|
||||
|
||||
- [ ] **7.1** Actualizar `CLAUDE.md` — agregar `knowledges/` a la estructura de directorios.
|
||||
- [ ] **7.2** Actualizar `.gitignore` con `knowledges/data/`.
|
||||
- [ ] **7.3** Ejemplo de config habilitando shared knowledge en un agente existente.
|
||||
|
||||
---
|
||||
|
||||
## Ejemplo de config
|
||||
|
||||
```yaml
|
||||
tools:
|
||||
knowledge:
|
||||
enabled: true # knowledge privado del agente
|
||||
dir: "knowledge" # relativo a agents/<id>/
|
||||
|
||||
shared_knowledge:
|
||||
enabled: true # knowledge compartido
|
||||
dir: "knowledges" # relativo a la raiz del proyecto
|
||||
db_path: "knowledges/data/knowledge.db"
|
||||
```
|
||||
|
||||
## Ejemplo de flujo
|
||||
|
||||
```
|
||||
1. agente-A recibe: "investiga X y guarda lo que encuentres"
|
||||
→ LLM usa shared_knowledge_write(slug: "investigacion-x", content: "...")
|
||||
→ Se escribe knowledges/investigacion-x.md + actualiza FTS5
|
||||
|
||||
2. agente-B recibe: "que sabemos sobre X?"
|
||||
→ LLM usa shared_knowledge_search(query: "X")
|
||||
→ Encuentra el documento que escribio agente-A
|
||||
→ shared_knowledge_read(slug: "investigacion-x")
|
||||
→ Responde con la informacion
|
||||
|
||||
3. Agentes colaboran acumulando conocimiento en la misma base
|
||||
```
|
||||
|
||||
## Decisiones de diseno
|
||||
|
||||
- **Reusar FileStore**: no crear un store nuevo. `shell/knowledge.FileStore` ya tiene todo (CRUD, FTS5, Sync). Solo se instancia con una ruta diferente.
|
||||
- **WAL mode**: permite que multiples procesos lean/escriban concurrentemente. Es la forma estandar de compartir SQLite entre procesos.
|
||||
- **Prefix `shared_knowledge_`**: diferencia claramente las tools compartidas de las privadas. El LLM sabe cual usar segun contexto.
|
||||
- **Los .md se commitean, la DB no**: los documentos compartidos forman parte del repo (versionados). La DB FTS5 se reconstruye con `Sync()` al arrancar.
|
||||
- **Sin control de acceso por agente**: cualquier agente con shared_knowledge habilitado puede leer y escribir. Simplicidad primero; RBAC se puede agregar despues si hace falta.
|
||||
|
||||
## Prerequisitos
|
||||
|
||||
- Knowledge privado ya funcional (pkg/knowledge, shell/knowledge, tools/knowledgetools) — ya implementado.
|
||||
- No tiene dependencias externas nuevas.
|
||||
|
||||
## Riesgos
|
||||
|
||||
- **Contention en escritura**: si muchos agentes escriben simultaneamente, SQLite serializa las escrituras. Con WAL mode esto es manejable para el volumen esperado.
|
||||
- **Sync al arrancar**: si hay muchos documentos, el Sync inicial puede tardar. No deberia ser problema con volumenes pequenos.
|
||||
- **Conflictos de slug**: dos agentes podrian sobreescribir el mismo documento. Esto es intencional (ultimo gana), pero el LLM debe ser consciente via el system prompt.
|
||||
Reference in New Issue
Block a user