You need to enable JavaScript to run this app.
文档中心
向量数据库VikingDB

向量数据库VikingDB

复制全文
下载 pdf
最佳实践
短期对话上下文与长期记忆检索一体化最佳实践
复制全文
下载 pdf
短期对话上下文与长期记忆检索一体化最佳实践

核心概念与流程

在深入实践细节之前,我们首先需要理解几个核心概念以及它们如何协同工作,构成一个完整的记忆管理闭环。

概念名称

英文标识

核心定义

用途

用户消息

message

用户单次写入的原始对话内容(单轮,含 user/assistant 角色)。

记忆写入的最小单元。

对话链路

conversation

由多轮 message 构成的用户连续交互的聚合单元。

关联短期记忆,维系即时对话的连贯性。

短期记忆

Short-term Memory

存在于 conversation 缓存中、尚未被提炼为长期记忆的近期原始对话消息。

保证对话的即时上下文,响应速度快,无需检索。

长期记忆

Long-term Memory

从多轮对话中抽取、结构化并固化的核心信息(事件、用户画像等),存储于向量数据库中。

实现跨对话、长周期的个性化记忆,需要通过检索召回。

抽取周期

session

触发长期记忆抽取的业务单元。当一个 conversation 内累计的消息满足特定条件(如 Token 数、消息数或等待超时)时,一个 session 便结束,并启动抽取流程。

控制长期记忆的生成频率与时机。

端到端架构概览

Image

整个流程的核心思想是 “写后异步抽取、读时动态拼接”

  1. 实时写入 (streaming_write):你的应用将每一轮的用户对话实时发送到记忆服务。
  2. 存入短期记忆:消息被立刻存入高速的短期记忆缓存区,确保即时对话的上下文连续性。
  3. 智能触发抽取:记忆服务根据你配置的规则(如 token_count)自动监控对话累积量,一旦满足条件,便异步启动一个长期记忆抽取任务。
  4. 抽取并固化:抽取器将短期记忆中的多轮对话提炼为结构化的事件或用户画像,并存入长期记忆库。
  5. 统一获取上下文 (get_context):当需要与大语言模型(LLM)交互时,你的应用调用一个统一接口。
  6. 动态拼接与返回:该接口会智能地从短期记忆中提取最近对话,并从长期记忆库中检索相关的历史记忆和用户画像,最后将它们整合成一段优化过的、可直接供 LLM 使用的上下文(Context)。

接下来,我们将详细拆解每个环节的最佳实践。

详细使用指引

实时写入 (streaming_write): 构建记忆的基石

高效、可靠地写入对话数据是构建智能记忆系统的第一步。streaming_write 接口为你提供了一种低延迟、高吞吐的方式来实时记录每一轮对话,并在此基础上自动化长期记忆的生成。

接入形态:批式 vs. 实时

在接入记忆服务时,你需要首先在两种写入方式中做出选择:

写入方式

核心机制

优点

缺点

适用场景

批式写入 session_add

由业务方自行管理对话轮次,每次调用 API 都被视为一个完整的 session,并立即触发一次长期记忆抽取。

实现简单,对抽取时机的控制完全掌握在自己手中。

需要业务方自行实现复杂的缓存和批处理逻辑,难以平衡抽取频率与成本;无法利用平台提供的短期记忆能力。

迁移过渡期或对抽取时机有极端控制需求的特殊场景。

实时写入streaming_write

业务方只需实时发送每一轮对话。平台负责缓存短期记忆,并根据预设规则(token 数、消息数、超时)自动触发长期记忆抽取。

(推荐) 将复杂的会话管理和抽取触发逻辑下沉到平台,极大简化开发成本;无缝融合长短期记忆,性能更优。

抽取时机由平台管理,控制粒度相对较粗。

绝大多数新业务和希望优化现有架构的业务。

说明

最佳实践: 除非处于从旧系统迁移的临时阶段,否则始终推荐使用 streaming_write 实时写入方式。它不仅能降低你的开发和维护成本,还能让你充分利用平台提供的长短期记忆一体化能力。

核心参数配置 (extract_trigger)

streaming_write 接口的核心在于 extract_trigger 对象,它定义了何时将累积的短期记忆转化为长期记忆。你可以组合使用以下三个条件,平台会在满足任意一个条件时触发抽取:

参数

类型

描述

推荐值与决策框架

token_count

integer

(推荐) 触发抽取的累计 Token 阈值。当一个 conversation 内未抽取的消息 Token 总数达到此值时触发。

默认值: 1000。这个值需要根据你的业务场景和成本敏感度来权衡:

  • 高频交互/客服场景: 建议设置在 500 - 1500。较低的阈值能更快捕捉到新信息,但会增加抽取频率和成本。
  • 低频交互/分析场景: 可以设置在 2000 - 4000。较高的阈值能聚合更多上下文,可能提炼出更高质量的记忆,同时降低成本。

message_count

integer

触发抽取的累计消息(轮次)阈值。一轮包含 userassistant 的完整对话计为 2 条 message。

默认值: 20 (约 10 轮对话)。作为 token_count 的补充,防止因单轮消息过长或过短导致抽取时机不合理。通常使用默认值即可。

wait_timeout

integer

抽取等待的超时时间(秒)。当一个 conversation 在最后一条消息后,超过此时长未再收到新消息时,即使未达到前两个阈值,也会触发抽取。

默认值: 604800 (7天)。这是一个兜底策略,用于清理长时间不活跃的会话缓存。对于大多数实时应用,可以保持默认值或根据业务会话的平均生命周期设置,例如 86400 (1天)。

conversation_id 设计

conversation_id 是串联起用户一次完整对话流程的唯一标识。正确地设计和管理 conversation_id 至关重要。

  • 唯一性: 必须保证在一次连续的对话中,所有 streaming_writeget_context 请求都使用相同的 conversation_id
  • 生命周期: 一次对话的开始(例如,用户打开聊天窗口)即是 conversation_id 的生命周期开始;对话的结束(例如,用户关闭窗口、切换主题)即是其结束。建议为每个新的对话会话生成一个新的、唯一的 conversation_id
  • 格式建议: 推荐使用 业务前缀-用户ID-时间戳-随机串 的组合,例如 online-cs-user_12345-1678886400-a8d3b,以保证其唯一性和可追溯性。

获取与拼接 Context (get_context): 让 LLM 读懂记忆

get_context 接口是连接记忆服务和大型语言模型(LLM)的桥梁。它的核心任务是根据当前的对话 conversation_id,智能地将不同来源、不同类型的记忆(短期、长期、画像)融合成一段结构清晰、可直接注入到 Prompt 中的上下文,从而赋予 LLM “记忆”的能力。

调用时序与节奏

理解 get_context 在整个对话流程中的位置至关重要。下图清晰地展示了它与 streaming_write 及 LLM 推理的调用关系:
Image
核心调用节奏如下:

  1. 在你的应用准备调用 LLM 进行推理之前,先调用 get_context 接口。
  2. get_context 返回的 context_str 字符串,完整地、不加修改地插入到你为 LLM 设计的 Prompt 中的指定位置。
  3. 将拼接好的完整 Prompt 发送给 LLM。
  4. LLM 返回回复后,将这一轮新的对话(User + Assistant)再次通过 streaming_write 接口写入记忆服务,完成闭环。

Context 拼接顺序与格式化策略

get_context 返回的 context_str 内部已经过精心设计,遵循了特定的优先级和格式化策略,以达到最佳的理解效果。默认的拼接顺序如下,优先级由高到低:

  1. 用户画像 (Profile Memory): 关于用户的核心、稳定不变的特征。优先级最高,确保模型始终基于用户的核心画像进行个性化回应。
  2. 短期记忆 (Short-term Memory): 当前 conversation 中尚未被抽取的、最近的几轮原始对话。这部分保证了对话的即时连贯性,让模型能“记住”刚刚发生了什么。
  3. 同会话事件记忆 (Intra-conversation Events): 当前 conversation已经被抽取并固化的长期记忆。这部分内容是对较早前对话的总结,相比原始对话更精炼。
  4. 跨会话事件记忆 (Cross-conversation Events): 根据你的 query其他历史 conversation 中检索到的相关事件记忆。这部分实现了跨对话的知识关联。

默认格式化示例:

"""
以下是需优先参考的用户记忆,按优先级排序:

1. 用户画像
- 用户为28岁宝妈,宝宝1岁,居住于上海
- 用户偏好无乳糖、含DHA的宝宝奶粉,关注性价比

2. 最近对话内容
2026-02-03 09:10:30 user: 我家宝宝1岁,最近喝奶粉总胀气,有没有推荐的?
2026-02-03 09:11:15 assistant: 您好,宝宝胀气大概率和奶粉乳糖含量有关,建议优先选择无乳糖配方奶粉,我给您推荐2款高性价比的~

3. 事件记忆
- [创建时间]2026-02-02 15:30:20 [内容]用户偏好简洁的报告格式
- [创建时间]2026-02-02 15:33:20 [内容]用户反馈折线图的颜色主题不适合深色模式,已提交反馈单。
"""

说明

最佳实践:

  • 直接使用: 除非有极特殊的格式要求,否则直接使用 get_context 返回的 context_str 是最简单、最高效的方式。
  • 返回结构化数据: 如果你需要对不同类型的记忆做更精细的控制(例如,在 UI 上分开展示),get_context 同样会在响应中返回结构化的记忆列表,你可以基于此自定义拼接逻辑。

检索配置与时间衰减

为了从海量的长期记忆中精确找到最相关的信息,get_context 提供了强大的检索配置。

  • query: 这是进行长期记忆检索的核心。你应该传入当前用户最新的提问作为 query,以便平台能基于最新的意图进行语义匹配。
  • event_search_config & profile_search_config: 这两个对象允许你对事件记忆和画像记忆的检索进行精细控制。
    • filter: 可以基于 user_idmemory_typemetadata 进行精确过滤。
    • limit: 限制返回的记忆条数,是控制 Token 成本的关键。
  • 时间衰减 (time_decay_config):
    • weight: 时间衰减权重,取值 0 到 1。值越大,时间越近的记忆得分越高。推荐 0.5 ~ 0.8
    • no_decay_period: 最近 N 天内的记忆不受衰减影响。这对于保留近期重要事件非常有用。

控制 Token 成本与长度

注入到 Prompt 中的 Context 长度直接影响 LLM 的推理成本和性能。以下是控制 get_context 返回内容长度的几种策略:

  1. 限制 limit: 在 event_search_configprofile_search_config 中设置合理的 limit 是最直接的方法。例如,事件记忆通常设置在 5-10 条,画像记忆为 1-2 条。
  2. 摘要与裁剪: 平台在生成长期记忆时,已经进行了一轮摘要。对于短期记忆,平台会根据 token_count 自动进行滚动裁剪,只保留最新的内容。
  3. 调整 time_decay: 适当加大时间衰减权重,可以让系统更倾向于返回最新的记忆,间接减少了旧记忆的干扰,从而可能缩短总长度。
  4. 关闭特定记忆类型: 在极度成本敏感的场景下,你甚至可以在 get_context 请求中通过参数(如果接口支持)或在业务逻辑中,暂时禁用某一类型的记忆(如跨会话事件记忆),以最大程度地节省 Token。

API 使用示例

streaming_write API 示例

cURL 请求

这是一个典型的 streaming_write 请求,用于实时上传一轮对话。

curl -X POST 'https://api-knowledgebase.mlp.cn-beijing.volces.com/api/memory/session/streaming_write' \
-H 'Authorization: Bearer YOUR_MEMORY_API_KEY' \
-H 'Content-Type: application/json' \
-d '{
  "collection_name": "your_collection_name",
  "project_name": "default",
  "conversation_id": "conv_20260330_user123_xyz",
  "messages":[
    {
        "role": "user",
        "content": "我家宝宝1岁,最近喝奶粉总胀气,有没有推荐的?"
    },
    {
        "role": "assistant",
        "content": "您好,宝宝胀气大概率和奶粉乳糖含量有关,建议优先选择无乳糖配方奶粉。"
    }
  ],
  "extract_trigger": {
    "token_count": 1000,
    "message_count": 20,
    "wait_timeout": 604800
  },
  "metadata": {
    "user_id": "user_123",
    "source": "app_chat"
  }
}'

Python 请求

使用 SDK 可以更方便地进行集成。

import os
from vikingdb.memory import VikingMem
from vikingdb import APIKey

# 推荐从环境变量或安全配置中读取 API Key
API_KEY = os.environ.get("MEMORY_API_KEY", "YOUR_MEMORY_API_KEY")

# 1. 初始化客户端
client = VikingMem(
    host="api-knowledgebase.mlp.cn-beijing.volces.com",
    region="cn-beijing",
    auth=APIKey(api_key=API_KEY)
)

# 2. 获取记忆库实例
collection = client.get_collection(
    collection_name="your_collection_name",
    project_name="default"
)

# 3. 实时写入对话
def streaming_write_message():
    try:
        result = collection.streaming_write(
            conversation_id="conv_20260330_user123_xyz",
            messages=[
                {
                    "role": "user",
                    "content": "我家宝宝1岁,最近喝奶粉总胀气,有没有推荐的?"
                },
                {
                    "role": "assistant",
                    "content": "您好,宝宝胀气大概率和奶粉乳糖含量有关,建议优先选择无乳糖配方奶粉。"
                }
            ],
            extract_trigger={
                "token_count": 1000,
                "message_count": 20,
                "wait_timeout": 604800
            },
            metadata={
                "user_id": "user_123",
                "source": "app_chat"
            }
        )
        print(f"写入成功: {result}")
        return result
    except Exception as e:
        print(f"写入失败: {e}")
        # 在此添加重试或错误上报逻辑
        return None

if __name__ == "__main__":
    streaming_write_message()

get_context API 示例

cURL 请求

此示例展示了如何在获取上下文时,传入最新的用户 query 并配置检索参数。

curl -X POST 'https://api-knowledgebase.mlp.cn-beijing.volces.com/api/memory/get_context' \
-H 'Authorization: Bearer YOUR_MEMORY_API_KEY' \
-H 'Content-Type: application/json' \
-d '{
  "collection_name": "your_collection_name",
  "project_name": "default",
  "conversation_id": "conv_20260330_user123_xyz",
  "query": "除了无乳糖,还有没有含DHA的推荐?",
  "event_search_config": {
      "filter": {
          "user_id": "user_123"
      },
      "limit": 5,
      "time_decay_config": {
          "weight": 0.6,
          "no_decay_period": 3
      }
   },
   "profile_search_config": {
      "filter": {
          "user_id": "user_123"
      },
      "limit": 1
   }
}'

Python 请求

# ... (接上文的 client 和 collection 初始化)

# 4. 获取长短期记忆上下文
def get_memory_context():
    try:
        context_response = collection.get_context(
            conversation_id="conv_20260330_user123_xyz",
            project_name="default",
            query="除了无乳糖,还有没有含DHA的推荐?",
            event_search_config={
                "filter": {"user_id": "user_123"},
                "limit": 5,
                "time_decay_config": {"weight": 0.6, "no_decay_period": 3}
            },
            profile_search_config={
                "filter": {"user_id": "user_123"},
                "limit": 1
            }
        )
        # 直接获取拼接好的字符串
        prompt_context = context_response.context_str
        print("------ 拼接好的 Prompt 上下文 ------")
        print(prompt_context)
        
        # 也可以访问结构化的记忆内容
        # print("------ 短期记忆 ------")
        # for msg in context_response.short_term_memory:
        #     print(msg)

        return prompt_context
    except Exception as e:
        print(f"获取上下文失败: {e}")
        # 异常处理:可以返回一个空的上下文或默认兜底文案
        return ""

if __name__ == "__main__":
    # 完整流程:写入 -> 获取上下文
    streaming_write_message()
    get_memory_context()

常见问题 FAQ

说明

Q1: get_context 返回的长期记忆为空是什么原因?

A: 这通常不是一个错误。最常见的原因是该 conversation 累积的对话量尚未达到你在 streaming_write 中设置的 extract_trigger 触发条件(如 token_count)。因此,还没有任何长期记忆被生成。你的应用应该能正常处理这种情况,仅使用短期记忆进行对话。

说明

Q2: 我可以手动触发一次抽取吗?

A: streaming_write 的设计理念是自动化管理抽取。但如果你确实有手动触发的需求(例如,在对话结束时强制保存记忆),可以将 extract_trigger 中的 token_countmessage_count 设置为一个极小的值(如 1),这样下一次写入时几乎必然会触发抽取。完成后,记得将配置改回正常值。

说明

Q3: 如何处理非常长的单轮对话?

A: 如果单轮 messagecontent 本身就超过了 token_countstreaming_write 接口会在该轮写入后立即触发一次抽取。这意味着长对话会被高效地处理,不会“撑爆”短期记忆缓存。

说明

Q4: 我可以自定义 get_context 返回的格式吗?

A: get_context 接口本身提供了优化好的 context_str 字符串。如果你需要完全自定义的格式,可以利用响应中的结构化字段(context_parts),在你的业务后端自行拼接成任何你想要的格式。

说明

Q5: 从批式迁移到实时后,历史数据怎么办?

A: 历史数据(已经通过批式接口抽取的长期记忆)仍然存在于你的记忆库中。当你使用 get_context 时,只要 queryfilter 条件匹配,这些历史记忆同样可以被检索到。迁移主要影响的是未来的数据写入和短期记忆的管理方式。

最近更新时间:2026.04.21 20:41:56
这个页面对您有帮助吗?
有用
有用
无用
无用