Crear agentes custom
Las personas custom extienden Ongrid con tus propios specialists. Viven en disco como archivos <name>.md con frontmatter YAML, exactamente como las integradas — mismo loader, mismo registry, misma dispatch path. Escribe una, móntala y el coordinator puede despachar a ella.
Esta página es el contrato.
Layout de archivo
Una persona es un único archivo Markdown con frontmatter YAML:
---
name: specialist-clickhouse
description: ClickHouse 查询性能 / 分区健康 / mutation backlog 专家
when_to_use: |
When the user asks about:
- ClickHouse query plan / scan / shuffle slow
- Partition merges / mutation backlog
- Replication lag between replicas
- System.parts / system.mutations inspection
tools:
- query_knowledge
- query_clickhouse_system # custom BaseTool you registered
- query_promql # for clickhouse_* metrics
- host_bash
- get_edge_summary
disallowed_tools:
- host_restart_service
permission_mode: read-only
max_turns: 12
model: anthropic/claude-sonnet-4-7
critical_reminder: |
You're read-only. Never propose direct ALTER / OPTIMIZE without
citing the system.mutations evidence first. Always check the
replication lag before recommending any maintenance command.
---
# specialist-clickhouse
You are Ongrid's ClickHouse specialist.
## Step 0: knowledge base check (mandatory)
Before any inspection, call `query_knowledge` once with a natural-
language description of the question. Hit (score >= 0.6) → follow
the playbook. Cite as `(参考 KB: <title>)` in your final reply.
## Working style
1. Start with `query_clickhouse_system` for system.parts /
system.mutations / system.replication_queue. One call, broad
snapshot.
2. If a specific table is suspect, drill into `system.parts` for
that table with bytes / rows / merge_state.
3. For replication: `system.replication_queue` for failures,
`clickhouse_replica_delay_seconds` PromQL series for trend.
4. For query perf: `system.query_log` with `query_duration_ms`
sort + `read_rows` to find the heavy query.
## Output
- 现状 (1-2 sentences): which table, which metric, what's wrong.
- 证据 (2-3 lines): system.* row excerpts + PromQL value.
- 建议 (1 line): observation only, or "recommend dispatching
specialist-ops to run OPTIMIZE/ALTER under reviewer".Referencia de frontmatter
Los campos que el parser entiende (ParseAgentMd):
| Campo | Requerido | Tipo | Propósito |
|---|---|---|---|
name | sí | string | Clave de spawn. Debe ser único. snake_case o kebab-case. |
description | sí | string | Aparece en el catálogo de agentes del coordinator. |
when_to_use | sí | string | La primera línea aparece en el catálogo. Estricta-requerida porque el coordinator no puede elegir una persona sin ella. |
tools | no | []string | Whitelist de nombres de BaseTool. Vacío = no hereda nada. |
disallowed_tools | no | []string | Blacklist. Gana sobre whitelist; soporta wildcards (*_skill). |
permission_mode | no | string | read-only / mutating-with-confirm / dual-sign-required. Hoy es informacional; versiones futuras podrían auto-cablear decorators basados en esto. |
max_turns | no | int | Tope duro del loop ReAct. Por defecto 15. |
model | no | string | Identificador del LLM (p. ej. anthropic/claude-sonnet-4-7). Cae al default de la organización. |
critical_reminder | no | string | Envuelto en <critical-reminder>...</critical-reminder> en el system prompt. También re-inyectado por turno por la capa de grafo. |
initial_prompt | no | string | Antepuesto al primer turno de usuario del worker. Rara vez usado. |
background | no | bool | true = spawn async (la UI no bloquea). Usado por reviewer. |
omit_claude_md | no | bool | Suprime el base prompt del runtime para esta persona. |
metadata | no | map | Forma libre. metadata.ongrid.{scope, min_ongrid_version} lo lee el registry; el resto es pass-through. |
Los campos desconocidos se preservan en Agent.UnknownFields para que futuras adiciones al formato de persona de Claude Code (effort, isolation, mcp_servers, hooks, …) no rompan la carga.
tools vs disallowed_tools
Whitelist + blacklist, el negro gana. Así:
tools: ["query_*", "host_bash"] # everything starting with query_, plus bash
disallowed_tools: ["query_devices"] # but not this onedeja query_promql, query_logql, query_traceql, query_knowledge, … y host_bash, menos query_devices.
Wildcards: *_skill matchea cada nombre de tool que termine en _skill. Así es como el reviewer bloquea todas las ejecuciones de skill en una línea.
El AgentTool también se elimina automáticamente del bag de cualquier worker — los workers no pueden lanzar workers. No necesitas listarlo bajo disallowed_tools.
Dónde viven las personas
El runtime recorre dos raíces:
- La raíz horneada en imagen —
/app/agents/dentro del contenedor del manager. Contiene las seis personas distribuidas. Solo lectura dentro de la imagen; sobrevive al restart del contenedor pero no al custom code. - La raíz de marketplace —
/var/lib/ongrid/agents/(volumen montado). Las personas escritas por el usuario aterrizan aquí vía la UI Settings → Agents o vía la ruta de install del marketplace.
Ambas se mergean en el mismo AgentRegistry. Ante una colisión de nombre el loader registra un warning y conserva la primera carga. Para sobreescribir una persona integrada, guarda tu versión con el mismo name vía la UI de Settings — AgentRegistry.Replace hace upsert in place.
Por dónde empezar
La ruta más rápida es copiar agents/specialist-disk.md en tu editor, renombrarla y ajustar el toolbag. La forma lleva todas las convenciones (KB-first, receta de 4 pasos, formato de salida) que funcionan bien con el coordinator.
Hot reload vs restart
| Acción | ¿Hot-reloadable? | Cómo |
|---|---|---|
| Editar cuerpo de persona (system prompt) | Sí | Settings → Agents → Save |
| Cambiar whitelist de tools | Sí | Lo mismo. El filtro se aplica por spawn. |
Cambiar model / max_turns | Sí | Lo mismo. Nuevos spawns recogen los nuevos valores. |
| Añadir una nueva persona | Sí | Settings → Agents → New, o soltar archivo + Reload |
| Borrar una persona | Sí | Settings → Agents → Delete, o quitar archivo + Reload |
Sobreescribir una integrada (mismo name) | Sí | Replace upserta; el coordinator usa la nueva. |
| Cambiar qué tools existen en el bag | No | El registro de BaseTool es del lado del binario. |
| Añadir un nuevo BaseTool | No | Requiere cambio de código + reinicio del manager. |
Cambiar la semántica de default_locale | No | Eso es código del runtime. |
El lock alrededor de AgentRegistry es un sync.RWMutex. Un turno del coordinator en vuelo que ya obtuvo un puntero a la persona sigue usando el snapshot; el siguiente turno del coordinator ve la nueva persona.
Depuración
"El coordinator nunca despacha a mi persona"
- Revisa el catálogo de agentes en el system prompt del coordinator (el manager registra el prompt renderizado al arranque con
--log-level=debug). Tu persona debería aparecer con sudescriptiony la primera línea dewhen_to_use. - Si el catálogo no la trae: el loader registró un warning. Revisa
AgentRegistry.Warnings()vía la API (GET /api/v1/agents/warnings) o busca líneaschatruntime: parse <path>en el log del manager. - Si el catálogo la tiene pero el LLM no la elige: aprieta
when_to_use. Empieza con un patrón de trigger concreto; al LLM se le indica leer la primera línea como hint de matching.
"El worker se lanza pero falla de inmediato"
Causas comunes:
- Un tool en la whitelist no está en el bag. El runtime filtra y descarta silenciosamente cualquier cosa no presente; el worker no puede llamar a lo que no está. Revisa
GET /api/v1/skillspara el bag activo. - El identificador del modelo es incorrecto. El resolver de modelo de chat mapea
anthropic/<x>adefault_providersi no está configurado. Establecedefault_providerenanthropicen Settings → LLM o fija un provider+model concreto en la persona. max_turnses demasiado bajo. Un worker que se queda sin turnos antes de producir un mensaje de assistant final regresa comofailed. Sube a 15+ para cualquier persona no trivial.
"El worker devuelve OK pero la salida es basura"
El cuerpo de la persona es tu system prompt. Aprieta:
- Empieza con Paso 0: una sola llamada de KB forzada. Ancla al worker.
- Especifica el formato de salida verbatim en el cuerpo. El coordinator parsea sobre este formato.
- Usa
critical_reminderpara restricciones duras (read-only, sin PII, idioma de salida). Va envuelto en<critical-reminder>Y se re-inyecta por turno — el LLM lo ve en cada iteración.
Probando tu persona
Dos puntos de integración:
Desde la superficie de chat
Abre /chat, pregunta algo que matchee el when_to_use de tu persona. Mira la SPA — si el coordinator despacha, aparece un "Agent tile" con el name de tu persona + el description de AgentTool. Pulsa para ver la transcripción del worker.
Desde la API
curl -X POST http://localhost:8080/api/v1/chat \
-H 'Authorization: Bearer <token>' \
-H 'Content-Type: application/json' \
-d '{"prompt": "<the question that should trigger your persona>"}'La respuesta en streaming muestra:
- deltas
text— la prosa del coordinator. - envelopes
agent_tile— cada dispatch de AgentTool. - envelopes
task_notification— finalización del worker.
Si despacha a tu persona, el agent_tile.persona correspondiente será tu name.
Cuándo NO escribir una persona custom
- La tarea es una respuesta de 1 tool. No envuelvas "consulta mi Prometheus custom" en una persona. Registra un BaseTool custom y deja que el coordinator lo llame.
- La tarea es one-off. Las personas son para patrones recurrentes. Para una investigación de un solo disparo, pregunta al coordinator directamente.
- La tarea necesita llamar a los 5 specialists. Para eso está el coordinator; no escribas un meta-specialist que recree el comportamiento del coordinator.
Una buena regla: escribe una persona cuando la misma forma de pregunta se repita, la respuesta requiera 5+ llamadas de tool y el toolbag sea más estrecho que lo que lleva el coordinator.
Compartir personas
- Suelta el archivo
.mden tu repo deops. Móntalo en el contenedor del manager bajo/var/lib/ongrid/agents/. El registry lo recoge al arranque (o en una llamada Reload). - Para rollout a nivel de organización, despliega a través del marketplace de skills — el install del marketplace empaqueta personas + skills juntas y dispara un
Reloadautomáticamente.
Relacionado
- Overview de agentes — el cuadro completo.
- Formato de persona — cada campo, sin prosa.
- Skill manifest — formato compañero para distribuir personas junto a skills custom.