Skip to content

記憶體:使用 MemoryService 實現長期知識

python_only

我們已經了解 Session 如何追蹤單一進行中對話的歷史紀錄(events)與暫存資料(state)。但如果 agent 需要回憶過去對話中的資訊,或是存取外部知識庫,該怎麼辦?這時就需要長期知識(Long-Term Knowledge)以及MemoryService 的概念。

可以這樣理解:

  • Session / State 就像你在單一聊天中的短期記憶。
  • 長期知識(Long-Term Knowledge,MemoryService): 就像 agent 可以查詢的可搜尋檔案庫或知識圖書館,裡面可能包含許多過去對話或其他來源的資訊。

MemoryService 的角色

BaseMemoryService 定義了管理這個可搜尋長期知識庫的介面。其主要職責包括:

  1. 資訊擷取(add_session_to_memory): 將(通常已完成的)Session 內容擷取並加入長期知識庫中。
  2. 資訊搜尋(search_memory): 允許 agent(通常透過 Tool)查詢知識庫,根據搜尋查詢取得相關片段或上下文。

選擇合適的記憶體服務

Agent Development Kit (ADK) 提供兩種不同的 MemoryService 實作,分別適用於不同的使用情境。請參考下表,選擇最適合你 agent 的方案。

功能 InMemoryMemoryService [全新!] VertexAiMemoryBankService
持久性 無(重啟後資料會遺失) 有(由 Vertex AI 管理)
主要使用情境 原型開發、本機開發與簡單測試 從使用者對話中建立有意義、可演進的記憶
記憶體擷取 儲存完整對話內容 從對話中擷取有意義的資訊,並與現有記憶整合(由大型語言模型 (LLM) 驅動)
搜尋能力 基本關鍵字比對 進階語意搜尋
設定複雜度 無,預設即啟用 低,需要在 Vertex AI 上有 Agent Engine
相依套件 Google Cloud 專案、Vertex AI API
適用時機 需要跨多個 session 聊天記錄進行搜尋、原型開發時 希望 agent 能記住並學習過去互動內容時

In-Memory 記憶體

InMemoryMemoryService 會將 session 資訊儲存在應用程式的記憶體中,並以基本關鍵字比對方式進行搜尋。它不需要任何設定,最適合用於不需持久化的原型開發與簡單測試情境。

from google.adk.memory import InMemoryMemoryService
memory_service = InMemoryMemoryService()

範例:新增與搜尋記憶體

本範例為簡化說明,展示使用 InMemoryMemoryService 的基本流程。

完整程式碼
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.",
)

# 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()

Vertex AI Memory Bank

VertexAiMemoryBankService 讓你的 agent 能夠連接到 Vertex AI Memory Bank,這是一項由 Google Cloud 完全託管的服務,為對話型 agent 提供先進且持久的記憶體功能。

運作方式

此服務會自動處理兩項關鍵操作:

  • 產生記憶(Generating Memories): 在每次對話結束時,Agent Development Kit (ADK) 會將該 session 的事件傳送到 Memory Bank,Memory Bank 會智慧地處理並將資訊儲存為「記憶」。
  • 檢索記憶(Retrieving Memories): 你的 agent 程式碼可以對 Memory Bank 發出搜尋查詢,以從過去的對話中檢索相關記憶。

先決條件

在使用此功能之前,你必須具備:

  1. Google Cloud 專案: 並已啟用 Vertex AI API。
  2. Agent Engine: 你需要在 Vertex AI 中建立一個 Agent Engine。這將提供你設定所需的 Agent Engine ID
  3. 驗證(Authentication): 確保你的本機環境已通過驗證,可以存取 Google Cloud 服務。最簡單的方式是執行:
    gcloud auth application-default login
    
  4. 環境變數: 此服務需要你的 Google Cloud 專案 ID 以及 Location。請將它們設為環境變數:
    export GOOGLE_CLOUD_PROJECT="your-gcp-project-id"
    export GOOGLE_CLOUD_LOCATION="your-gcp-location"
    

設定

要將你的 agent 連接到 Memory Bank,可以在啟動 Agent Development Kit (ADK) 伺服器時使用 --memory_service_uri 旗標(adk webadk api_server)。URI 必須符合 agentengine://<agent_engine_id> 的格式。

bash
adk web path/to/your/agents_dir --memory_service_uri="agentengine://1234567890"

或者,你也可以手動實例化VertexAiMemoryBankService,並將其傳遞給Runner,以將 agent 設定為使用 Memory Bank。

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
)

runner = adk.Runner(
    ...
    memory_service=memory_service
)

在 Agent 中使用 Memory

完成服務設定後,Agent Development Kit (ADK) 會自動將 session state 儲存到 Memory Bank。若要讓你的 agent 使用這些記憶資料,你需要在 agent 的程式碼中呼叫 search_memory 方法。

這通常會在每個 agent 回合的開始階段執行,以便在產生回應前取得相關的上下文資訊。

範例:

from google.adk.agents import Agent
from google.genai import types

class MyAgent(Agent):
    async def run(self, request: types.Content, **kwargs) -> types.Content:
        # Get the user's latest message
        user_query = request.parts[0].text

        # Search the memory for context related to the user's query
        search_result = await self.search_memory(query=user_query)

        # Create a prompt that includes the retrieved memories
        prompt = f"Based on my memory, here's what I recall about your query: {search_result.memories}\n\nNow, please respond to: {user_query}"

        # Call the LLM with the enhanced prompt
        return await self.llm.generate_content_async(prompt)

進階概念

記憶體在實務上的運作方式

記憶體工作流程在內部包含以下步驟:

  1. Session 互動: 使用者透過Session與 agent 互動,該 session 由SessionService管理。事件(Event)會被加入,並且 state 可能會被更新。
  2. 寫入記憶體: 在某個時點(通常是在 session 結束或產生了重要資訊時),你的應用程式會呼叫memory_service.add_session_to_memory(session)。這個方法會從 session 的事件中擷取相關資訊,並將其加入長期知識儲存區(記憶體中的字典或 RAG Corpus)。
  3. 後續查詢:不同(或同一)個 session 中,使用者可能會提出需要過去脈絡的問題(例如:「我們上週討論過 project X 什麼內容?」)。
  4. Agent 使用記憶體工具: 配備記憶體檢索工具(如內建的load_memory工具)的 agent 會辨識出需要過去脈絡,並呼叫該工具,傳入查詢字串(例如:「discussion project X last week」)。
  5. 執行搜尋: 該工具內部會呼叫memory_service.search_memory(app_name, user_id, query)
  6. 回傳結果: MemoryService會在其儲存區中(使用關鍵字比對或語意搜尋)搜尋,並以SearchMemoryResponse形式回傳相關片段,內容包含MemoryResult物件的列表(每個物件可能包含來自相關過去 session 的事件)。
  7. Agent 使用結果: 工具會將這些結果回傳給 agent,通常作為 context 或 function response 的一部分。agent 便可利用這些檢索到的資訊,來組成最終回覆給使用者的答案。

agent 可以存取多個記憶體服務嗎?

  • 標準設定下:不行。 這個 framework(adk webadk api_server)設計上一次只能透過--memory_service_uri旗標設定一個記憶體服務。這個單一服務會提供給 agent,並透過內建的self.search_memory()方法存取。從設定的角度來看,你只能為該 process 下所有 agent 選擇一個後端(InMemoryVertexAiMemoryBankService)。

  • 在你的 agent 程式碼中:可以,絕對可以。 沒有任何限制阻止你在 agent 的程式碼內手動 import 並實例化另一個記憶體服務。這讓你能在單一 agent 回合中存取多個記憶體來源。

舉例來說,你的 agent 可以利用 framework 設定的VertexAiMemoryBankService來回憶對話歷史,同時手動實例化InMemoryMemoryService來查詢技術手冊中的資訊。

範例:同時使用兩個記憶體服務

以下是在你的 agent 程式碼中實作此功能的方法:

from google.adk.agents import Agent
from google.adk.memory import InMemoryMemoryService, VertexAiMemoryBankService
from google.genai import types

class MultiMemoryAgent(Agent):
    def __init__(self, **kwargs):
        super().__init__(**kwargs)

        self.memory_service = InMemoryMemoryService()
        # Manually instantiate a second memory service for document lookups
        self.vertexai_memorybank_service = VertexAiMemoryBankService(
            project="PROJECT_ID",
            location="LOCATION",
            agent_engine_id="AGENT_ENGINE_ID"
        )

    async def run(self, request: types.Content, **kwargs) -> types.Content:
        user_query = request.parts[0].text

        # 1. Search conversational history using the framework-provided memory
        #    (This would be InMemoryMemoryService if configured)
        conversation_context = await self.memory_service.search_memory(query=user_query)

        # 2. Search the document knowledge base using the manually created service
        document_context = await self.vertexai_memorybank_service.search_memory(query=user_query)

        # Combine the context from both sources to generate a better response
        prompt = "From our past conversations, I remember:\n"
        prompt += f"{conversation_context.memories}\n\n"
        prompt += "From the technical manuals, I found:\n"
        prompt += f"{document_context.memories}\n\n"
        prompt += f"Based on all this, here is my answer to '{user_query}':"

        return await self.llm.generate_content_async(prompt)