记忆:通过 MemoryService 实现长期知识存储¶
虽然 Session 跟踪单次对话的历史记录(events)和临时数据(state),但智能体可能需要从过去的交互中回忆信息。这就是长期知识和 MemoryService 的用武之地。可以这样理解:
Session/State: 这是你在一次特定对话中的短期记忆。- 长期知识(
MemoryService): 这是一个可搜索的档案库或知识库,智能体可以从中查询信息,可能包含来自多次过去对话或其他来源的内容。
MemoryService 的作用¶
BaseMemoryService(或 Go 中的 Service)定义了管理可搜索的长期知识存储的接口。它支持以下操作:
- 摄入信息:
add_session_to_memory:接收一个已完成的Session,并将相关信息添加到长期知识存储中。这种方式非常适合自动捕获对话的核心内容。add_events_to_memory:追加事件增量(例如最新的对话轮次),无需重新摄入整个会话。当你需要在长时间运行的会话中途写入记忆时非常有用。add_memory:将显式的MemoryEntry对象直接添加到记忆中。此方法提供精细控制,适用于从其他来源注入特定事实。
- 搜索信息(
search_memory): 允许智能体(通常通过Tool)查询知识存储,并根据搜索查询检索相关片段或上下文。
add_events_to_memory 和 add_memory 是可选的,并非每个服务都实现了它们,因此在依赖它们之前,请确认你选择的服务是否支持。
选择合适的记忆服务¶
Python ADK 提供了三种 MemoryService 实现。请参考下表决定哪种最适合你的智能体。
| 功能 | InMemoryMemoryService | VertexAiMemoryBankService | VertexAiRagMemoryService |
|---|---|---|---|
| 持久性 | 无,重启后数据丢失 | 有,由 Agent Platform 管理 | 有,存储在 Knowledge Engine 中 |
| 主要用途 | 原型开发、本地开发和简单测试。 | 从用户对话中构建有意义的、持续演化的记忆。 | 对完整对话语料库进行向量搜索检索,或与其他 RAG 索引内容一起使用。 |
| 记忆提取 | 存储完整对话 | 从对话中提取有意义的信息,并由 LLM 驱动与现有记忆进行整合 | 存储完整对话,由 Knowledge Engine 建立索引。 |
| 搜索能力 | 基本关键字匹配。 | 高级语义搜索。 | 基于 Knowledge Engine 的向量相似度搜索。 |
| 设置复杂度 | 无,这是默认选项。 | 低。需要在 Agent Platform 上创建一个 Agent Runtime 实例。 | 中等。需要 Knowledge Engine。 |
| 依赖项 | 无。 | Google Cloud Project、Agent Platform API | Google Cloud Project、Knowledge Engine、Agent Platform SDK(可选安装)。 |
| 使用场景 | 当你想要在原型开发阶段跨多个会话的聊天历史进行搜索时。 | 当你希望智能体记住并从过去的交互中学习时。 | 当你已有 RAG 基础设施或想要检索原始对话记录时。 |
VertexAiRagMemoryService 仅在安装了 Agent Platform SDK 后才会从 google.adk.memory 中导出。Memory Bank 和基于 RAG 的记忆在下方的记忆库和 RAG 记忆中进行了说明。
InMemoryMemoryService¶
InMemoryMemoryService 将会话信息存储在应用程序的内存中,并使用基本关键字匹配进行搜索。它无需任何设置,最适合原型开发和不需要持久性的简单测试场景。
示例:添加和搜索记忆
此示例演示了使用 InMemoryMemoryService 的基本流程,以保持简洁。
import asyncio
from google.adk.agents import LlmAgent
from google.adk.sessions import InMemorySessionService, Session
from google.adk.memory import InMemoryMemoryService # 导入 MemoryService
from google.adk.runners import Runner
from google.adk.tools import load_memory # 查询记忆的工具
from google.genai.types import Content, Part
# --- 常量 ---
APP_NAME = "memory_example_app"
USER_ID = "mem_user"
MODEL = "gemini-flash-latest" # 使用有效的模型
# --- 智能体定义 ---
# 智能体 1:简单的信息捕获智能体
info_capture_agent = LlmAgent(
model=MODEL,
name="InfoCaptureAgent",
instruction="确认用户的陈述。",
)
# 智能体 2:可以使用记忆的智能体
memory_recall_agent = LlmAgent(
model=MODEL,
name="MemoryRecallAgent",
instruction="回答用户的问题。如果答案可能在过去的对话中,请使用 'load_memory' 工具",
tools=[load_memory] # 为智能体提供工具
)
# --- 服务 ---
# 服务必须在运行器之间共享以共享状态和记忆
session_service = InMemorySessionService()
memory_service = InMemoryMemoryService() # 用于演示的内存服务
async def run_scenario():
# --- 场景 ---
# 第 1 轮:在会话中捕获一些信息
print("--- 第 1 轮:捕获信息 ---")
runner1 = Runner(
# 从信息捕获智能体开始
agent=info_capture_agent,
app_name=APP_NAME,
session_service=session_service,
memory_service=memory_service # 为运行器提供记忆服务
)
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="我最喜欢的项目是 Alpha 项目。")], role="user")
# 运行智能体
final_response_text = "(无最终响应)"
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"智能体 1 响应: {final_response_text}")
# 获取完成的会话
completed_session1 = await runner1.session_service.get_session(app_name=APP_NAME, user_id=USER_ID, session_id=session1_id)
# 将此会话的内容添加到记忆服务
print("\n--- 将会话 1 添加到记忆 ---")
await memory_service.add_session_to_memory(completed_session1)
print("会话已添加到记忆中。")
# 第 2 轮:在新会话中回忆信息
print("\n--- 第 2 轮:回忆信息 ---")
runner2 = Runner(
# 使用第二个智能体,它有记忆工具
agent=memory_recall_agent,
app_name=APP_NAME,
session_service=session_service, # 重用相同的服务
memory_service=memory_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="我最喜欢的项目是什么?")], role="user")
# 运行第二个智能体
final_response_text_2 = "(无最终响应)"
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"智能体 2 响应: {final_response_text_2}")
# 要运行此示例,你可以执行以下代码:
# asyncio.run(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)));
}
});
}
}
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.",
),
tools = listOf(LoadMemoryTool()), // Give the agent the tool
)
// --- 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,
sessionService = sessionService, // Reuse the same service
memoryService = memoryService, // Reuse the same service
)
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}")
}
}
}
}
在工具中搜索记忆¶
你也可以在自定义工具中通过工具上下文来搜索记忆。
// 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 ?: "" }
} ?: ""
}
记忆库¶
VertexAiMemoryBankService 将你的智能体连接到 Memory Bank,这是一个全托管的 Google Cloud 服务,为对话式智能体提供复杂且持久的记忆能力。
工作原理¶
该服务处理两个关键操作:
- 生成记忆: 在对话结束时,你可以将会话的事件发送到 Memory Bank,它会智能地处理并将信息存储为"记忆"。
- 检索记忆: 你的智能体代码可以向 Memory Bank 发出搜索查询,以检索过去对话中的相关记忆。
使用 add_memory 直接摄入记忆¶
除了从会话历史中生成记忆外,VertexAiMemoryBankService 还支持通过 add_memory 方法直接摄入记忆。此方法让你可以精确控制存储在 Memory Bank 中的事实。
其工作方式取决于 enable_consolidation 选项:
-
直接创建(默认): 默认情况下,
add_memory调用底层的memories.createAPI。你提供的每个MemoryEntry都会作为一个独立的记忆条目被添加。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.")])) ] ) -
带整合的创建: 如果你在
custom_metadata中将enable_consolidation设置为True,服务将使用memories.generateAPI。此设置允许 Memory Bank 智能地将新的记忆条目与现有的相关记忆进行整合,防止冗余并构建更连贯的知识库。
先决条件¶
在使用此功能之前,你需要具备以下条件:
- Google Cloud 项目: 已启用 Agent Platform API。
- Agent Runtime: 你需要在 Agent Platform 上创建一个 Agent Runtime。你不需要将智能体部署到 Agent Runtime 就可以使用 Memory Bank。此设置将为你提供配置所需的 Agent Runtime ID。
-
身份验证: 确保你的本地环境已通过身份验证以访问 Google Cloud 服务。最简单的方式是运行:
-
环境变量: 该服务需要你的 Google Cloud 项目 ID 和位置。将它们设置为环境变量:
有关从 ADK 智能体连接到 Google Cloud 的更多信息,请参阅连接到 Google Cloud 和 Agent Platform。
配置¶
要将你的智能体连接到 Memory Bank,请在启动 ADK 服务器(adk web 或 adk api_server)时使用 --memory_service_uri 标志。统一资源标识符(URI)的格式必须为 agentengine://<agent_engine_id>。
或者,你可以通过手动实例化 VertexAiMemoryBankService 并将其传递给 Runner 来配置你的智能体使用 Memory Bank。
RAG 记忆¶
VertexAiRagMemoryService 将对话存储在 Knowledge Engine 中,并通过向量相似度进行检索。当你已有 RAG 基础设施或需要原始对话记录检索而非 Memory Bank 提供的 LLM 提取记忆时使用。需要 Agent Platform SDK。
在智能体中使用记忆¶
当配置了记忆服务时,你的智能体可以使用工具或回调来检索记忆。ADK 包含两个用于检索记忆的预置工具:
PreloadMemory: 在每轮开始时始终检索记忆(类似于回调)。LoadMemory: 仅当你的智能体认为检索记忆有帮助时才进行检索。
示例:
要从会话中提取记忆,你需要调用 add_session_to_memory。例如,你可以通过回调自动执行此步骤:
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="回答用户的问题",
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: "回答用户的问题",
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: "回答用户的问题",
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)),
)
}
高级概念¶
记忆在实际中如何工作¶
记忆工作流包括以下步骤:
- 会话交互: 用户通过由
SessionService管理的Session与智能体进行交互。在此交互过程中,事件被记录,会话状态可能会更新。 - 摄入记忆: 当会话结束或捕获到重要信息时,你的应用程序调用
memory_service.add_session_to_memory(session)。此操作提取关键数据并将其持久化到长期知识存储中,例如 Agent Runtime Memory Bank。 - 后续查询: 在不同的会话或同一会话中,你可能提出一个需要过去上下文的问题,例如"我们上周讨论了关于项目 X 的什么内容?"。
- 智能体使用记忆工具: 配备了记忆检索工具的智能体(例如内置的
load_memory工具)识别到需要过去上下文。它调用该工具,提供搜索查询(例如"上周讨论项目 X")。 - 执行搜索: 该工具在内部调用
memory_service.search_memory(app_name=..., user_id=..., query=...)。 - 返回结果:
MemoryService搜索其存储,使用关键字匹配或语义搜索,并将匹配的片段作为SearchMemoryResponse返回,其中包含MemoryEntry对象列表,每个对象持有content,以及所有可选的:author、timestamp和custom_metadata。 - 智能体使用结果: 该工具将这些结果返回给智能体,通常作为上下文或函数响应的一部分。智能体然后可以使用这些检索到的信息来制定对用户的最终回答。
智能体可以访问多个记忆服务吗?¶
- 通过标准配置:不行。 框架(
adk web、adk api_server)设计为一次配置一个记忆服务,通过--memory_service_uri标志。该单一服务被连接到运行器,并通过tool_context.search_memory()和callback_context.search_memory()暴露。 - 在智能体代码中:可以。 你可以实例化第二个
BaseMemoryService并从自定义工具中调用它,该工具已经拥有用于框架配置服务的ToolContext。
例如,你的智能体可以使用框架配置的 InMemoryMemoryService 来处理对话历史,并手动实例化第二个服务,如 VertexAiMemoryBankService、VertexAiRagMemoryService(用于文档语料库)或任何其他 BaseMemoryService 实现,用于独立的知识库。
示例:使用两个记忆服务¶
from google.adk.agents import Agent
from google.adk.memory import InMemoryMemoryService
from google.adk.tools import ToolContext
# 用于文档查找的第二个记忆服务;可以是任何 BaseMemoryService。
docs_memory = InMemoryMemoryService()
async def search_all_memory(query: str, tool_context: ToolContext) -> dict:
"""同时搜索对话记忆和文档语料库。"""
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=(
"使用对话历史和文档知识库回答问题。使用 search_all_memory 工具。"
),
tools=[search_all_memory],
)