尧图精选

Agent记忆设计:从上下文管理到用户画像的工程实践

🕒 发布时间:2026/9/11 6:58:56 📁 来源:尧图网络
AI Agent热度持续走高但真正落地的时候你会发现大多数Demo型Agent都有一个共同的短板记不住事。你跟它聊过的事它转眼就忘你明确告诉过它的偏好它下次见面依然一脸茫然甚至同一个用户同一个会话里稍微绕个弯它就开始“失忆”。这两年聊AI Agent讲到工具调用、流程编排、多智能体协作的都很多但我一直觉得“记忆”才是把Agent从玩具变成工具的那道分水岭。这一篇我打算把“让 Agent 记住你”这件事彻底拆开聊清楚记忆分几层、每层怎么设计、技术选型怎么做以及我在实际项目中踩过的那些坑。如果你正准备自己动手搭一个Agent或者正在做Agent相关产品的技术方案这篇可以给你一套从原理到落地的参考路径也可以帮你规避一些文档里不会写的工程陷阱。内容会偏工程实践但我会尽量把“为什么这么做”讲透而不是只丢一堆配置。1. 为什么“记性差”是Agent落地的头号瓶颈1.1 先把“对话、会话、记忆”这三个概念对齐很多刚接触Agent的人会把“对话历史”和“记忆”混为一谈这是第一个需要纠正的点。对话历史指的是当前这次会话里用户和模型之间来回的消息列表。它天然存在于一次交互过程中一旦会话结束、或者上下文窗口被清空这些消息就跟着没了。会话级记忆比对话历史稍微进了一步它通常保存在服务端把某一次会话的关键信息抽出来存成结构化或半结构化的数据。比如用户在这次会话里明确说过“我住在杭州”“我平时骑电动车通勤”这些属于会话级记忆。它的特点是和特定的Session绑定下次开新会话时如果要继续用就必须显式地把它带出来。长期记忆则是跨会话、跨场景的。用户的姓名、职业、常用地址、口味偏好、历史诉求、偏好表达方式这些信息被单独抽取出来存到数据库或者向量库里等下次用户再来时Agent能主动检索并调用这些信息哪怕中间隔了几个月。搞清楚这三层后面很多架构设计就顺了。换句话说“让Agent记住你”这个目标本质上是把用户信息从“消息流里的临时数据”升级成“可跨会话复用的持久化资产”。1.2 记忆缺失带来的三件尴尬事我自己在真实场景里遇到过很多次记忆缺失导致的尴尬局面最典型的有三类。第一类是重复询问。用户第一次问Agent“帮我整理一下上海周末适合骑行的路线”Agent给了五六条路线用户对比了半天隔了两天再来问“上次说的第二条路线的起点在哪里”如果Agent没有记忆它完全不知道“上次”是什么。用户只能重新描述一遍需求体验感和一个智能客服没什么区别。第二类是偏好违背。用户明确说过“我不吃辣”“我预算控制在300元以内”结果Agent在下一次对话里依然推荐川菜、推荐人均500的餐厅。这种问题不单是体验层的更严重的是会让用户产生“这个Agent不靠谱”的判断一旦用户形成这种认知再想拉回来就很难了。第三类是身份断层。比如一个Agent同时服务企业的采购、销售、财务多个角色同一个账号在不同场景下给出了不同需求如果没有记忆Agent就没办法为每个角色维护差异化的上下文。结果就是采购来问的时候Agent推荐了一批销售方向的方案不但没用反而干扰决策。这三类问题在Demo阶段几乎不会暴露但一进入真实业务立刻就会变成用户流失的原因。所以我现在做任何Agent项目记忆模块的优先级一定排在“花哨的多轮插件调用”之前。1.3 记忆为什么是Agent架构里的“地基”如果给Agent画像它至少需要三样东西稳定的目标理解能力、可靠的工具执行能力、跨时间的用户理解能力。前两项解决的是“你让我干什么我能不能干成”第三项解决的是“我能不能越来越懂你”。尤其是当Agent从一个单轮问答工具变成“数字员工”的时候记忆的价值会被迅速放大。试想一下如果一家公司把一个Agent当成售前客服来用它接待了一批又一批用户却完全不记得老用户的行业、规模、历史询价记录那这个Agent的角色定位就只是一个聊天机器人做不了客户关系维护。从工程角度看记忆还直接影响Agent的上下文成本。一个没有记忆设计的Agent为了保持“假装记得你”只能把每次把用户全部历史消息都塞进大模型上下文结果就是Token消耗越来越大、响应越来越慢、费用飙升。而有记忆设计的Agent只需要把高度提炼的偏好摘要和少量相关记忆片段送进模型就能达到接近的效果。所以说记忆设计既是体验问题也是成本问题。2. 记忆的分层模型与工程选型2.1 短期记忆上下文窗口的四种管理策略短期记忆本质上就是大模型上下文窗口里能承载的信息。现在模型上下文动辄128K甚至200K听起来很大但真实业务里塞满的速度远超想象。一段几千字的文档、几次工具调用返回、几十轮对话就可能让上下文爆掉。所以管理短期记忆核心不是“能塞多少”而是“怎么在有限空间里保留最有价值的信息”。我常用的策略是这四种组合搭配。第一种是截断。最简单粗暴只保留最近N轮消息。优点是零成本实现适合工具类、命令式的短对话场景缺点是早期关键信息会被挤掉用户开头说的一个约定可能在第十轮之后就被冲掉了。第二种是摘要压缩。每经过一定轮数就调用一次模型把前面的对话内容压缩成一个摘要再把摘要放到上下文里继续生成。这是我在真实项目里用得最多的方式。需要注意的是摘要不能只做一次要设计成“滚动摘要”也就是新摘要要结合上一轮摘要和新增消息一同生成这样才能保证信息不丢失。第三种是关键词抽取。适合那些对实时事实要求不高的场景。把对话里的关键实体——人名、地名、产品名、数字、偏好词——单独抽出来以结构化字段的形式放进系统提示词里。这个方案的优势是稳定不容易被对话过程中的闲聊冲淡。第四种是间隔标记。通过在消息列表中插入特殊的分隔标记让模型在注意力层面区分“很久以前的关键信息”和“最近的即时信息”。这个方法实现上比前三者复杂但效果有时候比单纯截断好尤其是在处理一次性长文档时。我个人的建议是不要只依赖一种策略。一个健康的短期记忆模块通常是“截断保底 摘要压缩主力 关键词抽取增强”的组合。2.2 长期记忆向量检索不是唯一答案长期记忆的第一反应是向量数据库加相似度检索这个方向本身没问题但很多人有个误区以为上了向量检索就万事大吉了。实际上长期记忆的工程难点并不仅仅是“存进去、查出来”还包括怎么组织、怎么更新、怎么避免检索干扰。而且不同场景下的长期记忆存储方式完全不一样。如果是事实型的偏好信息比如“用户住在朝阳区”“用户公司规模约200人”“用户指定联系人方式为邮件”用结构化字段存最稳定查询最快可解释性也最强。我通常会把这类记忆做成KV结构的用户画像表每次对话开始时就按用户ID拉取直接注入提示词不需要做向量检索。如果是叙事型的历史交互记录比如“用户上次咨询了关于CRM系统的选型重点关注了报表能力和移动端体验”这类信息适合用向量存储因为用户描述和历史记录之间的相似度匹配天然适合语义检索。如果是一组实体之间的关系比如“用户同时负责采购和技术选型”“用户最近三个月密集关注新能源相关的供应商”这类信息更适合存成图谱结构带着关系去检索而不是用独立的向量片段去近似。所以长期记忆的选型逻辑是这样先看数据类型再看检索模式最后才决定是用向量库、关系型数据库还是图数据库。不要一上来就铺一个Milvus或者Pinecone那是在给复杂度买单。2.3 记忆的写入时机什么时候该“记一笔”记忆功能设计里比“怎么存”更难的是“什么时候存”。如果每次对话结束就把全部内容洗一遍存进去成本高不说还容易把噪音一起存进去。我采用的策略是“事件驱动 定期沉淀”双轨并行。事件驱动写入指在对话过程中检测到明确的信息变更时立刻触发保存。这类事件包括用户修改了自己的资料、用户提出了新的明确偏好、用户完成了一个明确目标比如“选型完成最终定了A厂商”。这些事件有一个共同的判断标准——信息具有“稳定性”不会因为上下文切换而失效。定期沉淀写入指在会话进行到一定阶段或者结束时把整个会话中的待定信息做一次统一的抽取和整理更新到长期记忆里。这个操作一般在后台异步跑不会阻塞用户的下一次请求。这里有两个细节要注意。一是事件写入的触发条件要尽量配置化让业务侧可以调整“什么算重要信息”而不是把判断逻辑焊死在代码里。二是重复信息的更新策略如果用户这次说“还是不加糖吧”覆盖的是之前喝酒的时候顺手说的“偶尔喝奶茶”那么存储引擎要支持按字段更新而不是无脑追加一条否则记忆库里会积累大量相互矛盾的历史检索时前排结果互相打架。2.4 工具选型LangGraph、内置Memory模块、自研怎么选我经常被问到“做记忆直接用框架内置的Memory行不行还是要自己写”。这个问题得分开看。如果你只是做一个单机Demo或者启动页级的个人助理直接用LangChain的Memory模块或者LangGraph内置的持久化检查点完全够用节省时间不用重复造轮子。但一旦进入多用户、多租户生产环境框架内置的Memory一般会暴露出三类问题一是存储结构偏通用不够贴合你的业务实体二是检索策略简单大概率只做了最近K轮或者向量TopK缺乏精细化的相关度控制三是数据隔离和权限管理能力偏弱多用户场景容易串号。这时候就需要在框架基础上包一层自己的记忆服务。我自己的工程偏好是框架负责记忆的“基础读写”——包括消息持久化、会话断点恢复、短期上下文的组装自己负责记忆的“业务加工”——包括用户画像抽取、偏好识别、记忆合并与更新、检索后的重排与过滤。这样既利用了框架的成熟能力又保留了对记忆质量的控制权。至于MCP这类协议记忆模块完全可以用标准接口的方式挂在Agent总线里把“记忆”当作一个可被Agent调用的外部工具这个方向对多Agent协作场景尤其重要因为每个子Agent都可以独立访问统一的记忆库避免各自为政。3. 实操用LangGraph搭建一个带长期记忆的Agent3.1 环境准备与整体架构设计这一节我拿LangGraph为例走一遍真实搭建流程。之所以选LangGraph是因为它在编排带持久化状态的Agent流程时比原生LangChain更顺手图结构天然适合“对话→判断→记忆读写→回复”这种多步骤状态流转。首先准备好基础依赖在我的实践中最低限度是这么几个pip install langgraph langchain-openai chromadb sentence-transformers其中chromadb负责向量存储sentence-transformers负责本地生成Embedding也可以用OpenAI的Embedding接口但本地模型在隐私和数据合规场景下更可控。整体架构我划分为五个节点接收输入、意图识别、记忆检索、业务处理、记忆更新。接收输入节点负责把用户的请求标准化意图识别节点判断这次对话是否需要检索长期记忆记忆检索节点从向量库和用户画像表中拉取相关信息业务处理节点把记忆信息和大模型调用组装起来生成回复记忆更新节点在回复生成后异步抽取新信息并写回存储。3.2 定义对话状态与用户画像结构LangGraph的核心概念是State所有节点之间通过State传递数据。在设计记忆功能时State至少要包含三类信息当前消息、短期上下文、用户ID。我建议把用户ID和长期记忆的检索结果单独拆字段不要混在消息列表里这样后续做日志排查会更清晰。这里给出一个最小可用的状态定义from typing import TypedDict, Annotated from langgraph.graph.message import add_messages class AgentState(TypedDict): user_id: str messages: Annotated[list, add_messages] memory_notes: list[str] retrieved_memories: list[str] profile: dict其中memory_notes用于存放短期记忆的临时摘要retrieved_memories用于存放从长期记忆中检索出来的片段profile是用户的结构化画像。这个分层的好处是短期记忆和长期记忆互不污染任何一层出问题都方便单独调试。3.3 会话级记忆状态检查点与消息压缩LangGraph内置了Checkpoint机制可以在每次节点执行后保存状态快照这样Agent在流程中断后可以从某一步恢复而不是从头重跑。对于会话级记忆来说这个机制直接解决了“对话中断后恢复现场”的问题。from langgraph.checkpoint.memory import MemorySaver memory MemorySaver() graph workflow.compile(checkpointermemory) config {configurable: {thread_id: session-12345}}thread_id就是会话ID每次用户回到同一个会话时只要传入相同的thread_idAgent就能把之前的状态恢复出来。这个机制在生产环境里可以平滑换成PostgreSQL或者Redis作为底层存储实现跨实例的会话级记忆共享。但要注意Checkpoint保存的是全部消息不会自动做压缩。所以我还需要在状态流转里加一个消息精简节点当消息条数超过阈值时取出最早的一段做摘要然后把摘要作为一个特殊消息重新放回消息列表替代原来的多条原始消息。这样会话级记忆既具备恢复能力又不会无限膨胀。3.4 长期记忆向量化存储与检索节点实现长期记忆部分我采用“用户画像表 向量记忆库”的双通道方案。用户画像表用结构化字段存储向量记忆库存储那些难以格式化的语义化信息。先看向量记忆的写入。用户每次会话结束时我会跑一个抽取任务把这次对话中值得长期保存的信息转化为一条条独立的记忆文本。这一步我直接让大模型按一个固定模板输出用户ID: {user_id} 时间: {timestamp} 内容: {对用户需求或偏好的简洁描述} 标签: {主题标签}然后用一个Embedding模型把“内容”字段向量化连同时间、标签一起存入向量库。检索的时候用当前用户的提问去匹配但检索范围严格限定在该用户自己的记忆集合里这是数据隔离的底线也是避免记忆串号的关键。检索节点的核心代码如下def retrieve_memories(state: AgentState): if not state.get(messages): return {retrieved_memories: []} query state[messages][-1].content query_embedding embed_model.encode(query).tolist() results vector_store.query( query_embeddings[query_embedding], n_results5, where{user_id: state[user_id]}, include[documents, metadatas, distances] ) docs results[documents][0] distances results[distances][0] filtered [] for doc, dist in zip(docs, distances): if dist 0.75: # 相似度阈值 filtered.append(doc) return {retrieved_memories: filtered}这里有几个参数需要根据自己的场景反复调后面第5章我会展开讲。3.5 记忆与生成的融合提示词组装策略记忆检索出来之后怎么塞进大模型的上下文里也是一门学问。我的做法是做两级融合。第一级是把结构化画像直接注入系统提示词。比如“用户当前信息姓名张某所在城市杭州饮食偏好不吃辣预算区间300-500元”。这类信息要放在系统提示词最靠前的位置让模型在生成时始终能“看到”这些约束。第二级是把向量检索出来的非结构化记忆片段放到对话前的记忆上下文中并在每条记忆前加上元信息比如记忆时间、来源话题这样模型能判断这条记忆的新旧和适用性而不是把三个月前一条过期的记忆当作当前事实。我特别不建议的做法是把检索出来的大批记忆原文直接塞进消息列表和当前对话混在一起。那样既会让模型分不清“哪个是用户现在说的哪个是历史记忆”也容易造成token浪费和注意力的稀释。记忆是参考材料不是对话本身这条边界一定要保持住。4. 让Agent记住你的偏好从对话数据到用户画像4.1 偏好抽取Prompt模板与结构化输出的配合让Agent记住用户偏好第一步是把自然语言对话里的偏好信息抽出来。这个环节如果纯靠代码写规则去匹配关键词效果会很差。用户在对话里的表达千奇百怪“太辣了受不了”“上次吃的那家还不错”“我一般下午才有空”这些话里都有可抽取的信息但没有任何一个正则能稳定覆盖。我的做法是让大模型做抽取并强制按JSON结构输出。下面这个模板是我经过多轮迭代后比较稳定的一版你是用户画像分析器。请从以下对话中提取与用户偏好、个人信息、明确态度相关的信息。 输出格式为JSON数组每个元素包含 - name: 字段名如饮食偏好 - value: 字段值如不吃辣 - category: 分类可选值为 basic_info / preference / attitude / goal - confidence: 置信度0到1之间只有大于0.7的才输出 对话内容 {transcript}抽取完成后会得到一批候选字段再按字段名去更新数据库。这里有个细节不要直接覆盖旧值而是要保留一个“更新时间戳”并在两个值冲突时让用户确认或者以最近一次为准并记录旧值。这个设计可以避免用户一句无心的玩笑话把历史真实偏好覆盖掉。4.2 标签化记忆与自然语言记忆的取舍我见过不少团队在记忆设计上纠结到底把记忆做成结构化标签还是一段自然语言文本标签化记忆的优点是可检索、可统计、可做精确过滤比如“预算500”能直接走SQL查询而且在大屏统计用户画像时非常好用。缺点是信息密度低用户很多细微的偏好无法用标签穷尽。比如“用户喜欢在项目紧张时用番茄工作法但周末更倾向自由安排”这种带条件偏好的信息标签化基本表达不了。自然语言记忆的优点是信息无损可以保留用户的原话语义更贴近真实表达缺点是检索噪音大更新合并逻辑复杂而且容易把错误语义存下来。我现在采用的方案是两者结合能标签化的信息尽量标签化作为画像表里的结构化字段比如收件地址、餐饮禁忌、联系时间带语境和条件的信息比如“如果遇到XX情况用户更倾向于YY做法”就写成年月日语义化的记忆文本存向量库。这样既有标签的精确性也有自然语言的丰富度。4.3 记忆可信度不是所有“用户说的”都该被记住做记忆系统最容易踩的暗坑是把用户所有表达都当成稳定事实存下来。用户可能只是随口吐槽一句“这家店太贵了”并不代表他以后都不接受高价消费用户可能在一次探索性尝试中说“我想试试做跨境电商”但这未必是他要长期投入的方向。所以我在做记忆抽取时明确要求模型输出confidence字段并且规定只有confidence高于阈值的才写入长期记忆。低置信度的信息最多留在会话级记忆里随会话过期。更深一层记忆必须有“来源可追溯性”。每条记忆都要记录它来自哪一次对话、当时的原文是什么。这样当记忆出错或者和当前需求冲突时可以回溯原始对话检查是抽取错误还是用户后来改了主意。如果没有这个能力记忆库会慢慢变成一个不可解释的黑盒越用越不敢信。5. 实战中踩过的坑与排查方法5.1 Token膨胀一次对话烧掉几千Token第一个遇到的坑是Token膨胀而且膨胀速度比想象中快得多。尤其是当你把历史消息、系统提示词、记忆片段、工具返回结果全部拼在一起时一次请求轻松超过上万Token。我排查成本时发现大头往往不是用户对话而是系统提示词和注入的历史记录。解决思路分三步第一步给每条消息设置“过期时间”第二步对工具返回结果做摘要再入会话记忆第三步把所有注入的模块做Token配额比如系统提示词固定分配1000Token记忆片段分配500Token超出部分强制截断。在实际调优中我还发现一个反直觉的现象不用的记忆不要硬塞。与其检索五条记忆但其中三条不相关不如只放一条高度相关的记忆加一条用户画像。记忆的关键是精准不是数量。5.2 检索不精准相似但不相关怎么破向量检索最大的坑是“语义相似但业务不相关”。比如用户问“上次说的那个供应商后来怎么样了”检索结果可能匹配出大量关于供应商行业分析的内容唯独少了他真正的诉求——和上次咨询的供应商报价相关的纪要。解决办法有两个方向。第一个方向是在存储时给每条记忆打业务标签检索时用“用户提问里面的业务关键词 标签过滤 向量相似度”三重条件一起过滤而不是只靠向量。第二个方向是检索后加一个“相关性重排”节点用更精细的模型对候选集重新打分过滤掉低质量结果。我以为第一步用标签过滤解决90%的问题重排是锦上添花。如果业务数据本身噪音不大正确率就能接受没必要上重排模型。5.3 多用户数据隔离一条记忆串到另一个人头上多用户场景里最可怕的不是“想不起来”而是“想错了人”。这个问题一旦出现就是对产品信任度的致命打击。我遇到过一次事故排查下来发现是向量库里没有强制按user_id做filter导致检索时候把A用户的记忆返回给了B用户。修复方案很简单就是每一处query都强制带上user_id过滤条件并且写单元测试覆盖。更稳妥的做法是把每个用户的记忆存到独立的collection或独立的分区里从物理上隔离而不是只在查询时靠条件过滤。5.4 用户改了口型记忆更新与冲突处理没有人永远不变用户的偏好也会迭代。用户上个月说“我只喝美式”这个月改成“最近喜欢上拿铁了”。如果没有处理机制Agent会同时查到两条互相矛盾的记忆然后表现得非常不稳定。我处理这个问题的方式是给每条记忆加一个“状态”字段取值是active、pending、expired。新抽取的记忆默认pending只有连续两次会话中被验证或确认后才转成active当新信息和active记忆冲突时旧的active记忆降级为expired保留但不参与默认检索。这个机制不需要用户显式操作却能有效避免偏好漂移带来的混乱。5.5 冷启动与遗忘策略记忆系统的冷启动问题说白了就是新用户一点记忆都没有Agent怎么给出像样的“记住你”体验。我的建议是冷启动阶段不要硬装熟而是主动引导在对话中自然地询问一些关键偏好的问题然后把答案存进画像表。注意措辞要自然不要像填表单一样连着追问十个问题用户会烦。遗忘策略也很重要。记忆不是越多越好长期不访问的记忆应该逐渐降低优先级定期归档。我设定的规则是180天未被检索触达的记忆自动降级365天未被触达的记忆进入冷存储不再参与在线检索。这个策略既控制了存储成本也避免大量过时的记忆干扰实时决策。6. 从“记住对话”到“记住你”下一步的进阶方向6.1 记忆的结构化从散点记录到关系图谱单条记忆用得再溜也只是“记住了一堆事实”。要让Agent真正理解用户还需要把分散的记忆组织成关系结构。比如“用户住在杭州”“用户工作是产品经理”“用户正在做AI Agent相关项目”这三条记忆单独看都是独立事实但放在一起就勾勒出一个立体的用户侧写。我下一步的实验方向是引入轻量级图谱存储来管理记忆之间的关系。图谱的节点是实体边是关系这样Agent不仅能回答“用户的偏好是什么”还能回答“用户偏好背后的原因可能是什么”推理能力会明显增强。目前这套方案在Neo4j和R2R这类工具上都有现成实现但工程成本还是比普通向量库要高适合在核心用户群上灰度。6.2 主动遗忘与隐私合规记忆能力越强用户隐私风险越大。一个能记住你一切的Agent从另一个角度看就是一个汇集了大量敏感数据的系统。做工程的人一定要在产品设计之初就把数据安全放进来而不是等服务跑大了再补。我的常规做法是所有记忆数据强制加密存储用户画像表启用字段级权限控制删除用户数据时要连向量库里的Embedding一并删除。更重要的是给用户“忘掉我”的权利产品界面上必须有“清除记忆”入口一旦用户触发就要彻底且不可逆地删除该用户的全部记忆。这个能力既是合规要求也是建立用户信任的基础。6.3 多模态与团队级记忆的想象空间随着多模态模型成熟记忆的形态也在从纯文本扩展到语音、图片、视频。用户之前发过一张会议室照片说“就订这个风格”Agent下一次设计方案时如果能检索到这张照片理解会完全不同。这块目前还在早期但我觉得一定是Agent记忆下一阶段的标配。再往外想一步如果沿ChatGPT这种个人助理的路子再往前推团队级记忆也很有想象空间一个项目组共用一个知识库Agent记住团队的技术栈偏好、文档规范、历史决策原因新成员加入后可以直接通过Agent了解项目背景。这个场景比个人偏好更复杂涉及到权限模型和知识的可信度判断但商业价值巨大。回到实践层面我的体感是做记忆功能一定要先控制范围不要第一步就想做个面面俱到的“全人类记忆系统”。先在一个垂直场景里跑通“写入→存储→检索→更新→遗忘”这条链路再逐步加复杂度。记忆模块是Agent真正走向生产环境最重要的分水岭它决定了用户是把Agent当成一个临时工具还是当成一个值得托付的长期助手。
上一篇/下一篇内容由系统自动关联 返回资讯列表 →