Skip to content

记忆:使用 MemoryService 实现长期知识

Supported in ADKPython v0.1.0TypeScript v0.2.0Go v0.1.0Java v0.1.0

我们已经了解了 Session 如何为单个、正在进行的对话跟踪历史记录(events)和临时数据(state)。但是,如果智能体需要从过去的对话中回忆信息怎么办?这就是长期知识MemoryService 的概念发挥作用的地方。

你可以这样理解:

  • Session / State: 就像你在一次特定聊天中的短期记忆。
  • 长期知识 (MemoryService): 就像一个可搜索的档案或知识库,智能体可以查阅,其中可能包含来自许多过去聊天或其他来源的信息。

MemoryService 的角色

BaseMemoryService(Go 中为 Service)定义了管理这个可搜索的长期知识存储的接口。它支持四个操作:

  1. 摄取会话(add_session_to_memory): 获取一个(通常已完成)Session 的内容,并将相关信息添加到长期知识存储中。
  2. 增量摄取事件(add_events_to_memory): 追加事件增量(例如最新的轮次),无需重新摄取整个会话。当你想在长时间运行的会话中途写入记忆时非常有用。
  3. 直接写入记忆项(add_memory): 插入预先构建的 MemoryEntry 项,适用于支持在基于事件的提取之外进行直接写入的服务。
  4. 搜索(search_memory): 允许智能体(通常通过 Tool)查询知识存储并根据搜索查询检索相关片段。

操作 2 和 3 是可选的——基类的 add_events_to_memoryadd_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 将会话信息存储在应用程序的内存中,并对搜索执行基本的关键词匹配。它不需要额外设置,最适合原型设计和不需要持久性的简单测试场景。

from google.adk.memory import InMemoryMemoryService
memory_service = InMemoryMemoryService()
import { InMemoryMemoryService } from '@google/adk';
const memoryService = new InMemoryMemoryService();
import (
  "google.golang.org/adk/memory"
  "google.golang.org/adk/session"
)

// 服务必须在运行器之间共享以共享状态和记忆。
sessionService := session.InMemoryService()
memoryService := memory.InMemoryService()
import com.google.adk.memory.InMemoryMemoryService;

InMemoryMemoryService memoryService = new 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());
           }
        });
  }
}

在工具中搜索记忆

你也可以通过使用工具上下文在自定义工具中搜索记忆。

from google.adk.tools import ToolContext

async def search_past_conversations(
    query: str, tool_context: ToolContext
) -> dict:
    response = await tool_context.search_memory(query)
    return {
        "results": [
            part.text
            for entry in response.memories
            for part in (entry.content.parts or [])
            if part.text
        ]
    }
// 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,
))
// 在工具实现中
async runAsync({ args, toolContext }: RunAsyncToolRequest) {
  const query = args['query'] as string;
  const response = await toolContext.searchMemory(query);
  // 处理响应
  return {
    memories: response.memories.map(m => m.content.parts?.map(p => p.text).join(' ')).join('\n')
  };
}
// 在工具实现中
public Single<ToolOutput> execute(ToolContext context) {
  String query = ...; // 从参数中获取查询关键词
  return context.searchMemory(query)
      .map(response -> {
          // 处理响应
          return new ToolOutput(response.memories().toString());
      });
}

记忆库 (Memory Bank)

VertexAiMemoryBankService 将你的智能体连接到记忆库,这是一个完全托管的 Google Cloud 服务,为对话式智能体提供复杂的持久记忆功能。

工作原理

该服务处理两个关键操作:

  • 生成记忆:在对话结束时,你可以将会话的事件发送到记忆库,它会智能地处理并将信息作为“记忆”存储起来。
  • 检索记忆:你的智能体代码可以针对记忆库发出搜索查询,以检索过去对话中的相关记忆。

先决条件

  1. 一个 Google Cloud 项目: 已启用 Agent Platform API。
  2. 一个 Agent Runtime: 你需要在 Agent Platform 上创建一个 Agent Runtime。你不需要将智能体部署到 Agent Runtime 即可使用记忆库。这将为你提供配置所需的 Agent Runtime ID
  3. 身份验证: 确保你的本地环境已通过身份验证以访问 Google Cloud 服务。最简单的方法是运行:
    gcloud auth application-default login
    
  4. 环境变量:该服务需要你的 Google Cloud 项目 ID 和位置。请将它们设置为环境变量:
    export GOOGLE_CLOUD_PROJECT="your-gcp-project-id"
    export GOOGLE_CLOUD_LOCATION="your-gcp-location"
    

配置

要将你的智能体连接到记忆库,你需要在启动 ADK 服务器(adk webadk api_server)时使用 --memory_service_uri 标志。URI 必须采用 agentengine://<agent_engine_id> 格式。

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

或者,你可以通过手动实例化 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。

from google.adk.memory import VertexAiRagMemoryService

memory_service = VertexAiRagMemoryService(
    rag_corpus="projects/PROJECT_ID/locations/LOCATION/ragCorpora/CORPUS_ID",
    similarity_top_k=5,
    vector_distance_threshold=0.6,
)

在你的智能体中使用记忆

当配置了记忆服务时,你的智能体可以使用工具或回调来检索记忆。ADK 包含两个用于检索记忆的预置工具:

  • PreloadMemory: 在每轮开始时始终检索记忆(类似于回调)。
  • LoadMemory: 仅当你的智能体认为检索记忆有帮助时才进行检索。

示例:

from google.adk.agents import Agent
from google.adk.tools.preload_memory_tool import PreloadMemoryTool

agent = Agent(
    model=MODEL_ID,
    name='weather_sentiment_agent',
    instruction="...",
    tools=[PreloadMemoryTool()]
)
import { LlmAgent, PRELOAD_MEMORY } from '@google/adk';

const agent = new LlmAgent({
    model: MODEL_ID,
    name: 'weather_sentiment_agent',
    instruction: "...",
    tools: [PRELOAD_MEMORY]
});
import (
    "google.golang.org/adk/agent/llmagent"
    "google.golang.org/adk/tool"
    "google.golang.org/adk/tool/preloadmemorytool"
)

agent, _ := llmagent.New(llmagent.Config{
    Model:       model,
    Name:        "weather_sentiment_agent",
    Instruction: "...",
    Tools:       []tool.Tool{preloadmemorytool.New()},
})
import com.google.adk.agents.LlmAgent;
import com.google.adk.tools.LoadMemoryTool;

LlmAgent agent = new LlmAgent.Builder()
    .model(MODEL_ID)
    .name("weather_sentiment_agent")
    .instruction("...")
    .tools(new LoadMemoryTool())
    .build();

要从你的会话中提取记忆,你需要调用 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},
})

高级概念

记忆在实践中的工作原理

记忆工作流在内部涉及以下步骤:

  1. 会话交互: 用户通过 SessionService 管理的 Session 与智能体进行交互。事件被添加,状态可能被更新。
  2. 摄入到记忆: 在某个时刻(通常是当会话被认为已完成或产生了重要信息时),你的应用程序调用 memory_service.add_session_to_memory(session)。这会从会话事件中提取相关信息,并将其添加到长期知识存储中(内存字典或 Agent Runtime 记忆库)。
  3. 后续查询:不同(或相同)的会话中,用户可能会提出需要过去上下文的问题(例如,"我们上周关于项目 X 讨论了什么?")。
  4. 智能体使用记忆工具: 配备了记忆检索工具(如内置的 load_memory 工具)的智能体会识别出需要过去上下文。它调用该工具,提供搜索查询(例如,"上周项目 X 的讨论")。
  5. 搜索执行: 工具内部调用 memory_service.search_memory(app_name=..., user_id=..., query=...)
  6. 返回结果: MemoryService 搜索其存储(使用关键词匹配或语义搜索),并返回匹配的片段作为 SearchMemoryResponse,其中包含 MemoryEntry 对象列表(每个对象包含 content、可选的 author、可选的 timestamp 和可选的 custom_metadata)。
  7. 智能体使用结果: 工具将这些结果返回给智能体,通常作为上下文或函数响应的一部分。然后智能体可以使用这些检索到的信息来制定对用户的最终答案。

智能体可以访问多个记忆服务吗?

  • 通过标准配置:不能。 框架(adk webadk 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],
)