DEV Community

Cover image for Your Google ADK Agent Has Four Places to Put Context. Choose Carefully.
Raju Dandigam
Raju Dandigam

Posted on

Your Google ADK Agent Has Four Places to Put Context. Choose Carefully.

The first version of an agent usually has one place for context: the prompt.

The user request goes in. Conversation history goes in. Tool results, preferences, account details, retrieved documents, and generated files follow. The agent appears to “remember” because every useful fact is repeatedly placed in front of the model.

That design works until the context becomes expensive, stale, difficult to delete, or visible in a place where it never belonged.

Google’s Agent Development Kit gives TypeScript applications four distinct homes for context: session events, session state, long-term memory, and artifacts. Gemini’s Interactions API adds provider-managed conversation continuity, but that is a transport feature—not a fifth application memory store.

The production question is not merely:

Can the agent access this information?

It is:

Where should this information live, for how long, and who is allowed to retrieve it?

Series note: This is Part 4 of Reliable Google AI Agents in TypeScript. The examples were checked against @google/adk 2.0.0 and @google/genai 2.21.0 in September 2026.

One context bucket creates several bugs

Imagine a travel agent helping a user compare hotels.

During the conversation, the user says they prefer quiet rooms. A search tool returns 40 options. The agent produces a comparison spreadsheet. Later, the user starts a new conversation about a different trip.

If everything is prompt history, several failures become likely:

Information Wrong home Failure
40 raw hotel results Repeated prompt history Cost and stale-data growth
Budget for this trip Permanent memory Temporary constraint becomes a lasting preference
“Prefers quiet rooms” Current turn only Useful preference disappears next session
Comparison spreadsheet Message blob No clean versioning, replacement, or deletion path

The data was available. It simply lived in the wrong place.

1. Session events explain what happened

An ADK Session represents one conversation thread. Its events form the chronological record of user messages, agent responses, tool activity, state changes, and errors.

Events answer continuity and debugging questions:

  • What did the user ask in this thread?
  • Which tool ran?
  • What result came back?
  • What did the agent do next?

That does not make the transcript the user’s permanent profile. A completed hotel search belongs in the session because it explains the current run. A preference that should be available next month needs a deliberate cross-session policy.

Otherwise, a conversation log quietly turns into an ungoverned memory database.

2. State is the working scratchpad

ADK state holds dynamic values that code, tools, callbacks, or agents need while work is in progress.

In TypeScript, state can be updated through a context object:

toolContext.state.set("temp:hotel_count", hotels.length);
toolContext.state.set("selected_currency", "USD");
Enter fullscreen mode Exit fullscreen mode

Those two keys deliberately have different lifetimes:

  • temp: is invocation-scoped and is discarded after the invocation.
  • A key with no recognized prefix belongs to the current session.
  • user: is shared across that user’s sessions when the chosen session service persists it.
  • app: is shared at the application scope when the service supports persistence.

The storage implementation matters. An in-memory service still loses state when the process restarts. A prefix expresses scope; it does not magically provide durable infrastructure.

State is useful for values such as a normalized destination, approval status, workflow checkpoint, or the number of results fetched. It should not become a cache for every response the application has ever seen.

3. Memory is searchable knowledge across sessions

ADK’s MemoryService solves a different problem: finding useful knowledge that may come from earlier sessions or another curated source.

A stable preference such as “usually prefers hotels near public transit” may deserve retrieval on a future trip. The exact 40-hotel result from six months ago probably does not.

A production memory pipeline therefore needs policy around:

  • what is eligible for ingestion;
  • whether raw events or summaries are stored;
  • how retrieval is scoped to the correct user or tenant;
  • when facts expire;
  • how a user corrects or deletes them;
  • what the execution record reveals about retrieval.

If an answer depends on remembered knowledge, the trace should show that a memory search occurred and what category of memory affected the decision. That is enough for diagnosis without copying an entire personal profile into telemetry.

4. Artifacts are files with a lifecycle

Generated reports, images, audio, PDFs, and other binary outputs are not chat messages.

ADK artifacts are named and versioned. A plain filename is scoped to the session; a user: filename can be available across that user’s sessions when the artifact service supports it.

That gives a report a cleaner lifecycle:

hotel-comparison.csv        version 1
hotel-comparison.csv        version 2
user:travel-profile.pdf     user-scoped
Enter fullscreen mode Exit fullscreen mode

The session can contain a reference to the artifact while the artifact service owns its bytes, versions, and retrieval. Regenerating a report creates a new version instead of another ambiguous message blob.

Gemini history is continuity, not memory policy

The Gemini Interactions API can continue work by passing a prior previous_interaction_id. The server retrieves the earlier conversation inputs and outputs so the client does not have to resend the full transcript.

That is useful provider-side continuity, but it does not decide whether a preference is durable, whether a document deserves an artifact lifecycle, or whether data may cross into another workflow. Interaction-scoped settings such as tools, system instructions, and generation configuration also need to be specified again on the next interaction.

The Interactions API currently stores interactions by default unless store: false is used. Because the API is still beta and retention varies by tier, storage should be an explicit product decision—not a side effect of adopting an SDK.

Make placement reviewable in code

Teams make better decisions when each context category has an owner and retention rule.

type ContextHome =
  | "session_event"
  | "session_state"
  | "long_term_memory"
  | "artifact";

type ContextPolicy = {
  name: string;
  home: ContextHome;
  retention: "invocation" | "session" | "user_managed";
  containsSensitiveData: boolean;
  deletionOwner: "session_service" | "memory_service" | "artifact_service";
};

const policies: ContextPolicy[] = [
  {
    name: "latest_hotel_search_count",
    home: "session_state",
    retention: "invocation",
    containsSensitiveData: false,
    deletionOwner: "session_service",
  },
  {
    name: "transit_preference",
    home: "long_term_memory",
    retention: "user_managed",
    containsSensitiveData: false,
    deletionOwner: "memory_service",
  },
  {
    name: "comparison_report",
    home: "artifact",
    retention: "user_managed",
    containsSensitiveData: true,
    deletionOwner: "artifact_service",
  },
];
Enter fullscreen mode Exit fullscreen mode

The exact schema is less important than forcing a choice before data starts flowing.

Trace boundaries, not private payloads

While building AgentInspect, I found that context problems are easier to diagnose when the execution record preserves boundaries rather than raw values.

trip-planner
├─ memory.search          category=hotel_preference
├─ tool.search_hotels
├─ state.write            key=temp:hotel_count
├─ artifact.save          name=hotel-comparison.csv version=2
└─ final_response
Enter fullscreen mode Exit fullscreen mode

This explains what influenced the run without storing the user’s preference text, full search response, or report contents. A deterministic check can also flag a prohibited transition—for example, a raw tool response being sent to long-term memory or a sensitive artifact being created without an explicit retention class.

AgentInspect does not currently claim a first-class ADK adapter. The pattern is framework-neutral: once your integration records these boundaries, the execution shape becomes reviewable and testable.

A practical context-placement review

Before adding a value to an agent, ask:

  1. Is it evidence of what happened, or data needed for future decisions?
  2. Should it survive one invocation, one session, or multiple sessions?
  3. Is it searchable knowledge or a named file?
  4. Which user, tenant, or application is allowed to retrieve it?
  5. What deletes or corrects it?
  6. Can the trace explain its use without logging the private value?

The model does not need one giant memory.

The system needs several bounded forms of context with different lifetimes and owners. When events, state, memory, artifacts, and provider-managed history are treated as interchangeable, the agent may appear intelligent while the application becomes expensive and impossible to govern.

When each has a defined role, context becomes architecture instead of prompt accumulation.

References

Earlier in the series: Gemini Function Calling Is Not an Agent Runtime · Testing Google ADK TypeScript Agents Without Chasing Sentences · From Local Traces to Production Observability for Google AI Agents

Top comments (0)