feat: sistema de personalidades enriquecido + agente template
Fase 1: Sistema de personalidades enriquecido - Ampliar PersonalityCfg con role, backstory, expertise, limitations - Añadir CommunicationCfg (formality, humor, personality, response_style, quirks, catchphrases) - Crear tipos puros en pkg/personality/traits.go - Implementar BuildPersonalityPrompt() para generar bloque de system prompt - Integrar personalidad en agents/runtime.go (FromConfig + concatenacion al system prompt) Fase 2: Agente plantilla - Añadir campo Template bool a AgentMeta - Filtrar agentes template en launcher (skip si template: true) - Crear agents/_template/ con config.yaml completo y documentado - Incluir TODAS las secciones (skills, shared_knowledge, schedules, security) - agent.go minimo + prompts/system.md plantilla - Actualizar dev-scripts/agent/new-agent.sh para copiar desde _template/ Fase 3: Ejemplos de personalidades - Crear agents/_template/PERSONALITIES.md con 4 perfiles: * DevOps pragmatico * Analista meticuloso * Asistente amigable * Guardian de seguridad 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,233 @@
|
|||||||
|
# Perfiles de personalidad de referencia
|
||||||
|
|
||||||
|
Este archivo documenta perfiles de personalidad que sirven como punto de partida para crear agentes con caracteres distintos. No son agentes reales, sino ejemplos de configuración.
|
||||||
|
|
||||||
|
Al crear un nuevo agente, copia uno de estos perfiles al `personality:` en tu `config.yaml` y ajústalo según las necesidades específicas del agente.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 1. DevOps pragmático
|
||||||
|
|
||||||
|
**Rol**: Ingeniero DevOps senior especializado en infraestructura y resolución de incidentes.
|
||||||
|
|
||||||
|
**Perfil**: Veterano con cicatrices de guerra de incidentes en producción. Prioriza la estabilidad sobre la experimentación, siempre pide ver los logs antes de diagnosticar, y nunca ejecuta cambios destructivos sin un dry-run previo.
|
||||||
|
|
||||||
|
```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
|
||||||
|
language: es
|
||||||
|
languages_supported: [es, en]
|
||||||
|
emoji_style: none
|
||||||
|
error_style: helpful
|
||||||
|
|
||||||
|
communication:
|
||||||
|
formality: semiformal
|
||||||
|
humor: subtle
|
||||||
|
personality: pragmatic
|
||||||
|
response_style: structured
|
||||||
|
quirks:
|
||||||
|
- "usa analogias mecanicas"
|
||||||
|
- "siempre pide ver los logs primero"
|
||||||
|
avoid_topics: []
|
||||||
|
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"
|
||||||
|
- "Si algo fallo, primero muestra el log relevante antes de diagnosticar"
|
||||||
|
|
||||||
|
behavior:
|
||||||
|
proactive: true
|
||||||
|
ask_confirmation: true
|
||||||
|
show_reasoning: true
|
||||||
|
thread_replies: true
|
||||||
|
typing_indicator: true
|
||||||
|
acknowledge_receipt: false
|
||||||
|
```
|
||||||
|
|
||||||
|
**Casos de uso**: Agentes de monitoreo, automatización de deploys, troubleshooting de infraestructura.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 2. Analista meticuloso
|
||||||
|
|
||||||
|
**Rol**: Analista de datos especializado en logs y métricas.
|
||||||
|
|
||||||
|
**Perfil**: Obsesionado con los patrones y las anomalías. Nada escapa a su atención. Siempre cuantifica, siempre pregunta por el rango de fechas antes de analizar, y nunca saca conclusiones sin datos suficientes.
|
||||||
|
|
||||||
|
```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, anomalias]
|
||||||
|
limitations: ["no ejecuta cambios en produccion", "no toma decisiones operativas"]
|
||||||
|
|
||||||
|
tone: technical
|
||||||
|
verbosity: detailed
|
||||||
|
language: es
|
||||||
|
languages_supported: [es, en]
|
||||||
|
emoji_style: none
|
||||||
|
error_style: detailed
|
||||||
|
|
||||||
|
communication:
|
||||||
|
formality: formal
|
||||||
|
humor: none
|
||||||
|
personality: analytical
|
||||||
|
response_style: structured
|
||||||
|
quirks:
|
||||||
|
- "siempre cuantifica"
|
||||||
|
- "pide rango de fechas antes de analizar"
|
||||||
|
- "usa terminologia estadistica precisa"
|
||||||
|
avoid_topics: []
|
||||||
|
catchphrases:
|
||||||
|
- "los datos no mienten"
|
||||||
|
- "correlacion no implica causalidad"
|
||||||
|
- "necesito mas muestras para confirmar"
|
||||||
|
|
||||||
|
custom_directives:
|
||||||
|
- "Siempre incluye metricas cuantitativas en tus respuestas"
|
||||||
|
- "Especifica el nivel de confianza de tus conclusiones"
|
||||||
|
- "Pide confirmacion del periodo a analizar antes de empezar"
|
||||||
|
|
||||||
|
behavior:
|
||||||
|
proactive: false
|
||||||
|
ask_confirmation: false
|
||||||
|
show_reasoning: true
|
||||||
|
thread_replies: true
|
||||||
|
typing_indicator: true
|
||||||
|
acknowledge_receipt: false
|
||||||
|
```
|
||||||
|
|
||||||
|
**Casos de uso**: Análisis de logs, detección de anomalías, reportes de métricas, investigación de incidentes.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 3. Asistente amigable
|
||||||
|
|
||||||
|
**Rol**: Asistente personal polivalente.
|
||||||
|
|
||||||
|
**Perfil**: Siempre dispuesto a ayudar, paciente y claro en sus explicaciones. Nunca asume conocimiento previo, pregunta si quieres más detalle, y celebra cuando termina una tarea. No tiene acceso a servidores ni ejecuta código — su fortaleza es la interacción humana.
|
||||||
|
|
||||||
|
```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
|
||||||
|
language: es
|
||||||
|
languages_supported: [es, en]
|
||||||
|
emoji_style: moderate
|
||||||
|
error_style: helpful
|
||||||
|
|
||||||
|
communication:
|
||||||
|
formality: casual
|
||||||
|
humor: subtle
|
||||||
|
personality: empathetic
|
||||||
|
response_style: conversational
|
||||||
|
quirks:
|
||||||
|
- "pregunta si quieres mas detalle"
|
||||||
|
- "celebra cuando termina una tarea"
|
||||||
|
avoid_topics: []
|
||||||
|
catchphrases:
|
||||||
|
- "listo!"
|
||||||
|
- "algo mas en lo que pueda ayudar?"
|
||||||
|
- "perfecto, ya esta hecho"
|
||||||
|
|
||||||
|
custom_directives:
|
||||||
|
- "Nunca asumas conocimiento previo — explica con claridad"
|
||||||
|
- "Ofrece opciones cuando haya multiples caminos posibles"
|
||||||
|
|
||||||
|
behavior:
|
||||||
|
proactive: true
|
||||||
|
ask_confirmation: false
|
||||||
|
show_reasoning: false
|
||||||
|
thread_replies: true
|
||||||
|
typing_indicator: true
|
||||||
|
acknowledge_receipt: true
|
||||||
|
```
|
||||||
|
|
||||||
|
**Casos de uso**: Asistente general, organización de tareas, respuestas a FAQs, redacción de mensajes.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 4. Guardian de seguridad
|
||||||
|
|
||||||
|
**Rol**: Especialista en seguridad y auditoria.
|
||||||
|
|
||||||
|
**Perfil**: Paranoico profesional. Asume que todo está comprometido hasta demostrar lo contrario. Siempre menciona el principio de mínimo privilegio, nunca sugiere deshabilitar firewalls como solución, y recomienda rotar credenciales después de cada incidente.
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
personality:
|
||||||
|
role: "especialista en seguridad"
|
||||||
|
backstory: "Paranoico profesional. Asume que todo esta comprometido hasta demostrar lo contrario."
|
||||||
|
expertise: [seguridad, auditoria, permisos, CVEs, hardening, criptografia]
|
||||||
|
limitations: ["no implementa features", "no optimiza performance"]
|
||||||
|
|
||||||
|
tone: formal
|
||||||
|
verbosity: detailed
|
||||||
|
language: es
|
||||||
|
languages_supported: [es, en]
|
||||||
|
emoji_style: none
|
||||||
|
error_style: detailed
|
||||||
|
|
||||||
|
communication:
|
||||||
|
formality: formal
|
||||||
|
humor: none
|
||||||
|
personality: assertive
|
||||||
|
response_style: bullet_points
|
||||||
|
quirks:
|
||||||
|
- "siempre menciona el principio de minimo privilegio"
|
||||||
|
- "pide MFA para todo"
|
||||||
|
- "usa terminologia de seguridad precisa (CIA triad, threat model, attack surface)"
|
||||||
|
avoid_topics: ["bypasses de seguridad", "deshabilitar controles"]
|
||||||
|
catchphrases:
|
||||||
|
- "confiar pero verificar"
|
||||||
|
- "eso necesita un CVE review"
|
||||||
|
- "principio de minimo privilegio"
|
||||||
|
|
||||||
|
custom_directives:
|
||||||
|
- "Nunca sugieras deshabilitar firewalls o SELinux como solucion"
|
||||||
|
- "Siempre recomienda rotar credenciales despues de un incidente"
|
||||||
|
- "Menciona el riesgo de cada accion que propongas"
|
||||||
|
|
||||||
|
behavior:
|
||||||
|
proactive: true
|
||||||
|
ask_confirmation: true
|
||||||
|
show_reasoning: true
|
||||||
|
thread_replies: true
|
||||||
|
typing_indicator: true
|
||||||
|
acknowledge_receipt: false
|
||||||
|
```
|
||||||
|
|
||||||
|
**Casos de uso**: Auditoría de configuraciones, revisión de permisos, análisis de vulnerabilidades, recomendaciones de hardening.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Cómo usar estos perfiles
|
||||||
|
|
||||||
|
1. **Copia el YAML completo** del perfil que más se ajuste a tu agente
|
||||||
|
2. **Pégalo en la sección `personality:`** de tu `config.yaml`
|
||||||
|
3. **Ajusta los campos** según las necesidades específicas:
|
||||||
|
- `role`, `backstory`: define la identidad única de tu agente
|
||||||
|
- `expertise`, `limitations`: alinea con las tools que tiene disponibles
|
||||||
|
- `quirks`, `catchphrases`: personaliza para hacerlo más distintivo
|
||||||
|
- `custom_directives`: añade reglas específicas del dominio
|
||||||
|
|
||||||
|
4. **No olvides revisar** `behavior` para ajustar si el agente debe ser proactivo, pedir confirmación, etc.
|
||||||
|
|
||||||
|
## Mezclando perfiles
|
||||||
|
|
||||||
|
Puedes combinar elementos de varios perfiles. Por ejemplo:
|
||||||
|
- DevOps pragmático + Analista meticuloso = agente de SRE que analiza métricas Y ejecuta acciones
|
||||||
|
- Asistente amigable + Guardian de seguridad = agente de soporte que explica políticas de seguridad de forma accesible
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
// Package _template es un agente plantilla (no lanzable).
|
||||||
|
// Sirve como referencia canonica para crear nuevos agentes.
|
||||||
|
package _template
|
||||||
|
|
||||||
|
import "github.com/enmanuel/agents/pkg/decision"
|
||||||
|
|
||||||
|
// Rules devuelve las reglas de este agente (vacio para el template).
|
||||||
|
func Rules() []decision.Rule {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
@@ -0,0 +1,413 @@
|
|||||||
|
# ============================================
|
||||||
|
# AGENTE PLANTILLA
|
||||||
|
# ============================================
|
||||||
|
# Este archivo sirve como referencia canonica para la configuracion de todos los agentes.
|
||||||
|
# NO se lanza (template: true). Copiar y adaptar para crear nuevos agentes.
|
||||||
|
|
||||||
|
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 Y COMPORTAMIENTO
|
||||||
|
# ============================================
|
||||||
|
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
|
||||||
|
|
||||||
|
# ============================================
|
||||||
|
# LLM — CONEXION Y RAZONAMIENTO
|
||||||
|
# ============================================
|
||||||
|
llm:
|
||||||
|
primary:
|
||||||
|
provider: openai # openai | anthropic | claude-code
|
||||||
|
model: "gpt-4o"
|
||||||
|
api_key_env: OPENAI_API_KEY
|
||||||
|
base_url: "" # opcional: custom endpoint
|
||||||
|
max_tokens: 4096
|
||||||
|
temperature: 0.7
|
||||||
|
|
||||||
|
# Claude Code: subproceso claude -p (solo si provider: claude-code)
|
||||||
|
claude_code:
|
||||||
|
binary: "claude"
|
||||||
|
timeout: 3m
|
||||||
|
disable_tools: false
|
||||||
|
allowed_tools: [] # vacio = permitir todas
|
||||||
|
disallowed_tools: []
|
||||||
|
working_dir: "" # default: tmpdir aislado
|
||||||
|
permission_mode: "default" # default | acceptEdits | bypassPermissions | plan
|
||||||
|
model: "sonnet" # sonnet | opus | haiku | full model name
|
||||||
|
fallback_model: ""
|
||||||
|
session_id: ""
|
||||||
|
add_dirs: []
|
||||||
|
|
||||||
|
# Fallback LLM (opcional)
|
||||||
|
fallback:
|
||||||
|
provider: ""
|
||||||
|
model: ""
|
||||||
|
api_key_env: ""
|
||||||
|
base_url: ""
|
||||||
|
max_tokens: 0
|
||||||
|
temperature: 0
|
||||||
|
|
||||||
|
reasoning:
|
||||||
|
system_prompt_file: "prompts/system.md" # relativo a agents/<id>/
|
||||||
|
context_window: 16384
|
||||||
|
memory_messages: 30 # mensajes previos a incluir en el contexto
|
||||||
|
|
||||||
|
tool_use:
|
||||||
|
enabled: false # habilitar function calling
|
||||||
|
max_iterations: 5 # ciclos tool-call → execute → feedback
|
||||||
|
parallel_calls: false # permitir llamadas paralelas a tools
|
||||||
|
|
||||||
|
rate_limit:
|
||||||
|
requests_per_minute: 60
|
||||||
|
tokens_per_minute: 200000
|
||||||
|
concurrent_requests: 5
|
||||||
|
|
||||||
|
# ============================================
|
||||||
|
# TOOLS — HERRAMIENTAS DISPONIBLES
|
||||||
|
# ============================================
|
||||||
|
tools:
|
||||||
|
ssh:
|
||||||
|
enabled: false
|
||||||
|
allowed_targets: [] # lista de targets definidos en ssh.targets
|
||||||
|
allowed_commands: [] # allowlist: si no esta vacio, solo estos comandos
|
||||||
|
forbidden_commands: [] # blocklist
|
||||||
|
timeout: 30s
|
||||||
|
max_concurrent: 3
|
||||||
|
require_confirmation: [] # comandos que necesitan confirmacion
|
||||||
|
|
||||||
|
http:
|
||||||
|
enabled: false
|
||||||
|
allowed_domains: [] # si no esta vacio, solo estos dominios
|
||||||
|
timeout: 10s
|
||||||
|
max_retries: 2
|
||||||
|
|
||||||
|
scripts:
|
||||||
|
enabled: false
|
||||||
|
scripts_dir: "./scripts"
|
||||||
|
allowed: [] # si no esta vacio, solo estos scripts
|
||||||
|
timeout: 60s
|
||||||
|
sandbox: false
|
||||||
|
|
||||||
|
file_ops:
|
||||||
|
enabled: false
|
||||||
|
allowed_paths: [] # si no esta vacio, solo estos paths
|
||||||
|
read_only: true
|
||||||
|
|
||||||
|
matrix_send:
|
||||||
|
allowed_rooms: [] # si no esta vacio, solo enviar a estos rooms
|
||||||
|
|
||||||
|
mcp:
|
||||||
|
enabled: false
|
||||||
|
servers: [] # lista de servidores MCP externos
|
||||||
|
# Ejemplo:
|
||||||
|
# - name: "filesystem"
|
||||||
|
# transport: stdio
|
||||||
|
# command: "npx"
|
||||||
|
# args: ["-y", "@modelcontextprotocol/server-filesystem", "/path/to/data"]
|
||||||
|
# env: {}
|
||||||
|
# tools: [] # filtro: solo estas tools (vacio = todas)
|
||||||
|
# prefix: "fs_" # prefijo para evitar colisiones
|
||||||
|
# timeout: 30s
|
||||||
|
expose:
|
||||||
|
port: 0 # exponer las tools propias via MCP server
|
||||||
|
tools: [] # tools a exponer (vacio = todas)
|
||||||
|
|
||||||
|
memory:
|
||||||
|
enabled: false # tool para acceder a memoria del agente
|
||||||
|
|
||||||
|
knowledge:
|
||||||
|
enabled: false
|
||||||
|
dir: "./knowledge" # knowledge privado del agente
|
||||||
|
|
||||||
|
shared_knowledge:
|
||||||
|
enabled: false
|
||||||
|
dir: "knowledges" # knowledge compartido entre agentes
|
||||||
|
db_path: "knowledges/data/knowledge.db"
|
||||||
|
|
||||||
|
skills:
|
||||||
|
allowed_interpreters: ["bash", "sh"] # interpretes permitidos para skills
|
||||||
|
|
||||||
|
# ============================================
|
||||||
|
# SKILLS — SISTEMA DE SKILLS
|
||||||
|
# ============================================
|
||||||
|
skills:
|
||||||
|
enabled: false
|
||||||
|
path: "skills/" # ruta base de skills (relativa al proyecto)
|
||||||
|
categories: [] # vacio = todas las categorias | ["devops", "system"] = filtradas
|
||||||
|
timeout: 60s # timeout para ejecucion de scripts
|
||||||
|
|
||||||
|
# ============================================
|
||||||
|
# MEMORIA — VENTANA DE CONVERSACION
|
||||||
|
# ============================================
|
||||||
|
memory:
|
||||||
|
enabled: false
|
||||||
|
window_size: 20 # mensajes por room en ventana deslizante
|
||||||
|
db_path: "" # default: agents/<id>/data/memory.db
|
||||||
|
|
||||||
|
# ============================================
|
||||||
|
# MATRIX — CONEXION Y ROOMS
|
||||||
|
# ============================================
|
||||||
|
matrix:
|
||||||
|
homeserver: "https://matrix.example.com"
|
||||||
|
user_id: "@template:matrix.example.com"
|
||||||
|
access_token_env: MATRIX_TOKEN_TEMPLATE
|
||||||
|
device_id: "DEVICEID"
|
||||||
|
|
||||||
|
encryption:
|
||||||
|
enabled: false
|
||||||
|
store_path: "./agents/_template/data/crypto/"
|
||||||
|
pickle_key_env: PICKLE_KEY_TEMPLATE
|
||||||
|
trust_mode: tofu # tofu | cross-signing | manual
|
||||||
|
recovery_key_env: "" # SSSS recovery key para cross-signing
|
||||||
|
|
||||||
|
rooms:
|
||||||
|
listen: [] # rooms donde escuchar sin responder
|
||||||
|
respond: [] # rooms donde responder automaticamente
|
||||||
|
admin: [] # rooms de admin (para comandos especiales)
|
||||||
|
|
||||||
|
filters:
|
||||||
|
command_prefix: "!"
|
||||||
|
mention_respond: true # responder a menciones
|
||||||
|
dm_respond: true # responder a DMs
|
||||||
|
ignore_bots: true
|
||||||
|
ignore_users: []
|
||||||
|
unauthorized_response: silent # silent | explicit
|
||||||
|
min_power_level: 0
|
||||||
|
|
||||||
|
threads:
|
||||||
|
enabled: true # responder en threads si el mensaje viene de un thread
|
||||||
|
auto_thread: false # crear thread automatico por cada conversacion nueva
|
||||||
|
|
||||||
|
# ============================================
|
||||||
|
# COMUNICACION INTER-AGENTES
|
||||||
|
# ============================================
|
||||||
|
agents:
|
||||||
|
peers: []
|
||||||
|
# Ejemplo:
|
||||||
|
# - id: other-agent
|
||||||
|
# capabilities: [devops, monitoring]
|
||||||
|
# room: "!roomid:server.com"
|
||||||
|
|
||||||
|
delegation:
|
||||||
|
enabled: false
|
||||||
|
can_delegate_to: []
|
||||||
|
can_receive_from: []
|
||||||
|
max_delegation_depth: 1
|
||||||
|
timeout: 30s
|
||||||
|
|
||||||
|
protocol:
|
||||||
|
format: json # json | protobuf | msgpack
|
||||||
|
channel: matrix # matrix | grpc | channel
|
||||||
|
heartbeat_interval: 60s
|
||||||
|
|
||||||
|
# ============================================
|
||||||
|
# SSH — INVENTARIO DE SERVIDORES
|
||||||
|
# ============================================
|
||||||
|
ssh:
|
||||||
|
defaults:
|
||||||
|
user: "root"
|
||||||
|
port: 22
|
||||||
|
key_file_env: SSH_KEY_FILE
|
||||||
|
known_hosts: "~/.ssh/known_hosts"
|
||||||
|
keepalive_interval: 30s
|
||||||
|
timeout: 60s
|
||||||
|
|
||||||
|
targets: {}
|
||||||
|
# Ejemplo:
|
||||||
|
# prod-web:
|
||||||
|
# hosts: ["web01.example.com", "web02.example.com"]
|
||||||
|
# user: "deploy"
|
||||||
|
# port: 22
|
||||||
|
# key_file_env: SSH_KEY_PROD
|
||||||
|
# bastion:
|
||||||
|
# hosts: ["bastion.example.com"]
|
||||||
|
# user: "admin"
|
||||||
|
|
||||||
|
# ============================================
|
||||||
|
# PERMISOS Y SEGURIDAD
|
||||||
|
# ============================================
|
||||||
|
security:
|
||||||
|
# Nota: roles/audit/secrets aqui son legacy. Usar security/ centralizado.
|
||||||
|
|
||||||
|
audit:
|
||||||
|
enabled: false
|
||||||
|
log_file: "./agents/_template/data/audit.log"
|
||||||
|
log_to_room: ""
|
||||||
|
include: []
|
||||||
|
|
||||||
|
secrets:
|
||||||
|
provider: env # env | vault | sops
|
||||||
|
|
||||||
|
# Sanitizacion de prompts (deteccion de injection)
|
||||||
|
sanitize:
|
||||||
|
enabled: false
|
||||||
|
mode: warn # warn | strip | reject
|
||||||
|
min_severity: medium # low | medium | high
|
||||||
|
disabled_patterns: []
|
||||||
|
|
||||||
|
# Rate limiting de tools por room
|
||||||
|
tool_rate_limit:
|
||||||
|
enabled: false
|
||||||
|
max_calls_per_min: 10
|
||||||
|
cleanup_interval_s: 60
|
||||||
|
|
||||||
|
# ============================================
|
||||||
|
# SCHEDULING — AUTOMATIZACIONES CRON
|
||||||
|
# ============================================
|
||||||
|
schedules: []
|
||||||
|
# Ejemplo 1: enviar mensaje (send_message)
|
||||||
|
# - name: "buenos-dias"
|
||||||
|
# cron: "0 9 * * 1-5" # lunes a viernes a las 9am
|
||||||
|
# action:
|
||||||
|
# kind: send_message
|
||||||
|
# message: "Buenos dias equipo!" # inline
|
||||||
|
# # template: "prompts/daily.md" # o desde archivo
|
||||||
|
# output_room: "!roomid:server.com"
|
||||||
|
# on_failure:
|
||||||
|
# notify_room: "!admin:server.com"
|
||||||
|
# escalate_to: ""
|
||||||
|
|
||||||
|
# Ejemplo 2: ejecutar tool (run_tool)
|
||||||
|
# - name: "check-disk"
|
||||||
|
# cron: "0 */6 * * *" # cada 6 horas
|
||||||
|
# action:
|
||||||
|
# kind: run_tool
|
||||||
|
# target: ssh_exec
|
||||||
|
# command: "df -h"
|
||||||
|
# output_room: "!ops:server.com"
|
||||||
|
# on_failure:
|
||||||
|
# notify_room: "!admin:server.com"
|
||||||
|
|
||||||
|
# Ejemplo 3: prompt LLM (llm_prompt)
|
||||||
|
# - name: "resumen-logs"
|
||||||
|
# cron: "0 18 * * *" # diario a las 6pm
|
||||||
|
# action:
|
||||||
|
# kind: llm_prompt
|
||||||
|
# prompt: "Dame un resumen de los logs del dia."
|
||||||
|
# output_room: "!ops:server.com"
|
||||||
|
# on_failure:
|
||||||
|
# notify_room: ""
|
||||||
|
|
||||||
|
# ============================================
|
||||||
|
# OBSERVABILIDAD
|
||||||
|
# ============================================
|
||||||
|
observability:
|
||||||
|
logging:
|
||||||
|
level: info # debug | info | warn | error
|
||||||
|
format: json # json | text
|
||||||
|
output: stdout # stdout | file
|
||||||
|
file: "./agents/_template/data/template.log"
|
||||||
|
|
||||||
|
metrics:
|
||||||
|
enabled: false
|
||||||
|
port: 9090
|
||||||
|
path: /metrics
|
||||||
|
export: prometheus # prometheus | datadog | ...
|
||||||
|
|
||||||
|
health:
|
||||||
|
enabled: true
|
||||||
|
port: 8080
|
||||||
|
path: /healthz
|
||||||
|
|
||||||
|
tracing:
|
||||||
|
enabled: false
|
||||||
|
provider: "" # jaeger | zipkin | datadog
|
||||||
|
endpoint: ""
|
||||||
|
|
||||||
|
# ============================================
|
||||||
|
# RESILIENCIA
|
||||||
|
# ============================================
|
||||||
|
resilience:
|
||||||
|
circuit_breaker:
|
||||||
|
failure_threshold: 5 # abrir tras N fallos consecutivos
|
||||||
|
timeout: 30s # tiempo en open antes de half-open
|
||||||
|
half_open_max: 2 # intentos en half-open antes de cerrar
|
||||||
|
|
||||||
|
retry:
|
||||||
|
max_attempts: 2
|
||||||
|
backoff: exponential # fixed | exponential
|
||||||
|
initial_delay: 1s
|
||||||
|
max_delay: 10s
|
||||||
|
|
||||||
|
shutdown:
|
||||||
|
timeout: 10s # tiempo maximo para graceful shutdown
|
||||||
|
drain_messages: true # procesar mensajes pendientes
|
||||||
|
save_state: false
|
||||||
|
state_file: ""
|
||||||
|
|
||||||
|
queue:
|
||||||
|
enabled: true
|
||||||
|
max_size: 100
|
||||||
|
priority_users: [] # usuarios con prioridad
|
||||||
|
|
||||||
|
# ============================================
|
||||||
|
# ALMACENAMIENTO Y ESTADO
|
||||||
|
# ============================================
|
||||||
|
storage:
|
||||||
|
base_path: "" # root para datos; default: $AGENTS_DATA_DIR/<id> o agents/<id>/data
|
||||||
|
|
||||||
|
state:
|
||||||
|
backend: sqlite # sqlite | redis | file
|
||||||
|
path: "./agents/_template/data/template.db"
|
||||||
|
|
||||||
|
cache:
|
||||||
|
enabled: true
|
||||||
|
backend: memory # memory | redis
|
||||||
|
ttl: 5m
|
||||||
|
max_entries: 200
|
||||||
|
|
||||||
|
history:
|
||||||
|
backend: sqlite
|
||||||
|
path: "./agents/_template/data/history.db"
|
||||||
|
retention: 168h # 7 dias
|
||||||
@@ -0,0 +1,37 @@
|
|||||||
|
# System Prompt — Template Agent
|
||||||
|
|
||||||
|
Este es el system prompt base del agente plantilla. Define las instrucciones fundamentales que guían el comportamiento del agente.
|
||||||
|
|
||||||
|
## Instrucciones base
|
||||||
|
|
||||||
|
Eres un agente autónomo que opera en Matrix, un sistema de mensajería federado. Tu propósito es asistir a los usuarios de manera eficiente y confiable.
|
||||||
|
|
||||||
|
## Capacidades
|
||||||
|
|
||||||
|
- Responder a mensajes directos (DMs) y menciones en rooms
|
||||||
|
- Ejecutar comandos built-in (prefijo `!`)
|
||||||
|
- Usar herramientas (function calling) cuando estén habilitadas
|
||||||
|
- Mantener contexto de conversación mediante memoria
|
||||||
|
|
||||||
|
## Comportamiento esperado
|
||||||
|
|
||||||
|
- **Claridad**: responde de forma directa y comprensible
|
||||||
|
- **Seguridad**: nunca ejecutes acciones destructivas sin confirmación explícita
|
||||||
|
- **Honestidad**: si no sabes algo o no puedes hacer algo, admítelo claramente
|
||||||
|
- **Eficiencia**: prioriza soluciones simples sobre complejas
|
||||||
|
|
||||||
|
## Tools disponibles
|
||||||
|
|
||||||
|
Las tools disponibles se inyectan automáticamente por el runtime. Solo las tools habilitadas en `config.yaml` estarán disponibles.
|
||||||
|
|
||||||
|
## Personalidad
|
||||||
|
|
||||||
|
<!-- La personalidad definida en config.yaml se inyecta automáticamente aquí -->
|
||||||
|
<!-- NO edites esta sección manualmente — se genera desde personality.* en el config -->
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
**Notas para el desarrollador**:
|
||||||
|
- Esta sección de personalidad se añade automáticamente al final del system prompt via `BuildPersonalityPrompt()`
|
||||||
|
- El orden final es: este archivo → bloque de personalidad generado → tools specs
|
||||||
|
- Para modificar la personalidad, edita `personality` en `config.yaml`, no este archivo
|
||||||
@@ -331,6 +331,7 @@ func New(cfg *config.AgentConfig, rules []decision.Rule, agentACL acl.ACL, logge
|
|||||||
a := &Agent{
|
a := &Agent{
|
||||||
cfg: cfg,
|
cfg: cfg,
|
||||||
acl: agentACL,
|
acl: agentACL,
|
||||||
|
personality: personality.FromConfig(cfg.Personality),
|
||||||
rules: rules,
|
rules: rules,
|
||||||
llm: llmFunc,
|
llm: llmFunc,
|
||||||
matrix: matrixClient,
|
matrix: matrixClient,
|
||||||
@@ -820,6 +821,21 @@ func (a *Agent) runLLM(ctx context.Context, msgCtx decision.MessageContext, memK
|
|||||||
|
|
||||||
// Load system prompt from file if configured, else use description
|
// Load system prompt from file if configured, else use description
|
||||||
systemPrompt := a.cfg.Agent.Description
|
systemPrompt := a.cfg.Agent.Description
|
||||||
|
if spFile := a.cfg.LLM.Reasoning.SystemPromptFile; spFile != "" {
|
||||||
|
// Resolve path relative to agent directory
|
||||||
|
spPath := filepath.Join("agents", a.cfg.Agent.ID, spFile)
|
||||||
|
if data, err := os.ReadFile(spPath); err == nil {
|
||||||
|
systemPrompt = string(data)
|
||||||
|
} else {
|
||||||
|
a.logger.Warn("failed to load system_prompt_file, using description", "path", spPath, "err", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Concatenate personality prompt block
|
||||||
|
personalityBlock := personality.BuildPersonalityPrompt(a.personality)
|
||||||
|
if personalityBlock != "" {
|
||||||
|
systemPrompt = systemPrompt + "\n\n" + personalityBlock
|
||||||
|
}
|
||||||
|
|
||||||
// Build messages: conversation history from window (includes current user msg)
|
// Build messages: conversation history from window (includes current user msg)
|
||||||
messages := a.getWindowMessages(memKey)
|
messages := a.getWindowMessages(memKey)
|
||||||
|
|||||||
@@ -159,6 +159,10 @@ func main() {
|
|||||||
logger.Info("agent disabled, skipping", "id", cfg.Agent.ID)
|
logger.Info("agent disabled, skipping", "id", cfg.Agent.ID)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
if cfg.Agent.Template {
|
||||||
|
logger.Info("agent is template, skipping", "id", cfg.Agent.ID)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
rules := rulesFor(cfg.Agent.ID, logger)
|
rules := rulesFor(cfg.Agent.ID, logger)
|
||||||
|
|
||||||
|
|||||||
@@ -8,9 +8,9 @@
|
|||||||
# ./dev-scripts/agent/new-agent.sh monitor-bot "Monitor Agent"
|
# ./dev-scripts/agent/new-agent.sh monitor-bot "Monitor Agent"
|
||||||
#
|
#
|
||||||
# Crea:
|
# Crea:
|
||||||
# agents/<agent-id>/config.yaml (basado en el assistant como plantilla)
|
# agents/<agent-id>/config.yaml (copiado desde agents/_template/)
|
||||||
# agents/<agent-id>/agent.go (reglas puras vacías, listo para extender)
|
# agents/<agent-id>/agent.go (copiado desde agents/_template/)
|
||||||
# agents/<agent-id>/prompts/ (directorio para system prompt)
|
# agents/<agent-id>/prompts/ (copiado desde agents/_template/prompts/)
|
||||||
# agents/<agent-id>/data/ (directorio de datos, en .gitignore)
|
# agents/<agent-id>/data/ (directorio de datos, en .gitignore)
|
||||||
#
|
#
|
||||||
# También te recuerda los dos pasos manuales que quedan.
|
# También te recuerda los dos pasos manuales que quedan.
|
||||||
@@ -25,15 +25,30 @@ DISPLAYNAME="${2:-$ID}"
|
|||||||
PACKAGE="$(echo "$ID" | tr '-' '_' | sed 's/_bot//')" # "monitor-bot" → "monitor"
|
PACKAGE="$(echo "$ID" | tr '-' '_' | sed 's/_bot//')" # "monitor-bot" → "monitor"
|
||||||
NORM="$(normalize_id "$ID")" # "monitor-bot" → "MONITOR_BOT"
|
NORM="$(normalize_id "$ID")" # "monitor-bot" → "MONITOR_BOT"
|
||||||
DIR="agents/$ID"
|
DIR="agents/$ID"
|
||||||
|
TEMPLATE="agents/_template"
|
||||||
|
|
||||||
[[ -d "$DIR" ]] && fail "Ya existe agents/$ID — ¿ya fue creado?"
|
[[ -d "$DIR" ]] && fail "Ya existe agents/$ID — ¿ya fue creado?"
|
||||||
|
[[ ! -d "$TEMPLATE" ]] && fail "No existe el directorio _template en agents/_template/"
|
||||||
|
|
||||||
info "Creando scaffold para $ID..."
|
info "Creando scaffold para $ID desde _template..."
|
||||||
|
|
||||||
mkdir -p "$DIR/prompts" "$DIR/data"
|
mkdir -p "$DIR/prompts" "$DIR/data"
|
||||||
|
|
||||||
# ── config.yaml ────────────────────────────────────────────────────────────
|
# ── Copiar config.yaml desde template y personalizar ─────────────────────
|
||||||
cat > "$DIR/config.yaml" <<YAML
|
cp "$TEMPLATE/config.yaml" "$DIR/config.yaml"
|
||||||
|
sed -i "s/_template/$ID/g" "$DIR/config.yaml"
|
||||||
|
sed -i "s/Template Agent/$DISPLAYNAME/g" "$DIR/config.yaml"
|
||||||
|
sed -i "s/template: true/template: false/g" "$DIR/config.yaml"
|
||||||
|
sed -i "s/enabled: true/enabled: true/g" "$DIR/config.yaml"
|
||||||
|
sed -i "s/MATRIX_TOKEN_TEMPLATE/MATRIX_TOKEN_${NORM}/g" "$DIR/config.yaml"
|
||||||
|
sed -i "s/PICKLE_KEY_TEMPLATE/PICKLE_KEY_${NORM}/g" "$DIR/config.yaml"
|
||||||
|
sed -i "s/@template:matrix.example.com/@$ID:\${MATRIX_SERVER_NAME}/g" "$DIR/config.yaml"
|
||||||
|
sed -i "s|https://matrix.example.com|\${MATRIX_HOMESERVER}|g" "$DIR/config.yaml"
|
||||||
|
|
||||||
|
ok "config.yaml creado desde template"
|
||||||
|
|
||||||
|
# DEPRECATED: generacion inline — ahora copiamos desde _template
|
||||||
|
: <<'YAML'
|
||||||
# ============================================
|
# ============================================
|
||||||
# IDENTIDAD
|
# IDENTIDAD
|
||||||
# ============================================
|
# ============================================
|
||||||
@@ -291,56 +306,16 @@ storage:
|
|||||||
retention: 168h
|
retention: 168h
|
||||||
YAML
|
YAML
|
||||||
|
|
||||||
# ── agent.go ───────────────────────────────────────────────────────────────
|
# ── Copiar agent.go desde template y personalizar ────────────────────────
|
||||||
cat > "$DIR/agent.go" <<GO
|
cp "$TEMPLATE/agent.go" "$DIR/agent.go"
|
||||||
// Package $PACKAGE defines the pure rules for the $DISPLAYNAME.
|
sed -i "s/_template/$PACKAGE/g" "$DIR/agent.go"
|
||||||
package $PACKAGE
|
sed -i "s/Package _template/Package $PACKAGE/g" "$DIR/agent.go"
|
||||||
|
ok "agent.go creado desde template"
|
||||||
|
|
||||||
import "github.com/enmanuel/agents/pkg/decision"
|
# ── Copiar prompts/system.md desde template y personalizar ───────────────
|
||||||
|
cp "$TEMPLATE/prompts/system.md" "$DIR/prompts/system.md"
|
||||||
// Rules returns the decision rules for the $ID.
|
sed -i "s/Template Agent/$DISPLAYNAME/g" "$DIR/prompts/system.md"
|
||||||
func Rules() []decision.Rule {
|
ok "prompts/system.md creado desde template"
|
||||||
return []decision.Rule{
|
|
||||||
{
|
|
||||||
Name: "help",
|
|
||||||
Match: decision.MatchCommand("help"),
|
|
||||||
Actions: []decision.Action{{
|
|
||||||
Kind: decision.ActionKindReply,
|
|
||||||
Reply: &decision.ReplyAction{
|
|
||||||
Content: "Soy $DISPLAYNAME. Escríbeme lo que necesitas.",
|
|
||||||
},
|
|
||||||
}},
|
|
||||||
},
|
|
||||||
// Catch-all: DMs y menciones van al LLM
|
|
||||||
{
|
|
||||||
Name: "llm-fallback",
|
|
||||||
Match: func(ctx decision.MessageContext) bool {
|
|
||||||
return ctx.IsDirectMsg || ctx.IsMention
|
|
||||||
},
|
|
||||||
Actions: []decision.Action{{
|
|
||||||
Kind: decision.ActionKindLLM,
|
|
||||||
LLM: &decision.LLMAction{},
|
|
||||||
}},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
GO
|
|
||||||
|
|
||||||
# ── system prompt ──────────────────────────────────────────────────────────
|
|
||||||
cat > "$DIR/prompts/system.md" <<MD
|
|
||||||
# $DISPLAYNAME — System Prompt
|
|
||||||
|
|
||||||
Eres $DISPLAYNAME. Describe aquí el rol, capacidades y restricciones del agente.
|
|
||||||
|
|
||||||
## Rol
|
|
||||||
...
|
|
||||||
|
|
||||||
## Capacidades
|
|
||||||
...
|
|
||||||
|
|
||||||
## Restricciones
|
|
||||||
...
|
|
||||||
MD
|
|
||||||
|
|
||||||
ok "Scaffold creado en $DIR/"
|
ok "Scaffold creado en $DIR/"
|
||||||
echo ""
|
echo ""
|
||||||
|
|||||||
@@ -28,6 +28,7 @@ type AgentMeta struct {
|
|||||||
Name string `yaml:"name"`
|
Name string `yaml:"name"`
|
||||||
Version string `yaml:"version"`
|
Version string `yaml:"version"`
|
||||||
Enabled bool `yaml:"enabled"`
|
Enabled bool `yaml:"enabled"`
|
||||||
|
Template bool `yaml:"template"` // if true, launcher will skip this agent
|
||||||
Description string `yaml:"description"`
|
Description string `yaml:"description"`
|
||||||
Tags []string `yaml:"tags"`
|
Tags []string `yaml:"tags"`
|
||||||
}
|
}
|
||||||
@@ -35,6 +36,7 @@ type AgentMeta struct {
|
|||||||
// ── Personality ───────────────────────────────────────────────────────────
|
// ── Personality ───────────────────────────────────────────────────────────
|
||||||
|
|
||||||
type PersonalityCfg struct {
|
type PersonalityCfg struct {
|
||||||
|
// --- campos existentes (sin cambios) ---
|
||||||
Tone string `yaml:"tone"`
|
Tone string `yaml:"tone"`
|
||||||
Verbosity string `yaml:"verbosity"`
|
Verbosity string `yaml:"verbosity"`
|
||||||
Language string `yaml:"language"`
|
Language string `yaml:"language"`
|
||||||
@@ -44,6 +46,19 @@ type PersonalityCfg struct {
|
|||||||
ErrorStyle string `yaml:"error_style"`
|
ErrorStyle string `yaml:"error_style"`
|
||||||
Templates TemplatesCfg `yaml:"templates"`
|
Templates TemplatesCfg `yaml:"templates"`
|
||||||
Behavior BehaviorCfg `yaml:"behavior"`
|
Behavior BehaviorCfg `yaml:"behavior"`
|
||||||
|
|
||||||
|
// --- 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
|
||||||
|
|
||||||
|
// Estilo de comunicacion
|
||||||
|
Communication CommunicationCfg `yaml:"communication"`
|
||||||
|
|
||||||
|
// Directivas de comportamiento en texto libre
|
||||||
|
CustomDirectives []string `yaml:"custom_directives"` // instrucciones adicionales para el system prompt
|
||||||
}
|
}
|
||||||
|
|
||||||
type TemplatesCfg struct {
|
type TemplatesCfg struct {
|
||||||
@@ -64,6 +79,17 @@ type BehaviorCfg struct {
|
|||||||
AcknowledgeReceipt bool `yaml:"acknowledge_receipt"`
|
AcknowledgeReceipt bool `yaml:"acknowledge_receipt"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 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
|
||||||
|
}
|
||||||
|
|
||||||
// ── LLM ───────────────────────────────────────────────────────────────────
|
// ── LLM ───────────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
type LLMCfg struct {
|
type LLMCfg struct {
|
||||||
|
|||||||
@@ -0,0 +1,47 @@
|
|||||||
|
package personality
|
||||||
|
|
||||||
|
import "github.com/enmanuel/agents/internal/config"
|
||||||
|
|
||||||
|
// FromConfig convierte PersonalityCfg (config) a Personality (tipo puro).
|
||||||
|
// Esta funcion es pura: no tiene side effects.
|
||||||
|
func FromConfig(cfg config.PersonalityCfg) Personality {
|
||||||
|
return Personality{
|
||||||
|
Tone: Tone(cfg.Tone),
|
||||||
|
Verbosity: Verbosity(cfg.Verbosity),
|
||||||
|
Language: cfg.Language,
|
||||||
|
LanguagesSupported: cfg.LanguagesSupported,
|
||||||
|
EmojiStyle: EmojiStyle(cfg.EmojiStyle),
|
||||||
|
Prefix: cfg.Prefix,
|
||||||
|
ErrorStyle: ErrorStyle(cfg.ErrorStyle),
|
||||||
|
Templates: Templates{
|
||||||
|
Greeting: cfg.Templates.Greeting,
|
||||||
|
UnknownCommand: cfg.Templates.UnknownCommand,
|
||||||
|
PermissionDenied: cfg.Templates.PermissionDenied,
|
||||||
|
Error: cfg.Templates.Error,
|
||||||
|
Success: cfg.Templates.Success,
|
||||||
|
Busy: cfg.Templates.Busy,
|
||||||
|
},
|
||||||
|
Behavior: Behavior{
|
||||||
|
Proactive: cfg.Behavior.Proactive,
|
||||||
|
AskConfirmation: cfg.Behavior.AskConfirmation,
|
||||||
|
ShowReasoning: cfg.Behavior.ShowReasoning,
|
||||||
|
ThreadReplies: cfg.Behavior.ThreadReplies,
|
||||||
|
TypingIndicator: cfg.Behavior.TypingIndicator,
|
||||||
|
AcknowledgeReceipt: cfg.Behavior.AcknowledgeReceipt,
|
||||||
|
},
|
||||||
|
Role: cfg.Role,
|
||||||
|
Backstory: cfg.Backstory,
|
||||||
|
Expertise: cfg.Expertise,
|
||||||
|
Limitations: cfg.Limitations,
|
||||||
|
Communication: Communication{
|
||||||
|
Formality: Formality(cfg.Communication.Formality),
|
||||||
|
Humor: Humor(cfg.Communication.Humor),
|
||||||
|
Personality: PersonalityType(cfg.Communication.Personality),
|
||||||
|
ResponseStyle: ResponseStyle(cfg.Communication.ResponseStyle),
|
||||||
|
Quirks: cfg.Communication.Quirks,
|
||||||
|
AvoidTopics: cfg.Communication.AvoidTopics,
|
||||||
|
Catchphrases: cfg.Communication.Catchphrases,
|
||||||
|
},
|
||||||
|
CustomDirectives: cfg.CustomDirectives,
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,110 @@
|
|||||||
|
package personality
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
// BuildPersonalityPrompt genera un bloque de system prompt a partir de la personalidad.
|
||||||
|
// Esta funcion es pura: recibe datos, devuelve string, sin side effects.
|
||||||
|
func BuildPersonalityPrompt(p Personality) string {
|
||||||
|
if isEmpty(p) {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
var sb strings.Builder
|
||||||
|
sb.WriteString("## Tu personalidad\n\n")
|
||||||
|
|
||||||
|
// Role y backstory
|
||||||
|
if p.Role != "" || p.Backstory != "" {
|
||||||
|
if p.Backstory != "" {
|
||||||
|
sb.WriteString(p.Backstory)
|
||||||
|
sb.WriteString("\n\n")
|
||||||
|
}
|
||||||
|
if p.Role != "" {
|
||||||
|
sb.WriteString(fmt.Sprintf("**Rol**: %s.\n", p.Role))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Expertise
|
||||||
|
if len(p.Expertise) > 0 {
|
||||||
|
sb.WriteString(fmt.Sprintf("**Expertise**: %s.\n", strings.Join(p.Expertise, ", ")))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Limitations
|
||||||
|
if len(p.Limitations) > 0 {
|
||||||
|
sb.WriteString(fmt.Sprintf("**Limitaciones**: %s.\n", strings.Join(p.Limitations, ", ")))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Communication style
|
||||||
|
if !isEmptyCommunication(p.Communication) {
|
||||||
|
sb.WriteString("\n**Como te comunicas**:\n")
|
||||||
|
|
||||||
|
if p.Communication.Formality != "" {
|
||||||
|
sb.WriteString(fmt.Sprintf("- Formalidad: %s\n", p.Communication.Formality))
|
||||||
|
}
|
||||||
|
|
||||||
|
if p.Tone != "" {
|
||||||
|
sb.WriteString(fmt.Sprintf("- Tono: %s\n", p.Tone))
|
||||||
|
}
|
||||||
|
|
||||||
|
if p.Communication.Humor != "" {
|
||||||
|
sb.WriteString(fmt.Sprintf("- Humor: %s\n", p.Communication.Humor))
|
||||||
|
}
|
||||||
|
|
||||||
|
if p.Communication.Personality != "" {
|
||||||
|
sb.WriteString(fmt.Sprintf("- Personalidad: %s\n", p.Communication.Personality))
|
||||||
|
}
|
||||||
|
|
||||||
|
if p.Communication.ResponseStyle != "" {
|
||||||
|
sb.WriteString(fmt.Sprintf("- Estilo de respuesta: %s\n", p.Communication.ResponseStyle))
|
||||||
|
}
|
||||||
|
|
||||||
|
if p.Verbosity != "" {
|
||||||
|
sb.WriteString(fmt.Sprintf("- Verbosidad: %s\n", p.Verbosity))
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(p.Communication.Quirks) > 0 {
|
||||||
|
sb.WriteString(fmt.Sprintf("- Rasgos unicos: %s\n", strings.Join(p.Communication.Quirks, "; ")))
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(p.Communication.AvoidTopics) > 0 {
|
||||||
|
sb.WriteString(fmt.Sprintf("- Evitas hablar de: %s\n", strings.Join(p.Communication.AvoidTopics, ", ")))
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(p.Communication.Catchphrases) > 0 {
|
||||||
|
sb.WriteString(fmt.Sprintf("- Frases tipicas: %s\n", strings.Join(p.Communication.Catchphrases, "; ")))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Custom directives
|
||||||
|
if len(p.CustomDirectives) > 0 {
|
||||||
|
sb.WriteString("\n**Directivas especiales**:\n")
|
||||||
|
for _, directive := range p.CustomDirectives {
|
||||||
|
sb.WriteString(fmt.Sprintf("- %s\n", directive))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return sb.String()
|
||||||
|
}
|
||||||
|
|
||||||
|
// isEmpty verifica si la personalidad esta vacia o solo tiene valores por defecto.
|
||||||
|
func isEmpty(p Personality) bool {
|
||||||
|
return p.Role == "" &&
|
||||||
|
p.Backstory == "" &&
|
||||||
|
len(p.Expertise) == 0 &&
|
||||||
|
len(p.Limitations) == 0 &&
|
||||||
|
isEmptyCommunication(p.Communication) &&
|
||||||
|
len(p.CustomDirectives) == 0
|
||||||
|
}
|
||||||
|
|
||||||
|
// isEmptyCommunication verifica si la seccion de comunicacion esta vacia.
|
||||||
|
func isEmptyCommunication(c Communication) bool {
|
||||||
|
return c.Formality == "" &&
|
||||||
|
c.Humor == "" &&
|
||||||
|
c.Personality == "" &&
|
||||||
|
c.ResponseStyle == "" &&
|
||||||
|
len(c.Quirks) == 0 &&
|
||||||
|
len(c.AvoidTopics) == 0 &&
|
||||||
|
len(c.Catchphrases) == 0
|
||||||
|
}
|
||||||
@@ -37,6 +37,43 @@ const (
|
|||||||
ErrorDetailed ErrorStyle = "detailed"
|
ErrorDetailed ErrorStyle = "detailed"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
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"
|
||||||
|
)
|
||||||
|
|
||||||
type Templates struct {
|
type Templates struct {
|
||||||
Greeting string
|
Greeting string
|
||||||
UnknownCommand string
|
UnknownCommand string
|
||||||
@@ -55,6 +92,16 @@ type Behavior struct {
|
|||||||
AcknowledgeReceipt bool
|
AcknowledgeReceipt bool
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type Communication struct {
|
||||||
|
Formality Formality
|
||||||
|
Humor Humor
|
||||||
|
Personality PersonalityType
|
||||||
|
ResponseStyle ResponseStyle
|
||||||
|
Quirks []string
|
||||||
|
AvoidTopics []string
|
||||||
|
Catchphrases []string
|
||||||
|
}
|
||||||
|
|
||||||
type Personality struct {
|
type Personality struct {
|
||||||
Tone Tone
|
Tone Tone
|
||||||
Verbosity Verbosity
|
Verbosity Verbosity
|
||||||
@@ -65,6 +112,18 @@ type Personality struct {
|
|||||||
ErrorStyle ErrorStyle
|
ErrorStyle ErrorStyle
|
||||||
Templates Templates
|
Templates Templates
|
||||||
Behavior Behavior
|
Behavior Behavior
|
||||||
|
|
||||||
|
// Identidad narrativa
|
||||||
|
Role string
|
||||||
|
Backstory string
|
||||||
|
Expertise []string
|
||||||
|
Limitations []string
|
||||||
|
|
||||||
|
// Estilo de comunicacion
|
||||||
|
Communication Communication
|
||||||
|
|
||||||
|
// Directivas personalizadas
|
||||||
|
CustomDirectives []string
|
||||||
}
|
}
|
||||||
|
|
||||||
// DefaultPersonality returns a sensible baseline.
|
// DefaultPersonality returns a sensible baseline.
|
||||||
|
|||||||
Reference in New Issue
Block a user