Query Execution Flow
A Query is how you ask an agent, team, model, or tool to do something. This page traces what happens after you create one — from the controller picking it up, through execution on an engine, to the response written back to the resource.
At a glance
Every query moves through four stages:
- Declare — you create a
Querywith an input and a target. - Dispatch — the controller resolves the target and sends it to an execution engine over A2A.
- Execute — the engine runs the agent or team: model calls, tools, memory, and streaming.
- Respond — the result is written to the query’s status and saved to memory.
The rest of this page covers each stage in detail, then how the query is stored on either backend. For the platform components and how Ark fits a cloud deployment, see Core Architecture.
1. Declare the query
A Query specifies what to run and how:
| Field | Purpose |
|---|---|
input | The user’s question or request. |
target | The agent, team, model, or tool to run — or a selector to match one by labels. |
parameters (optional) | Values used in the input or passed to agent parameters via queryParameterRef. |
memory (optional) | A Memory resource for conversation context. |
sessionId (optional) | Conversation session identifier. |
serviceAccount (optional) | Service account to run under, for RBAC isolation. |
apiVersion: ark.mckinsey.com/v1alpha1
kind: Query
metadata:
name: my-query
spec:
input: "What is the weather today?"
target:
type: agent
name: weather-agent
parameters:
- name: agent_name
value: "WeatherBot"
memory:
name: conversation-memory2. The controller dispatches it
The Query controller picks up the new resource and:
- Resolves the target — the agent, team, model, or tool named in the spec, or the first match of the label selector.
- Resolves the dispatch address — the built-in completions engine for teams, models, tools, and agents on the default or
a2aengine; or a named execution engine when the target agent specifies one. - Sends an A2A
SendMessageto that address with the target and a reference to the query. - Waits for the response and writes the result to the query’s status.
The controller always dispatches over A2A — it never executes a target in-process. For a2a agents the completions engine is the dispatch target and proxies the call to the agent’s external A2A server.
3. The engine executes the target
The Query controller detects the new Query and:
- Resolves the target (agent, team, model, or tool) from the spec or label selector
- If the target agent names a custom
executionEngine, the controller resolves thatExecutionEngineresource’s address since these agents proxy to external services - Otherwise, the controller dispatches to the built-in completions engine. It prefers a namespace-local
ExecutionEnginenamedark-completions(a per-tenant completions deployment) when one is present and ready, and falls back to the central completions engine inark-system(the--completions-addrdefault) when it is not - Sends an A2A
SendMessageto the resolved address with the target and query reference, then waits for the response and writes the result to the Query status
One model, two deployment topologies. Execution engines — including the built-in completions engine — are deployed per tenant when isolation is needed (own namespace, ServiceAccount, RBAC, NetworkPolicy, and cloud workload identity), and the central
ark-completionsinark-systemremains the shared default for simple or single-tenant installs. Built-in Agents need noexecutionEnginefield either way; the controller resolves the right engine by namespace. The completions engine receives the message and sets up the run: it reads theQueryand the target resource from Kubernetes, creates a memory client and loads conversation history, opens an event stream for chunks toark-broker, then executes the target.
For an agent, it:
- Resolves the agent’s model.
- Resolves parameters from static values, ConfigMaps, Secrets, and query parameters.
- Builds the prompt from the system prompt, memory context, and user input.
- Prepares the available tools and MCP servers.
- Runs the turn loop — call the model, execute any tool calls, repeat until none remain.
- Streams chunks to
ark-brokeras they arrive.
For a team, it:
- Applies the team’s strategy —
sequentialorselector(optionally constrained by agraph). The deprecatedround-robinand standalonegraphstrategies are migrated by the admission webhook (round-robin→sequentialwith loops;graph→selector). - Coordinates execution across the members.
- Routes recursively — members with their own execution engine go back through A2A; the rest run locally.
4. The response is written back
When execution finishes:
- The engine saves the new messages to memory, sends a final stream chunk, and closes the stream.
- It returns the A2A response with the assistant message, token usage, and conversation ID.
- The controller writes the response, token usage, and conversation ID to the query’s status.
- The controller marks the query done (
phase: done).
How the query is stored
The Query and its status are persisted like any other Kubernetes resource. The execution path above is identical on both storage backends — only where the resource lives changes.
etcd (default)
In the standard deployment, Ark resources are stored in etcd alongside standard Kubernetes resources. The controller learns about a new query through a Kubernetes watch notification, then dispatches it.
PostgreSQL (aggregated)
For larger deployments, Ark can store its resources in PostgreSQL behind a Kubernetes aggregated API server instead of etcd. The Kubernetes API server proxies ark.mckinsey.com requests to the embedded Ark API server, which persists to Postgres. Execution is unchanged; the write path notifies the controller directly in-process, and pg_notify fans the event out to other replicas.
Which backend you run is a deployment choice that’s transparent to clients — kubectl and the Ark API work the same either way. See Core Architecture → Storage backends for the trade-off and PostgreSQL Storage Backend for setup and operation.
Custom execution engines
A named ExecutionEngine runs an agent on a different runtime (e.g. LangChain or CrewAI) instead of the built-in completions engine. When an agent references one, the controller dispatches the query to that engine over A2A. The message carries a reference to the Query (name and namespace) plus the input; the engine reads the agent config, tools, and conversation history from Kubernetes, processes the request, and returns the result over A2A.
Error handling
Failed queries are marked with an error status and a detailed message. Common failures:
- Model errors — API failures, rate limits, invalid responses.
- Tool errors — tool execution failures or timeouts.
- Resource errors — missing agents, models, or tools.
- Permission errors — RBAC violations or service-account issues.
Observability
Every step of execution is observable:
- Kubernetes events — resource creation and status changes.
- OpenTelemetry traces — detailed execution spans across the controller and engines.
- Logs — structured logging throughout the pipeline.
- Metrics — performance and error metrics.
See Observability for setup.
Example: a weather query
- You create a
Querytargetingweather-agent. - The controller resolves
weather-agentand dispatches to the completions engine. - The engine builds the prompt with the agent’s weather tools available.
- The model calls the
get-weathertool with a location. - The tool returns weather data.
- The model produces a natural-language answer.
- The controller writes the answer to the query’s status.
- The conversation is saved to memory for the next turn.