记忆:通过MemoryService实现长期知识存储
我们已经了解了Session如何跟踪单个持续对话的历史记录(events)和临时数据(state)。但如果智能体需要回忆过去对话的信息或访问外部知识库呢?这正是长期知识和MemoryService概念发挥作用的地方。
可以这样理解:
Session/State: 就像你在某次特定聊天中的短期记忆。- 长期知识(
MemoryService):就像一个可搜索的档案库或知识库,智能体可以随时查阅,可能包含来自过去多次对话或其他来源的信息。
MemoryService 角色
BaseMemoryService 定义了管理这个可搜索的长期知识存储的接口。其主要职责包括:
- 信息摄取 (
add_session_to_memory): 获取一个(通常已完成的)Session的内容,并将相关信息添加到长期知识存储中。 - 信息搜索 (
search_memory): 允许智能体(通常通过Tool)查询知识库,并根据搜索查询检索相关片段或上下文。
MemoryService 实现
ADK提供了实现这种长期知识存储的不同方法:
-
InMemoryMemoryService- 工作原理: 将会话信息存储在应用程序内存中,并为搜索执行基本的关键词匹配。
- 持久性: 无。如果应用程序重启,所有存储的知识都将丢失。
- 要求:无需额外条件。
- 最佳适用场景:原型设计、简单测试、仅需基础关键词召回且无需持久化的场景。
-
VertexAiRagMemoryService- 工作原理: 利用Google Cloud的Vertex AI RAG(检索增强生成)服务。它将会话数据摄取到指定的RAG语料库中,并利用强大的语义搜索能力进行检索。
- 持久性: 是的。知识会持久存储在配置的Vertex AI RAG Corpus中。
- 要求: 一个Google Cloud项目、适当的权限、必要的SDK(
pip install google-adk[vertexai])以及预先配置好的Vertex AI RAG Corpus资源名称/ID。 - 最佳适用场景:需要可扩展、持久且语义相关知识检索的生产应用程序,特别是部署在Google Cloud上的情况。
# Requires: pip install google-adk[vertexai] # Plus GCP setup, RAG Corpus, and authentication from google.adk.memory import VertexAiRagMemoryService # The RAG Corpus name or ID RAG_CORPUS_RESOURCE_NAME = "projects/your-gcp-project-id/locations/us-central1/ragCorpora/your-corpus-id" # Optional configuration for retrieval SIMILARITY_TOP_K = 5 VECTOR_DISTANCE_THRESHOLD = 0.7 memory_service = VertexAiRagMemoryService( rag_corpus=RAG_CORPUS_RESOURCE_NAME, similarity_top_k=SIMILARITY_TOP_K, vector_distance_threshold=VECTOR_DISTANCE_THRESHOLD )
内存在实际中的工作原理
典型的工作流程包括以下步骤:
- 会话交互:用户通过
Session与智能体进行交互,该会话由SessionService管理。可以添加事件,并可能更新状态。 - 存入记忆:在某个时刻(通常是当会话被认为已完成或产生了重要信息时),您的应用程序会调用
memory_service.add_session_to_memory(session)。这将从会话事件中提取相关信息,并将其添加到长期知识存储(内存字典或RAG语料库)中。 - 后续查询:在不同(或相同)的会话中,用户可能会提出需要历史上下文的问题(例如:"上周我们关于项目X讨论了什么?")。
- 智能体使用记忆工具: 配备记忆检索工具(如内置的
load_memory工具)的智能体会识别出需要过去上下文的情况。它会调用该工具,提供一个搜索查询(例如"上周讨论项目X")。 - 搜索执行: 该工具内部调用
memory_service.search_memory(app_name, user_id, query)。 - 返回结果:
MemoryService搜索其存储(使用关键词匹配或语义搜索)并返回相关片段作为SearchMemoryResponse,其中包含一个MemoryResult对象列表(每个对象可能包含来自相关过去会话的事件)。 - 智能体使用结果: 该工具将这些结果返回给智能体,通常作为上下文或函数响应的一部分。智能体随后可以利用检索到的信息来构建最终给用户的答案。
示例:添加和搜索记忆
此示例演示了使用InMemory服务的基本流程,以简化操作。
Full Code
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-2.0-flash" # 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.",
# output_key="captured_info" # Could optionally save to state too
)
# 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 and Runner ---
session_service = InMemorySessionService()
memory_service = InMemoryMemoryService() # Use in-memory for demo
runner = 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
)
# --- Scenario ---
# Turn 1: Capture some information in a session
print("--- Turn 1: Capturing Information ---")
session1_id = "session_info"
session1 = session_service.create_session(APP_NAME, USER_ID, session1_id)
user_input1 = Content(parts=[Part(text="My favorite project is Project Alpha.")])
# Run the agent
final_response_text = "(No final response)"
for event in runner.run(USER_ID, session1_id, 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 = session_service.get_session(APP_NAME, USER_ID, session1_id)
# Add this session's content to the Memory Service
print("\n--- Adding Session 1 to Memory ---")
memory_service.add_session_to_memory(completed_session1)
print("Session added to memory.")
# Turn 2: In a *new* (or same) session, ask a question requiring memory
print("\n--- Turn 2: Recalling Information ---")
session2_id = "session_recall" # Can be same or different session ID
session2 = session_service.create_session(APP_NAME, USER_ID, session2_id)
# Switch runner to the recall agent
runner.agent = memory_recall_agent
user_input2 = Content(parts=[Part(text="What is my favorite project?")])
# Run the recall agent
print("Running MemoryRecallAgent...")
final_response_text_2 = "(No final response)"
for event in runner.run(USER_ID, session2_id, user_input2):
print(f" Event: {event.author} - Type: {'Text' if event.content and event.content.parts and event.content.parts[0].text else ''}"
f"{'FuncCall' if event.get_function_calls() else ''}"
f"{'FuncResp' if event.get_function_responses() else ''}")
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 Final Response: {final_response_text_2}")
break # Stop after final response
# Expected Event Sequence for Turn 2:
# 1. User sends "What is my favorite project?"
# 2. Agent (LLM) decides to call `load_memory` tool with a query like "favorite project".
# 3. Runner executes the `load_memory` tool, which calls `memory_service.search_memory`.
# 4. `InMemoryMemoryService` finds the relevant text ("My favorite project is Project Alpha.") from session1.
# 5. Tool returns this text in a FunctionResponse event.
# 6. Agent (LLM) receives the function response, processes the retrieved text.
# 7. Agent generates the final answer (e.g., "Your favorite project is Project Alpha.").