Skip to Content

Memory

A Memory points Ark at an HTTP service that stores conversation history keyed by conversationId / queryId. The CRD is backend-agnostic — any service that implements the Memory HTTP API can back it (a database, queue, or other store). The in-cluster ark-broker is the reference implementation. Queries reference a Memory via spec.memory.name; when unset, resolution falls back to a Memory named default in the same namespace.

Spec

apiVersion: ark.mckinsey.com/v1alpha1 kind: Memory metadata: name: ark-broker spec: # --- Required ---------------------------------------------------------- address: # the memory service endpoint (ValueSource) valueFrom: serviceRef: # point at an in-cluster service name: ark-broker port: "8080" # value: http://memory.example.com:8080 # OR a direct URL # --- Optional ---------------------------------------------------------- headers: # HTTP headers sent on every backend request - name: Authorization value: valueFrom: secretKeyRef: name: memory-credentials key: token

Fields

FieldTypeRequiredDescription
spec.addressValueSourceyesThe memory service endpoint. Supply a direct value (URL) or a valueFrom source — serviceRef (in-cluster service), configMapKeyRef, secretKeyRef, or queryParameterRef. The resolved value is written back to status.lastResolvedAddress.
spec.headers[]list of HeadernoHTTP headers included in every request to the backend. Each entry has a name and a value that is either a direct value or a valueFrom source (configMapKeyRef, secretKeyRef, queryParameterRef). Use for auth tokens or routing headers.

Address resolution

spec.address is a ValueSource, so the backend endpoint can be a literal URL or resolved from cluster state:

spec: address: valueFrom: serviceRef: name: ark-broker # resolves to the in-cluster service address port: "8080"
spec: address: value: http://memory.example.com:8080 # direct URL

The controller resolves the address and records the result in status.lastResolvedAddress. Consumers (the completions executor, the Query cleanup finalizer) read the resolved address, falling back to spec.address if the status field is empty.

Using memory from a query

A query selects a Memory via spec.memory.name. If a Memory named default exists in the namespace, it is used automatically for queries that don’t name one. Memory works with all query target types — agents, models, and tools. When a conversationId is provided, messages are persisted to the memory chain regardless of the target type.

Memory in a query to a tool:

apiVersion: ark.mckinsey.com/v1alpha1 kind: Query metadata: name: tool-with-memory spec: input: "Remember this message for later" target: type: tool name: noop conversationId: "my-conversation"

A follow-up query to an agent on the same conversation ID has access to the tool query’s messages:

apiVersion: ark.mckinsey.com/v1alpha1 kind: Query metadata: name: follow-up-agent-query spec: input: "What was the last message?" target: type: agent name: my-agent conversationId: "my-conversation"

Memory can also be specified via the CLIs:

# Using fark fark query --input "Start our conversation" --conversation-id "my-conversation" my-agent # Using ark ark query tool/noop "Remember this message for later" --conversation-id "my-conversation" ark query agent/my-agent "What was the last message?" --conversation-id "my-conversation"

Session ID vs Conversation ID:

  • sessionId: Groups related queries for tracking and telemetry purposes.
  • conversationId: Conversation threading identifier sent to all execution engines as A2A contextId. The completions engine uses it for memory continuity; named engines can use it for their own session management.

When creating a query in the dashboard it is also possible to specify the memory resource. Note that in the dashboard ‘chat’ window, no memory is used — messages are simply stored client-side as is common for chat applications.

Memory HTTP API

A memory backend is an HTTP server implementing the following endpoints. The ark-broker is the reference implementation; the Ark API gateway proxies the same contract.

MethodEndpointDescription
POST/conversationsCreate a new conversation
GET/conversationsList all conversation IDs
GET/conversations/{id}Get conversation details and messages
DELETE/conversations/{id}Delete a specific conversation
POST/messagesStore messages (requires conversation_id)
GET/messagesRetrieve messages with optional filtering
DELETE/queries/{queryId}/messagesDelete all messages for a query across all conversations
GET/healthHealth check

Create Conversation

POST /conversations

Creates a new conversation and returns its ID. This must be called before storing messages.

Response:

{ "conversation_id": "550e8400-e29b-41d4-a716-446655440000" }

Store Messages

POST /messages

Stores messages for an existing conversation. The conversation_id is required.

{ "conversation_id": "550e8400-e29b-41d4-a716-446655440000", "query_id": "query-uuid", "ttl_seconds": 2592000, "messages": [ { "role": "user", "content": "What is the weather like?" }, { "role": "assistant", "content": "I don't have access to real-time weather data." } ] }

ttl_seconds (optional) sets a per-message expiry: each stored message is stamped with an expires_at snapshot and filtered out on read once it elapses. This expires messages on a timer — it does not delete them; see Delete Query Messages for explicit removal. When omitted, the backend applies its default visibility TTL.

Get Conversation

GET /conversations/{id}

Returns conversation details including all messages:

{ "conversation_id": "550e8400-e29b-41d4-a716-446655440000", "messages": [ { "timestamp": "2024-01-01T12:00:00Z", "conversation_id": "550e8400-e29b-41d4-a716-446655440000", "query_id": "query-uuid", "message": { "role": "user", "content": "Hello" }, "sequence": 1 } ] }

Retrieve Messages

GET /messages?conversation_id={id}&query_id={id}&limit={n}&offset={n}

Retrieves stored conversation messages:

  • conversation_id (optional) - Filter by conversation
  • query_id (optional) - Filter by query
  • limit (optional, default: 100) - Max messages to return
  • offset (optional, default: 0) - Skip messages for pagination

Returns timestamped message records:

{ "messages": [ { "timestamp": "2024-01-01T12:00:00Z", "conversation_id": "conv-abc123", "query_id": "query-uuid", "message": { "role": "user", "content": "What is the weather like?" } } ], "total": 100, "limit": 50, "offset": 0 }

List Conversations

GET /conversations

Returns object with conversation IDs:

{"conversations": ["conv-1", "conv-2", "conv-3"]}

Delete Query Messages

DELETE /queries/{queryId}/messages

Hard-deletes all messages for a query across every conversation. The Ark controller calls this to clean up broker state when a Query is deleted — see Query → Deletion and cleanup. The operation is idempotent.

Response:

{ "status": "success", "message": "Query <queryId> messages deleted" }

A backend that does not implement this endpoint should return 404 or 405; the controller treats both as “not supported” and skips cleanup.

Status

status: phase: ready lastResolvedAddress: http://ark-broker.default.svc.cluster.local:8080 message: Memory backend reachable

Status fields

FieldDescription
status.phaseOne of running, ready, error.
status.lastResolvedAddressLast resolved value of spec.address, kept for reference. Consumers read this first, falling back to spec.address.
status.messageAdditional information about the current status (e.g. a resolution or reachability error).

kubectl get memories prints NAME, PHASE (.status.phase), ADDRESS (.status.lastResolvedAddress), and AGE.

  • Query — references a Memory via spec.memory.
  • Broker Service — the reference memory backend and its HTTP API.
  • Ark API — gateway that proxies the memory contract.
Last updated on