Skip to content

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:

markdown
---
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):

CampoRequeridoTipoPropósito
namestringClave de spawn. Debe ser único. snake_case o kebab-case.
descriptionstringAparece en el catálogo de agentes del coordinator.
when_to_usestringLa primera línea aparece en el catálogo. Estricta-requerida porque el coordinator no puede elegir una persona sin ella.
toolsno[]stringWhitelist de nombres de BaseTool. Vacío = no hereda nada.
disallowed_toolsno[]stringBlacklist. Gana sobre whitelist; soporta wildcards (*_skill).
permission_modenostringread-only / mutating-with-confirm / dual-sign-required. Hoy es informacional; versiones futuras podrían auto-cablear decorators basados en esto.
max_turnsnointTope duro del loop ReAct. Por defecto 15.
modelnostringIdentificador del LLM (p. ej. anthropic/claude-sonnet-4-7). Cae al default de la organización.
critical_remindernostringEnvuelto en <critical-reminder>...</critical-reminder> en el system prompt. También re-inyectado por turno por la capa de grafo.
initial_promptnostringAntepuesto al primer turno de usuario del worker. Rara vez usado.
backgroundnobooltrue = spawn async (la UI no bloquea). Usado por reviewer.
omit_claude_mdnoboolSuprime el base prompt del runtime para esta persona.
metadatanomapForma 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í:

yaml
tools: ["query_*", "host_bash"]    # everything starting with query_, plus bash
disallowed_tools: ["query_devices"] # but not this one

deja 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:

  1. 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.
  2. 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)Settings → Agents → Save
Cambiar whitelist de toolsLo mismo. El filtro se aplica por spawn.
Cambiar model / max_turnsLo mismo. Nuevos spawns recogen los nuevos valores.
Añadir una nueva personaSettings → Agents → New, o soltar archivo + Reload
Borrar una personaSettings → Agents → Delete, o quitar archivo + Reload
Sobreescribir una integrada (mismo name)Replace upserta; el coordinator usa la nueva.
Cambiar qué tools existen en el bagNoEl registro de BaseTool es del lado del binario.
Añadir un nuevo BaseToolNoRequiere cambio de código + reinicio del manager.
Cambiar la semántica de default_localeNoEso 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"

  1. 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 su description y la primera línea de when_to_use.
  2. 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íneas chatruntime: parse <path> en el log del manager.
  3. 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/skills para el bag activo.
  • El identificador del modelo es incorrecto. El resolver de modelo de chat mapea anthropic/<x> a default_provider si no está configurado. Establece default_provider en anthropic en Settings → LLM o fija un provider+model concreto en la persona.
  • max_turns es demasiado bajo. Un worker que se queda sin turnos antes de producir un mensaje de assistant final regresa como failed. 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_reminder para 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

bash
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 .md en tu repo de ops. 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 Reload automáticamente.

Relacionado