Ciclo de vida de la solicitud
Qué ocurre entre “el usuario envía un mensaje” y “el agente responde”: la ruta completa, con anotaciones sobre streaming, llamadas a herramientas y puertas de seguridad.
Entrante
flowchart LR
A[External event] -->|webhook / push / poll / WS| B[Channel adapter]
B -->|decode, dedup, pair-check| C[Inbound envelope]
C -->|workspace binding| D[Runtime: process_message]
Un adaptador de canal (por ejemplo, discord.rs, telegram.rs, email_channel.rs) recibe eventos nativos de la plataforma y los convierte en un sobre de entrada uniforme. El adaptador maneja:
- Decodificación: carga útil específica de la plataforma → formato de mensaje canónico
- Deduplicación: evita reproducir el mismo mensaje dos veces (reinicios, reintentos)
- Verificación de pares: aplica la política
[channels.<name>.allowed_users]/ IAM antes de que el evento llegue al runtime
Si el canal no está emparejado o el usuario no tiene permiso, el evento se descarta antes de que el tiempo de ejecución lo vea.
Bucle del agente
sequenceDiagram
participant CH as Channel
participant RT as Runtime
participant SEC as Security
participant MEM as Memory / history
participant PR as Provider
participant TL as Tool
CH->>RT: process_message(envelope)
Note over RT: resolve memory-inject policy from the turn's TurnOrigin
RT->>MEM: recall(query, session scopes)
MEM-->>RT: entries
Note over RT: render [Memory context] preamble (engine-side)
RT->>PR: chat(system, history, tools)
loop Streaming
PR-->>RT: StreamEvent::TextDelta
RT-->>CH: draft update (if channel supports it)
end
PR-->>RT: StreamEvent::ToolCall(args)
RT->>SEC: evaluate_tool_access(name, args, risk)
alt Blocked
SEC-->>RT: Err(reason)
RT->>PR: chat(..., + tool_error)
else Approval required
SEC->>CH: ask_operator(prompt)
CH-->>SEC: approved / denied
else Allowed
SEC-->>RT: Ok
end
RT->>TL: invoke(args)
TL-->>RT: ToolResult
RT->>MEM: append to turn/session history
RT->>PR: chat(..., + tool_result)
PR-->>RT: StreamEvent::TextDelta (final)
RT-->>CH: reply(final)
RT->>MEM: persist conversation/session history
Propiedades clave:
- El streaming es de extremo a extremo. El proveedor transmite tokens. Si el adaptador de canal informa
supports_draft_updates(), el runtime edita un mensaje enviado in situ a medida que llega el texto. Discord, Slack y Telegram admiten esta funcionalidad. - Las llamadas a herramientas están en curso. El modelo puede emitir una llamada a una herramienta mientras aún está generando texto. El entorno de ejecución lee el flujo hasta completarlo, muestra el texto visible a medida que llega, luego recupera las llamadas a herramientas, las valida, las invoca, proporciona el resultado y comienza un nuevo flujo para el siguiente turno.
- La finalización de la transmisión sigue los eventos del protocolo. Los proveedores dan por finalizada una transmisión correcta cuando llega su evento SSE terminal, en lugar de esperar a que el servidor cierre la conexión. Los cuerpos de respuesta silenciosos producen un error una vez transcurrido el tiempo de espera del proveedor por inactividad de bytes, mientras que las generaciones activas pueden superar el tiempo de espera de las solicitudes sin transmisión.
- La seguridad controla cada llamada a herramientas.
evaluate_tool_accessconsulta el nivel de autonomía, las listas de permitidos/denegados y los límites de rutas. Las llamadas de riesgo medio bajo la autonomíaSupervisedpasan a la vía de aprobación del operador. - El contexto de memoria es inyectado por el motor. Antes de la primera llamada al proveedor, el motor del turno resuelve una política de inyección a partir de
TurnOrigindel turno (quién inició el turno): los subturnos anidados nunca inyectan, los orígenes programados (cron, daemon) inyectan con las entradas de categoría de conversación excluidas, y los orígenes orientados al usuario inyectan (excluyendo las entradas de conversación cuando el turno no tiene ámbito de sesión). Un punto de creación puede suprimir la inyección para cualquier origen (por ejemplo, un trabajo cron conuses_memory = false), y los turnos que no llevan ningún backend de memoria la omiten por completo. Un único renderer aplica la degradación temporal, el filtrado por relevancia, un conjunto de omisión por prompt poisoning y límites de presupuesto de forma uniforme en todas las rutas; los backends de memoria solo responden arecall, no formatean el contexto. - El historial y la memoria son independientes. El historial de la sesión preserva la continuidad de la conversación, las llamadas a herramientas y los resultados de las herramientas. Las escrituras explícitas en memoria conservan entradas seleccionadas en el backend de memoria. Los recibos viajan en banda en el texto de la conversación en lugar de como un artefacto persistido separado. Para obtener detalles sobre la propiedad de los payloads, consulta Memory and payload lifecycle.
Recibos de la herramienta
Las ejecuciones exitosas de herramientas pueden recibir un recibo HMAC-SHA256 que se añade al texto del resultado de la herramienta y se devuelve al modelo en la conversación, demostrando que el resultado firmado provino del runtime. El HMAC se basa en una clave efímera en memoria y se calcula sobre tool_name || args || result || timestamp. Los recibos no se escriben en un registro separado en disco y no están encadenados; el modelo puede repetirlos, pero no puede falsificar uno nuevo válido sin la clave. Consulta Tool receipts.
Saliente
Los mensajes salientes vuelven a pasar por el mismo adaptador de canal. Los adaptadores con soporte para múltiples mensajes (Discord, Slack) pueden transmitir respuestas largas como una secuencia de mensajes; otros (correo electrónico, SMS) vacían el contenido al completar el flujo.
Dónde se encuentra en el código
- Bucle del agente:
crates/zeroclaw-runtime/src/agent/turn/(run_tool_call_loop), con puntos de entrada encrates/zeroclaw-runtime/src/agent/loop_.rs(process_message,run) - Inyección de contexto de memoria:
crates/zeroclaw-runtime/src/agent/memory_inject.rs(resolve_inject_policy,render_memory_context), indexada porTurnOriginde los tipos de ingress dezeroclaw-apie invocada por el motor de turnos - Verificaciones de acceso a llamadas de herramientas:
crates/zeroclaw-runtime/src/security/(iam_policy.rsevaluate_tool_access) - Orquestación de canales:
crates/zeroclaw-channels/src/orchestrator/ - Streaming del proveedor:
crates/zeroclaw-api/src/model_provider.rs(enumStreamEvent, reexportado desdezeroclaw-providers),compatible.rs(analizador SSE)
Desde #7415, cada transporte (channels, CLI, cron, gateway WebSocket, RPC/zerocode, ACP y la API integrada Agent) ejecuta el mismo motor de turnos: run_tool_call_loop en crates/zeroclaw-runtime/src/agent/turn/. Los puntos de entrada de streaming e integrados son envoltorios ligeros en agent.rs que configuran ajustes por llamante (dedup, comportamiento del límite de iteraciones, emisión de eventos) alrededor del bucle compartido. El módulo turn/ consta de un archivo por paso:
| Archivo(s) | Paso |
|---|---|
mod.rs | orquestador: control de iteración, ajustes, drenaje de dirección |
history_window.rs · tool_specs.rs · vision_route.rs | antes de la llamada: mantenimiento del historial, especificaciones de herramientas, enrutamiento de visión |
provider_call.rs · stream_consume.rs · stream_guard.rs | la llamada al LLM, el consumo del stream, la protección del protocolo a mitad del stream |
parse_response.rs · protocol_detect.rs · context_recovery.rs | interpretación de respuestas, detección de problemas de análisis, recuperación de desbordamiento |
approval_gate.rs · call_prep.rs | aprobación y preparación de llamadas a herramientas (deduplicación, hooks, valores predeterminados de entrega) |
post_exec.rs · results_collect.rs · history_append.rs · max_iter.rs | registro de resultados, detección de bucles, anexado al historial, límite de iteraciones |
context.rs · events.rs · knobs.rs · steering.rs · outcome.rs · redact.rs · delivery_defaults.rs | tipos compartidos: contexto de turno, eventos, ajustes por llamador, dirección, resultados, depuración de credenciales |