跳到正文
北京时间
原文
LlamaIndex:产品、工程与评测·· 2025-05-13精选AI 评分60

LlamaIndex 为 Agent 推出改进的长短期 Memory 组件

Improved Long & Short-Term Memory for LlamaIndex Agents

AI 导读

LlamaIndex 发布改进的 Memory 组件,为 Agent 提供短长期记忆能力。基础形式将聊天记录按 token 上限存入 SQL 数据库(默认内存 SQLite)。

推荐理由

官方介绍了新版 Memory 组件的设计与用法,开发者可以对照三类长期记忆块判断是否迁移自己的 Agent 工作流。

正文 · AI 翻译
与 Logan Markewich 合著

对于任何需要保留过去对话、用户及其过往交互等基本信息的智能体应用来说,记忆无疑是一个核心组件。因此,作为我们对 LlamaIndex 最新一轮改进的一部分,我们推出了全新且改进的 Memory 组件。

在本文中,我们将介绍新的 LlamaIndex 记忆组件的一些核心功能,以及如何在你自己的智能体应用中开始使用它。首先,我们将探讨最基础的记忆实现,即简单地存储聊天消息历史。然后,我们还将探讨更高级的实现,即我们最新加入的长期记忆块。

立即探索我们的免费和付费方案。

何时使用记忆

并非所有 AI 应用都需要记忆实现。我们今天看到的许多智能体应用并不一定依赖聊天历史或关于用户的持久信息。例如,我们自己的 LlamaExtract 就是一个智能体文档提取应用,它使用 LLM 从复杂文件中提取结构化信息。在此过程中,它并不依赖保留任何形式的记忆。

但是,如果一个应用的本质依赖于你不想重复的持久信息(例如某人的姓名和年龄),和/或任何形式的对话流(因此几乎任何带有聊天界面的应用),那么记忆很快就会成为一个相当关键的组件。

想想这个简单(且令人恼火)的聊天流程,其中没有实现任何形式的记忆:

没有记忆的对话

请注意,当我问到第二个问题时,智能体无法为我提供任何答案。原因很简单,它不知道“Can you tell me what the weather is like there?”这句话中的“there”指的是什么。

这可以通过一个非常简单的步骤来解决:我们存储聊天消息历史,并将其作为上下文发送给 LLM。这正是 LlamaIndex Memory 最基本形式的起点:

记忆的基本用法

在最简单的形式中,LlamaIndex Memory 会将任意数量的、符合给定 token 限制的聊天消息存储到 SQL 数据库中,默认是内存中的 SQLite 数据库。简而言之,这个记忆能够存储聊天消息,直到达到限制。一旦达到该限制(取决于记忆的配置方式),聊天历史中最旧的消息要么被丢弃,要么被刷新到长期记忆。

因此,在其最基本的形式中,LlamaIndex Memory 组件允许你构建能够保留过去对话的智能体工作流。你可以通过使用记忆初始化智能体来将聊天历史存储到这个记忆中:

from llama_index.core.agent.workflow import FunctionAgent
from llama_index.core.memory import Memory

memory = Memory.from_defaults(session_id="my_session", token_limit=40000)

agent = FunctionAgent(llm=llm, tools=tools)

response = await agent.run("<question that invokes tool>", memory=memory)

或者,通过使用 memory.put_messages() 将特定的聊天消息添加到记忆中:

memory = Memory.from_defaults(session_id="my_session",
                              token_limit=40000)
memory.put_messages(
    [
        ChatMessage(role="user", content="Hello, world!"),
        ChatMessage(role="assistant", content="Hello, world to you too!"),
    ]
)
chat_history = memory.get()

长期记忆块

对于某些用例,上述简单的聊天历史实现可能就足够了。但对于其他用例,实现更高级的功能是有意义的。我们引入了 3 个新的“记忆块”,它们充当长期记忆:

  • 静态记忆块:用于跟踪静态的、不变的信息
  • 事实提取记忆块:用于填充从对话中提取的事实列表
  • 向量记忆块:允许我们利用经典的嵌入生成向量搜索,既用于存储聊天历史,也用于获取旧对话的相关部分。

内存组件可以用任意数量的这些长期记忆块进行初始化。然后,每当我们达到短期记忆的 token 限制时,它就会将相关信息写入这些长期记忆块。要添加长期记忆块,我们使用 memory_blocks 初始化内存:

blocks = [<list of long-term memory blocks>]

memory = Memory.from_defaults(
    session_id="my_session",
    memory_blocks=blocks,
    insert_method="system")

让我们更详细地了解如何初始化每个块。

静态信息作为长期记忆

想象一下,有些信息可能与你的智能体应用相关,但不太可能随时间变化。例如,你的名字、你住在哪里、你在哪里工作等。StaticMemoryBlock 允许你将此信息作为智能体的附加上下文提供:

from llama_index.core.memory import StaticMemoryBlock

static_info_block = StaticMemoryBlock(
    name="core_info", 
	static_content="My name is Tuana, and I live in Amsterdam. I work at LlamaIndex.",
	priority=0
)

你可能会认为这里的信息是事实,因此更适合事实提取块。主要区别在于,事实提取块本身是一个由 LLM 驱动的提取系统,旨在对话进行时提取事实。

事实作为长期记忆

我个人的最爱。FactExtractionMemoryBlock 是一个独特的长期记忆块,它使用默认提示(你可以覆盖)进行初始化,该提示指示 LLM 从正在进行的对话中提取事实列表。要初始化此块:

from llama_index.core.memory import FactExtractionMemoryBlock
from llama_index.llms.openai import OpenAI

llm = OpenAI(model="gpt-4o-mini")
 
facts_block = FactExtractionMemoryBlock(
    name="extracted_info",
	llm=llm,
    max_facts=50,
    priority=1
)


然后,例如,在与智能体进行长时间对话并提供了一些关于我自己的信息后,我们可能会得到以下内容:

memory.memory_blocks[1].facts

# ['User is 29 years old.', 'User has a sister.']

向量搜索作为长期记忆

最后,我们还有 VectorMemoryBlock,它必须使用像 Weaviate、Qdrant 或类似的向量存储进行初始化。这里的想法很简单:一旦我们达到短期记忆的 token 限制,内存组件就会将所有已发生的聊天消息写入我们用来初始化此块的向量存储中。一旦我们这样做,任何正在进行的对话,在适当的情况下,都可以从历史记录中获取相关对话,并在回复用户之前将其用作上下文。

from llama_index.core.memory import VectorMemoryBlock

vector_block = VectorMemoryBlock(
    name="vector_memory",
    # required: pass in a vector store like qdrant, chroma, weaviate, milvus, etc.
    vector_store=vector_store,
    priority=2,
    embed_model=embed_model,
    similarity_top_k=2,
)

自定义记忆

在所有这些之上,我们还让你可以通过扩展 BaseMemoryBlock 类来引入你自己的记忆块。例如,下面我们定义了一个记忆块,用于统计对话中给定名字的提及次数:

from typing import Optional, List, Any
from llama_index.core.llms import ChatMessage
from llama_index.core.memory.memory import BaseMemoryBlock

class MentionCounter(BaseMemoryBlock[str]):
    """
    A memory block that counts the number of times a user mentions a specific name.
    """
    mention_name: str = "Logan"
    mention_count: int = 0

    async def _aget(self, messages: Optional[List[ChatMessage]] = None, **block_kwargs: Any) -> str:
        return f"Logan was mentioned {self.mention_count} times."

    async def _aput(self, messages: List[ChatMessage]) -> None:
        for message in messages:
            if self.mention_name in message.content:
                self.mention_count += 1

    async def atruncate(self, content: str, tokens_to_truncate: int) -> Optional[str]:
        return ""

未来改进

我们很希望你能尝试这些针对智能体记忆组件的最新改进。如果你尝试了,请在我们的社区 Discord 服务器上告诉我们你的体验。

但是,话虽如此,我们预计这个组件会有一些改进。例如,当前实现只允许你将记忆用作智能体后端的一部分。我们很希望提供将记忆用作工具调用智能体的工具套件之一的选项。

此外,我们目前仅支持 SQL 数据库(包括你可以配置的远程数据库)。但是,我们也希望添加对 MongoDB、Redis 等 NoSQL 数据库的支持。

最后,我们可以想象许多场景,其中 FactExtractionMemoryBlock 可能会从结构化输出中受益。这将允许我们使用一组预定义的字段(事实)来初始化该块,智能体将在过程中填充这些字段。

要开始并了解有关如何构建具有短期和长期记忆的智能体的更多信息:

来源:LlamaIndex:产品、工程与评测 · llamaindex.ai