你有没有遇到过这种情况:你和某个AI助手聊了半小时,它把你的项目背景、忌讳、喜欢的表达风格都摸得一清二楚。第二天你重新打开对话框,它像失忆了一样,把你的需求从头又问一遍,甚至重复推荐你已经明确否掉的方案。用户问一句“你记不记得我说过什么”——这个问题,是所有从Demo走向生产级的AI Agent都要面对的灵魂拷问。
这个问题的根子,不在模型能力,而在记忆系统缺失。模型再聪明,一次对话能装的信息也有限,对话一关更是片甲不留。所以当我开始从零搭建一个生产级记忆型AI Agent时,第一件事不是挑模型,而是设计一套能“记得住、想得起、忘得掉”的记忆系统。整个项目我选了AgentScope作为底座——它不只是一个Agent开发框架,更像是一套为多Agent协作和运行时管理设计的完整环境。
这篇东西不写空理论,完全围绕项目实操展开:为什么Agent需要记忆、记忆系统怎么设计、“记忆=score+时间半衰期”到底怎么落地、AgentScope怎么用、以及真正上生产时会踩到哪些文档里不会写的坑。适合正在做Agent产品、被“长期对话失忆”折磨过、或者打算用AgentScope搭业务系统的朋友。读完你至少能照着搭出一版带跨会话记忆的Agent,并且知道怎么把它往生产方向迭代。
1. 为什么普通Agent越用越傻:记忆缺失的三个典型症状
不管你是用LangChain、自研框架还是AgentScope,只要没做记忆系统,Agent用起来必然出现下面三个症状。先认清症状,后面对症设计才有依据。
1.1 会话一长,关键信息被“淹没”
现在的LLM上下文窗口越来越大,128K、200K已经不稀奇,但窗口大不等于记忆好。实际跑过的人都有体会:当上下文里堆积了几万token的聊天流水账时,模型对用户真正的偏好在注意力层面会被稀释。比如用户在对话第10轮说“我不用Java,团队全是Go”,到第80轮你再问它技术栈偏好,它可能就忘了或者答得模棱两可。因为那段信息混在两百条闲聊里,相对权重太低了。
这不是模型笨,是我们把“存储”和“记忆”混为一谈了。存储是把所有东西堆在桌面上,记忆是从桌面上挑出此刻最重要的三样放到手边。不做取舍的上下文堆积,本质上只是换了一种丢信息的方式——以前是关对话丢,现在是开对话也丢。
1.2 会话一关,一切归零
这个最好理解。传统ChatBot也好,加了流式输出的Agent也好,如果不做任何持久化,新会话就是一张白纸。用户上礼拜跟你确认过的订单编号、供应商报价、项目截止时间,全部消失。他们不会觉得这是技术限制,只会觉得“AI真笨,一点不长记性”。
跨会话失忆最直接的影响是信任崩塌。用户愿意多花时间向你描述需求,是因为预期你下次能基于这些信息继续服务。一旦发现你全忘了,他就得从头解释,这个体验坏掉的速度比任何功能短板都快。
1.3 有“记忆”但不会“用”
还有一种情况比没记忆更气人——有的系统确实把对话历史存进数据库了,但用的时候一股脑灌进Prompt。于是你会看到一个Agent面对8000字的陈旧信息,不仅没变聪明,反而更爱胡说八道。因为它把“历史消息”当成了“事实知识”,没有区分哪些是当时的废话、哪些是值得长期遵循的用户意图。
记忆的真正定义不是“存了多少”,而是“在正确的时机,把正确的信息,以正确的方式送到模型面前”。存储、检索、遗忘、注入,这四个环节缺一不可,任何一个做不好,记忆系统都是摆设。
2. 记忆系统设计四问:存什么、怎么存、何时取、如何忘
动手写代码之前,先把设计问题想清楚。我建议所有做记忆型Agent的人,都先回答这四问。表面上这四个问题是常识,但落地时90%的方案都折在其中某一条上。
2.1 存什么:从对话流里提炼“值得记住的事”
记忆不是日志。把每一轮用户消息原封不动存进去,那不叫记忆系统,叫监控存档。真正值得进入长期记忆的信息,我按经验分成四类:
- 用户画像:姓名、身份、团队规模、技术栈、工作节奏、沟通偏好、禁忌。这类信息稳定,价值高,是要长期保存的“硬通货”。
- 关键事实:已完成的事项、确认过的决策、重要事件的时间点。比如“用户已经确认Q2上线”“供应商换成了XX”。
- 待办与承诺:用户明确要求后续处理的事情,以及你答应过要做的事。这类信息有时效性,到期前要能主动调出来。
- 偏好与经验:用户对某个方案的态度、踩过的坑、强调过的原则。比如“用户强烈反对在生产环境直接改库”。
从原始对话到结构化记忆,这一步我建议用一个专门的编码Prompt,让LLM做信息抽取和结构化。不要相信正则表达式和关键词,自然语言里的隐含信息太多。下面是我项目里实际用的一套抽取指令,效果比较稳:
你是记忆编码器。从下面这段对话中提取值得长期记住的信息,只输出JSON数组。 每条记忆字段: - type: user_profile / fact / todo / preference - content: 一段概括性描述,第三人称 - importance: 0到1之间的数字,表示这条信息对后续对话的价值 - expire_at: 可选,绝对时间戳,表示这条信息何时失效 提取原则: 1. 只提取对“未来还能复用”的信息,不存储寒暄和一次性请求。 2. 用户明确表达过的偏好、禁忌、承诺优先。 3. 宁可少提取,不要为了凑数而提取。加了这段Prompt之后,记忆中“垃圾条目”的比例大幅下降。关键心得是:编码质量直接决定整个记忆系统的天花板,宁可少存,不要乱存。
2.2 怎么存:双网络记忆模型的具体映射
“双网络记忆模型”听起来玄乎,说白了就是把记忆分成两个通道,各干各的:
短期工作记忆,对应当前会话内的上下文。它容量有限、速度要求高、跟着会话走。工程上我就是一个环形缓冲区,保留最近N轮关键消息的摘要,会话结束就归档。它负责解决“上下文淹没”问题——不是把历史全塞进去,而是把历史压成几条要点放在手边。
长期语义记忆,对应跨会话的持久化知识。它存储在向量库+结构化数据库里,容量理论上无限,但访问成本高。它负责解决“会话一关一切归零”的问题——用户下一次来,先把和他相关的长期记忆捞出来,重建对话背景。
双网络不是简单的“一个Redis一个MySQL”,而是两个职责完全不同的通道。短期记忆要的是“快”和“精”,长期记忆要的是“全”和“稳”。两者协同的接口就是记忆管理器:长期记忆负责“记得”,短期记忆负责“手边就有”。
2.3 如何取:什么时候让记忆进入模型视野
记忆不是每个token都要看,而是在几个关键时机注入:
- 会话启动时:把该用户的长期记忆概要拉出来,作为System Prompt的种子信息。
- 每轮用户输入后、模型推理前:用当前用户消息做检索,召回top-k条相关记忆,拼进上下文。
- 调用工具前:如果Agent准备执行外部操作,先查一下记忆里有没有相关历史。比如用户两小时前刚改过某个配置,这次工具调用前就应该提醒模型“配置可能已被修改”。
取回的候选集不能直接堆进去,要先做一次“价值排序”。我的做法是:把语义相似度和记忆价值分融合成一个排序分,取前5条注入。这个“先取回再重排”的套路,是记忆系统和RAG系统的通用解法。
2.4 如何忘:记忆=score+时间半衰期怎么落地
记忆不是库存,不能只进不出。没有遗忘机制的记忆系统,迟早被噪声淹没。这里就用到了我在标题里写的那个公式:记忆=score+时间半衰期。
简单说,每条记忆有三个基础属性:重要度分数(score)、创建时间、最后访问时间。它的实时价值随时间和访问频率变化,计算公式如下:
import time def memory_value(base_score: float, last_access_at: float, now: float, half_life: float = 86400, access_bonus: float = 0.0) -> float: """ 记忆价值 = 基础分数 * 时间衰减 + 访问加成 半衰期参数 half_life 表示价值衰减到一半所需时间。 """ elapsed_hours = (now - last_access_at) / 3600.0 decay = 0.5 ** (elapsed_hours / (half_life / 3600.0)) return base_score * decay + access_bonus为什么用半衰期而不是线性衰减?因为人脑的记忆遗忘曲线本来就是指数型的——刚记完忘得最快,然后越来越慢。工程上指数衰减还有一个好处:参数直观,说“半衰期30天”比说“每小时衰减系数0.997”容易让团队理解。
不同类型记忆的半衰期我建议差异化设置:
| 记忆类型 | 默认半衰期 | 说明 |
|---|---|---|
| 用户画像 | 30天 | 画像信息稳定性高,可设更长 |
| 关键事实 | 7天 | 项目决策类,中期复用 |
| 偏好经验 | 14天 | 需要较长期保留 |
| 待办承诺 | 按截止日期 | 到期前价值不降,到期后直接清除 |
| 会话细节 | 2小时 | 快速过期,防止污染 |
访问加成是另一个关键:用户每次提到某条记忆,它的价值就该被“激活”一次,最后访问时间刷新,相当于复习了一遍。这个机制模拟的是“被反复确认的信息更重要”。访问加成上限建议封顶,比如最多加0.3,防止一条老记忆因为频繁踩中而永远有效。
遗忘本身不用主动“删”。系统运行时,检索阶段会把即时价值低于阈值的候选过滤掉;后台再用离线任务把长期低于阈值的记录归档或删除。这样用户感知层面“忘了该忘的”,存储层面也不会膨胀。
3. 技术选型:AgentScope凭什么当生产级Agent的底座
记忆系统设计清楚了,接下来是工程底座。我调研了LangChain、AutoGen、AgentScope几家,最终项目落在AgentScope上。这个选择不是因为它最火,而是因为它解决了我最头疼的几个生产级问题。
3.1 AgentScope的项目定位和核心抽象
AgentScope是阿里巴巴通义实验室开源的多智能体开发框架。它的核心抽象不算多,但组合起来很强:
- Agent:智能体单元,可以是一个人设角色、一个工具封装、或者一个纯逻辑节点。每个Agent自己有状态和行为循环。
- Message:Agent之间传递的消息对象,消息有类型、内容和来源。这个设计比我见过的一些框架“裸传dict”的做法严谨得多。
- Pipeline/Workflow:消息流转的编排机制,决定消息先发给谁、如何汇聚、如何分叉。
框架底层是基于Actor模型的并发机制,每个Agent在自己的运行时里跑,消息异步流转。这就带来一个对生产级至关重要的能力:Agent可以被拆成独立的服务部署,而不是只能单进程串行跑。记忆模块、检索模块、工具模块,都能各自独立扩展。
3.2 和LangChain、AutoGen相比,差异在哪
我直接列一张我选型时候的对比表,都是真实体感:
| 维度 | LangChain | AutoGen | AgentScope |
|---|---|---|---|
| 编排方式 | 链式为主,复杂图需拼装 | 多Agent对话驱动 | 消息流驱动,显式Pipeline |
| 生产部署 | 偏单进程,服务化要另搭 | 偏研究和多Agent模拟 | 有分布式运行时,Agent可服务化 |
| 消息机制 | 弱类型,靠约定 | 有ConversableAgent但偏重 | Message一等公民,字段明确 |
| 记忆/检索 | 靠外部Memory模块,代码要自己串 | 有Memory能力但要自己配 | 可把记忆检索封装成独立Agent |
| 多Agent中台化 | 一般 | 一般 | 适合作为多Agent中台底座 |
说白了,LangChain赢在组件生态,但组件之间的黏合度一般;AutoGen赢在对话驱动的多Agent创新,但复杂业务编排和部署要自己花很多功夫;AgentScope赢在架构干净、消息机制健壮、面向生产的多Agent服务化。我的项目里需要把记忆检索、持久化、RAG、工具调用都做成可独立扩缩容的模块,AgentScope的消息驱动模型是最贴合这个目标的。
3.3 在AgentScope里给记忆模块一个“位置”
AgentScope里做记忆型Agent,我建议把记忆系统也建模成一个Agent——姑且叫MemoryAgent。主Agent负责和用户对话、推理、决策,MemoryAgent负责记忆的写入和读取。两者通过Message通信。
这样一个设计的最大好处:记忆的读写变成了消息传递,而不是函数调用。主Agent想存记忆,发一条消息给MemoryAgent;想取记忆,发一条查询消息。MemoryAgent可以单独扩展、单独部署、单独压测。将来多个业务Agent想共享一套记忆服务,只需把MemoryAgent的访问地址共享出去,记忆就中台化了。
4. 从零搭一个“能记住用户”的Agent:完整实操
接下来是硬核部分。按照上面说的设计,我用AgentScope搭一个带跨会话记忆的Agent,代码不是玩具,基本逻辑可以直接用在生产原型上。
4.1 工程骨架与依赖
项目结构我建议这样拆:
my_memory_agent/ ├── agent.py # AgentScope接入与主循环 ├── memory/ │ ├── __init__.py │ ├── models.py # 记忆数据结构 │ ├── value.py # 价值评估与半衰期衰减 │ ├── store.py # 持久化存储层(SQLite起步) │ ├── encoder.py # LLM记忆编码 │ └── manager.py # 记忆管理器,对外提供读写接口 ├── prompt_templates.py # 各类Prompt模板 ├── schemas.py # 通信数据结构,AgentScope Message子类 ├── config.py # 配置 └── requirements.txt依赖只需要几个核心包:agentscope、openai(或你用的模型SDK)、sqlite3(Python自带)、numpy(可选,用于向量运算)。如果后面要上向量检索,再加一个chromadb或者接Milvus/Qdrant的客户端。
4.2 记忆数据结构与价值评估实现
记忆条目是整套系统的心脏。我定义的数据结构如下:
@dataclass class MemoryItem: memory_id: str owner_id: str # 用户ID,用于会话隔离 content: str # 记忆内容 memory_type: str # user_profile / fact / todo / preference base_score: float # 0~1,初始重要度 value: float # 实时价值,由value.py计算 created_at: float last_access_at: float access_count: int expire_at: float | None # 可选过期时间实时价值计算就是上一章那段代码的封装。我单独抽成模块,是因为后续要反复调用,而且方便做单元测试。测试用例我会覆盖:分数高的记忆过期后会不如分数低的新记忆、访问会刷新价值、不同类型半衰期不同。
4.3 记忆编码器:从对话流中抽取结构化记忆
编码器用LLM做抽取,这个不用避讳。它的职责是:输入用户最新一轮的消息、当前短期记忆摘要、已有长期记忆摘要,输出“值得更新的记忆列表”。
class MemoryEncoder: def encode(self, user_content: str, recent_summary: str, existing_summary: str) -> list[dict]: prompt = MEMORY_ENCODE_PROMPT.format( user_content=user_content, recent_summary=recent_summary, existing_summary=existing_summary, ) raw = self.llm_json(prompt) # 强制JSON输出 items = parse_memory_json(raw) # 校验schema return [item for item in items if item["importance"] >= 0.3]这里有个关键动作:不是每一轮对话都跑编码器,那样LLM调用成本会失控。我的策略是:当用户消息满足以下任一条件时才触发编码——用户在做自我介绍、用户明确表达偏好或禁忌、用户布置了待办、用户否定了之前的某个方案;或者当前会话累计超过10轮、短期摘要需要归档时,做一次整体编码。
4.4 把记忆管理器接入AgentScope的Agent循环
重头戏在这里。AgentScope里的Agent核心方法是响应消息。我在主Agent的推理循环里,显式插入记忆读取:
from agentscope.agent import AgentBase from agentscope.message import Msg class MemoryAgent(AgentBase): def __init__(self, name, memory_manager, llm): super().__init__(name) self.memory_manager = memory_manager self.llm = llm def reply(self, x: dict = None) -> dict: # 1. 从用户消息中解析出当前用户ID和内容 content = x["content"] owner_id = x.get("owner_id", "default") # 2. 检索相关记忆 retrieved = self.memory_manager.retrieve( owner_id=owner_id, query=content, top_k=5, ) # 3. 构建记忆上下文块 memory_block = self._format_memory_block(retrieved) # 4. 组装Prompt messages = [ {"role": "system", "content": SYSTEM_PROMPT + memory_block}, {"role": "user", "content": content}, ] # 5. 调用模型 response = self.llm(messages) # 6. 异步写入本轮值得编码的记忆 self.memory_manager.enqueue_encoding(owner_id, content) return Msg(self.name, response, "assistant")retrieve和enqueue_encoding的具体实现就是记忆管理器的核心,直接放出来:
class MemoryManager: def __init__(self, store, encoder, half_life_map=None): self.store = store self.encoder = encoder self.half_life_map = half_life_map or { "user_profile": 30 * 86400, "fact": 7 * 86400, "preference": 14 * 86400, "todo": 15 * 86400, } def retrieve(self, owner_id, query, top_k=5): now = time.time() candidates = self.store.search(owner_id, query) weighted = [] for item in candidates: hl = self.half_life_map.get(item.memory_type, 7 * 86400) value = memory_value(item.base_score, item.last_access_at, now, hl) # 融合:语义相关度占大头,记忆价值做加权 score = item.similarity * 0.6 + value * 0.4 weighted.append((score, item)) # 顺带刷新访问状态和访问次数 self.store.touch(item.memory_id, now) weighted.sort(key=lambda x: x[0], reverse=True) return [item for _, item in weighted[:top_k] if memory_value(item.base_score, item.last_access_at, now, self.half_life_map.get(item.memory_type, 86400)) >= 0.15]读到的那条0.15阈值很关键——低于这个值的记忆,说明已经“忘得差不多了”,没必要再占用上下文窗口。
4.5 验证:造一个“记得住”的对话场景
原型跑通之后,一定用下面这个三层验证法测一遍,光“聊着感觉不错”不算数:
第一层,会话内记忆验证:用户在第1轮明确说“我叫林杰,后端主用Go”,聊到第20轮时问“我的技术栈是什么?”,Agent要能准确回答。这一层不过关,说明短期记忆都有问题,别急着往下走。
第二层,跨会话记忆验证:结束会话,重新开一个新的会话,模拟同一用户ID。用户问“你记得我叫什么吗”,Agent应该从长期记忆里捞出来。
第三层,模糊查询验证:用户不用“名字”这种精确词,而是说“我之前强调过不想用Java,现在项目方案有没有违背这一点?”——这要求记忆系统能基于语义把“用户偏好-技术栈-禁忌”这条记忆召回并注入上下文,而不是靠关键词匹配。
三层全过,记忆Agent的核心闭环就通了。我在自己机器上跑这个验证时,前两次都挂在第三层——检索召回了相关记忆,但Prompt模板里给模型的位置太靠后,导致模型没重视。后来把记忆块放在System Prompt靠前位置,并显式标注“以下是从长期记忆中恢复的用户信息,请优先遵循”,效果明显改善。
5. 生产级改造:从“能跑”到“能扛”
原型能跑通,离生产级还差着十万八千里。生产级意味着数据不能丢、并发不能串、体积不会无限膨胀、出了事能追溯。这一章讲的都是我上生产时踩过真坑之后总结出来的改造点。
5.1 持久化与向量检索的选型
第一步,记忆不能只存在内存里。我用SQLite做结构化记忆的持久化起步,开启WAL模式,事务性好、零运维、备份简单。生产环境数据量大了再平滑迁移到PostgreSQL。
第二步,为语义检索引入向量存储。最开始我用Chroma本地模式,开发调试方便;后来并发上来了,切换到Milvus或者Qdrant。要注意的是,AgentScope本身并不绑定检索组件,这块是需要你自己接入的。我的存储架构是“二级存储”:
- 结构化字段(owner_id、memory_type、expire_at、score)走SQL,负责过滤和排序。
- 语义相似度走向量库,负责“召回相关片段”。
- 最终候选集合并,再做一次价值重排。
这个架构的好处是:可以用SQL精确控制“我这个用户只能搜到自己的记忆”,向量库只负责粗召回,不需要承担权限过滤这种它不擅长的事。
5.2 会话隔离与用户级权限
生产环境最大的隐患不是模型不够聪明,而是A用户看到了B用户的记忆。这不是危言耸听,我见过一个原型系统,检索条件里漏了owner_id,用户A问“我之前说的那个方案”,系统从全局库召回了一段其实是用户B说过的方案,差点造成信息泄露。
解决办法就一条铁律:所有记忆读写路径必须显式携带owner_id,并在SQL和向量检索两层都加过滤条件。向量检索那一层尤其容易被忽略,因为向量库里存的是浮点向量,你以为自己过滤了,实际组装的Query里忘了带filter参数。
另一个细节:多租户场景下,记忆模块的LLM编码和检索服务要做资源隔离,至少不能因为一个租户的突发流量把别的租户的MemoryAgent打挂。基于AgentScope的Message分发机制,这个问题可以靠给每个租户分配独立的MemoryAgent实例解决。
5.3 记忆膨胀、冲突与重写策略
记忆只进不去的系统,三个月后必然被垃圾信息淹没。生产环境必须有后台任务处理四件事:
- 合并:同一用户的多条相似记忆合并成一条,比如用户三次表达“后端不用Java”,只保留一条带重要度累加的记录。
- 归档:即时价值长期低于阈值、且超过N次没有被召回的记忆,转入冷存储或者删除。
- 冲突修复:用户说“A”,后来又明确说“不要A了”,以更晚的、基础分更高的那条为准,并把旧条目标记为过期。这里尤其要注意“用户主动否定”的语义,编码器要能识别出否定语气。
- 用户主动删除:这是生产级Agent的底线能力。用户说“忘掉我之前说的事情”,系统必须能真正删除对应记忆,而不是假装删除。GDPR这类合规要求对个人数据删除有硬性规定,记忆系统天然属于个人数据范畴,不可逆删除接口必须从第一天就做好。
5.4 可观测性与评估:不能只靠“聊得爽”
记忆系统是隐形的,它好不好用,直接看对话质量,但对话质量是主观的。生产上必须建立可观测指标,否则你根本不知道记忆系统是在帮忙还是帮倒忙:
- 记忆命中率:每轮对话检索出的记忆里,有多少条实际被模型采用(体现在最终回答中)。用LLM评估答案时,看引用记忆的交叉命中率。
- 记忆召回准确率:人工抽检一批对话,判断“该被回忆起来的信息是否被召回”。这是RAG里标准的recall指标。
- 记忆写入精度:定期抽检记忆库的条目,看多少条是垃圾或重复。写入精度低,说明编码器Prompt或触发条件有问题。
- 会话满意度:跨会话场景下,用户第二次会话的完成率、撤回率、投诉率,跟首次会话做对比。
这块评估体系建好之后,你会发现自己对记忆系统的优化有了方向,而不是靠感觉调参数。
6. 踩过的坑与下一步方向
最后这部分是我最想写的。代码写一百遍,不如一遍踩坑记忆深。这几个坑,我相信每个自己做Agent记忆系统的人都会遇到。
6.1 踩坑一:把全部历史都塞进Prompt,代价超出你想象
最早一版我图省事,直接把整个会话历史塞进上下文,心想“反正128K窗口够大”。结果第一,费用翻了快三倍;第二,模型在长上下文中找关键信息的表现很不稳定,尤其当那个关键信息藏在中后段时。后来我做了两件事才解决:一是会话历史先提炼成结构化摘要再放进去,二是记忆检索只取重排后的top-k条。上下文不是仓库,是工作台。放上去的每一块信息,都在占模型的注意力。
6.2 踩坑二:半衰期参数拍脑袋定,导致记忆集体“蒸发”
有一阵子我把所有记忆类型的半衰期都设成24小时,想着“反正每天都会聊”。结果一个周末过去了,用户的画像记忆价值全部衰减到接近0,周一用户回来,Agent“失忆”了。用户非常愤怒,说“我上周才跟你讲过我是谁”。
教训是:不同记忆类型必须差异化设置半衰期,而且要考虑真实业务节奏。用户画像类至少30天起步,项目决策类按项目周期来。另外,访问加成和“回访保活机制”要配合使用——用户主动提了某条信息,系统不只刷新该条记忆的访问时间,还要顺带把同主题关联记忆的访问时间也刷新一点。这模拟的是“用户记起了一件事,往往其他相关的事也在他脑子里活跃”。
6.3 踩坑三:记忆编码器输出脏数据,把整个库搞臭
LLM做结构化抽取,偶尔会输出残缺JSON、或者把一句玩笑编码成“重要事实”。开始我没做数据校验,结果库里混进不少垃圾记忆,检索的时候还总能命中,因为语义相关。
后来我加了几个保险:
- 输出必须过Pydantic校验,解析失败就重试一次,再失败就丢弃。
- 编码时要求模型给每条记忆打置信度,低于0.5的直接不写库。
- 写入走队列,编码器故障不影响主Agent运行。记忆是辅助系统,不能因为记忆模块挂了把主Agent拖垮。
6.4 下一步:记忆中台化、多Agent共享、跨会话技能迁移
系统走上正轨后,我打算往三个方向迭代。
一是基于AgentScope的消息机制,把MemoryAgent彻底做成服务化中台。多个业务Agent共用一个记忆服务,按owner_id天然隔离。这是“AI Agent中台”里比较落地的一块,比反复给每个Agent单独配记忆省力太多。
二是跨会话技能的迁移。现在记忆里存的是“事实”,下一步要存“技能”:比如用户在多个项目里都用同一个代码规范,我可以把“用户偏好XX规范”固化成Agent的默认行为,这已经不是记忆,而是基于记忆沉淀出技能。类似workbuddy这类工具已经在做跨对话Skill,思路会越来越成熟。
三是记忆召回重排。当前重排用的是“相关度+价值”的线性公式,比较粗糙。后面计划训练/微调一个轻量rerank模型,或者干脆用LLM做二次重排,让召回结果更贴合用户当前意图。
我个人的体会是:记忆系统不是Agent的一个可选组件,而是决定Agent能否从“Demo玩具”进化成“生产级生产力工具”的分水岭。它本质上是一套设计哲学——你要想清楚Agent该记住什么、遗忘什么、如何在合适的时候调用。技术方案可以迭代,模型可以换,但“把记忆当一等公民”这个设计原则,越早定下来,后面省的事越多。
最后送大家一个小技巧:验证记忆系统,别铺一大堆测试场景,先跑通一个最简单的——“用户主动问你记不记得他上礼拜说过什么”。这个场景跑通,记忆链路基本就通了;跑不通,再花哨的功能都是白搭。