Delegación y SubAgentes
Un SubAgent es una ejecución hija efímera generada por un agente padre que hereda la identidad del padre de forma predeterminada: el mismo alias de agente, la misma SecurityPolicy, la misma lista de permitidos de memoria, el mismo proveedor de modelo configurado, el mismo registro de herramientas. Auditable como hija mediante un intervalo de rastreo agent.<alias>.subagent.<run_id>.
Los SubAgents no son un concepto de configuración independiente. No existe un bloque [subagents.*] en el esquema. La identidad de cada SubAgent es la del bucle de agente del elemento padre que lo generó.
Cuándo usar spawn_subagent vs delegate
Hay dos herramientas cerca. No son intercambiables.
spawn_subagent: ejecuta el MISMO agente nuevamente bajo su propia identidad para una subtarea específica. El hijo ve el envoltorio completo de permisos del padre menos cualquier restricción adicional. Úselo cuando el padre quiere aislar una subtarea interna fuera de su historial de conversación principal sin cambiar de identidad.delegate: transfiere la solicitud a un agente DIFERENTE configurado (nombrado por alias). El agente objetivo se ejecuta bajo su propia identidad y proveedor de modelo, pero la delegación está sujeta a restricciones: el perfil de riesgo del llamador debe establecerdelegation_policy mode = "allow"(el valor predeterminado es"forbidden"), y el objetivo debe ser accesible como un par del mismo perfil o como una entrada explícita dedelegates. Las entradas explícitas eligenmode = "bounded"omode = "independent", lo que determina si el límite de herramientas del llamador sigue aplicándose. Úsalo cuando otro especialista configurado deba encargarse del trabajo. Consulta Delegation gating abajo.
Esta página documenta spawn_subagent de principio a fin. delegate se encuentra en crates/zeroclaw-runtime/src/tools/delegate.rs y es una superficie independiente.
Cómo se instancia un SubAgent
Dos sitios de generación convergen en SubAgentSpawn (crates/zeroclaw-runtime/src/subagent/mod.rs:97):
- Desde un bucle de agente: el modelo llama a la herramienta
spawn_subagentcon una cadenaprompt. La herramienta se registra como cualquier otra en el registro (crates/zeroclaw-runtime/src/tools/mod.rs,SpawnSubagentTool::new). - Desde cron: los trabajos
JobType::Agentse ejecutan a través derun_agent_job(crates/zeroclaw-runtime/src/cron/scheduler.rs), que construye el mismoSubAgentContextpero marca al hijo como una ejecución de nivel superior (no como un SubAgent) para que pueda generar por sí mismo un nivel de subagente.
Ambas rutas invocan:
#![allow(unused)]
fn main() {
SubAgentSpawn::for_agent(config, parent_alias)? // resolver identidad del padre
.build(SubAgentOverrides::default())? // validar cualquier estrechamiento
}
for_agent lee el risk_profile del padre y [agents.<alias>.workspace.read_memory_from] para construir la lista de permitidos heredada; el propio alias del padre siempre se agrega, de modo que un SubAgent siempre ve las filas de memoria propias de su padre. build aplica una restricción opcional (consulta Herencia de permisos más abajo) y devuelve un SubAgentContext validado.
Ciclo de vida
Síncrono, en proceso, un único runtime de tokio. Nada cruza el límite del proceso.
- El bucle de herramientas del padre despacha
spawn_subagent. La herramienta lee su argumentoprompty rechaza si está vacío. - La herramienta comprueba dos guardas en orden:
- Límite de profundidad 1. Si la ejecución que realiza la llamada era en sí misma un SubAgent (
AgentRunOverrides.is_subagent == true), rechaza con"spawn_subagent: a subagent may not spawn its own subagents (depth-1 cap)". Los SubAgents no pueden hacer recursión. - Puerta de herramientas del perfil de riesgo. Si el
[risk_profiles.<alias>].allowed_toolsdel elemento padre no está vacío y no incluyespawn_subagent, o siexcluded_toolslo incluye, rechaza con un mensaje que indique el alias del elemento padre.
- Límite de profundidad 1. Si la ejecución que realiza la llamada era en sí misma un SubAgent (
- La herramienta llama a
SubAgentSpawn::for_agent+build. Los fallos (alias de padre desconocido, anulación de escalado) se manifiestan comoToolResult { success: false, error: "subagent spawn failed: ..." }. - La herramienta construye
AgentRunOverrides { security, memory: None, is_subagent: true, suppress_memory_inject: true }(el origenSubTurndel hijo ya omite la inyección de memoria del motor; la bandera hace explícita la exclusión) y espera acrate::agent::run(crates/zeroclaw-runtime/src/agent/loop_.rs,pub async fn run) dentro de un ámbito de trazado con clavesubagent-<uuid>. La ejecucióntooldel padre se bloquea hasta que el hijo devuelve. - El bucle del agente hijo se ejecuta hasta completarse. Su registro de herramientas se construye desde cero, con
is_subagent_caller: truefluyendo hacia su propioSpawnSubagentTool, de modo que cualquier intento de recursión se rechaza en la misma barrera de profundidad 1. - El hijo devuelve
Result<String>. La herramientaspawn_subagentdel padre lo envuelve:- Éxito:
ToolResult { success: true, output: <child's final response>, error: None }. La salida vacía se reemplaza con el literal"subagent completed without output". - Fallo:
ToolResult { success: false, error: Some("subagent run failed: ...") }.
- Éxito:
- El bucle de herramientas del padre continúa con ese
ToolResulten su contexto de conversación. Los turnos intermedios y las llamadas a herramientas del hijo NO se reproducen en el historial del padre; solo aparece la respuesta final.
Lo que se entrega de vuelta upstream
Una cosa: el mensaje final del asistente del proceso hijo, como cadena, envuelto en ToolResult.output.
- Las llamadas a herramientas del hijo, los turnos de razonamiento intermedios y cualquier escritura en memoria que el hijo haya realizado son observables en los registros estructurados bajo el span de rastreo del hijo, pero no entran en el historial de conversación del padre.
- La sesión del proceso hijo reside bajo la ruta
subagent-<uuid>(ocron-<uuid>para ejecuciones iniciadas por cron). Esta es la clave del historial de conversación, no una ubicación del sistema de archivos; aísla el historial del proceso hijo del historial del proceso padre. - Las escrituras de memoria realizadas por el hijo se escriben en la identidad del padre (el mismo UUID de agente en los backends SQL/Postgres; el mismo directorio del workspace para Markdown). Las ejecuciones generadas por cron deshabilitan
memory.auto_save, por lo que las escrituras opcionales siguen funcionando, pero la recuperación rutinaria no se acumula.
No hay canal de transmisión ni de progreso parcial de vuelta al elemento padre. Los SubAgents de larga duración bloquean la ejecución de herramientas del elemento padre durante toda su duración; no existe un control de tiempo de espera por llamada.
Múltiples llamadas en un solo turno
El bucle del agente aplica una protección contra llamadas duplicadas por turno: una herramienta llamada dos veces con argumentos idénticos en el mismo turno normalmente tiene la segunda llamada omitida. spawn_subagent y delegate están exentos de esa protección. Lanzar varios con el mismo prompt (redundancia, muestreo, distribución en paralelo) es un patrón intencional, no una repetición accidental, por lo que cada llamada idéntica se ejecuta y cada resultado se devuelve. Sin la exención, solo se ejecutaría la primera llamada idéntica y solo su salida llegaría al modelo.
Cuando la ejecución paralela de herramientas está habilitada (parallel_tools = true en el perfil de runtime), múltiples llamadas a spawn_subagent en un mismo turno se ejecutan de forma concurrente y la respuesta final de cada hijo se devuelve al padre, asociada a su propia llamada de herramienta. delegate tiene su propio fan-out explícito mediante el argumento parallel: [...] (consulte la sección de cadenas de salida); esa ruta genera cada destino en su propia tarea y agrega todos los resultados.
Herencia de permisos
Un SubAgent hereda los permisos del padre de forma literal a menos que el sitio de generación proporcione un SubAgentOverrides restrictivo. Actualmente, ambos sitios de generación in-tree pasan SubAgentOverrides::default() (heredarlo todo). La superficie de override se entrega y valida; una futura ruta de restricción proporcionada por el llamador se integra sin cambios en tiempo de ejecución.
Herencia eje por eje:
SecurityPolicy: se hereda mediante la clonación deArc<SecurityPolicy>. La ruta de sobrescritura (SubAgentOverrides::policy = Some(policy)) ejecutaSecurityPolicy::ensure_no_escalation_beyond(crates/zeroclaw-config/src/policy.rs) y rechaza cualquier campo que añada privilegios que el padre no tenga. Los ejes validados incluyen el nivel de autonomía, allowed_roots (rw + ro + solo escritura), allowed_commands, workspace_only, forbidden_paths en la dirección padre ⊆ hijo, shell_env_passthrough,max_actions_per_hour,max_cost_per_day_cents,shell_timeout_secs,block_high_risk_commandsyrequire_approval_for_medium_risk. Los rechazos encadenan unaEscalationViolationprecisa para que los diagnósticos identifiquen el campo infractor.- Presupuestos de acciones / costos:
PerSenderTrackerse comparte entre padre e hijo mediante clonación deArc. Ruta de herencia textual: el hijo mantiene el mismoArc<SecurityPolicy>, por lo que las escrituras enrecord_action()/record_cost()afectan al mismo bucket. Ruta de anulación:SubAgentSpawn::buildcopia explícitamente el campotrackerdel padre en la política restringida del hijo. Un SubAgent no puede eludirmax_actions_per_hournimax_cost_per_day_centsmediante la generación de procesos, el límite es compartido. - Registro de herramientas: el registro del proceso hijo se construye desde cero mediante
tools::all_tools_with_runtimebajo la política heredada. A continuación, el registro pasa porapply_policy_tool_filter(crates/zeroclaw-runtime/src/agent/loop_.rs), que descarta cualquier herramienta cuyo nombre no supere alguna de las dos verificaciones:- Las
allowed_tools/excluded_toolsde la política (obtenidas delrisk_profiledel elemento padre). - El argumento
allowed_toolsproporcionado por el llamador aagent::run.spawn_subagentestá en el registro, pero su flagis_subagent_callerestá establecido entruepara el hijo, por lo que el rechazo de profundidad 1 se activa antes de cualquier trabajo de generación. El mismo flagis_subagent_callerelimina por completomodel_switchdel registro del hijo: un SubAgent hereda el modelo del padre tal cual (ver eje 5) y no debe poder cambiar el modelo activo a espaldas del padre, por lo que la herramienta simplemente no se le ofrece.
- Las
- Lista de permitidos de memoria: un
HashSet<String>de alias de agentes hermanos (las claves de configuración[agents.<alias>]). Se hereda delworkspace.read_memory_fromdel padre más el alias propio del padre. La ruta de anulación (SubAgentOverrides::allowed_agent_aliases) se valida como un subconjunto; cualquier alias que no esté en la lista del padre se rechaza por nombre. El alias propio del padre siempre se vuelve a añadir, de modo que un SubAgent siempre ve las filas de su padre. - Proveedor de modelo: heredado de la resolución de
[agents.<alias>] model_providerdel padre. La temperatura proviene de la entrada del proveedor del padre (config.model_provider_for_agent(parent_alias).and_then(|e| e.temperature)). Esta herencia se aplica de forma obligatoria, no es simplemente un valor predeterminado:model_switchestá excluido del registro de herramientas del SubAgent (véase el eje 3), por lo que un SubAgent no puede cambiar su propio modelo. Para ejecutar una subtarea en un modelo diferente, usedelegatehacia un agente hermano cuyomodel_providernombre ese modelo. - Identidad en la capa de datos: el mismo UUID en la tabla
agents(backends SQL), el mismo directorio de workspace para Markdown, el mismo almacén de secretos. La distinción entre padre e hijo es puramente de observabilidad: un span de trazado separado y una clave de sesión de historial de conversación separada.
Cómo un usuario activa uno
Tú no llamas estas herramientas directamente; lo hace el bot, desde dentro de su turno. Como usuario, influyes en la elección del bot según cómo formules la solicitud. No hay ningún comando especial, ni sintaxis de barra, ni JSON que el usuario escriba. Que el modelo elija spawn_subagent o delegate depende de su prompt del sistema, del texto de description de la herramienta (visible para el modelo) y de la redacción del usuario. La formulación influye; no obliga.
Lo que SÍ se puede hacer determinista es la disponibilidad: no se pueden elegir las herramientas que no están en el registro del agente padre. La compuerta de perfil de riesgo se encuentra en [risk_profiles.<alias>].allowed_tools y [risk_profiles.<alias>].excluded_tools. Una lista allowed_tools no vacía debe incluir spawn_subagent o delegate para que el modelo vea esa herramienta; una lista allowed_tools vacía deja la disponibilidad de herramientas sin restricciones, a menos que excluded_tools nombre la herramienta. Reinicia el daemon después de editar la configuración.
Lo que es verificable de extremo a extremo:
- Las cadenas de salida y rechazo controladas por el protocolo son contratos literales de Rust. La entrega de errores de autocompletado del terminal visibles para el usuario es un contrato del catálogo de Fluent: la fuente en inglés se indica a continuación, y un catálogo en un idioma distinto del inglés o una sustitución en disco que defina la misma clave puede mostrarla de forma diferente.
- Los parámetros de configuración literales que cambian el comportamiento (
allowed_tools,max_delegation_depth, etc.). - La forma estructurada del span de trazado que delimita todo lo emitido durante la ejecución secundaria.
Lo que NO es verificable a partir de esta documentación:
- Si tu bot específico, con tu modelo específico, con tu prompt de sistema específico, elegirá la herramienta cuando se le pida “Genera un subagente para …”. La redacción marca la diferencia; los resultados varían. Si el bot no elige la herramienta, la palanca más fiable es ampliar el prompt de sistema del bot con instrucciones explícitas (“Cuando se solicite una subtarea concreta, usa la herramienta
spawn_subagent”). - El texto exacto que el bot te escribe en su respuesta final. El bot lee la salida de la herramienta y genera su propia respuesta a partir de ella. El texto de salida de la herramienta puede citarse, parafrasearse o resumirse.
spawn_subagent: cadenas de rechazo que el modelo ve
Estos son exactos, obtenidos de crates/zeroclaw-runtime/src/tools/spawn_subagent.rs. El modelo los recibe como la cadena de error de la herramienta y reacciona en consecuencia. La respuesta del bot visible para el usuario es lo que el modelo escriba a continuación; suele hacer referencia al rechazo o reproducirlo.
- Argumento
promptvacío o ausente:Missing or empty 'prompt' parameter - Caller is itself a SubAgent (depth-1 cap):
spawn_subagent: a subagent may not spawn its own subagents (depth-1 cap) - Control de herramientas del perfil de riesgo del elemento padre excluye
spawn_subagent:spawn_subagent: refused — agent '<parent_alias>' risk_profile does not list spawn_subagent in allowed_tools - Error de alias padre desconocido / spawn build:
subagent spawn failed: <wrapped error> - La ejecución secundaria devolvió un error:
subagent run failed: <wrapped error>
Si la operación tiene éxito, la salida de la herramienta ES el texto de respuesta final del proceso hijo. Si el proceso hijo devolvió una cadena vacía, la salida es el marcador de posición literal: subagent completed without output. No hay un prefijo fijo que buscar con grep en el caso de éxito.
spawn_subagent: cómo verificar que se ejecutó realmente
Inspecciona el final de tu log. El proceso hijo generado por la herramienta se ejecuta dentro de un scope! que emite un tracing span llamado zeroclaw_scope (con target zeroclaw_log_internal_scope) que transporta agent_alias=<parent> y session_key=<uuid>. Cada línea de log emitida durante la ejecución del hijo transporta esos campos. El propio turno del padre tiene su propio session_key; un NUEVO valor de session_key que aparece a mitad de turno para el mismo agent_alias es la señal de que se ejecutó un SubAgent. La ruta de sesión del historial de conversación del hijo es subagent-<uuid> (un identificador tipo sistema de archivos, distinto del campo de tracing).
Los trabajos de agentes lanzados por cron usan un nombre de span diferente y más explícito: subagent (literal) con los campos category="cron", agent_alias=<owning agent>, cron_job_id=<id>, run_id=<uuid>, spawn_site="cron". Las rutas de cron se pueden filtrar fácilmente con grep: grep 'spawn_site="cron"' zeroclaw.log. Ten en cuenta que las ejecuciones lanzadas por cron son de nivel superior (is_subagent=false); ellas mismas pueden llamar a spawn_subagent una vez.
Esta es una señal débil para la ruta de generación de subagentes del bucle del agente. Un registro dedicado de “subagente iniciado / completado” enrutado a través de attribution_span!(tool) está registrado como un seguimiento pendiente en el código; una vez que el bucle del agente envuelva la ejecución de herramientas en un span de atribución, cada record! dentro de la herramienta llevará tool=spawn_subagent automáticamente y la pregunta se convertirá en un grep trivial.
Control de delegación
delegate aplica dos controles en crates/zeroclaw-runtime/src/tools/delegate.rs antes de que se ejecute un agente de destino, en este orden:
-
delegation_policy.mode: el perfil de riesgo del invocador debe permitir la delegación.[risk_profiles.<alias>].delegation_policyes{ mode = "forbidden" }por defecto; establezcamode = "allow"para permitir la delegación en absoluto. Cuando está prohibida, el rechazo es:la delegación está prohibida para el llamador "<caller>" por la política delegation_policy del perfil de riesgo "<caller_profile>"; establezca [risk_profiles.<caller_profile>].delegation_policy mode = "allow"Esto se puede editar en el panel del gateway y en zerocode en Config → Risk profiles →
<profile>→delegation_policy.mode(un selector forbidden/allow). -
Reachability: el agente objetivo debe estar en el conjunto alcanzable del llamador, resuelto por
Config::reachable_delegate_target_configs. El conjunto alcanzable es la unión de dos fuentes por agente en[agents.<caller>], menos el propio llamador:-
pares del mismo perfil: cualquier otro agente que comparta el perfil de riesgo del llamador, incluido cuando
delegate_same_risk_profile = true(el valor predeterminado). Establécelo enfalsepara excluir al llamador de la autorización automática de pares. -
lista explícita de delegación:
delegates, una lista posiblemente vacía de destinos a los que el llamador puede delegar incluso a través de perfiles de riesgo. Las entradas de cadena son convenientes para la edición manual y representan destinos acotados. Las entradas de objeto hacen explícito el modo:delegates = [ "reviewer", { agent = "sysadmin", mode = "independent" }, ]Cuando se guarda la configuración, cada entrada se escribe en forma de objeto con
mode = "bounded"omode = "independent". No despliegue este formato de configuración antes de que los binarios del daemon y de la UI se hayan actualizado a una versión que admita modos delegados. Los binarios anteriores esperan quedelegatescontenga solo cadenas; una entrada de objeto hace que la secciónagentssea inválida para ese binario y el cargador resiliente descarta la sección para que las superficies de reparación puedan seguir iniciando. Cuando el objetivo está fuera de ese conjunto, la negativa nombra la causa. Por ejemplo:
delegate target “
<target>” no es accesible desde “<caller>”: perfil de riesgo diferente (el llamador usa “<caller_profile>”, el objetivo usa “<target_profile>”). delegate_same_risk_profile solo alcanza agentes con el mismo perfil de riesgo; añade una entrada explícita [agents.<caller>].delegates con el modo previsto, o cambia el risk_profile de uno de los agentes.delegate target “<target>” no es alcanzable desde “<caller>”: delegate_same_risk_profile está deshabilitado y el objetivo no aparece en [agents.<caller>].delegatesdelegate target "<target>" no es accesible desde "<caller>": el agente de destino está deshabilitadoUn objetivo acotado hereda el rastreador de acciones/costos del llamador. Cuando el objetivo acotado comparte el perfil de riesgo del llamador, también hereda el límite del espacio de trabajo de sesión del llamador. Se permite un objetivo acotado entre perfiles cuando es alcanzable a través de la lista de delegados del llamador y
delegation_policy; se ejecuta bajo la política resuelta del objetivo, mientras que la disponibilidad de herramientas agénticas está limitada por el registro de herramientas del llamador.Un objetivo independiente solo está disponible cuando se enumera explícitamente con
mode = "independent". Sigue requiriendodelegation_policy.mode = "allow"y alcanzabilidad mediantedelegates, pero una vez seleccionado resuelve la propia política del agente objetivo sin el techo de no escalación, la invalidación del espacio de trabajo de la sesión ni el rastreador de acciones/costes del llamante. -
La lista anunciada se incluye en la descripción del parámetro agent en el esquema de la herramienta. Enumera exactamente este conjunto alcanzable, y solo cuando delegation_policy.mode = "allow". Los agentes deshabilitados (enabled = false) nunca son alcanzables, ya sea como pares del mismo perfil o como entradas explícitas de delegates.
En la delegación agentiva acotada, las herramientas del subagente se toman del registro del llamador ya filtrado por la política, intersectado con el propio allowed_tools del destino. Un allowed_tools vacío en el destino significa “heredar”: el subagente se ejecuta con el registro delegable completo del llamador en lugar de ser rechazado. Una lista no vacía se intersecta con ese registro. En cualquier caso, el registro del llamador es el techo: un destino acotado entre perfiles cuyo perfil de riesgo nombra una herramienta que nunca se concedió al llamador no la recibe. Por tanto, la delegación acotada está limitada por herramientas, no es una comprobación completa de SecurityPolicy::ensure_no_escalation_beyond. Si esa intersección está vacía, el destino sigue recibiendo un turno normal del modelo agentivo sin herramientas.
En la delegación agentic independiente, las herramientas del subagente se construyen a partir de la propia política configurada y del registro en tiempo de ejecución del agente de destino, como al abrir un chat nuevo con ese destino. El registro principal no se usa como límite superior. La herramienta delegate sigue eliminada del registro secundario para que la delegación agentic no pueda recursar a través de otra llamada a delegate.
La profundidad está limitada según el runtime_profile.max_delegation_depth del elemento padre. Establécelo en 1 para permitir que el agente principal realice un único salto de delegación sin más subdelegaciones.
Política de herramienta objetivo agéntica
Si [runtime_profiles.<target>].agentic = true del agente de destino, delegate construye el registro de herramientas del subbucle de destino a partir de las herramientas disponibles del padre (mode = "bounded") o del propio registro en tiempo de ejecución del destino (mode = "independent"). El perfil de riesgo del destino luego filtra ese registro:
- Una lista vacía configurada de
[risk_profiles.<target_profile>].allowed_toolsdeja sin restricciones el registro seleccionado. - Una lista
allowed_toolsno vacía conserva solo los nombres de herramientas que coincidan exactamente. [risk_profiles.<target_profile>].excluded_toolssiempre se resta del resultado.delegatesiempre se elimina del registro secundario para que la delegación agéntica no pueda recurrir a través de otra llamada adelegate.
Esta directiva reside en el objetivo, no en el llamador. Los pares del mismo perfil usan el perfil de riesgo compartido. Los delegados explícitos entre perfiles usan el perfil de riesgo del objetivo tras los controles de accesibilidad y de política de delegación. Los delegados agentivos acotados reciben solo el registro de herramientas limitado por el llamador e intersectado con la política de herramientas del objetivo; los delegados agentivos independientes reciben el registro de herramientas propiedad del objetivo. Un perfil de riesgo del objetivo ausente rechaza antes de que comience el subbucle. Un perfil configurado que deja cero herramientas hijas ejecutables sigue permitiendo una pasada normal del modelo sin herramientas.
Cuando la cadena de proveedores Reliable configurada para el destino mezcla candidatos compatibles con herramientas nativas y candidatos que solo admiten texto, strict_tool_parsing = false usa un único protocolo de herramientas de texto/XML para todo el turno agéntico, de modo que todas las alternativas de reserva accesibles puedan ejecutar herramientas. Si siguen existiendo herramientas efectivas y strict_tool_parsing = true, ZeroClaw rechaza la cadena mixta antes de realizar una solicitud al proveedor, porque el análisis estricto prohíbe ese protocolo de reserva de texto/XML. Las cadenas uniformes no cambian: las cadenas compuestas exclusivamente por proveedores nativos usan transporte de herramientas nativas, mientras que las cadenas deliberadamente compuestas solo por texto siguen la política de herramientas de texto configurada.
delegate: cadenas de salida que el modelo ve
Las cadenas de error visibles para el usuario son mensajes de Fluent localizados. Su fuente de verdad en inglés es crates/zeroclaw-runtime/locales/en/cli.ftl; los ejemplos siguientes muestran los valores actuales del catálogo en inglés, no un contrato de cadenas a nivel de protocolo. Las cadenas restantes enumeradas son salidas de protocolo/herramientas, a menos que esta sección las etiquete como claves de Fluent.
-
Éxito síncrono: la salida comienza con
[Agent '<target>' (<provider_type>/<model>)]\n, seguida de una respuesta no vacía del agente de destino. Cuando el destino se recupera mediante una alternativa de proveedor configurada, su encabezado identifica en su lugar el proveedor/modelo solicitado y el proveedor/modelo servido, por ejemplo[Agent 'reviewer' (requested: anthropic.primary/claude; served: openai.terra/gpt-5.6-terra, agentic)]. En el caso de un destino agéntico, esta atribución describe la solicitud del modelo que produjo la respuesta final, no una solicitud anterior que solo produjo una llamada a una herramienta. El resultado también termina con eldelegate-provider-fallback-warninglocalizado. En inglés:Warning: The delegated agent recovered through a provider fallback. Provider failure details were logged and omitted from this result.Esta atribución y esta advertencia pertenecen al resultado delegado; no deben presentarse como una alternativa del agente que realiza la llamada. Omiten intencionadamente los detalles del error del proveedor rechazado, los endpoints y las credenciales. Reintentar el mismo candidato configurado no produce esta advertencia; llegar a un candidato configurado posterior sí la produce, incluso cuando sus etiquetas de proveedor y modelo coinciden con las del primer candidato. -
Una respuesta vacía de terminal es un fallo síncrono: su campo de error usa
cli-delegate-error-invalid-semantic-completion, conagent_nameestablecido en el destino. En inglés:Agent '<target>' failed: model provider returned an invalid semantic completion. -
Otros errores síncronos: el campo de error comienza con
Agent '<target>' failed: <wrapped error>. Si todos los candidatos de proveedor configurados fallan,<wrapped error>es el resumen seguro y ordenado de Reliable de los eventos de fallo, el recuento de reintentos, la clase de fallo, la fase y la indicación fija de remediación. Los cuerpos de respuesta del proveedor, los puntos de conexión, los alias, los modelos y las credenciales no se devuelven al agente solicitante; cuando se necesiten más detalles, investigue los registros de intentos del proveedor conforme a la política habitual de registro de operadores de la instalación. El resultado sigue siendo un error, no una advertencia de recuperación. -
Tiempo de espera síncrono (cuando el perfil de tiempo de ejecución del destino establece
delegation_timeout_secs): el campo de error esAgent '<target>' timed out after <N>s. -
Generación en segundo plano exitosa: la salida es el literal de tres líneas
Background task started for agent '<target>'. task_id: <uuid> Use action='check_result' with task_id='<uuid>' to retrieve the result.El archivo de resultados se encuentra en
<workspace>/delegate_results/<uuid>.json. Mientras se ejecuta, el campostatusdel archivo esrunning; los estados terminales soncompleted,failedocancelled. Una tarea completada que se recuperó mediante un mecanismo de respaldo del proveedor configurado almacena en suoutputla misma atribución entre lo solicitado y lo servido, así como la advertencia genérica de recuperación; recupérela concheck_resultoawait_sessions. Una tarea fallida almacena el mismo resumen terminal seguro que la delegación síncrona, no los detalles de la respuesta del proveedor. -
action="check_result"con un id de tarea desconocido: el error esNo result found for task_id '<uuid>'. -
action="await_sessions"contask_ids: [<uuid>, ...]espera varios archivos de resultados en segundo plano a la vez. La salida es un objeto JSON constatus(completeotimeout),completed,pending,missing,failedyresults.timeout_mstiene un valor predeterminado de 30000 y un máximo de 120000; en caso de timeout, la herramienta devuelve resultados parciales y un error que indica que una o más tareas siguen pendientes o faltan. Los IDs de tarea duplicados se rechazan. -
Salida de fan-out paralelo: comienza con
[Parallel delegation: <N> agents]\n\n, seguida de bloques por agente separados por\n\n, cada uno de los cuales comienza con--- <target> (success=<bool>) ---\n. Un destino recuperado conserva la atribución de solicitado frente a servido y la advertencia de respaldo genérico dentro de su propio bloque. En caso de error por agente, el bloque interno es--- <target> (success=false) ---\nError: <wrapped error>. -
Agente de destino desconocido: el error es
Unknown agent '<target>'. Available agents: <comma-separated list>. -
Profundidad excedida (controlada por el
runtime_profile.max_delegation_depthdel padre, predeterminado 3): el error esDelegation depth limit reached (<depth>/<max>). -
Acción desconocida: error es
Acción desconocida '<value>'. Use delegate/check_result/list_results/cancel_task/await_sessions. -
Destino independiente cuyo perfil de riesgo tiene entradas
always_ask: el error esdelegate target "<target>" cannot run in independent mode from "<caller>": risk profile "<profile>" has always_ask entries (<list>). See ZeroClaw docs, "Delegation & SubAgents" > "What's not supported". -
Destino agéntico con un perfil de riesgo de destino faltante: el error es
Agent '<target>' is agentic but risk_profile '<target_profile>' is not configured. -
Objetivo agentivo sin herramientas secundarias ejecutables: no se emite ningún error para el conjunto de herramientas vacío en sí; el objetivo recibe una interacción normal del modelo sin herramientas.
delegate: cómo verificar que realmente se ejecutó
delegate no emite hoy un span de tracing dedicado. La señal es la aparición del bucle del agente objetivo en el log, que hereda el ámbito en el que estuviera el dispatch de la llamada a herramienta del padre. Los spawns en modo en segundo plano son más fáciles de verificar fuera de banda: el archivo de resultados <workspace>/delegate_results/<uuid>.json existe en disco y contiene los campos status + output del agente objetivo; cat o jq funcionan sin tocar el log en absoluto.
(Los trabajos de agentes lanzados por cron son un sitio de generación independiente y usan el span subagent explícito descrito anteriormente; delegate y cron no son la misma ruta.)
Lo que no está en esta página (intencionalmente)
- Transcripciones de conversaciones de ejemplo. Cualquier cosa que escribiera aquí describiendo “lo que dirá el bot” dependería del modelo. La respuesta del bot es consecuencia de la salida de la herramienta, el modelo, el prompt del sistema y el estado actual de la conversación, ninguno de los cuales está controlado por esta página. La capa verificable es lo que devuelve la herramienta (arriba) y lo que captura el registro.
- Un marcador de registro dedicado de “subagent fired” / “delegate fired”. Registrado como seguimiento del lado del código. Hoy, los operadores verifican mediante la forma del scope descrita arriba (que es la señal estructural existente) y mediante el archivo de resultados del modo en segundo plano.
Elección entre spawn_subagent y delegate
spawn_subagent | delegate | |
|---|---|---|
| Identidad | Igual que el elemento padre (mismo UUID, mismo perfil de riesgo) | Identidad del agente de destino (alias diferente; par del mismo perfil o delegado explícito de perfil cruzado) |
| Modelo de permisos | La política del padre textualmente (o un subconjunto restringido) | Los destinos acotados se ejecutan bajo la política de destino con el registro de herramientas agénticas del llamador como límite superior; los destinos independientes se ejecutan bajo la política de destino y el registro propiedad del destino |
| Proveedor de modelos | Del padre | Proveedor configurado del agente de destino |
| Profundidad de generación | Límite máximo en 1 | Hasta runtime_profile.max_delegation_depth (predeterminado 3) |
| Modo en segundo plano | No compatible | background: true devuelve un task_id |
| Distribución en paralelo | Sin argumento integrado; las llamadas múltiples en un mismo turno se ejecutan de forma concurrente cuando parallel_tools = true | parallel: [...] ejecuta múltiples objetivos de forma concurrente |
| Restricción de acceso | risk_profile.allowed_tools no vacío debe incluir spawn_subagent; excluded_tools no debe incluirlo | El campo no vacío risk_profile.allowed_tools del llamador debe incluir delegate; excluded_tools no debe incluirlo; el delegation_policy mode = "allow" del llamador; y el destino está en el conjunto alcanzable del llamador (par del mismo perfil o entrada explícita en delegates) |
| Usar cuando | Subtarea interna que debe permanecer dentro de la misma identidad | ¿Quieres que otro especialista configurado diferente (otro modelo, otro alias) asuma la tarea bajo delegación limitada o independiente? |
Qué no es compatible
- Recursión más allá del nivel 1. Un SubAgent no puede generar su propio SubAgent. El límite es un rechazo absoluto en la herramienta, no un presupuesto. Las ejecuciones lanzadas por cron comienzan en el nivel 0 y pueden generar un nivel; los SubAgent lanzados por el bucle del agente están en el nivel 1 y rechazan generar más.
- Una identidad independiente para el hijo. Los SubAgents comparten el UUID del agente padre. Para ejecutarse con una identidad diferente, usa
delegatepara delegar en un agente hermano configurado. - Presupuesto de tiempo por generación. No existe el argumento
timeout_secs. El proceso padre se bloquea durante toda la duración de la ejecución del hijo; la cancelación debe propagarse a través del ámbito de interrupción más amplio. - Transmisión del progreso de vuelta al elemento principal. El elemento principal ve la respuesta final del elemento secundario como una sola cadena tras la finalización.
- Un bloque de configuración
[agents.<alias>].subagent_*. El validador y el tipo de override se incluyen hoy; la superficie de configuración orientada al operador que conecta el narrowing definido por el llamador no está en esta versión. Ambos sitios de spawn pasanSubAgentOverrides::default()hasta que esa superficie esté disponible. - Destinos
delegateindependientes conalways_ask. La delegación independiente se bloquea cuando el perfil de riesgo del agente de destino tiene entradasalways_askno vacías. El tiempo de ejecución se niega antes de iniciar el destino, incluida la delegación en segundo plano y en paralelo. Este bloqueo se mantiene hasta que el reenvío de aprobaciones para agentes secundarios independientes sea compatible en una futura versión de ZeroClaw.