AI & ML
Your Google ADK Agent Has Four Places to Put Context. Choose Carefully.
Raju Dandigam DEV Community
4 views
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");
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
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",
},
];
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
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:
Is it evidence of what happened, or data needed for future decisions?
Should it survive one invocation, one session, or multiple sessions?
Is it searchable knowledge or a named file?
Which user, tenant, or application is allowed to retrieve it?
What deletes or corrects it?
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
ADK conversational context
ADK state and state scopes
ADK long-term memory
ADK artifacts
Gemini Interactions API
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
Read original: https://dev.to/raju_dandigam/your-google-adk-agent-has-four-places-to-put-context-choose-carefully-5c9i
← Previous
15 AI Coding Prompts for Codex and Claude Code
Next →
Ten fine-tuning mistakes I see students make (and made myself)
Related
Building The Real Jarvis: Did OpenAI Just Create Iron Man's AI?
AI & ML
1
DEV Community
A one-day GenAI workshop syllabus for colleges (hands-on, no fluff)
AI & ML
1
DEV Community
Change-Intent Governance: The Missing Layer
AI & ML
1
DEV Community
Mentoring first-year students: what works and what wastes their time
AI & ML
1
Dev.to (EN Zone)
Comments0
No comments yet — be the first