Execution Flow
How requests flow through Agent Kernel from user input to agent response: synchronously, streamed, or through queues.
Request Lifecycle
Every execution surface converges on the same runtime pipeline, but they enter through distinct service layers: HTTP-shaped surfaces call the ChatService presentation wrappers (process_*), channels that own their transport (messaging integrations, the thread handler) call the ChatService execution core (execute/execute_stream) with prebuilt request lists, and stateful clients (CLI, A2A, MCP) use AgentService directly. Everything then flows through Runtime.run() (or Runtime.stream()) → pre-hooks → Runner → framework → post-hooks → session persistence.
Which layer does new code call?
- A new HTTP-shaped surface that returns JSON/SSE with the standard error shapes calls the
presentation wrappers (
process_*). - A new channel or integration that owns its own transport, reply formatting, and error UX calls the
core (
execute/execute_stream), passing a prebuilt request list when it builds its own attachments. - An interactive or stateful client that manages agent and session lifecycle itself (REPL-like)
uses
AgentService. - Cross-cutting behavior that must apply to every run regardless of surface goes in a
Runtimepre/post hook, not in a service layer. - Entry surfaces never call
Runtimedirectly.
For the underlying distinction between the two services (stateful conversation object vs stateless request processor), see ChatService vs AgentService.
Detailed Flow
1. Request Reception
The request enters through one of the execution surfaces:
- CLI: interactive terminal input
- REST API: HTTP
POST /api/v1/chat(JSON) orPOST /api/v1/chat-multipart(file uploads) - Thread handler: the same REST routes served by
AgentThreadRequestHandlerwith conversation-thread recording, plus the thread read routes - AWS Lambda: API Gateway event routed by the
Lambdahandler - SQS queue: in queue mode, a request message consumed by the agent-runner Lambda or ECS consumer threads
- WebSocket: a message on the configured chat route via AWS API Gateway WebSocket (async/stream modes)
- MCP/A2A: protocol-specific request against the mounted
/mcpor/a2aroutes - Messaging platforms: webhook events from Slack, WhatsApp, Messenger, Instagram, Telegram, Teams, or Gmail
2. Request Building and Agent Resolution
The ChatService execution core validates the payload and builds a list of typed requests (AgentRequestText, AgentRequestImage, AgentRequestFile, plus AgentRequestAny entries for any additional context fields). Callers that construct their own request lists (messaging integrations downloading platform attachments, the thread handler) pass them in and the builder is skipped. The core then selects the agent and session through AgentService:
from agentkernel.core.service import AgentService
service = AgentService()
service.select(name="assistant", session_id="user-123") # loads or creates the session
Under the hood the agent registry lives on the runtime:
from agentkernel.core import Runtime
runtime = Runtime.current()
agent = runtime.agents().get("assistant")
session = runtime.sessions().get("user-123") or runtime.sessions().new("user-123")
When chat is served by the thread handler (AgentThreadRequestHandler), user_id is required and the user message (with any attachments) is recorded to the conversation thread before the run. Other surfaces do not record threads.
3. Agent Execution
Runtime.run() acquires the session lock, runs the hook pipeline, and delegates to the framework-specific runner:
reply = await runtime.run(agent, session, requests)
- Session lock:
async with sessionserializes concurrent requests per session and makesSession.current()available. - Pre-hooks: agent hooks first, then system hooks (input guardrails, multimodal preprocessing). A pre-hook may rewrite the request list or halt by returning an
AgentReply. Runner.run(): converts requests to the framework's native format, restores framework session state, executes, and converts the result back to anAgentReply(AgentReplyText,AgentReplyImage, orAgentReplyAnyfor structured output).- Post-hooks: system hooks (output guardrails) first, then agent hooks.
- Persistence: the session store saves the session; the volatile cache is cleared in a
finallyblock.
4. Response Return
The reply travels back through the surface it arrived on: JSON body for REST, Lambda response for API Gateway, a message on the output queue in queue mode, or a WebSocket push. The thread handler also appends the assistant reply to the conversation thread.
Synchronous Flow (Sequence)
Streaming Flow (execution.mode: stream)
With execution.mode: stream, the REST API switches POST /api/v1/chat (and /chat-multipart) to a Server-Sent Events response, driven by Runtime.stream():
The frame shape, stated once. Every frame carries event — the typed event it was built from.
delta is present only for a text_delta, so the boundary frames above, and any tool-call frame,
carry no delta key at all (the payload is dumped with exclude_none=True, so an absent value is an
absent key). The terminal {"done": true} frame carries neither. A client that concatenates the reply
must therefore test for the key rather than assume every non-terminal frame has one.
- If a pre-hook halts (e.g., an input guardrail trips), the stream yields a single
StreamChunkwitherrorset anddone: true. deltais populated only wheneventis aTextDelta; every other event type (message/step boundaries, tool calls, reasoning) carrieseventalone, with nodeltakey in the wire frame.- Framework support: OpenAI Agents SDK, LangGraph, and Google ADK stream natively. CrewAI and Smolagents declare
supports_streaming = Falseand raiseNotImplementedErrorin stream mode. - On AWS serverless and AWS ECS containerized, the same
StreamChunks are delivered as WebSocketSTREAM_CHUNKmessages instead of SSE; see below.
The Queue Pipeline Abstraction
Chat execution is one fixed abstraction with pluggable edges (#495). The five logical components and the message envelope between them never change; what varies by configuration is the queue transport underneath, the reply delivery path, and how the components map onto processes:
What the abstraction fixes, and what it leaves open:
- Fixed: the five components, the normalized message envelope (
body, routing attributesrequest_id/user_id/endpoint_url/status_code,group_id, dedup id, receive count), and the failure contract: a message that exhaustsmax_receive_counttriggers a permanent-failure error reply, so the caller never hangs. - Pluggable: the queue transport (
in_memorydefault;sqson AWS via the deployment adapters;kafka/natsfor on-prem / Kubernetes), the response store backend, and the reply delivery path perexecution.mode. - Shared machinery: the Agent Runner and Response Handler are both driven by
ConsumerLoop: one implementation of batch fetch, receive-count checking, and the permanent-failure-then-acknowledge flow, reused by every transport (the ECSECSSQSConsumerruns on it too). - Topology is configuration: single-process (all five components as threads: the local default), two-process (IO + agent runner over a broker: AWS ECS today), or three-way (AWS Lambda). See the architecture overview for the topology diagrams.
Queue Pipeline Flow (default, in-process)
Chat requests on the REST surface run through the queue execution pipeline above. With the
default in_memory transport, a bare RESTAPI.run() boots all five components as threads in one
process: the flow below is identical to the AWS broker flow further down, with the queues living
in process memory:
rest_sync(and unset mode) waits server-side;rest_asyncreturns202 ACCEPTED+request_idfor polling;streamfans each token out as its own output-queue message and the request handler bridges them to the open SSE response.- Failed messages are redelivered up to
max_receive_count, then a permanent-failure error is delivered so the caller never hangs. Duplicaterequest_ids are dropped within the dedup window. - Surfaces mounted with explicit handlers (the thread handler, messaging integrations, custom handlers) do not enqueue: they keep their direct inline execution.
- Try it:
examples/api/openaiwalks all three modes with curl.
Queue-Based Flow (AWS)
On AWS the same pipeline runs over durable SQS FIFO queues, with the components split across Lambda functions or ECS containers. This is the recommended production topology on AWS for both Lambda and ECS:
rest_syncholds the HTTP connection and polls the response store server-side;rest_asyncreturns arequest_idimmediately for the client to poll.- Conversation-thread recording does not apply in queue mode; threads are a feature of the thread handler on the self-hosted REST API (see Conversation Threads).
- FIFO queues use
MessageGroupId = session_id(per-session ordering) and deduplication IDs; failed messages are retried after the visibility timeout, and dead-letter queues catch messages exceedingmax_receive_count. - See the Queue Mode Guide for retry/DLQ details and ECS threading internals.
WebSocket Flow (AWS Serverless)
In async and stream modes, clients hold a WebSocket connection to API Gateway. Connection IDs are recorded in DynamoDB by a connection-handler Lambda, and replies are pushed back through the still-open socket:
See AWS Serverless Deployment for configuration, authentication, and Terraform wiring.
Mode Selection Cheat Sheet
execution.mode | Transport | Reply delivery | Queues | Response store | Available on |
|---|---|---|---|---|---|
| (unset) / default | HTTP | JSON on the same connection | - | - | Everywhere RESTAPI/CLI runs |
rest_sync | HTTP | JSON on the same connection (server polls store) | Required | Required | AWS Lambda, AWS ECS |
rest_async | HTTP | Client polls with request_id | Required | Required | AWS Lambda, AWS ECS |
async | WebSocket | Single CHAT_RESPONSE push | Optional | Not used | AWS Lambda, AWS ECS |
stream | SSE (REST) or WebSocket (AWS serverless, AWS ECS) | Event-level StreamChunks — every frame carries event, delta only on text_delta | Optional (WebSocket path) | Not used | REST API surfaces; AWS Lambda WebSocket; AWS ECS WebSocket |
Multimodal Flow
When multimodal support is enabled and a request carries images or files, the system pre-hook transforms the request before the agent sees it:
The agent's conversation history stays free of binary data; the auto-registered analyze_attachments tool retrieves stored attachments on demand. See Multimodal.
