三亩地 三亩地SAN MU DI · CODE DIARY
ARTICLE DETAIL

日记详情

真实记录编程学习的某一天,欢迎挑你感兴趣的翻一翻。

大模型记忆系统架构设计:从向量化检索到个性化对话实践

大模型记忆系统架构设计:从向量化检索到个性化对话实践

1. 项目概述:当大模型需要“记住”用户

在AI应用遍地开花的今天,大模型对话的“金鱼记忆”问题越来越突出。你跟一个智能客服聊了十分钟,它可能还记得你第一句话问的是啥;你跟一个内容创作助手反复打磨一篇文章,它却常常忘记你十分钟前强调过的核心风格。这种上下文遗忘,本质上是当前主流大模型架构的固有局限——它们通常只处理固定长度的上下文窗口,超出部分的信息就“消失”了。

“记忆系统”就是为了解决这个问题而生的工程组件。它不是一个学术概念,而是一套实实在在的、能让大模型应用“记住”用户历史交互、偏好、习惯甚至未完成任务的技术设施。你可以把它想象成给大模型这个“天才健忘症患者”配了一个超级外脑和一位贴身的私人秘书。这个秘书(记忆系统)负责观察、记录、整理你和模型的所有对话,并在你需要的时候,精准地从海量记录中找出最相关的信息,悄悄递给模型,让它能做出更连贯、更个性化的回应。

货拉拉作为同城货运的巨头,其业务场景对记忆系统的需求尤为强烈。司机和货主在平台上的每一次交互——从询价、议价、约定时间地点,到运输中的异常反馈、费用变更——都不是孤立的。一个高效的记忆系统能让智能客服、调度助手甚至风控模型,都具备“认人识事”的能力,从而提升服务效率与用户体验。今天,我们就来深入拆解这套系统中最核心、也最考验工程功力的部分:从海量历史数据中“提取”关键记忆,并在需要时“召回”它们。

2. 记忆系统的核心架构与设计思路

一个完整的记忆系统远不止是存和取那么简单。它需要像人脑的记忆机制一样,具备编码、存储、巩固、检索和遗忘等多个环节。在工程上,我们将其抽象为一个分层、异步的流水线。

2.1 整体架构视图

我们的记忆系统主要由四个核心模块构成,它们协同工作,形成一个闭环:

  1. 记忆提取器:这是系统的“感官”和“过滤器”。它实时监听用户与模型的所有交互(对话流、事件日志等),运用规则引擎和轻量级模型,从中识别并抽取出值得存储为长期记忆的“记忆点”。比如,用户说“我每次发货都喜欢下午,因为上午仓库太忙”,这里“偏好下午发货”就是一个高价值的记忆点。
  2. 记忆向量化与存储:提取出的记忆点是原始的文本片段。为了高效检索,我们需要将其转化为机器更易理解的格式——向量(一组高维数字)。这个过程通常由嵌入模型完成。转化后的向量,连同原始文本、元数据(如用户ID、时间戳、记忆类型)一起,存入专门的向量数据库。向量数据库的优势在于能基于向量相似度进行快速近似检索。
  3. 记忆召回器:当新的用户查询到来时,召回器开始工作。它首先将当前查询也向量化,然后以该查询向量为“探针”,去向量数据库中寻找最相似的K个记忆向量。这个过程就是“召回”,目标是尽可能不遗漏任何相关记忆。
  4. 记忆重排序与注入:召回的记忆可能有几十条,但大模型的上下文窗口是宝贵的。我们需要一个更精细的“重排序”层,对召回结果进行二次打分和排序,筛选出最相关、最重要的几条记忆。最后,这些记忆会被格式化成特定的提示词(如“根据用户历史信息:...”),注入到大模型的输入上下文中,悄然影响其生成结果。

这个架构的核心设计思路是“解耦”与“分层”。提取、向量化、召回、重排序每个环节独立发展,便于迭代优化。例如,我们可以单独升级嵌入模型来提升向量质量,而不影响召回逻辑。

2.2 为什么是“向量数据库+重排序”?

你可能会问,直接用传统数据库(如MySQL)存文本,用关键词匹配(如ES)来检索不行吗?对于记忆系统,这通常不够。

  • 语义模糊性:用户表达是多样的。“下午发货”和“别在上午安排”表达的是同一个偏好。关键词匹配难以捕捉这种语义关联。
  • 核心需求是“相似”而非“相同”:我们找的是“相关”记忆,不一定是包含相同词汇的记忆。向量相似度检索天生为此而生。
  • 效率与规模:当记忆条目达到百万、千万级时,基于向量索引的近似最近邻搜索(ANN)在速度和资源消耗上远优于复杂的文本匹配计算。

而“重排序”是对向量检索“粗召回”的必要补充。向量检索可能因为语义漂移或嵌入模型局限性,把一些表面相似但实际无关的记忆排在前列。重排序模型(通常是一个轻量的交叉编码器)会同时看查询和每一条召回记忆,进行更精细的语义匹配打分,从而提升最终结果的精准度。

3. 记忆提取:从对话流中捕捉“黄金片段”

记忆提取是记忆系统的源头,决定了记忆库的质量。我们的目标不是存下每一句对话,那会成为信息垃圾场。我们要存的是浓缩的、结构化的、未来可能被用到的知识。

3.1 提取策略:规则与模型双驱动

我们采用“规则为主,模型为辅”的混合策略,在保证可控性的前提下,逐步提升智能化。

  • 基于规则的提取

    • 显式声明:直接捕捉用户或模型带有明确记忆标记的语句。例如,用户说“请记住,我公司的发票抬头是XX科技有限公司”。我们可以通过正则表达式或关键词(“记住”、“我公司是”、“我的地址是”)来触发提取。
    • 对话结构分析:在客服、导购等场景,对话常有固定模式。例如,在询价环节,用户提供的“货物尺寸”、“重量”、“出发地/目的地”等信息,可以通过槽位填充的方式提取为记忆。
    • 这是最稳定、可解释性最强的初期方案。
  • 基于轻量级模型的提取

    • 当规则难以覆盖复杂、隐式的表达时,就需要模型上场。我们并不直接使用昂贵的大模型进行实时分析,而是训练或微调一个轻量的文本分类或序列标注模型。
    • 任务定义:可以将提取任务定义为判断“当前句子是否为值得存储的记忆点”(二分类),或者识别出句子中属于记忆实体的部分(命名实体识别,如“偏好时间”、“禁忌物品”)。
    • 数据构建:从历史对话日志中,由运营或专家标注一批正负样本,即可训练一个高效的分类器。这个模型可以识别出“我老婆对猫毛过敏,以后叫车千万别派有宠物的司机”这类隐含重要信息的句子。

3.2 记忆的结构化与消歧

提取出的原始文本需要被加工成结构化的记忆对象,以便于后续管理和检索。一个基本的记忆对象可能包含以下字段:

{ "memory_id": "uuid", "user_id": "user_123", "content": "偏好下午发货,因为上午仓库繁忙", "embedding": [0.12, -0.05, ..., 0.78], // 向量表示 "type": "user_preference", // 记忆类型 "entity": {"time": "afternoon", "reason": "warehouse_busy"}, // 结构化信息 "source": "dialog_20231027_155302", "timestamp": "2023-10-27T15:53:02Z", "confidence": 0.92, // 提取置信度 "expires_at": "2024-04-27T15:53:02Z" // 可选,过期时间 }

关键点在于“消歧”。比如,用户说“苹果”,指的是水果、手机公司还是电影?这需要结合上下文进行实体链接。我们在提取阶段可以引入一个简单的上下文窗口(如前后各两句话),利用轻量级NER模型或词典,对核心实体进行初步消歧,并将结果填入entity字段。这能为后续的向量化和召回提供更丰富的信号。

实操心得:冷启动与数据飞轮记忆系统启动初期,高质量的记忆数据很少。我们的做法是:先用严格的规则提取少量高置信度记忆,确保记忆库的“纯净度”。同时,将模型难以判断的样本(低置信度)流入一个人工审核队列,审核后的结果反哺模型训练。随着系统运行,这个“数据飞轮”会越转越快,提取能力也越来越强。

4. 记忆的向量化与索引构建:打造高效的“记忆仓库”

提取出的结构化记忆,需要通过向量化才能被高效检索。这一步的核心是嵌入模型向量数据库的选型与优化。

4.1 嵌入模型选型与优化

嵌入模型负责将文本映射为向量。它的质量直接决定了“记忆相似度”计算是否准确。

  • 选型考量

    • 通用 vs. 领域:像text-embedding-ada-002这样的通用模型开箱即用,效果不错。但对于货运垂直领域,特定词汇(如“厢货”、“平板车”、“搬运费”)的语义可能捕捉不佳。理想路径是:先用通用模型上线,同时收集领域数据,后期微调或训练领域专用嵌入模型。
    • 多语言支持:货拉拉业务可能涉及多语言用户,需考虑模型的多语言能力。
    • 上下文长度:有些记忆内容较长,需要嵌入模型支持长文本。
    • 性能与成本:模型的推理速度、尺寸和API调用成本(如果使用云端服务)需纳入评估。
  • 优化实践

    • 指令微调:我们发现,在微调时给嵌入模型加入简单的指令,能显著提升其在记忆检索任务上的表现。例如,在训练样本的文本前加上“表示为用于检索的记忆:”,让模型更好地理解我们的使用场景。
    • 混合索引:除了对完整的content字段生成向量,我们还可以对结构化的entity字段(如{"time": "afternoon"})生成向量。在检索时,可以尝试融合两种向量的相似度得分,或者建立两个独立的向量索引供后续融合召回。

4.2 向量数据库的工程实践

我们选择了Milvus作为向量数据库。它开源、功能丰富、社区活跃,适合大规模向量检索场景。

  • 索引类型选择:Milvus支持HNSW、IVF_FLAT等多种索引。HNSW(Hierarchical Navigable Small World)因其在召回率和查询速度上的良好平衡,成为我们的首选。对于亿级以下的记忆库,HNSW能提供毫秒级的检索延迟。
    • M(建立图时每个点的连接数)和efConstruction(索引构建参数)影响索引构建速度和精度。我们通过实验,在内存允许的情况下适当调高这些参数,以换取更高的召回率。
    • efSearch(搜索参数)则在查询时动态调整,平衡搜索速度和召回率。
  • 分区与集合设计
    • 按用户分区:这是最自然的分区策略。将同一用户的所有记忆存储在同一个分区(Collection Partition)中。这样,99%的查询都只需要在一个用户分区内进行,极大缩小了搜索范围,提升了性能和资源隔离性。
    • 按记忆类型分集合:如果记忆类型差异很大(如“个人偏好”和“订单事实”),可以考虑为不同类型建立不同的集合(Collection),并为每个集合选择最合适的嵌入模型和索引参数。
  • 元数据过滤:向量检索经常需要结合元数据过滤。例如,“召回用户A最近一个月内关于‘价格’类型的记忆”。Milvus支持在向量相似度搜索前/后对标量字段(如user_id,type,timestamp)进行过滤。我们的经验是,对于分区键(如user_id)这类高筛选度的条件,优先用分区剪枝;对于其他条件,根据其筛选度决定在检索前过滤(减少搜索量)还是检索后过滤(保证召回率)。

踩坑记录:向量维度对齐与版本管理嵌入模型升级意味着向量维度可能改变。直接切换会导致旧向量索引全部失效。我们的解决方案是:

  1. 双写过渡期:新模型上线后,在一段时间内,对每条新记忆用新旧两个模型分别生成向量,写入两个不同版本的集合。
  2. 查询融合:查询时,同时查询新旧两个集合,对召回结果进行去重和融合排序。
  3. 异步迁移:后台任务逐步将旧集合中的向量用新模型重新计算,迁移到新集合。
  4. 建立严格的版本号机制,在记忆对象中记录embedding_model_version,确保追溯性。

5. 记忆召回策略:在毫秒间找到“相关记忆”

召回环节的目标是“快”和“全”,即快速返回尽可能多的相关候选记忆。我们实现了多路召回策略,以应对不同的查询场景。

5.1 基于向量相似度的主通路召回

这是最核心的召回路径。当用户发起一个新对话时:

  1. 使用与记忆库相同的嵌入模型,将当前的查询语句(或结合了最近几句对话的上下文)转化为查询向量。
  2. 在向量数据库中,以该查询向量为中心,搜索最相似的K个向量(例如,K=50)。这就是初步的召回结果。

关键参数K的选择K值不是越大越好。太大会引入更多噪声,增加后续重排序的压力;太小可能遗漏关键记忆。我们通过AB测试,根据“记忆被成功注入并产生正面效果”的比例,来动态调整不同场景下的K值。对于常规对话,K=30是一个不错的起点。

5.2 基于元数据与关键词的辅助召回

单纯依赖向量检索有时会“跑偏”。因此,我们增加了辅助召回通路作为补充和纠偏:

  • 时间衰减召回:用户的近期记忆通常比远古记忆更重要。我们设计了一个时间衰减函数,在向量相似度得分的基础上,叠加一个基于记忆新鲜度的加分。例如,final_score = similarity_score + alpha * recency_score。这能让一周内的记忆获得更高的排名。
  • 关键词增强召回:对于包含明确实体(如地点“深圳北站”、货物类型“家具”)的查询,我们会同时使用传统搜索引擎(如Elasticsearch)对记忆的contententity字段进行关键词检索。将ES返回的结果与向量召回结果进行融合。
  • 类型过滤召回:在客服场景,如果当前对话被识别为“投诉类”,我们可以优先召回“投诉历史”或“解决方案”类型的记忆。

5.3 多路召回结果的融合

我们采用了“向量召回为主,辅助召回为纠偏”的融合策略:

  1. 主通路:向量数据库召回Top K条结果,得到列表A。
  2. 辅助通路:根据元数据过滤、关键词检索等得到列表B。
  3. 融合:对列表B中的每条记忆,计算其与查询的向量相似度(如果之前没有),然后将其“插入”到列表A的合适位置。插入的规则可以是:如果B中某条记忆的相似度高于A中第M位的记忆,则将其插入。这里M是一个阈值,比如10。这保证了主通路结果的主体地位,同时让辅助通路能将有价值但被向量检索遗漏或排后的记忆“提拔”上来。

6. 记忆重排序与注入:把对的记忆,放在对的上下文里

召回阶段的结果仍然是粗糙的。重排序的目标是“精”和“准”,从几十条候选记忆中,筛选出最相关、最关键的3-5条,并将其组织成大模型能理解的提示词。

6.1 重排序模型的选择与训练

我们放弃了使用超大模型进行重排序(成本太高),而是采用交叉编码器

  • 原理:与生成向量用的双编码器(如BERT,将查询和记忆分别编码为向量再计算相似度)不同,交叉编码器将查询和记忆文本拼接在一起,同时输入模型,让模型直接输出一个相关度分数。这种方式能进行更精细的语义交互,精度更高,但无法像双编码器那样预先计算好记忆向量。
  • 训练数据:我们从真实的对话日志中构造训练样本。将一次成功的、注入了记忆的对话作为正例,随机采样其他不相关的记忆作为负例。让模型学习区分“相关”与“不相关”。
  • 模型轻量化:我们使用bert-base这类基础模型进行微调,并将其蒸馏为更小的模型(如 TinyBERT),以满足线上服务对低延迟的要求。

6.2 重排序的上下文感知

重排序模型不能只看孤立的查询和记忆。我们尝试将更丰富的上下文信息融入重排序:

  • 输入[CLS] 当前查询 [SEP] 候选记忆 [SEP] 最近三轮对话历史 [SEP]
  • 这样,模型在判断“下午发货”这条记忆是否相关时,也能考虑到用户刚刚说过“我明天要发货”,从而给出更准确的分数。

6.3 记忆的格式化与安全注入

经过重排序筛选出的最终记忆列表,需要被安全、有效地注入到大模型的提示词中。

  • 格式化模板:我们设计了一个清晰、无歧义的模板来组织记忆。例如:
    以下是用户的历史相关信息,供你在回复时参考: 1. [记忆1内容] (来源:对话时间1) 2. [记忆2内容] (来源:对话时间2)
    明确标注来源,既能提升模型使用的准确性,也便于后续可解释性分析。
  • 注入位置与长度控制:记忆通常被放在系统提示词(System Prompt)之后,用户当前查询之前。必须严格控制注入记忆的总长度,确保其与对话历史、当前查询的总和不超过模型上下文窗口,并预留足够的空间给模型生成回答。我们会有一个截断策略,优先保留重排序分数更高的记忆片段。
  • 安全与伦理过滤:在注入前,记忆内容会经过一个安全过滤器,防止任何不当、偏见或敏感信息被传递给模型。同时,我们尊重用户隐私,对于明确标记为“临时”或“一次性”的记忆,不会在后续对话中召回。

注意事项:记忆的“幻觉”与冲突记忆系统可能召回错误或过时的记忆。更棘手的是,可能召回多条相互冲突的记忆(如用户上次说“喜欢李师傅”,这次说“李师傅开车太晃”)。我们的应对策略是:

  1. 置信度与时间加权:在重排序分数中,融入记忆提取时的置信度和时间衰减因子,让高置信、新鲜的记忆排名更靠前。
  2. 冲突检测与消解:在格式化时,如果检测到多条记忆在关键实体上冲突,可以尝试在提示词中加入一句简短的说明,如“请注意,用户关于司机偏好的表述可能存在变化”,将冲突暴露给大模型,让模型基于更全面的上下文自行判断。
  3. 设置记忆优先级与生命周期:为不同类型的记忆设置不同的优先级和过期时间。例如,“过敏信息”优先级高、不过期;“临时偏好”优先级低、24小时后过期。

7. 系统实现、评估与迭代

7.1 工程实现要点

整个系统我们采用微服务架构,核心服务包括:

  • 记忆提取服务:订阅对话消息队列,实时处理。
  • 向量化与写入服务:异步处理提取服务发出的记忆事件,调用嵌入模型API,写入向量数据库和元数据库。
  • 记忆召回与重排序服务:提供低延迟的API,接收查询,返回排序后的记忆列表。
  • 记忆管理后台:供运营人员查看、搜索、修正或删除用户记忆,处理用户的数据权利请求(如遗忘权)。

异步流水线是关键。从对话发生到记忆可用,允许有秒级延迟。这让我们可以用批处理的方式调用嵌入模型,降低成本;也便于在写入前做一些去重、合并的后处理。

7.2 如何评估记忆系统的效果?

记忆系统的好坏,不能只看检索本身的指标(如召回率、准确率),更要看其对上层AI应用效果的提升。

  • 离线评估
    • 检索指标:构建测试集,评估记忆召回的Hit Rate@K(前K条结果中至少包含一条相关记忆的概率)和MRR(平均倒数排名)。
    • 人工评测:采样大量对话,由评测员判断系统召回的记忆是否相关、有用,以及最终模型的回复是否因记忆而变得更准确、更个性化。
  • 在线评估(A/B测试)
    • 核心业务指标:在客服场景,对比开启/关闭记忆功能的实验组,看问题解决率对话轮次用户满意度评分是否有显著提升。
    • 记忆使用指标:监控记忆注入率(有多少对话成功注入了记忆)、记忆采纳信号(能否通过模型回复检测到其确实参考了注入的记忆,例如通过特定关键词或逻辑一致性判断)。

7.3 持续迭代的方向

记忆系统是一个需要持续运营和迭代的工程。

  1. 提取模型持续优化:随着标注数据增多,不断优化记忆提取模型,识别更隐晦、更复杂的记忆点。
  2. 嵌入模型领域化:积累足够的领域对话数据后,微调或训练专属的嵌入模型,是提升召回效果性价比最高的方式之一。
  3. 个性化重排序:未来的重排序模型可以是个性化的,考虑用户的长期行为模式。例如,对于经常修改地址的用户,“地址”类记忆的权重可以动态调整。
  4. 记忆的主动应用:当前记忆是被动召回的。未来可以探索主动记忆应用,例如,在用户长时间未使用服务后再次打开App时,主动说“根据您的记录,您通常在这个时间段需要货运服务,是否需要现在下单?”。

构建一个大模型记忆系统,就像为AI打造一个不断成长的外脑。从精准的提取、高效的索引,到智能的召回与安全的注入,每一个环节都充满了工程上的权衡与挑战。这套系统上线后,最直观的感受就是AI对话变得“更懂你”了,它开始有了“记忆的温度”。而这一切的背后,是一套冷静、理性、持续优化的工程技术体系在支撑。技术的终点,始终是更好地服务于人。

← 返回列表