| [ Web Proxy ] |
| Viewing: https://adk.dev/integrations/../../workflows/../../../../../../../../sessions/memory/ | [Back] [Original] |
[logo]
MemoryService¶While a Session tracks the history (events) and temporary data (state) of
a single conversation, an agent may need to recall information from past
interactions. This is where the concept of Long-Term Knowledge and the
MemoryService come into play. Think of it this way:
Session / State: It's your short-term memory during one specific chat.MemoryService): It's a searchable archive or
knowledge library the agent can consult, potentially containing information
from many past chats or other sources.MemoryService role¶The BaseMemoryService (or Service in Go) defines the interface for managing
this searchable, long-term knowledge store. It supports these operations:
add_session_to_memory: Takes a completed Session and adds relevant
information to the long-term knowledge store. This approach is ideal for
automatically capturing the essence of a conversation.add_events_to_memory: Appends a delta of events (for example, the
latest turn) without re-ingesting the full session. Useful when you want
to write to memory partway through a long-running session.add_memory: Adds explicit MemoryEntry objects directly to the
memory. This method gives you fine-grained control and is useful for
injecting specific facts from other sources.search_memory): Lets an agent (typically via a
Tool) query the knowledge store and retrieve relevant snippets or context
based on a search query.add_events_to_memory and add_memory are optional and are not implemented by
every service, so confirm that your chosen service supports them before relying
on them.
The Python ADK ships three MemoryService implementations. Use the table below
to decide which is the best fit for your agent.
| Feature | InMemoryMemoryService | VertexAiMemoryBankService | VertexAiRagMemoryService |
|---|---|---|---|
| Persistence | None, data is lost on restart | Yes, managed by the Agent Platform | Yes, stored in Knowledge Engine |
| Primary Use Case | Prototyping, local development, and simple testing. | Building meaningful, evolving memories from user conversations. | Vector-search retrieval over the full conversation corpus, or alongside other RAG-indexed content. |
| Memory Extraction | Stores full conversation | Extracts meaningful information from conversations and consolidates it with existing memories powered by LLM | Stores full conversation, indexed by Knowledge Engine. |
| Search Capability | Basic keyword matching. | Advanced semantic search. | Vector similarity search over Knowledge Engine. |
| Setup Complexity | None. It's the default. | Low. Requires an Agent Runtime instance on Agent Platform. | Medium. Requires Knowledge Engine. |
| Dependencies | None. | Google Cloud Project, Agent Platform API | Google Cloud Project, Knowledge Engine, the Agent Platform SDK (optional install). |
| When to use it | When you want to search across multiple sessions chat histories for prototyping. | When you want your agent to remember and learn from past interactions. | When you already have RAG infrastructure or want to retrieve over raw conversation transcripts. |
You can always import VertexAiRagMemoryService from google.adk.memory, but
constructing it raises ImportError unless the Agent Platform SDK is installed
with pip install google-adk[gcp]. Memory Bank and RAG-backed memory are
documented in Memory Bank and RAG Memory below.
InMemoryMemoryService¶The InMemoryMemoryService stores session information in the application's
memory and performs basic keyword matching for searches. It requires no setup
and is best for prototyping and simple testing scenarios where persistence isn't
required.
Example: Add and search memory
This example demonstrates the basic flow using the InMemoryMemoryService for
simplicity.
import asyncio
from google.adk.agents import LlmAgent
from google.adk.sessions import InMemorySessionService, Session
from google.adk.memory import InMemoryMemoryService # Import MemoryService
from google.adk.runners import Runner
from google.adk.tools import load_memory # Tool to query memory
from google.genai.types import Content, Part
# --- Constants ---
APP_NAME = "memory_example_app"
USER_ID = "mem_user"
MODEL = "gemini-flash-latest" # Use a valid model
# --- Agent Definitions ---
# Agent 1: Simple agent to capture information
info_capture_agent = LlmAgent(
model=MODEL,
name="InfoCaptureAgent",
instruction="Acknowledge the user's statement.",
)
# Agent 2: Agent that can use memory
memory_recall_agent = LlmAgent(
model=MODEL,
name="MemoryRecallAgent",
instruction="Answer the user's question. Use the 'load_memory' tool "
"if the answer might be in past conversations.",
tools=[load_memory] # Give the agent the tool
)
# --- Services ---
# Services must be shared across runners to share state and memory
session_service = InMemorySessionService()
memory_service = InMemoryMemoryService() # Use in-memory for demo
async def run_scenario():
# --- Scenario ---
# Turn 1: Capture some information in a session
print("--- Turn 1: Capturing Information ---")
runner1 = Runner(
# Start with the info capture agent
agent=info_capture_agent,
app_name=APP_NAME,
session_service=session_service,
memory_service=memory_service # Provide the memory service to the Runner
)
session1_id = "session_info"
await runner1.session_service.create_session(app_name=APP_NAME, user_id=USER_ID, session_id=session1_id)
user_input1 = Content(parts=[Part(text="My favorite project is Project Alpha.")], role="user")
# Run the agent
final_response_text = "(No final response)"
async for event in runner1.run_async(user_id=USER_ID, session_id=session1_id, new_message=user_input1):
if event.is_final_response() and event.content and event.content.parts:
final_response_text = event.content.parts[0].text
print(f"Agent 1 Response: {final_response_text}")
# Get the completed session
completed_session1 = await runner1.session_service.get_session(app_name=APP_NAME, user_id=USER_ID, session_id=session1_id)
# Add this session's content to the Memory Service
print("\n--- Adding Session 1 to Memory ---")
await memory_service.add_session_to_memory(completed_session1)
print("Session added to memory.")
# Turn 2: Recall the information in a new session
print("\n--- Turn 2: Recalling Information ---")
runner2 = Runner(
# Use the second agent, which has the memory tool
agent=memory_recall_agent,
app_name=APP_NAME,
session_service=session_service, # Reuse the same service
memory_service=memory_service # Reuse the same service
)
session2_id = "session_recall"
await runner2.session_service.create_session(app_name=APP_NAME, user_id=USER_ID, session_id=session2_id)
user_input2 = Content(parts=[Part(text="What is my favorite project?")], role="user")
# Run the second agent
final_response_text_2 = "(No final response)"
async for event in runner2.run_async(user_id=USER_ID, session_id=session2_id, new_message=user_input2):
if event.is_final_response() and event.content and event.content.parts:
final_response_text_2 = event.content.parts[0].text
print(f"Agent 2 Response: {final_response_text_2}")
# To run this example, you can use the following snippet:
# asyncio.run(run_scenario())
# await run_scenario()
import {
InMemoryMemoryService,
InMemorySessionService,
LOAD_MEMORY,
LlmAgent,
Runner
} from '@google/adk';
import { createUserContent } from '@google/genai';
// --- Constants ---
const APP_NAME = "memory_example_app";
const USER_ID = "mem_user";
const MODEL = "gemini-2.5-flash";
// --- Agent Definitions ---
// Agent 1: Simple agent to capture information
const infoCaptureAgent = new LlmAgent({
model: MODEL,
name: "InfoCaptureAgent",
instruction: "Acknowledge the user's statement concisely.",
});
// Agent 2: Agent that can use memory
const memoryRecallAgent = new LlmAgent({
model: MODEL,
name: "MemoryRecallAgent",
instruction: "Answer the user's question. Use the 'load_memory' tool if the answer might be in past conversations.",
tools: [LOAD_MEMORY]
});
// Export for 'adk run' compatibility (to avoid 'No BaseAgent found' error)
export const root_agent = memoryRecallAgent;
// --- Services ---
const sessionService = new InMemorySessionService();
const memoryService = new InMemoryMemoryService();
async function runScenario() {
// --- Turn 1: Capture some information in a session ---
console.log("--- Turn 1: Capturing Information ---");
const runner1 = new Runner({
agent: infoCaptureAgent,
appName: APP_NAME,
sessionService,
memoryService
});
const session1Id = "session_info";
await sessionService.createSession({ appName: APP_NAME, userId: USER_ID, sessionId: session1Id });
const userInput1 = createUserContent("My favorite project is Project Alpha.");
let finalResponseText = "(No final response)";
for await (const event of runner1.runAsync({ userId: USER_ID, sessionId: session1Id, newMessage: userInput1 })) {
// Capture any text response from the agent
if (event.author === infoCaptureAgent.name && event.content?.parts) {
const text = event.content.parts.map(p => p.text || "").join("").trim();
if (text) finalResponseText = text;
}
}
console.log(`Agent 1 Response: ${finalResponseText}`);
// Get the completed session and add to Memory
const completedSession1 = await sessionService.getSession({ appName: APP_NAME, userId: USER_ID, sessionId: session1Id });
console.log("\n--- Adding Session 1 to Memory ---");
if (completedSession1) {
await memoryService.addSessionToMemory(completedSession1);
console.log("Session added to memory.");
}
// --- Turn 2: Recall the information in a new session ---
console.log("\n--- Turn 2: Recalling Information ---");
const runner2 = new Runner({
agent: memoryRecallAgent,
appName: APP_NAME,
sessionService,
memoryService
});
const session2Id = "session_recall";
await sessionService.createSession({ appName: APP_NAME, userId: USER_ID, sessionId: session2Id });
const userInput2 = createUserContent("What is my favorite project?");
let finalResponseText2 = "(No final response)";
for await (const event of runner2.runAsync({ userId: USER_ID, sessionId: session2Id, newMessage: userInput2 })) {
// Capture any text response from the agent
if (event.author === memoryRecallAgent.name && event.content?.parts) {
const text = event.content.parts.map(p => p.text || "").join("").trim();
if (text) finalResponseText2 = text;
}
}
console.log(`Agent 2 Response: ${finalResponseText2}`);
// Exit immediately to prevent the ADK CLI from starting an interactive loop
process.exit(0);
}
// Execute the scenario
runScenario().catch(err => {
console.error(err);
process.exit(1);
});
import (
"context"
"fmt"
"log"
"strings"
"google.golang.org/adk/v2/agent"
"google.golang.org/adk/v2/agent/llmagent"
"google.golang.org/adk/v2/memory"
"google.golang.org/adk/v2/model/gemini"
"google.golang.org/adk/v2/runner"
"google.golang.org/adk/v2/session"
"google.golang.org/adk/v2/tool"
"google.golang.org/adk/v2/tool/functiontool"
"google.golang.org/genai"
)
const (
appName = "go_memory_example_app"
userID = "go_mem_user"
modelID = "gemini-2.5-flash"
)
// Args defines the input structure for the memory search tool.
type Args struct {
Query string `json:"query" jsonschema:"The query to search for in the memory."`
}
// Result defines the output structure for the memory search tool.
type Result struct {
Results []string `json:"results"`
}
// memorySearchToolFunc is the implementation of the memory search tool.
// This function demonstrates accessing memory via agent.Context.
func memorySearchToolFunc(tctx agent.Context, args Args) (Result, error) {
fmt.Printf("Tool: Searching memory for query: '%s'\n", args.Query)
// The SearchMemory function is available on the context.
searchResults, err := tctx.SearchMemory(context.Background(), args.Query)
if err != nil {
log.Printf("Error searching memory: %v", err)
return Result{}, fmt.Errorf("failed memory search")
}
var results []string
for _, res := range searchResults.Memories {
if res.Content != nil {
results = append(results, textParts(res.Content)...)
}
}
return Result{Results: results}, nil
}
// Define a tool that can search memory.
var memorySearchTool = must(functiontool.New(
functiontool.Config{
Name: "search_past_conversations",
Description: "Searches past conversations for relevant information.",
},
memorySearchToolFunc,
))
// This example demonstrates how to use the MemoryService in the Go ADK.
// It covers two main scenarios:
// 1. Adding a completed session to memory and recalling it in a new session.
// 2. Searching memory from within a custom tool using the agent.Context.
func main() {
ctx := context.Background()
// --- Services ---
// Services must be shared across runners to share state and memory.
sessionService := session.InMemoryService()
memoryService := memory.InMemoryService() // Use in-memory for this demo.
// --- Scenario 1: Capture information in one session ---
fmt.Println("--- Turn 1: Capturing Information ---")
infoCaptureAgent := must(llmagent.New(llmagent.Config{
Name: "InfoCaptureAgent",
Model: must(gemini.NewModel(ctx, modelID, nil)),
Instruction: "Acknowledge the user's statement.",
}))
runner1 := must(runner.New(runner.Config{
AppName: appName,
Agent: infoCaptureAgent,
SessionService: sessionService,
MemoryService: memoryService, // Provide the memory service to the Runner
}))
session1ID := "session_info"
must(sessionService.Create(ctx, &session.CreateRequest{AppName: appName, UserID: userID, SessionID: session1ID}))
userInput1 := genai.NewContentFromText("My favorite project is Project Alpha.", "user")
var finalResponseText string
for event, err := range runner1.Run(ctx, userID, session1ID, userInput1, agent.RunConfig{}) {
if err != nil {
log.Printf("Agent 1 Error: %v", err)
continue
}
if event.LLMResponse.Content != nil && !event.LLMResponse.Partial {
finalResponseText = strings.Join(textParts(event.LLMResponse.Content), "")
}
}
fmt.Printf("Agent 1 Response: %s\n", finalResponseText)
// Add the completed session to the Memory Service
fmt.Println("\n--- Adding Session 1 to Memory ---")
resp, err := sessionService.Get(ctx, &session.GetRequest{AppName: appName, UserID: userID, SessionID: session1ID})
if err != nil {
log.Fatalf("Failed to get completed session: %v", err)
}
if err := memoryService.AddSessionToMemory(ctx, resp.Session); err != nil {
log.Fatalf("Failed to add session to memory: %v", err)
}
fmt.Println("Session added to memory.")
// --- Scenario 2: Recall the information in a new session using a tool ---
fmt.Println("\n--- Turn 2: Recalling Information ---")
memoryRecallAgent := must(llmagent.New(llmagent.Config{
Name: "MemoryRecallAgent",
Model: must(gemini.NewModel(ctx, modelID, nil)),
Instruction: "Answer the user's question. Use the 'search_past_conversations' tool if the answer might be in past conversations.",
Tools: []tool.Tool{memorySearchTool}, // Give the agent the tool
}))
runner2 := must(runner.New(runner.Config{
Agent: memoryRecallAgent,
AppName: appName,
SessionService: sessionService,
MemoryService: memoryService,
}))
session2ID := "session_recall"
must(sessionService.Create(ctx, &session.CreateRequest{AppName: appName, UserID: userID, SessionID: session2ID}))
userInput2 := genai.NewContentFromText("What is my favorite project?", "user")
var finalResponseText2 string
for event, err := range runner2.Run(ctx, userID, session2ID, userInput2, agent.RunConfig{}) {
if err != nil {
log.Printf("Agent 2 Error: %v", err)
continue
}
if event.LLMResponse.Content != nil && !event.LLMResponse.Partial {
finalResponseText2 = strings.Join(textParts(event.LLMResponse.Content), "")
}
}
fmt.Printf("Agent 2 Response: %s\n", finalResponseText2)
}
import com.google.adk.agents.LlmAgent;
import com.google.adk.agents.RunConfig;
import com.google.adk.events.Event;
import com.google.adk.runner.InMemoryRunner;
import com.google.adk.sessions.Session;
import com.google.adk.tools.LoadMemoryTool;
import com.google.genai.types.Content;
import com.google.genai.types.Part;
import java.util.Optional;
public class MemoryExample {
public static void main(String[] args) {
String appName = "memory_example_app";
String userId = "mem_user";
String model = "gemini-flash-latest";
// An agent that can recall past information using the load_memory tool.
LlmAgent agent =
LlmAgent.builder()
.model(model)
.name("MemoryAgent")
.instruction(
"Answer the user's question. Use the 'load_memory' tool "
+ "if the answer might be in past conversations.")
.tools(new LoadMemoryTool())
.build();
// InMemoryRunner bundles in-memory session and memory services and shares
// them across every session it creates.
InMemoryRunner runner = new InMemoryRunner(agent, appName);
// --- Turn 1: capture information in one session ---
Session captureSession =
runner.sessionService().createSession(appName, userId).blockingGet();
Content statement =
Content.fromParts(Part.fromText("My favorite project is Project Alpha."));
runner
.runAsync(userId, captureSession.id(), statement, RunConfig.builder().build())
.blockingSubscribe();
// Persist the finished session to memory.
Session completedSession =
runner
.sessionService()
.getSession(appName, userId, captureSession.id(), Optional.empty())
.blockingGet();
runner.memoryService().addSessionToMemory(completedSession).blockingAwait();
// --- Turn 2: recall the information in a new session ---
Session recallSession =
runner.sessionService().createSession(appName, userId).blockingGet();
Content question = Content.fromParts(Part.fromText("What is my favorite project?"));
runner
.runAsync(userId, recallSession.id(), question, RunConfig.builder().build())
.blockingForEach(
(Event event) -> {
if (event.finalResponse()) {
event
.content()
.flatMap(Content::parts)
.ifPresent(
parts ->
parts.forEach(part -> part.text().ifPresent(System.out::println)));
}
});
}
}
/**
* This example demonstrates the basic flow using the `InMemoryMemoryService` in Kotlin.
* It shows how to capture information in one session, add it to memory, and recall it in another.
*/
fun main() =
runBlocking {
// --- Constants ---
val appName = "memory_example_app"
val userId = "mem_user"
val model = Gemini(name = "gemini-flash-latest")
// --- Agent Definitions ---
// Agent 1: Simple agent to capture information
val infoCaptureAgent =
LlmAgent(
name = "InfoCaptureAgent",
model = model,
instruction = Instruction("Acknowledge the user's statement."),
)
// Agent 2: Agent that can use memory
val memoryRecallAgent =
LlmAgent(
name = "MemoryRecallAgent",
model = model,
instruction =
Instruction(
"Answer the user's question. Use the 'load_memory' tool " +
"if the answer might be in past conversations.",
),
// Give the agent the tool
tools = listOf(LoadMemoryTool()),
)
// --- Services ---
// Services must be shared across runners to share state and memory
val sessionService = InMemorySessionService()
val memoryService = InMemoryMemoryService()
// --- Turn 1: Capturing Information ---
println("--- Turn 1: Capturing Information ---")
val runner1 =
InMemoryRunner(
agent = infoCaptureAgent,
appName = appName,
sessionService = sessionService,
memoryService = memoryService,
)
val sessionId1 = "session_info"
val userInput1 = Content.fromText(Role.USER, "My favorite project is Project Alpha.")
// Run the agent
runner1
.runAsync(
userId = userId,
sessionId = sessionId1,
newMessage = userInput1,
).collect { event ->
event.content?.parts?.forEach { part ->
if (!part.text.isNullOrBlank()) {
println("Agent Response: ${part.text}")
}
}
}
// Get the completed session using SessionKey
val session1 = sessionService.getSession(SessionKey(appName, userId, sessionId1))
// Add this session's content to the Memory Service
println("\n--- Adding Session 1 to Memory ---")
if (session1 != null) {
memoryService.addSessionToMemory(session1)
println("Session added to memory.")
}
// --- Turn 2: Recalling Information ---
println("\n--- Turn 2: Recalling Information ---")
val runner2 =
InMemoryRunner(
agent = memoryRecallAgent,
appName = appName,
// Reuse the same service
sessionService = sessionService,
// Reuse the same service
memoryService = memoryService,
)
val sessionId2 = "session_recall"
val userInput2 = Content.fromText(Role.USER, "What is my favorite project?")
// Run the second agent
runner2
.runAsync(
userId = userId,
sessionId = sessionId2,
newMessage = userInput2,
).collect { event ->
event.content?.parts?.forEach { part ->
if (!part.text.isNullOrBlank()) {
println("Agent Response: ${part.text}")
}
}
}
}
You can also search memory from within a custom tool by using the tool context.
// Within a tool implementation
async runAsync({ args, toolContext }: RunAsyncToolRequest) {
const query = args['query'] as string;
const response = await toolContext.searchMemory(query);
// process response
return {
memories: response.memories.map(m => m.content.parts?.map(p => p.text).join(' ')).join('\n')
};
}
// memorySearchToolFunc is the implementation of the memory search tool.
// This function demonstrates accessing memory via agent.Context.
func memorySearchToolFunc(tctx agent.Context, args Args) (Result, error) {
fmt.Printf("Tool: Searching memory for query: '%s'\n", args.Query)
// The SearchMemory function is available on the context.
searchResults, err := tctx.SearchMemory(context.Background(), args.Query)
if err != nil {
log.Printf("Error searching memory: %v", err)
return Result{}, fmt.Errorf("failed memory search")
}
var results []string
for _, res := range searchResults.Memories {
if res.Content != nil {
results = append(results, textParts(res.Content)...)
}
}
return Result{Results: results}, nil
}
// Define a tool that can search memory.
var memorySearchTool = must(functiontool.New(
functiontool.Config{
Name: "search_past_conversations",
Description: "Searches past conversations for relevant information.",
},
memorySearchToolFunc,
))
suspend fun searchWithinTool(
context: ToolContext,
args: Map<String, Any>,
): String {
val query = args["query"] as String
val response =
context.invocationContext.memoryService?.searchMemory(
appName = context.invocationContext.session.key.appName,
userId = context.invocationContext.session.key.userId,
query = query,
)
// process response
return response?.memories?.joinToString("\n") {
it.content.parts.joinToString(" ") { p -> p.text ?: "" }
} ?: ""
}
The VertexAiMemoryBankService connects your agent to Memory
Bank,
a fully managed Google Cloud service that provides sophisticated, persistent
memory capabilities for conversational agents.
The service handles two key operations:
add_memory¶Besides generating memories from session history, VertexAiMemoryBankService
also supports direct memory ingestion via the add_memory method. This method
gives you precise control over the facts stored in the Memory Bank.
How it works depends on the enable_consolidation option:
Direct Creation (Default): By default, add_memory calls the underlying
memories.create API. Each MemoryEntry you provide is added as a distinct,
separate memory item.
from google.adk.memory import VertexAiMemoryBankService
from google.adk.memory.memory_entry import MemoryEntry
from google.genai.types import Content, Part
memory_service = VertexAiMemoryBankService(...)
await memory_service.add_memory(
app_name="my-app",
user_id="user-123",
memories=[
MemoryEntry(content=Content(parts=[Part(text="The user's favorite color is blue.")]))
]
)
Creation with Consolidation: If you set enable_consolidation to True
in the custom_metadata, the service uses the memories.generate API. This
setting allows the Memory Bank to intelligently consolidate the new memory
items with existing related memories, preventing redundancy and building a
more coherent knowledge base.
Before you can use this feature, you must have:
Authentication: Ensure your local environment is authenticated to access Google Cloud services. The simplest way is to run:
Environment Variables: The service requires your Google Cloud Project ID and Location. Set them as environment variables:
For more information on connecting to Google Cloud from ADK agents, see Connect to Google Cloud and Agent Platform.
To connect your agent to the Memory Bank, you use the --memory_service_uri
flag when starting the ADK server (adk web or adk api_server). The Uniform
Resource Identifier (URI) must be in the format
agentengine://<agent_engine_id>.
Or, you can configure your agent to use the Memory Bank by manually
instantiating the VertexAiMemoryBankService and passing it to the Runner.
/** Memory Bank keeps LLM-extracted memories in a Vertex AI Agent Engine. */
fun memoryBankRunner(agent: LlmAgent): InMemoryRunner {
val memoryService =
VertexAiMemoryBankService(
project = "PROJECT_ID",
location = "LOCATION",
agentEngineId = "AGENT_ENGINE_ID",
)
return InMemoryRunner(
agent = agent,
appName = "memory_bank_app",
memoryService = memoryService,
)
}
The VertexAiRagMemoryService stores conversations in Knowledge
Engine
and retrieves them by vector similarity. Use it when you already have RAG
infrastructure or want raw transcript retrieval rather than the LLM-extracted
memories produced by Memory Bank. Requires the Agent Platform SDK.
/**
* RAG memory stores whole transcripts in a Knowledge Engine corpus and retrieves them by vector
* similarity.
*/
fun ragMemoryRunner(agent: LlmAgent): InMemoryRunner {
val memoryService =
VertexAiRagMemoryService(
project = "PROJECT_ID",
location = "LOCATION",
// A bare corpus id, NOT a full resource name. Kotlin expands it to
// projects/{project}/locations/{location}/ragCorpora/{id} and rejects an
// already-expanded name -- unlike the Python tab above, which takes the full name.
ragCorpus = "CORPUS_ID",
similarityTopK = 5,
vectorDistanceThreshold = 0.6,
)
return InMemoryRunner(
agent = agent,
appName = "rag_memory_app",
memoryService = memoryService,
)
}
When a memory service is configured, your agent can use a tool or callback to retrieve memories. ADK includes two pre-built tools for retrieving memories:
Example:
To extract memories from your session, you need to call add_session_to_memory.
For example, you can automate this step with a callback:
from google.adk.agents import Agent
from google.adk.tools import preload_memory
async def auto_save_session_to_memory_callback(callback_context):
await callback_context.add_session_to_memory()
agent = Agent(
model=MODEL,
name="Generic_QA_Agent",
instruction="Answer the user's questions",
tools=[preload_memory],
after_agent_callback=auto_save_session_to_memory_callback,
)
import { LlmAgent, PRELOAD_MEMORY, SingleAgentCallback } from '@google/adk';
const autoSaveSessionToMemoryCallback: SingleAgentCallback = async (callbackContext) => {
if (callbackContext.invocationContext.memoryService) {
await callbackContext.invocationContext.memoryService.addSessionToMemory(
callbackContext.invocationContext.session
);
}
};
const agent = new LlmAgent({
model: MODEL,
name: "Generic_QA_Agent",
instruction: "Answer the user's questions",
tools: [PRELOAD_MEMORY],
afterAgentCallback: autoSaveSessionToMemoryCallback,
});
import (
"context"
"google.golang.org/adk/v2/agent"
"google.golang.org/adk/v2/agent/llmagent"
"google.golang.org/adk/v2/session"
"google.golang.org/adk/v2/tool"
"google.golang.org/adk/v2/tool/loadmemorytool"
)
func autoSaveSessionToMemoryCallback(ctx agent.CallbackContext, s session.Session) (*genai.Content, error) {
if err := ctx.Memory().AddSessionToMemory(context.Background(), s); err != nil {
return nil, err
}
return nil, nil
}
agent, _ := llmagent.New(llmagent.Config{
Model: model,
Name: "Generic_QA_Agent",
Instruction: "Answer the user's questions",
Tools: []tool.Tool{loadmemorytool.New()},
AfterAgentCallbacks: []agent.AfterAgentCallback{autoSaveSessionToMemoryCallback},
})
suspend fun autoSaveSessionToMemoryCallback(
context: CallbackContext,
): CallbackChoice<Unit, Content> {
context.addSessionToMemory()
return CallbackChoice.Continue(Unit)
}
fun agentWithCallback(model: Gemini) {
val agent =
LlmAgent(
model = model,
name = "Generic_QA_Agent",
instruction = Instruction("Answer the user's questions"),
tools = listOf(PreloadMemoryTool()),
afterAgentCallbacks = listOf(AfterAgentCallback(::autoSaveSessionToMemoryCallback)),
)
}
The CallbackContext.addSessionToMemory method is the default behavior for
memory and saves the whole session of your agent. When you want finer control,
CallbackContext also offers two more methods: addEventsToMemory, for a
chosen subset of events, and addMemory, for facts you construct yourself.
Both accept optional customMetadata, and both fill in the app, user and
session from the current invocation.
/**
* Saves a chosen set of events rather than the whole session, tagged so they can
* be filtered later. The events come from the caller: CallbackContext does not
* expose the session.
*/
suspend fun saveEventsToMemory(
context: CallbackContext,
events: List<Event>,
) {
// appName, userId and sessionId are taken from the invocation. Throws
// IllegalStateException if the runner has no memory service configured.
context.addEventsToMemory(events, customMetadata = mapOf("source" to "turn_callback"))
}
/** Writes an explicit fact, instead of letting the service derive one from events. */
suspend fun rememberPreferenceCallback(context: CallbackContext): CallbackChoice<Unit, Content> {
val preference = MemoryEntry(content = Content.fromText(Role.USER, "Prefers window seats."))
context.addMemory(listOf(preference))
return CallbackChoice.Continue(Unit)
}
All three throw IllegalStateException if the runner has no memory service
configured, so they fail at run time rather than at compile time.
Memory services extended from BaseMemoryService support adding sessions and
events to agent memory, including custom metadata. Use the
add_session_to_memory and add_events_to_memory methods of memory services
such as InMemoryMemoryService to amend memory data, as shown in the
following code example:
import asyncio
from google.adk.memory import InMemoryMemoryService
# Assume my_memory_service is an instance of InMemoryMemoryService
# and my_latest_events is a list of new adk.Event objects from the latest turn.
my_latest_events = [...]
async def update_incremental_memory(my_memory_service, my_latest_events):
# Example 1: Basic incremental update
await my_memory_service.add_events_to_memory(
app_name="my-app",
user_id="my-user",
events=my_latest_events,
session_id="my-optional-session-id"
)
# Example 2: Incremental update with Custom Metadata
await my_memory_service.add_events_to_memory(
app_name="my-app",
user_id="my-user",
events=my_latest_events,
session_id="my-optional-session-id",
custom_metadata={
"my_custom_key": "my_custom_value"
}
)
async def update_session_memory(my_memory_service, my_completed_session):
# Example 3: Applying custom metadata to a full session
await my_memory_service.add_session_to_memory(
session=my_completed_session,
custom_metadata={
"category": "user_preference"
}
)
The memory workflow includes the following steps:
Session,
managed by a SessionService. During this interaction, events are recorded
and session state may be updated.memory_service.add_session_to_memory(session). This action extracts key
data and persists it to your long-term knowledge store, such as the Agent
Runtime Memory Bank.load_memory tool, recognizes the need for past
context. It calls the tool, providing a search query (e.g., "discussion
project X last week").memory_service.search_memory(app_name=..., user_id=..., query=...).MemoryService searches its store, using keyword
matching or semantic search, and returns matching snippets as a
SearchMemoryResponse containing a list of MemoryEntry objects, each
holding content, and all optional: id, author, timestamp, and
custom_metadata.adk web, adk
api_server) is designed to be configured with one memory service at a time
via the --memory_service_uri flag. That single service is wired into the
runner and exposed through tool_context.search_memory() and
callback_context.search_memory().BaseMemoryService and consult it from a custom tool, which already has a
ToolContext for the framework-configured service.For example, your agent can use the framework-configured InMemoryMemoryService
for conversation history and manually instantiate a second service, a
VertexAiMemoryBankService, a VertexAiRagMemoryService over a docs corpus, or
any other BaseMemoryService implementation, for a separate knowledge base.
from google.adk.agents import Agent
from google.adk.memory import InMemoryMemoryService
from google.adk.tools import ToolContext
# Second memory service for docs lookup; could be any BaseMemoryService.
docs_memory = InMemoryMemoryService()
async def search_all_memory(query: str, tool_context: ToolContext) -> dict:
"""Search both the conversational memory and the docs corpus."""
conversational = await tool_context.search_memory(query)
docs = await docs_memory.search_memory(
app_name="docs", user_id="shared", query=query
)
return {
"from_conversations": [
part.text
for entry in conversational.memories
for part in (entry.content.parts or [])
if part.text
],
"from_docs": [
part.text
for entry in docs.memories
for part in (entry.content.parts or [])
if part.text
],
}
agent = Agent(
model="gemini-flash-latest",
name="multi_memory_agent",
instruction=(
"Answer questions using both your conversation history and the "
"docs knowledge base. Use the search_all_memory tool."
),
tools=[search_all_memory],
)
/**
* Example of using two memory services in Kotlin.
*/
suspend fun searchAllMemory(
toolContext: ToolContext,
query: String,
docsMemory: InMemoryMemoryService,
): Map<String, List<String>> {
// Search the conversational memory (configured in the runner)
val conversational =
toolContext.invocationContext.memoryService?.searchMemory(
appName = toolContext.invocationContext.session.key.appName,
userId = toolContext.invocationContext.session.key.userId,
query = query,
)
// Search a separate docs knowledge base
val docs =
docsMemory.searchMemory(
appName = "docs",
userId = "shared",
query = query,
)
return mapOf(
"from_conversations" to
(
conversational?.memories?.map {
it.content.parts.joinToString(" ") { p -> p.text ?: "" }
} ?: emptyList()
),
"from_docs" to
docs.memories.map {
it.content.parts.joinToString(" ") { p -> p.text ?: "" }
},
)
}
fun multiMemoryAgent(model: Gemini) {
// docs_memory could be any MemoryService implementation
val docsMemory = InMemoryMemoryService()
val agent =
LlmAgent(
model = model,
name = "multi_memory_agent",
instruction =
Instruction(
"Answer questions using both your conversation history and the " +
"docs knowledge base. Use the search_all_memory tool.",
),
// In a real app, you'd wrap searchAllMemory in a @Tool annotated class
// and pass docsMemory to its constructor.
)
}
| Web Proxy Viewer | New URL | Original Page |