Skip to main content
Version: Next

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 Runtime pre/post hook, not in a service layer.
  • Entry surfaces never call Runtime directly.

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) or POST /api/v1/chat-multipart (file uploads)
  • Thread handler: the same REST routes served by AgentThreadRequestHandler with conversation-thread recording, plus the thread read routes
  • AWS Lambda: API Gateway event routed by the Lambda handler
  • 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 /mcp or /a2a routes
  • 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)
  1. Session lock: async with session serializes concurrent requests per session and makes Session.current() available.
  2. 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.
  3. Runner.run(): converts requests to the framework's native format, restores framework session state, executes, and converts the result back to an AgentReply (AgentReplyText, AgentReplyImage, or AgentReplyAny for structured output).
  4. Post-hooks: system hooks (output guardrails) first, then agent hooks.
  5. Persistence: the session store saves the session; the volatile cache is cleared in a finally block.

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 StreamChunk with error set and done: true.
  • delta is populated only when event is a TextDelta; every other event type (message/step boundaries, tool calls, reasoning) carries event alone, with no delta key in the wire frame.
  • Framework support: OpenAI Agents SDK, LangGraph, and Google ADK stream natively. CrewAI and Smolagents declare supports_streaming = False and raise NotImplementedError in stream mode.
  • On AWS serverless and AWS ECS containerized, the same StreamChunks are delivered as WebSocket STREAM_CHUNK messages 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 attributes request_id/user_id/endpoint_url/status_code, group_id, dedup id, receive count), and the failure contract: a message that exhausts max_receive_count triggers a permanent-failure error reply, so the caller never hangs.
  • Pluggable: the queue transport (in_memory default; sqs on AWS via the deployment adapters; kafka/nats for on-prem / Kubernetes), the response store backend, and the reply delivery path per execution.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 ECS ECSSQSConsumer runs 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_async returns 202 ACCEPTED + request_id for polling; stream fans 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. Duplicate request_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/openai walks 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_sync holds the HTTP connection and polls the response store server-side; rest_async returns a request_id immediately 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 exceeding max_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.modeTransportReply deliveryQueuesResponse storeAvailable on
(unset) / defaultHTTPJSON on the same connection--Everywhere RESTAPI/CLI runs
rest_syncHTTPJSON on the same connection (server polls store)RequiredRequiredAWS Lambda, AWS ECS
rest_asyncHTTPClient polls with request_idRequiredRequiredAWS Lambda, AWS ECS
asyncWebSocketSingle CHAT_RESPONSE pushOptionalNot usedAWS Lambda, AWS ECS
streamSSE (REST) or WebSocket (AWS serverless, AWS ECS)Event-level StreamChunks — every frame carries event, delta only on text_deltaOptional (WebSocket path)Not usedREST 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.

Ready to Ship Your
First Agent?

Free, open-source, Apache 2.0. No licensing costs, no vendor lock-in. Join hundreds of developers building production AI agents with Agent Kernel.

Agent Kernel
Ask Agent Kernel