记忆:使用 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)查询知识存储并根据搜索查询检索相关片段。
操作 2 和 3 是可选的——基类的 add_events_to_memory 和 add_memory 实现会抛出 NotImplementedError,因此在依赖它们之前请检查你的具体服务实现。
选择合适的记忆服务¶
Python ADK 内置了三个 MemoryService 实现。请使用下表决定哪个最适合你的智能体。
| 功能 | InMemoryMemoryService | VertexAiMemoryBankService | VertexAiRagMemoryService |
|---|---|---|---|
| 持久性 | 无(重启后数据丢失) | 是(由 Agent Platform 管理) | 是(存储在知识引擎中) |
| 主要用途 | 原型设计、本地开发和简单测试 | 从用户对话中构建有意义的、不断演进的记忆 | 对整个对话语料库进行向量搜索检索,或与其他 RAG 索引内容一起使用 |
| 记忆提取 | 存储完整对话 | 从对话中提取有意义的信息并与现有记忆合并(由 LLM 驱动) | 存储完整对话,由知识引擎索引 |
| 搜索能力 | 基本关键词匹配 | 高级语义搜索 | 基于知识引擎的向量相似度搜索 |
| 设置复杂度 | 无。这是默认实现。 | 低。需要在 Agent Platform 上有一个 Agent Runtime 实例。 | 中等。需要知识引擎。 |
| 依赖项 | 无 | Google Cloud 项目、Agent Platform API | Google Cloud 项目、知识引擎、Agent Platform SDK(可选安装) |
| 使用场景 | 当你想在原型设计中跨多个会话的聊天历史进行搜索时 | 当你想让智能体记住并从过去的交互中学习时 | 当你已有 RAG 基础设施或想要检索原始对话记录时 |
VertexAiRagMemoryService 仅在安装了 Agent Platform SDK 时才会从 google.adk.memory 中导出。记忆库和基于 RAG 的记忆将在下方的记忆库和RAG 记忆中说明。
内存记忆 (In-Memory Memory)¶
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/agent"
"google.golang.org/adk/agent/llmagent"
"google.golang.org/adk/memory"
"google.golang.org/adk/model/gemini"
"google.golang.org/adk/runner"
"google.golang.org/adk/session"
"google.golang.org/adk/tool"
"google.golang.org/adk/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 tool.Context.
func memorySearchToolFunc(tctx tool.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 tool.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)
}
package com.google.adk.examples.sessions;
import com.google.adk.agents.LlmAgent;
import com.google.adk.memory.InMemoryMemoryService;
import com.google.adk.runner.Runner;
import com.google.adk.sessions.InMemorySessionService;
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 {
private static final String APP_NAME = "memory_example_app";
private static final String USER_ID = "mem_user";
private static final String MODEL = "gemini-flash-latest";
public static void main(String[] args) {
// 服务
InMemorySessionService sessionService = new InMemorySessionService();
InMemoryMemoryService memoryService = new InMemoryMemoryService();
// 智能体 1:捕获信息
LlmAgent infoCaptureAgent = new LlmAgent.Builder()
.model(MODEL)
.name("InfoCaptureAgent")
.instruction("确认用户的陈述。")
.build();
// 智能体 2:回忆记忆
LlmAgent memoryRecallAgent = new LlmAgent.Builder()
.model(MODEL)
.name("MemoryRecallAgent")
.instruction("回答用户的问题。如果答案可能在过去的对话中,请使用 'load_memory' 工具。")
.tools(new LoadMemoryTool())
.build();
// 第 1 轮
System.out.println("--- 第 1 轮:捕获信息 ---");
Runner runner1 = new Runner.Builder()
.agent(infoCaptureAgent)
.appName(APP_NAME)
.sessionService(sessionService)
.memoryService(memoryService)
.build();
String session1Id = "session_info";
// 创建会话
sessionService.createSession(APP_NAME, USER_ID, null, session1Id).blockingGet();
Content userInput1 = Content.fromParts(Part.fromText("我最喜欢的项目是 Alpha 项目。"));
runner1.runAsync(USER_ID, session1Id, userInput1)
.blockingForEach(event -> {
if (event.finalResponse() && event.content().isPresent()) {
System.out.println("智能体 1 响应: " + event.content().get().parts().get(0).text().get());
}
});
// 添加到记忆
System.out.println("\n--- 将会话 1 添加到记忆 ---");
Session completedSession1 = sessionService.getSession(APP_NAME, USER_ID, session1Id, Optional.empty()).blockingGet();
memoryService.addSessionToMemory(completedSession1).blockingAwait();
System.out.println("会话已添加到记忆。");
// 第 2 轮
System.out.println("\n--- 第 2 轮:回忆信息 ---");
Runner runner2 = new Runner.Builder()
.agent(memoryRecallAgent)
.appName(APP_NAME)
.sessionService(sessionService)
.memoryService(memoryService)
.build();
String session2Id = "session_recall";
sessionService.createSession(APP_NAME, USER_ID, null, session2Id).blockingGet();
Content userInput2 = Content.fromParts(Part.fromText("我最喜欢的项目是什么?"));
runner2.runAsync(USER_ID, session2Id, userInput2)
.blockingForEach(event -> {
if (event.finalResponse() && event.content().isPresent()) {
System.out.println("智能体 2 响应: " + event.content().get().parts().get(0).text().get());
}
});
}
}
在工具中搜索记忆¶
你也可以通过使用工具上下文在自定义工具中搜索记忆。
// memorySearchToolFunc is the implementation of the memory search tool.
// This function demonstrates accessing memory via tool.Context.
func memorySearchToolFunc(tctx tool.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,
))
记忆库 (Memory Bank)¶
VertexAiMemoryBankService 将你的智能体连接到记忆库,这是一个完全托管的 Google Cloud 服务,为对话式智能体提供复杂的持久记忆功能。
工作原理¶
该服务处理两个关键操作:
- 生成记忆:在对话结束时,你可以将会话的事件发送到记忆库,它会智能地处理并将信息作为“记忆”存储起来。
- 检索记忆:你的智能体代码可以针对记忆库发出搜索查询,以检索过去对话中的相关记忆。
先决条件¶
- 一个 Google Cloud 项目: 已启用 Agent Platform API。
- 一个 Agent Runtime: 你需要在 Agent Platform 上创建一个 Agent Runtime。你不需要将智能体部署到 Agent Runtime 即可使用记忆库。这将为你提供配置所需的 Agent Runtime ID。
- 身份验证: 确保你的本地环境已通过身份验证以访问 Google Cloud 服务。最简单的方法是运行:
- 环境变量:该服务需要你的 Google Cloud 项目 ID 和位置。请将它们设置为环境变量:
配置¶
要将你的智能体连接到记忆库,你需要在启动 ADK 服务器(adk web 或 adk api_server)时使用 --memory_service_uri 标志。URI 必须采用 agentengine://<agent_engine_id> 格式。
或者,你可以通过手动实例化 VertexAiMemoryBankService 并将其传递给 Runner 来配置智能体使用记忆库。
```python from google import adk from google.adk.memory import VertexAiMemoryBankService
agent_engine_id = agent_engine.api_resource.name.split("/")[-1]
memory_service = VertexAiMemoryBankService( project="PROJECT_ID", location="LOCATION", agent_engine_id=agent_engine_id )
RAG 记忆¶
VertexAiRagMemoryService 将对话存储在知识引擎中,并通过向量相似度进行检索。当你已有 RAG 基础设施,或需要原始对话记录检索而非记忆库生成的 LLM 提取记忆时,请使用此服务。需要 Agent Platform SDK。
在你的智能体中使用记忆¶
当配置了记忆服务时,你的智能体可以使用工具或回调来检索记忆。ADK 包含两个用于检索记忆的预置工具:
PreloadMemory: 在每轮开始时始终检索记忆(类似于回调)。LoadMemory: 仅当你的智能体认为检索记忆有帮助时才进行检索。
示例:
要从你的会话中提取记忆,你需要调用 add_session_to_memory。例如,你可以通过回调自动执行此操作:
from google.adk.agents import Agent
from google import adk
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=[adk.tools.preload_memory_tool.PreloadMemoryTool()],
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/agent"
"google.golang.org/adk/agent/llmagent"
"google.golang.org/adk/session"
"google.golang.org/adk/tool"
"google.golang.org/adk/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},
})
高级概念¶
记忆在实践中的工作原理¶
记忆工作流在内部涉及以下步骤:
- 会话交互: 用户通过
SessionService管理的Session与智能体进行交互。事件被添加,状态可能被更新。 - 摄入到记忆: 在某个时刻(通常是当会话被认为已完成或产生了重要信息时),你的应用程序调用
memory_service.add_session_to_memory(session)。这会从会话事件中提取相关信息,并将其添加到长期知识存储中(内存字典或 Agent Runtime 记忆库)。 - 后续查询: 在不同(或相同)的会话中,用户可能会提出需要过去上下文的问题(例如,"我们上周关于项目 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],
)