docs: add agent creation guide and LLM policies index
Documentación y políticas para la creación de agentes: - docs/creating-agents.md: guía completa paso a paso para humanos (scaffold, config, registro Matrix, avatar, verificación E2EE, arranque, troubleshooting) - .claude/policies/create_agent.md: policy para LLMs con estructura de archivos, convenciones y reglas a seguir al crear agentes - .claude/policies/index.md: índice de todas las policies disponibles - .claude/policies/create_tool.md: movido desde .claude/rules/ (misma policy) - CLAUDE.md: añadida sección de políticas, actualizada tabla de agentes con asistente-2, y actualizado el flujo de "Cómo añadir un nuevo bot" con pasos de avatar y verificación E2EE Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,144 @@
|
||||
# Cómo crear un nuevo agente
|
||||
|
||||
Guía para LLMs que asisten en la creación de agentes en este proyecto.
|
||||
|
||||
## Estructura requerida
|
||||
|
||||
Cada agente vive en `agents/<agent-id>/` con esta estructura:
|
||||
|
||||
```
|
||||
agents/<agent-id>/
|
||||
├── agent.go # Package propio, exporta Rules() []decision.Rule
|
||||
├── config.yaml # Configuración completa (ver schema en internal/config/schema.go)
|
||||
└── prompts/
|
||||
└── system.md # System prompt del LLM
|
||||
```
|
||||
|
||||
## Archivos a crear
|
||||
|
||||
### 1. `agents/<agent-id>/agent.go` — Reglas puras
|
||||
|
||||
```go
|
||||
package <agentpkg>
|
||||
|
||||
import "github.com/enmanuel/agents/pkg/decision"
|
||||
|
||||
func Rules() []decision.Rule {
|
||||
return []decision.Rule{
|
||||
// Regla help explícita
|
||||
{
|
||||
Name: "help",
|
||||
Match: decision.MatchCommand("help"),
|
||||
Actions: []decision.Action{{
|
||||
Kind: decision.ActionKindReply,
|
||||
Reply: &decision.ReplyAction{Content: "Descripción de capacidades del bot."},
|
||||
}},
|
||||
},
|
||||
// Catch-all → LLM
|
||||
{
|
||||
Name: "llm-all",
|
||||
Match: func(ctx decision.MessageContext) bool {
|
||||
return ctx.IsDirectMsg || ctx.IsMention
|
||||
},
|
||||
Actions: []decision.Action{{
|
||||
Kind: decision.ActionKindLLM,
|
||||
LLM: &decision.LLMAction{},
|
||||
}},
|
||||
},
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Reglas del archivo de reglas:**
|
||||
- **PURO**: sin imports de I/O, sin side effects, solo `pkg/decision`
|
||||
- El package name debe ser Go-valid (sin guiones): `agents/mi-bot/` → `package mibot`
|
||||
- Las reglas se evalúan en orden — poner las específicas antes del catch-all
|
||||
- El catch-all debe cubrir `ctx.IsDirectMsg || ctx.IsMention` como mínimo
|
||||
- `ActionKindReply` para respuestas estáticas, `ActionKindLLM` para respuestas dinámicas
|
||||
|
||||
### 2. `agents/<agent-id>/config.yaml` — Configuración
|
||||
|
||||
Usar como plantilla `agents/assistant/config.yaml` o `agents/asistente2/config.yaml`.
|
||||
|
||||
**Campos que SIEMPRE hay que personalizar:**
|
||||
|
||||
```yaml
|
||||
agent:
|
||||
id: <agent-id> # DEBE coincidir con el directorio y rulesRegistry
|
||||
name: "Display Name"
|
||||
description: "Qué hace este agente"
|
||||
|
||||
llm:
|
||||
primary:
|
||||
provider: openai # o anthropic
|
||||
model: gpt-4o # o claude-sonnet-4-20250514
|
||||
api_key_env: OPENAI_API_KEY # o ANTHROPIC_API_KEY
|
||||
tool_use:
|
||||
enabled: true/false # true si el agente usa herramientas
|
||||
|
||||
matrix:
|
||||
user_id: "@<agent-id>:matrix-af2f3d.organic-machine.com"
|
||||
access_token_env: MATRIX_TOKEN_<AGENT_UPPER>
|
||||
device_id: "<se actualiza después del registro>"
|
||||
```
|
||||
|
||||
**Convención de nombres de env vars:**
|
||||
- Token: `MATRIX_TOKEN_<ID_UPPER>` donde ID se convierte a mayúsculas y guiones a underscores
|
||||
- Ejemplo: `asistente-2` → `MATRIX_TOKEN_ASISTENTE2`
|
||||
- Password: `MATRIX_PASSWORD_<ID_UPPER>` con la misma convención
|
||||
|
||||
### 3. `agents/<agent-id>/prompts/system.md` — System prompt
|
||||
|
||||
Debe incluir:
|
||||
- Identidad del bot (quién es, qué hace)
|
||||
- Capacidades y limitaciones
|
||||
- Herramientas disponibles (si `tool_use.enabled: true`)
|
||||
- Estilo de respuesta (idioma, tono, formato)
|
||||
- Instrucciones de uso de herramientas (cuándo y cómo usarlas)
|
||||
|
||||
## Archivos a modificar
|
||||
|
||||
### 4. `cmd/launcher/main.go` — Registro en el launcher
|
||||
|
||||
Dos cambios:
|
||||
|
||||
**Import:**
|
||||
```go
|
||||
<agentpkg>agent "github.com/enmanuel/agents/agents/<agent-id>"
|
||||
```
|
||||
|
||||
**rulesRegistry:**
|
||||
```go
|
||||
var rulesRegistry = map[string]func() []decision.Rule{
|
||||
// ... agentes existentes ...
|
||||
"<agent-id>": <agentpkg>agent.Rules, // ← nuevo
|
||||
}
|
||||
```
|
||||
|
||||
**El ID en rulesRegistry DEBE coincidir exactamente con `agent.id` del config.yaml.**
|
||||
|
||||
### 5. `agents/runtime.go` — Registro de herramientas (solo si hay tools nuevas)
|
||||
|
||||
Si el agente necesita una herramienta nueva (no existente), ver la policy `create_tool.md`.
|
||||
|
||||
Las herramientas "siempre disponibles" (`current_time`, `matrix_send`) ya están registradas para todos los agentes.
|
||||
|
||||
## Después de crear los archivos
|
||||
|
||||
Verificar compilación:
|
||||
```bash
|
||||
go build -tags goolm ./...
|
||||
```
|
||||
|
||||
Luego seguir con registro, avatar, verificación y arranque (ver `docs/creating-agents.md`).
|
||||
|
||||
## Reglas generales
|
||||
|
||||
- **Nunca** poner side effects en `agent.go` — es código puro
|
||||
- **Siempre** verificar que `agent.id` coincide entre config.yaml, rulesRegistry y el directorio
|
||||
- **Siempre** compilar con `-tags goolm` para soporte E2EE
|
||||
- **Idioma**: español en configs, prompts y descripciones de dominio; inglés en código Go
|
||||
- **No** crear archivos `data/` — se generan automáticamente al arrancar
|
||||
- **No** commitear tokens ni passwords — solo van en `.env`
|
||||
- Si el agente usa tool_use, asegurarse de que `llm.tool_use.enabled: true` en el config
|
||||
- Usar `agents/asistente2/` como referencia completa de un agente con tools habilitadas
|
||||
@@ -0,0 +1,66 @@
|
||||
# Cómo crear una nueva herramienta (tool)
|
||||
|
||||
Las herramientas viven en `tools/` y siguen el patrón **spec puro + función impura**.
|
||||
|
||||
## Pasos
|
||||
|
||||
### 1. Crear el archivo `tools/<nombre>.go`
|
||||
|
||||
```go
|
||||
package tools
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
// NewMiTool creates a mi_tool tool that does X.
|
||||
// Accepts dependencies needed for execution (configs, clients, etc).
|
||||
func NewMiTool(/* deps */) Tool {
|
||||
return Tool{
|
||||
Def: Def{
|
||||
Name: "mi_tool",
|
||||
Description: "Description clara de qué hace la herramienta para el LLM.",
|
||||
Parameters: []Param{
|
||||
{Name: "param1", Type: "string", Description: "What this param is", Required: true},
|
||||
{Name: "param2", Type: "number", Description: "Optional param", Required: false},
|
||||
},
|
||||
},
|
||||
Exec: func(ctx context.Context, args map[string]any) Result {
|
||||
p1 := getString(args, "param1")
|
||||
if p1 == "" {
|
||||
return Result{Err: fmt.Errorf("mi_tool: param1 is required")}
|
||||
}
|
||||
|
||||
// Execute the actual work here (impure)
|
||||
output := doSomething(p1)
|
||||
|
||||
return Result{Output: output}
|
||||
},
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 2. Registrar en `agents/runtime.go` → `buildToolRegistry()`
|
||||
|
||||
```go
|
||||
if /* condición basada en config */ {
|
||||
reg.Register(tools.NewMiTool(/* deps */))
|
||||
logger.Debug("registered mi_tool")
|
||||
}
|
||||
```
|
||||
|
||||
### 3. Habilitar en el config del agente (`agents/<id>/config.yaml`)
|
||||
|
||||
Asegurarse de que `llm.tool_use.enabled: true` y la sección relevante de `tools:` esté habilitada.
|
||||
|
||||
## Reglas
|
||||
|
||||
- **Def es PURO**: solo datos (nombre, descripción, parámetros). Sin side effects.
|
||||
- **Exec es IMPURO**: hace I/O real. Recibe `context.Context` y `map[string]any`.
|
||||
- **Validar inputs**: siempre validar parámetros requeridos al inicio del Exec.
|
||||
- **Validar permisos**: usar los campos del config (AllowedDomains, AllowedPaths, etc.) para restringir acceso.
|
||||
- **Limitar output**: truncar a 64 KB máximo para no saturar el contexto del LLM.
|
||||
- **Usar `getString()`**: helper del package para extraer strings de args de forma segura.
|
||||
- **Param types válidos**: "string", "number", "integer", "boolean", "object", "array" (JSON Schema types).
|
||||
- **Descripción clara**: el LLM decide cuándo usar la tool basándose en el Description del Def.
|
||||
@@ -0,0 +1,23 @@
|
||||
# Políticas del proyecto
|
||||
|
||||
Guías operativas para LLMs que trabajan en este codebase. Cada política describe cómo ejecutar una tarea específica respetando la arquitectura y convenciones del proyecto.
|
||||
|
||||
## Políticas disponibles
|
||||
|
||||
| Política | Archivo | Cuándo aplicarla |
|
||||
|----------|---------|------------------|
|
||||
| **Crear agente** | [create_agent.md](create_agent.md) | Al crear un nuevo bot/agente Matrix completo |
|
||||
| **Crear herramienta** | [create_tool.md](create_tool.md) | Al añadir una nueva tool para LLM function calling |
|
||||
|
||||
## Cuándo consultar las políticas
|
||||
|
||||
- **Crear agente**: cuando el usuario pida crear un nuevo bot, agente, o asistente. Incluye la estructura de archivos, reglas puras, config YAML, system prompt y registro en el launcher.
|
||||
- **Crear herramienta**: cuando el usuario pida añadir una nueva herramienta/tool al sistema. Incluye el patrón Def (puro) + Exec (impuro), registro en runtime.go y habilitación en config.
|
||||
|
||||
## Principio general
|
||||
|
||||
Todas las políticas respetan el patrón **pure core / impure shell**:
|
||||
- `pkg/` es puro — nunca añadir side effects
|
||||
- `shell/` es impuro — todo I/O va aquí
|
||||
- `agents/` compone ambos — reglas puras + ensamblado con shell
|
||||
- `tools/` sigue el mismo patrón: `Def` (datos puros) + `Exec` (función impura)
|
||||
Reference in New Issue
Block a user