上个季度我们团队接手了一个客服答疑Agent的升级改造原来的系统勉强能跑用户对话稍微绕一点就答非所问——今天报修的设备明天再问它完全不记得用户只能把品牌型号重新报一遍。老业务吐槽说这哪叫Agent顶多算个高级接口查询工具。后来我们把整条链路拆开重做核心就落在三件事上Agent扩展范式怎么选、Memory记忆体系怎么搭、多轮记忆改造怎么做。改造完跑了整整一个月同一批用户的重复咨询满意度从62%涨到了88%用户终于不用反复交代自己的背景信息了。这篇文章就是我在这轮改造里积累下来的完整思路和踩坑记录适合正在做Agent开发、打算给Agent加记忆能力、或者准备把手头单轮对话工具升级成多轮记忆Agent的工程师参考。1. 扩展范式决定Agent的四肢三种主流机制怎么选1.1 无扩展能力的Agent只是个会说话的接口先说一个很多人容易忽略的前提一个Agent如果只能聊不能做那不叫智能体叫聊天机器人。真正的Agent至少要能调工具、查数据、写文件、操作外部系统而这些能力都依赖扩展机制来加载。我接手原有项目的时候那个系统本质上就是LLM 单函数调用的玩具结构。每个意图写一个匹配规则命中后调用一个写死的函数模型完全看不到函数之外的扩展空间。一旦业务方提了个不在预设规则里的需求开发就得加班改代码。这种方案的问题非常明显扩展成本高、耦合严重、模型没有选择权。1.2 Function Calling、ReAct、Skill注册制三种范式的适用边界目前在Agent开发里扩展范式的主流选择基本是这三条路Function Calling工具调用模型根据系统预设的工具Schema自主决定调用哪个函数、传什么参数。OpenAI、Claude、国产大模型都支持这个模式。优点是实现简单、决策可控缺点是函数一旦多了模型容易选错需要做好路由约束。ReAct推理-行动-观察循环模型边推理边行动把思考→执行→看结果→再思考串成一个循环适合需要多步操作才能完成的复杂任务。缺点是循环次数多了延迟高、token消耗大而且模型在循环里容易跑偏。Skill注册制技能封装把一组相关工具打包成技能Agent按场景加载对应技能包。比如客服场景加载工单技能退款技能排障技能而不是把所有函数一股脑丢给模型。这种方式在大规模Agent系统里最实用也是我们这次改造的核心范式。1.3 我最终选择的扩展范式技能包加函数路由改造后我选的是Skill注册制 函数白名单的混合方案理由很直接第一场景隔离。客服业务里面有几十个上游系统接口如果全部注册成Function模型在几十个相似函数里挑准确率一定崩。按技能包隔离后Agent先通过意图识别定位到售后排障技能包再从包内的五六个函数里选择路由空间小了一个数量级。第二权限可控。技能包可以配独立权限比如订单查询技能普通用户能用内部工单修改技能必须验证坐席身份。这比一把梭地开放所有工具安全得多。第三扩展成本低。新业务接入时只需要注册一个新的技能包注册信息包括技能描述、函数Schema、触发条件Agent的调度核心完全不用改。# 技能注册的伪代码示意 skill_registry {} def register_skill(name, description, tools, triggers, permissionuser): skill_registry[name] { description: description, tools: tools, # 函数定义列表 triggers: triggers, # 触发关键词/意图条件 permission: permission, } register_skill( nameafter_sale_troubleshooting, description处理售后排障、报修、设备回访等请求, tools[search_order, fetch_device_status, create_work_order], triggers[报修, 故障, 无法使用, 维修进度], permissionuser, )这套范式跑了大半个月工具选择准确率从改造前的74%提升到了93%效果非常明显。如果你正在搭新Agent我建议别一上来就堆Function先想想业务场景能不能拆成几个技能包。2. Memory记忆体系不是Redis缓存那么简单2.1 记忆的本质从无状态API到有状态协作者第一次给Agent加记忆的时候我和大多数人一样第一反应是用Redis把之前的对话存下来下次拼进Prompt不就行了。真正做完才发现这个思路连及格线都够不着。原因在于记忆不只是存储而是理解什么该记、什么该忘、什么该用。一个没有记忆的Agent在每一轮对话里都是失忆者它面对的是全新的空白上下文而一个有记忆的Agent应该是像一个接手老客户的老员工——知道客户以前买过什么、说过什么、抱怨过什么。2.2 把记忆拆成四层工作记忆、情境记忆、语义记忆、偏好记忆我们做记忆体系设计时参考了认知科学里对记忆的分类方式结合工程实际拆成了四层记忆层级对应内容存储方式更新频率工作记忆当前对话的临时上下文、用户刚说的话会话内直接用不落库每轮更新情景记忆历史对话记录、之前报修过什么对话日志 摘要索引每轮追加语义记忆用户画像、设备信息、业务规则结构化实体表定期更新偏好记忆用户沟通习惯、产品偏好偏好向量低频更新这个分层最大的价值是记忆的使用方式完全不同。工作记忆直接进Prompt情景记忆要检索命中才用语义记忆是全局强约束偏好记忆则只在推荐场景触发。如果不分层一股脑全塞进上下文模型会被几百条历史记录撑爆注意力反而答得更差。2.3 存储引擎选型向量库、KV库、关系表的混合架构记忆分层之后存储引擎的问题自然浮出来了。我们的选择是向量库做情景记忆的检索Redis做工作记忆和偏好缓存的读写PostgreSQL存用户实体和业务关系的结构化数据图结构用来表达实体之间的关联。场景化解释一下用户问我上次那个设备报修到底处理到哪个环节了这句话需要从历史对话里检索出上次那个设备对应的工单记录这里必须用向量相似度去找关键词匹配几乎找不到。用户画像和业务规则是高度结构化的比如用户等级VIP设备型号X200巡检仪放关系表里查询快、好维护。偏好记忆不需要太强的语义检索一份JSON塞进Redis就行读取延迟能控制在毫秒级。提示不要在项目初期就迷信向量库。如果你的历史记忆量在几十万条以内业务关系又很清晰那用PostgreSQLpgvector就够了真没必要单独部署一套Milvus。我见过太多团队还没建好记忆体系就先搭了三个中间件最后一个月过去了数据怎么写入都没想明白。2.4 记忆生命周期写入、衰减、归档、遗忘工程里最容易被忽略的环节是记忆的死亡管理。我们设计了四条策略写入策略不是所有对话都值得记。能沉淀为事实的信息用户身份、设备型号、明确偏好才写入语义记忆有价值的操作过程报修、退款、投诉写入情景记忆废话直接丢弃。衰减策略情景记忆设置时间衰减权重三个月前的维修记录权重降到新记录的30%检索排序时自动靠后。归档策略一年以上的旧工单、旧对话定期归档到冷存储不参与在线检索减少向量库压力。遗忘策略用户明确要求删除的信息、离职员工的档案、过期的营销偏好必须能真正物理删除这在记忆系统里是最基础也最容易被漏掉的合规要求。这套生命周期的管理一开始看着繁琐但等到记忆量上来之后你就会发现没有它系统早就被垃圾记忆拖垮了。3. 多轮记忆改造从无状态到有状态的完整升级路径3.1 改造前单轮无状态Agent的痛点复盘改造前的原系统是彻底的无状态结构用户每发一条消息服务端就直接把它丢给LLM生成完回复就结束什么都不留。这种结构有两个致命伤第一上下文缺失导致体验断裂。用户说那台设备还是不行系统根本不知道那台设备是哪台只能反问用户报修到一半下线第二天回来又得从头描述。第二业务处置无法连续。一个退换货流程需要校验订单、确认库存、生成退回单但每一步的中间状态都没存流程走一半就卡死。拆掉重做之后我们围绕会话结构和记忆管线做了两轮改造下面分别展开。3.2 会话层改造session、turn、event三层结构第一轮的改动是给所有对话建立结构化的会话框架。我们定义了三个基础对象Session会话一次完整的用户交互链路可以跨多天比如用户从报修到完成退款算一个Session。Session里存了用户ID、开始时间、目标、结束状态。Turn轮次用户说一句话加上Agent回一句话算一个Turn。Turn里存双方的原文、意图识别结果、命中的技能包。Event事件Turn里面发生的关键业务动作比如创建工单查询库存发送退款通知每个Event带时间戳和结果状态。这套结构的价值在于任何时刻你都能回答用户现在进行到哪一步了。系统重启、服务迁移、Session切换都不会丢状态。# 三层结构示意 class Session: id: str user_id: str goal: str | None created_at: datetime status: str # active / closed / expired class Turn: id: str session_id: str user_msg: str agent_msg: str intent: str skill_hit: str | None created_at: datetime class Event: id: str turn_id: str action: str # 如 create_work_order params: dict result: dict status: str created_at: datetime3.3 记忆写入管线什么时候沉淀、什么时候忽略有了上面的事件结构记忆写入这条管线才真正跑得起来。我们把记忆写入拆成四个步骤第一步抽取候选记忆。每一轮Turn结束后由一个专门的抽取模块判断该轮是否有值得记录的信息。判断依据是三类信号命名实体设备型号、订单号、人名、意图标签报修、投诉、退款、以及情绪信号强烈不满或高度满意。第二步去重与冲突检测。候选记忆要先和已有的语义记忆比对。比如用户第一次说我在上海半小时后说我在北京出差那所在地这条记忆不应该被覆盖成北京而应该补一个出差地点字段。这一层的处理非常关键否则用户的画像会被单次随口说的话污染。第三步重要性打分。我们给每条候选记忆打一个0到1的重要性分综合来源可信度用户主动陈述比分三方的减分项得分高、时间相关性和业务影响。低于0.6分的直接丢弃高分的写入长期记忆。第四步异步落库。记忆写入不能阻塞对话主链路全部走异步任务写入完成后再更新记忆索引。我在这块踩过很痛的坑当时同步写库导致对话响应延迟从400ms飙到1.2秒用户直接骂街。# 记忆写入管线伪代码 async def process_turn_memory(turn): candidates extract_memory_candidates(turn) for cand in candidates: if exists_similar(cand): merge_or_upsert(cand) # 冲突处理 continue score importance_score(cand) if score 0.6: await async_write_longterm_memory(cand) await update_memory_index()3.4 记忆读取管线上下文组装与Token预算写入只是第一步真正决定用户体验的是读取——到底哪些记忆该在什么时机被放进LLM的上下文里。我们的读取管线是三层组装策略第一层固定上下文系统指令 用户画像。用户ID一旦确定就把语义记忆里的用户核心画像姓名、等级、常用设备拼进系统指令全程不换。这部分优先级最高Token预算大概占总预算的20%。第二层情景检索相关历史对话。根据当前用户输入向量化后在历史对话记忆库里检索Top-K条最相关记录。这里的K不是固定的我们按当前对话复杂度动态调整简单问题K3复杂问题K8。预算大概占总预算的40%。第三层工作记忆当前会话动态信息。就是本Session内前面几轮的对话原文或摘要。我们用一个可滚动窗口只保留最近5到8轮原文更早的对话滚动压成摘要。预算大概占总预算的30%。这样三层组装出来的上下文既有稳定画像又有相关回忆还有连续性比单纯地把所有历史对话堆进去要干净得多也省Token得多。4. 记忆检索与冲突消解多轮对话的智商分水岭4.1 检索质量的三根支柱相关性、时效性、可信度很多人以为记忆检索就是向量相似度哪条历史对话和当前输入向量距离最近就召回哪条。真实跑上线不到两周你就会发现这种检索方式会给你召回一大堆看起来有关但实际没用的记忆。我总结下来合格的记忆检索必须同时考核三个维度相关性和历史对话的语义匹配度这个靠向量模型。时效性新记忆权重高于旧记忆需要时间衰减因子干预排序。可信度用户陈述的原始信息比模型推断的信息更可信主动确认的信息比自动抽取的信息更可信。三条混在一起排序之后召回的命中率才谈得上可用。4.2 混合检索加重排BM25加向量加时间衰减的搭配具体实现上我用的不是单一的向量检索而是混合检索加重新排序的架构第一路向量检索。使用对话Embedding模型把历史记忆和当前问题都向量化做Top-50粗召回。第二路关键词检索。用BM25做关键词匹配特别适合设备型号、工单号、城市名这类强标识信息。向量模型对X200-07这种字符串的理解很弱但BM25一抓一个准。第三路时间衰减融合。两路召回结果合并后每条记忆除以一个时间衰减系数。合并之后的Top-20候选再用一个Cross-Encoder重排序模型精排取Top-K进上下文。整条链路在高并发下依然能保持70到120毫秒的检索延迟。注意Cross-Encoder重排模型是小模型不能和大模型混淆。我当时直接用大模型做重排一次请求多等了一两秒还烧Token完全不划算。后来换成一个8亿参数的Cross-Encoder单条打分耗时5毫秒效果差了不到两个点。4.3 记忆冲突消解与事实校验多轮对话里最隐蔽的坑是记忆冲突。用户周一在长沙说设备安装在长沙园区周三在武汉问长沙那台设备能调过来吗如果记忆系统把用户最新位置覆盖掉就会出现定位在武汉但设备记录还在长沙的矛盾。我们处理冲突的方式是不覆盖做版本化。每条记忆保留一个属性时间线比如用户的所在地是一个时间线数组每个时间点记录对应的值。检索的时候优先取最新值但如果当前问题涉及的是过去某个时间点的业务就取当时的值。这套时间线模型在工程上比一张画像表复杂不少但真实业务场景里几乎每天都有这种冲突。如果你正在做多轮记忆改造我强烈建议从一开始就按时间线建模后面省下的补丁代码不是一点半点。5. 多轮记忆改造中翻过的车与填过的坑5.1 记忆污染用户随口一句话毁了画像第一版上线后我们收到最多的问题反馈是为什么用户没买过的东西被Agent说成买过排查后发现问题出在记忆写入的抽取环节。用户说我考虑看看你们那个新款抽取模块把它当成购买意图写入语义记忆用户想购买新款第二天用户又说算了太贵了模型又写入用户放弃购买。两条矛盾记忆同时存在Agent在回复里一会儿推荐新款一会儿推荐旧款。这个案例给我的教训非常直接意图类信息不能被当成事实记忆去存。后来的规则是只有明确发生的行为才入语义记忆一切意向、猜测、情绪只能留在情景记忆里供参考不能作为决策依据。业务上我们把这条规则叫做事实前置校验。5.2 上下文无限膨胀与Token失控改造初期我们架了一版无限记忆的上下文——把所有历史对话全文拼进Prompt。结果不用我说你也知道Token账单一个月翻了四倍模型输出质量反而下降。原因是上下文越长注意力分布越稀疏模型对关键信息的敏感度反而降低。之后我们做了三层治理第一历史滚动窗口只保留最近8轮原文第二更早的对话由摘要模型生成压缩摘要而不是堆原文第三严格控制进上下文的检索记忆数量最多不超过10条。做完这三步单次请求的平均Token消耗下降了58%响应延迟也明显改善。5.3 召回疲劳记忆太多反而让Agent变蠢还有一个有意思的坑记忆召回太多Agent会变成话题复读机。用户明明在问新功能Agent非要扯上三个月前的旧维修记录。因为检索系统太勤快把跟设备沾边的记忆全部召回模型就容易抓着旧信息不放。解决方案是给召回加一道场景门槛只有当前意图和候选记忆的意图标签匹配时该记忆才能进入最终上下文。比如当前意图是功能咨询那维修记录类记忆即使相似度高也不召回当前意图是售后排障维修记录才会放行。这一步加完之后用户的答非所问投诉率明显下降。5.4 并发会话与记忆锁的坑当同一个用户同时开着两个会话窗口时两个会话都要写入和读取同一份用户记忆就可能出现脏写一个会话把用户所在地改成广州另一个会话读的是旧数据回了跟广州冲突的回答。我们最后用用户ID级别的分布式锁解决写入冲突同一个用户ID的写操作串行化读操作走乐观并发控制用版本号校验。虽然牺牲了一点点并发度但换来的是记忆一致性这在对话场景里是值得的。6. 记忆框架选型与改造后的效果评估6.1 主流方案盘点Mem0、LangMem、MemoryScope与自研做Memory体系之前我们对比了当前主流的记忆开源方案各有适用的语境方案核心特点适合场景局限Mem0自动抽取、分层记忆、自带秩机制想快速上线记忆能力的团队较抽象深度定制要阅读源码LangMem深度绑定LangChain生态、会话管理完整已用LangChain的团队换框架成本高MemoryScope跨场景记忆共享、可视化管理强调记忆可观测性的项目社区相对年轻自研方案按业务裁剪、完全可控业务逻辑复杂、已有架构成熟开发和维护成本高6.2 评估指标多轮一致性、回忆命中率、响应延迟改造完成后我们建了一套面向记忆能力的评估体系核心看三个指标多轮一致性构造一个横跨10轮以上的测试对话检查Agent回答是否和之前的对话事实一致。比如第1轮告诉它设备在苏州第8轮问设备在哪答对才通过。回忆命中率在历史对话库里随机抽取200条事实人工构造200个需要引用该事实才能回答的问题跑Agent看能答对多少。我们改造前只有41%改造后到了82%。响应延迟与成本记录单轮对话的平均延迟和Token成本防止加记忆后性能劣化。改造后端到端P95延迟控制在2秒内成本比改造前反而低了22%。6.3 我的选型建议如果你问我最终建议我的观点是在业务还不复杂、记忆量在一两万条以内的时候直接用Mem0或LangMem快速跑通别自己造轮子但如果你和我一样接手的系统本身业务耦合深、多租户权限复杂、需要深度定制记忆的生命周期管理那自研记忆层是早晚的事。不管选哪条路上面讲的记忆分层、读写管线、冲突消解这些设计原则都是通用的框架只会帮你省掉一部分存储和调度的活真正的业务逻辑永远要自己掌握。我们最后保留了一个自研的轻量记忆核心外面套了向量索引整体代码量不到五千行。跑了一个月稳定性、扩展性都验证到了。如果你也在做多轮记忆改造建议先从最小闭环开始建好Session结构加一条写入管线再接一条读取管线跑通后再慢慢完善衰减和冲突处理。别想着一口气把所有能力都做满记忆系统是典型的增量迭代工程——每次只加一个可靠环节比一次堆一堆半成品靠谱得多。
企业数字化 ERP 产品动态
相关推荐
AI Agent长期记忆实战:agent-memory分层设计、召回机制与工程落地 上周有个朋友跟我吐槽:他本地部署了一套开源Agent,费了半天劲把工具调用调通了,结果第二天继续聊的时候,Agent完全不记得他昨天说过自己是前端开发者,又一次给他推了一堆后端框架的资料。这个问题我太熟了——做过Agen… · 2026/9/26 7:21:29
AI写代码快但安全谁负责?建立代码安全审查机制 1. AI写代码这件事,到底改变了什么这两年跟同行聊天,话题绕来绕去总会落到同一个点上:AI写代码是真快。以前一个CRUD接口从建表到联调,怎么也得小半天,现在把需求描述清楚,几十秒就能吐出一整套能跑的逻辑。… · 2026/9/26 7:21:17
Stacking模型融合:原理、代码与调试经验详解 做机器学习项目到一定阶段,你会遇到一个尴尬局面:单个模型的效果死活上不去,调参调到怀疑人生,验证集分数就是卡在某个瓶颈附近不动了。这时候很多人的第一反应是换更复杂的模型,或者堆更多特征,但往往忽略… · 2026/9/26 7:56:17
Python自动抢票脚本Autoticket:环境搭建与实战配置指南 1. 抢票这件事,为什么手动永远拼不过脚本每年一到演唱会、音乐节、话剧开票的日子,大麦网的服务器就要经历一次全民级别的压力测试。我身边不少朋友都有过这样的经历:提前十分钟守在手机前,倒计时归零的瞬间疯狂点击,结… · 2026/9/26 7:56:17
企业级AI智能体选型评估:从业务问题到POC落地的完整决策指南 开头就一句话:过去两年我帮不少企业做过AI智能体的选型评估,几乎每次都能看到同一个场景——售前Demo里,Agent在测试环境里流畅地处理工单、自动写代码、多轮对话调度工具,所有人都觉得“这就是我们想要的”;等项目一进… · 2026/9/26 7:56:17
ArkTS接口赋值与匿名实现:类型系统规则与避坑指南 前阵子有个同事拿着一行编译报错来找我。代码逻辑很简单:他定义了一个接口Person { name: string },然后写了一句let obj { name: "张三", age: 18 }; let p: Person obj;,DevEco Studio 直接给他画了红色波浪线。他盯着屏幕看了… · 2026/9/26 7:56:17
Linux连接Windows必用xfreerdp3:RDP 10.0兼容性实战指南 1. 项目概述:为什么在 Linux 上用 xfreerdp3 连 Windows 不再是“将就”,而是首选 最近帮三个不同行业的客户部署远程办公环境,发现一个明显趋势:只要终端是 Linux(不管是 Ubuntu 20.04 桌面版、CentOS 7 服务器、还是… · 2026/9/26 7:56:17
MySQL教材源码包使用指南:从环境配置到数据导入的完整教程 简介:面向MySQL数据库初学者的配套源代码包,围绕《MySQL数据库基础实例教程(第3版)(微课版)》设计,按例题、案例、实训、实战四个模块组织,覆盖从基础建表到综合项目开发的完整练习路… · 2026/9/26 7:56:11
数据库课后习题答案别硬背:当测试用例集刷,效率翻倍 简介:万常选版《数据库原理与设计》课后习题答案资源,覆盖第2至6章及第9章,适合正在学习关系模型、数据库建模、关系数据理论与模式求精的本科生、自学者作为复习与自测材料。压缩包共7个文件,含3个doc参考答案、2个sql示例脚本、… · 2026/9/26 0:00:21
OpenClaw 替代品?Hermes Agent 踩坑实录:macOS 飞书接入 TaoToken 配置 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/9/26 0:00:40
向下兼容与向上兼容:接口设计中的兼容性策略与工程实践 一次版本升级事故,是很多团队绕不过去的坎。线上环境里,服务端明明已经上线了新版接口,老的移动端还在照着旧文档传参数。请求一到网关,校验直接拒绝,用户操作失败,客服群炸了锅,开发群里开始互… · 2026/9/26 0:00:46