Skip to content
6 min read · 1,216 words

Assembly

ADK is not a pre-assembled agent framework. It is an execution chassis you assemble yourself.

Fast Track to Hello World

LLM batteries can supply an executor so you do not have to write one from scratch. You still must provide the 25 state callbacks — real or noop — and may optionally use Storage Batteries for spool/artifact bytes. See LLM Batteries and Storage Batteries.

The Chassis Contract

Think of ADK as an engine block without fuel injectors, a spark source, or a gas tank. You do not extend the chassis to get behavior; you slot in an executor, map storage callbacks to your database, and register tools. ADK ships the runtime contract, not your product. No executor, no storage, no agent.

ADK has no hidden defaults. Batteries exist — such as the Chat Completions adapter, the in-process WebLLM driver, the in-memory spool stores, and ADK-native tool batteries — but you opt into every single one explicitly. Nothing is loaded unless you wire it. This prevents silent defaults from becoming production obstacles when you need to change retry logic, swap models, or migrate databases. ADK owns the contract; you own the implementation.

When your executor invokes ADK tool execution/reporting APIs, ADK emits the lifecycle events and validates transitions. Opt-in batteries may enforce token budgets, but if you write a custom executor, you own the constraints. You provide the implementation; ADK enforces the loop.

Division of Ownership

This boundary is absolute:

CategoryADK OwnsYou Own
ExecutionTurnRunner, DispatchRunner, Turn LifecycleexecutorCallback (the model call)
StateType Validation, Immutability, Event Bus25 Storage Callbacks
LogicMiddleware Pipeline Structure, Dispatch LoopTools, Retrieval Strategy, Operator Instructions
ContextToken-aware Shape, Primitive StructuresPrompts, Templates, Message History
EnvironmentCross-realm Safety, Internal SignalingRuntime, Deployment, Observability Stack

The 25 Required Obligations

To run a custom storage configuration, satisfy ADK's storage contract: provide exactly 25 callbacks. ADK never guesses how to read or write your database. Every database operation is configured explicitly, and the runtime schema validator enforces strict parameter counts (arity) for every one.

Retrieval Obligations (7)

Provide the logic to fetch every primitive the agent might need during a turn. Fetch callbacks must declare exactly one parameter (arity 1) to pass runtime validation:

  • fetchMemoriesCallback
  • fetchMessagesCallback
  • fetchThoughtsCallback
  • fetchToolCallsCallback
  • fetchToolsCallback
  • fetchRetrievablesCallback
  • refreshStandingInstructionsCallback

ADK Does Not Auto-Hydrate Context

When a TurnContext is created, the turn-level context sets (ctx.turnMessages, ctx.turnMemories, ctx.turnThoughts, ctx.turnToolCalls, and ctx.turnRetrievables) start completely empty. ctx.standingInstructions starts from configured raw/source instructions and is not auto-refreshed. A DispatchContext snapshots the supplied/source state, which may already contain pre-fetched arrays or state derived from a populated TurnContext. ADK does not call the fetch callbacks automatically. Your pipeline middleware must call ctx.fetchMessages(), ctx.fetchMemories(), etc., and manually populate the context. Skip this step, and your executor runs blind.

Persistence Obligations (18)

For the five core primitives (Message, Memory, Thought, ToolCall, Retrievable) plus operator instructions (configured as strings or Tokenizable elements), provide three operations. Each persistence callback must declare exactly two parameters (arity 2) to pass runtime validation:

  1. Store — Write a new record.
  2. Mutate — Update an existing record.
  3. Delete — Remove a record.
Complete List of 25 Callbacks

Here is the full set of callback keys required by the TurnRunnerConfig schema:

Retrieval (Arity 1):

  • fetchMemoriesCallback
  • fetchMessagesCallback
  • fetchThoughtsCallback
  • fetchToolCallsCallback
  • fetchToolsCallback
  • fetchRetrievablesCallback
  • refreshStandingInstructionsCallback

Store (Arity 2):

  • storeMemoryCallback
  • storeMessageCallback
  • storeThoughtCallback
  • storeToolCallCallback
  • storeRetrievableCallback
  • storeStandingInstructionCallback

Mutate (Arity 2):

  • mutateMemoryCallback
  • mutateMessageCallback
  • mutateThoughtCallback
  • mutateToolCallCallback
  • mutateRetrievableCallback
  • mutateStandingInstructionCallback

Delete (Arity 2):

  • deleteMemoryCallback
  • deleteMessageCallback
  • deleteThoughtCallback
  • deleteToolCallCallback
  • deleteRetrievableCallback
  • deleteStandingInstructionCallback

No-ops Are Valid. Omissions Are Not.

Every one of these 25 callbacks can be a legal no-op (e.g., async (_ctx) => [] or async (_ctx, _value) => {}), but they must be provided. ADK does not fill in blanks. If any callback is missing or has the wrong parameter count in your config, the schema validator will fail at startup and block execution.

Note that storage batteries like InMemorySpoolStore or OpfsSpoolStore are spool-only. They persist raw SpooledArtifact or Media bytes, not the 25 state callbacks. Even when using a battery LLM provider, you still must provide the 25 state callbacks.

The Executor Contract

The executorCallback is where your LLM client integration lives. It must implement DispatchExecutorFn:

typescript
type DispatchExecutorFn = (
  ctx: DispatchContext,
  helpers: DispatchExecutorHelpers
) => void | Promise<void>

Dispatch execution must eventually reach exactly one terminal signal: ctx.ack() or ctx.nack(error), unless the run aborts.

  • No terminal signal: The dispatch loop continues until a later iteration signals or the run aborts.
  • Multiple terminal signals: ADK throws E_LLM_EXECUTION_ALREADY_SIGNALLED and terminates the run.

Fail Fast, Fail Loudly

ADK has zero tolerance for silent failures. If your executor throws or a pipeline middleware throws, ADK will immediately emit a detailed error on the observability bus and terminate the turn.

A silent failure in an agentic workflow is a hallucination waiting to happen. If a message fails to persist, ADK aborts the run rather than letting the agent proceed with corrupted context.

One critical pipeline rule: turnOutputPipeline does not run if there is an input pipeline failure or a dispatch failure. If the turn fails, the output pipeline is skipped entirely. Never put critical cleanup code there that must run on failure.

Event Buses: Functional vs. Observability

ADK exposes two distinct event channels:

  1. Functional Events (runner.on / runner.off / runner.once): These are the product delivery mechanisms. Consumers depend on these events to deliver assistant streaming output, yield tool results, or execute side effects. They are not internal control-flow gates, but they represent the system's actual product output.
  2. Observability Events (runner.observe / runner.unobserve): These are strictly for telemetry, tracing, logging, and metrics. Observability listeners must never contain logic required for the correctness of the run.

If your telemetry changes the behavior of your agent, you have introduced a side-channel bug.

How to Navigate This Section

If you read nothing else, start with Minimal Agent Assembly. The rest of this section refers back to that wiring.

  1. Minimal Agent Assembly — The bare minimum configuration to get a working TurnRunner loop compiled and running starting from a raw RawTurnContext.
  2. BYO LLM — How to wrap your model client in the executor contract and handle streaming or token budgets.
  3. BYO Storage — Concrete guide to implementing the 25 storage callbacks for your database.
  4. Wiring the Pipelines — Injecting middleware into turn and dispatch loops for prompt construction, validation, and safety filters.
  5. BYO Tools — How to define tools using Tool and manage them with ToolRegistry.
  6. BYO Retrieval — Custom context injection strategies and managing trust tiers.
  7. BYO Memory — Implementing persistent, auditable memory writes.
  8. Listening to the Assembly — Capturing functional streams and telemetry events correctly.
  9. LLM Batteries, Storage Batteries, and Tool Batteries — Pre-built integrations to accelerate your assembly.