MIRaS 与记忆型AI:从工程视角构建可持续记忆系统
发布时间:2026/9/1 16:56:50 作者:尧图编辑部 阅读量:1,286

记忆型 AIMemory-based AI并不是一个新名词但这周出现的 MIRaS 让这个话题重新回到技术讨论的中心。与单纯通过扩大参数量来提升模型能力不同基于记忆的 AI 试图让系统在运行过程中保留、检索和更新经验从而使助手不再“每次对话都从零开始”。MIRaS 作为一类新的 memory-based AI 名称被公开提出目前公开资料里的完整技术细节还不算多但这并不妨碍我们从工程角度拆解它背后真正要解决的那些问题记忆放在哪里、如何表示、如何被检索、如何更新以及如何避免记忆污染。这篇文章会从概念、核心机制、最小原型、评估方法、生产落地和排错链路几个层面系统梳理这类系统的技术全貌。如果在实际项目中要落地 MIRaS 这样的方案真正有价值的工作并不是“调用一个带记忆的模型”而是设计一整套记忆生命周期。很多人在做一个带长期记忆的 AI 助手时第一反应是“把所有历史消息都塞进上下文窗口”这在短期 Demo 里能跑一旦对话超过几十轮就会遇到上下文爆炸、关键信息被淹没、模型“记错”用户偏好等问题。MIRaS 这类新类别引发的讨论恰好暴露了这些工程盲点。1. 为什么记忆会成为 AI 系统的关键能力1.1 从无状态模型到有状态系统的转变早期的对话模型本质上是一个无状态函数。输入一句 prompt输出一段文本模型不保留上一轮的中间状态。这种设计的好处是接口简单、部署容易坏处是当用户需要连续交流、积累偏好、维护任务进度时系统会显得非常“健忘”。要让 AI 变得有状态工程上通常有两种方式。第一种是把所有历史记录拼进提示词让模型在生成时“重新阅读”一遍这属于伪记忆第二种是设计外部记忆系统向量数据库、键值存储、关系数据库或图数据库都可以作为记忆载体模型需要时通过检索把相关记忆重新注入上下文。MIRaS 讨论的焦点更偏向第二种。它代表的是这样一类系统模型本身是推理引擎记忆不是模型参数的一部分而是外部可读写、可检索、可失效的状态。这样一来模型可以持续适应个人偏好、项目背景和业务规则同时不需要为每个用户重新训练模型。1.2 Memory-based AI 与 RAG、上下文工程的关系很多人会把 memory-based AI 和 RAG检索增强生成混淆。严格来说两者并不在一个层次。RAG 解决的是“从外部知识库找到答案片段”它通常面向文档、网页、知识库这些相对稳定的静态内容。Memory-based AI 解决的是“从系统自身的交互历史中找到上下文”它更强调动态的、私有的、随对话进展不断变化的信息。上下文工程则更宽泛它决定“当前这次生成要给模型看哪些内容”。Memory-based AI 是上下文工程的一种实现策略先把长时记忆存储在外部再在生成前召回并拼装成上下文。因此可以把 MIRaS 理解为一种“以记忆为中心”的系统架构而不是某一个具体模型。它的关键在于如何把一小部分最相关的历史信息选出来而不是把全部历史都塞进去。1.3 MIRaS 这类新类别想解决什么问题MIRaS 作为新类别出现最值得关注的是它把“记忆”提到了系统设计的一等公民位置。传统 AI 系统里记忆是散落在数据库、日志、缓存和会话状态里的而 MIRaS 希望提供统一的记忆抽象让开发者在构建 AI 应用时能像使用缓存一样方便地使用记忆。这个思路带来的工程问题包括记忆对象如何建模是纯文本、结构化 key-value还是知识图谱。记忆如何分片哪些属于全局记忆哪些属于用户级记忆哪些属于会话级记忆。记忆如何召回在什么时机触发检索检索结果如何参与生成。记忆如何更新用户纠正了之前的信息后新记忆如何覆盖旧记忆。记忆如何遗忘过期、错误、不相关的记忆如何被降权或删除。这些问题并不只是学术问题。任何准备把 AI 应用从“一次性问答”升级为“持续服务”的团队都会在生产环境中遇到同样的挑战。2. 理解 MIRaS先拆解记忆系统的四个核心层次2.1 记忆的表示知识、事实、事件与状态如何结构化记忆系统第一步要回答的问题是一条记忆长什么样。最简单的表示是字符串例如“用户喜欢喝冰美式”。但这种表示不适合程序自动更新和冲突处理。更稳妥的做法是给每条记忆附带元信息字段。可以用 JSON 表示一条标准化记忆{ memory_id: mem_10001, user_id: user_123, scope: user, fact_type: preference, content: 用户喜欢喝冰美式, keywords: [咖啡, 冰美式], confidence: 0.95, created_at: 2025-01-01T10:00:00Z, updated_at: 2025-01-01T10:00:00Z, source: dialog, status: active }这里的scope决定记忆作用范围是全局、用户还是会话fact_type用于区分偏好、事实、进度、事件confidence表示模型对该记忆的置信度status用于软删除。实际项目中这个结构需要根据业务场景扩展但核心思路是记忆不能是文档堆必须可结构化、可过滤、可合并。2.2 记忆的存储短期、长期与外部持久化记忆存储要区分三个层次工作记忆当前对话上下文通常存在于模型的上下文窗口内。短期记忆最近 N 轮会话可以持久化到 Redis、数据库或日志中。长期记忆跨会话的关键信息保存在向量数据库、关系数据库或专门记忆存储中。学习环境可以先把所有记忆放到 SQLite 或内存里生产环境则要考虑读写性能、容灾备份、权限隔离和一致性。下表给出了常见的存储选型思路存储类型适合场景优点缺点关系数据库结构化事实、用户偏好、状态记录一致性高、易过滤语义检索弱向量数据库非结构化文本、语义相似度召回适合相似内容检索精确过滤和事务能力有限键值存储会话状态、短期缓存读写快缺少复杂查询图数据库实体关系、社交关系、依赖链关系遍历强维护成本高文件系统小规模原型、日志归档简单直接查询能力弱生产环境很少只用一种存储。常见架构是“关系数据库存索引和元信息向量数据库存语义向量”或者“Redis 缓短期记忆对象存储存原始文本”。2.3 记忆的检索什么时候取、取什么、怎么排序检索是记忆系统的核心能力。检索时机通常有三种每次生成前自动检索适合需要持续引用用户偏好的场景。多轮对话中按事件触发检索例如用户提到某个项目名时才去查该项目历史。用户主动查询历史例如“我之前是不是说过我要出差”。检索方式可以混合使用关键词过滤、向量相似度、时间衰减、置信度加权、实体链接。推荐排序时用分数加权比单一向量相似度更稳定。下面是一个简单的评分公式思路final_score w1 * semantic_score w2 * recency_score w3 * confidence_score其中w1、w2、w3是权重。语义分数实现语义相似度时间分数保证最近的信息更容易被召回置信度分数降低模糊记忆对结果的干扰。不要只依赖向量相似度否则容易找回“看起来像但实际过时”的内容。2.4 记忆的更新写入、合并、遗忘与一致性记忆不是只读档案它需要被持续修正。用户说“我现在搬到了上海”系统要能更新常住城市用户说“刚才那句话当我没说”系统要能撤销对应记忆。更新策略至少有四种覆盖新记忆直接替换旧记忆适合地址、手机号这类唯一事实。追加新记忆是旧记忆的补充适合兴趣标签、项目历史。冲突检测新旧记忆矛盾时保留一个高置信版本或者生成一个待确认状态等用户明确后再修订。降级长期未使用的记忆逐渐降低权重必要时进入冷归档。一致性是容易被忽略的点。如果记忆系统支持异步写入可能出现用户刚修改偏好下一轮检索仍然拿到旧值。生产环境下至少要做到“写入后短时间内强一致长期允许最终一致”。3. 工程视角一个最小可运行的 Memory-based AI 原型3.1 技术选型和环境准备为了演示 MIRaS 的核心思路这里实现一个最小原型。它不依赖重量级框架只需要 Python 3.8 以上环境和两个轻量依赖pip install numpySQLite 使用 Python 标准库向量相似度用 numpy 实现。原型包含三个能力写入记忆、检索记忆、把检索结果拼接成上下文。真实项目中可以把 embedding 部分替换成专用模型但最小原型里用词袋向量也能验证整个链路是否通畅。3.2 使用 SQLite 保存记忆元数据用 numpy 计算相似度先创建记忆存储类负责保存记忆文本、关键词向量和相关元信息。这里为了保持代码可运行向量我们手工抽取关键词不做复杂 embedding。import json import sqlite3 from typing import List, Dict, Optional import numpy as np class MemoryStore: def __init__(self, db_path: str memory.db): self.conn sqlite3.connect(db_path) self._create_table() def _create_table(self): self.conn.execute( CREATE TABLE IF NOT EXISTS memory ( memory_id INTEGER PRIMARY KEY AUTOINCREMENT, user_id TEXT NOT NULL, scope TEXT NOT NULL DEFAULT user, content TEXT NOT NULL, keywords TEXT NOT NULL, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, status TEXT NOT NULL DEFAULT active ) ) self.conn.commit() def add(self, user_id: str, content: str, keywords: List[str], scope: str user): self.conn.execute( INSERT INTO memory (user_id, scope, content, keywords) VALUES (?, ?, ?, ?) , (user_id, scope, content, json.dumps(keywords))) self.conn.commit() return self.conn.lastrowid def _keyword_vector(self, keywords: List[str]) - np.ndarray: vec np.zeros(len(keywords), dtypefloat) for i, keyword in enumerate(keywords): vec[i] 1.0 return vec def search(self, user_id: str, query_keywords: List[str], top_k: int 3) - List[Dict[str, any]]: rows self.conn.execute( SELECT memory_id, content, keywords, created_at FROM memory WHERE user_id ? AND status active , (user_id,)).fetchall() results [] for memory_id, content, keywords_json, created_at in rows: stored_keywords json.loads(keywords_json) overlap len(set(query_keywords) set(stored_keywords)) if overlap 0: continue results.append({ memory_id: memory_id, content: content, score: overlap, created_at: created_at }) results.sort(keylambda x: x[score], reverseTrue) return results[:top_k]这个示例用关键词重叠度代替语义相似度优点是能跑通全流程缺点是召回质量有限。生产环境中_keyword_vector和search会替换成 embedding 模型和向量数据库查询但整体框架不变。3.3 用函数封装记忆的写入和检索为了让记忆逻辑复用可以把写入和检索封装成独立函数。这样业务层不需要关心 SQLite 或向量库细节。retrieval MemoryStore(demo.db) def save_user_fact(user_id: str, content: str, keywords: List[str]): retrieval.add(user_id, content, keywords) def load_relevant_memories(user_id: str, query_keywords: List[str], top_k: int 3) - str: memories retrieval.search(user_id, query_keywords, top_k) if not memories: return lines \n.join([f- {m[content]} for m in memories]) return f以下是用户历史记忆中可能与当前对话相关的内容\n{lines}函数的好处是隔离变化。如果后续从 SQLite 换成向量数据库只需要修改MemoryStore内部实现save_user_fact和load_relevant_memories供上层调用的签名保持不变。3.4 在对话流程中接入记忆检索在真实 AI 应用里记忆检索结果需要被拼接到 prompt 中。下面是简洁示意def llm_generate(system_prompt: str, user_query: str) - str: # 这里对接真实大模型接口 return f[mock answer] memory context loaded. query: {user_query} def chat_with_memory(user_id: str, user_query: str, query_keywords: List[str]): memory_context load_relevant_memories(user_id, query_keywords) system_prompt 你是一个长期陪伴用户的助手。请结合用户历史记忆回答当前问题。如果记忆信息不足请明确询问用户。 if memory_context: system_prompt \n\n memory_context return llm_generate(system_prompt, user_query)这里mock answer只在工程演示时使用真实项目需要替换模型调用。关键点是把记忆检索放在系统提示词中而不是让用户自己复述历史当检索不到相关内容时要主动提问不要编造记忆。3.5 运行验证与结果分析在 Python 中执行下面代码save_user_fact(u1, 用户喜欢喝冰美式, [咖啡, 冰美式]) save_user_fact(u1, 用户计划三月去成都出差, [出差, 成都, 三月]) query 我出差时想喝什么咖啡 answer chat_with_memory(u1, query, [出差, 咖啡, 偏好]) print(answer)预期结果中load_relevant_memories会同时召回“用户喜欢喝冰美式”和“用户计划三月去成都出差”。因为当前查询的关键词与两条记忆都有重叠所以都会进入上下文。推理模型就能把“出差”和“咖啡偏好”组合起来回答。这个最小原型已经能体现 memory-based AI 的核心闭环写入 - 检索 - 拼接上下文 - 生成 - 再写入。后续要提升效果只需要替换检索算法和模型调用不需要改动整体流程。4. 评估与验证如何判断 MIRaS 类系统真正记住了4.1 先构建带记忆要求的评测集评估 memory-based AI 不能只用“单轮问答准确率”还要验证它是否真正记住、正确更新、合理遗忘。建议准备一个带状态的评测集每个样本包含三步初始信息例如“用户住在北京”。多轮对话更新信息例如“用户已经搬到上海”。最终问题例如“用户现在住在哪里”。评测集不用追求数量庞大但必须覆盖记忆的读、写、改、删、冲突处理。4.2 从准确性、一致性、时效性三维度打表评测维度测试方式通过标准记忆召回准确率检索历史看是否命中正确事实Top 5 命中率不低于预期更新一致性修改旧信息后再问新值回答新值不再使用旧值冲突处理注入两条矛盾记忆不回答错误信息能主动澄清遗忘能力删除某条记忆后再问不引用已删除内容时间衰减很久之前的低权重记忆回答优先参考近期内容隐私合规删除指定用户全部记忆检索结果不再出现该用户任何信息这些指标每个都可以拆成自动化用例。生产环境建议在版本发布前跑一遍记忆回归防止新功能引入“记忆串号”或“旧信息污染”问题。4.3 从离线评测到线上观测离线评测无法覆盖真实用户行为的多样性。上线后要额外观测以下指标记忆写入成功率尤其是异步写入时是否出现丢失。记忆检索召回率是否经常出现“检索不到”导致模型答错。平均响应耗时检索和排序是否成为性能瓶颈。用户反馈率用户是否频繁纠正助手“你记错了”。线上观测建议把记忆操作的日志单独打出来至少包含记忆 ID、用户 ID、操作类型、触发来源、执行结果。这样遇到问题时能快速回放是写入失败、检索失败还是生成阶段用错了记忆。4.4 学习环境与生产环境的评估差异学习环境里用几个固定的对话样例就能验证效果。生产环境则必须考虑多用户隔离、记忆覆盖、数据权限和回滚。学习环境可以先不关心性能生产环境则要设计缓存预热、批量写入、数据分表等方案。这里有一个很重要的原则不要只验证“能检索到相关记忆”还要验证“不会检索到不该出现的记忆”。多用户系统里用户 A 的记忆一旦被用户 B 的提问召回就是严重数据事故。5. 落地时最容易踩的坑与排查路径5.1 模型“记住”了旧信息却不按新信息回答现象用户已经主动修改了偏好系统回复时仍然使用旧偏好。可能原因新记忆写入失败或写入到了错误的用户 ID。旧记忆没有被标记失效照常参与召回。新记忆和旧记忆同时进入上下文模型选择了一条权重更高的旧信息。检索排序规则中旧记忆因为时间权重计算错误排在前面。排查方式先查记忆表确认新值是否写入再查检索日志确认本次召回结果列表里是否包含旧记忆最后查 prompt看新旧内容是否同时出现。处理建议更新唯一事实时直接把旧记忆status改为inactive并插入新记忆如果旧记忆不能立刻删除至少在检索排序时对updated_at较近的记忆加权。5.2 检索命中不相关记忆现象用户问 A 事模型却把 B 事的历史翻出来回答。可能原因检索系统只做了向量相似度没有做意图过滤。记忆写入时没有清洗导致大量无意义文本入库。关键词提取不准确比如把“苹果”同时关联到水果和公司。Top K 设置过大把相关性低的记录也放进上下文。排查方式在检索模块加 debug 开关输出每次查询的五个候选记忆和分数确认问题出在召回阶段还是排序阶段。处理建议把检索分为两阶段第一轮用关键词或结构化字段粗筛第二轮再用向量相似度精排同时给每条记忆增加时间和话题标签减少跨话题误召回。5.3 两条记忆互相冲突现象记忆库里同时存在“用户住在北京”和“用户住在上海”模型不知道听谁的。冲突产生的原因很多可能是更新失败、多端写入或用户曾撤回信息。处理方式有两条路径自动路径比较updated_at保留时间更新、置信度更高的记忆。人工路径当置信度都不高时把冲突问题抛给用户询问不要自作主张。推荐实现一个记忆覆盖函数在写入阶段就做去重和冲突检测而不是让模型在生成时遇到冲突。生成阶段适合处理模糊性不适合处理数据一致性。5.4 隐私删除困难现象用户申请删除所有个人记忆但系统仍然在某些日志或缓存里保留旧记忆。原因通常是删除只改了应用层记忆表没有清理备份、日志、向量库副本或缓存。处理建议删除操作要设计为“级联删除”或“标记删除 清理任务”。向量数据库中的向量也要同步删除历史对话日志中如果包含明文记忆内容需要做脱敏或单独加密存储。上线前先明确记忆数据生命周期而不是等接到删除请求再补救。5.5 生产故障排查清单问题环节检查内容日志关键字写入失败数据库连接、字段长度、序列化是否成功memory insert failed检索为空用户 ID 是否传错、向量索引是否更新memory not found召回错误关键词或向量异常、排序权重错误search topk模型答错prompt 中记忆是否拼接正确prompt context性能下降检索耗时长、索引未命中query timeout生产排查建议按照“先看数据再看代码最后看模型”的顺序。90% 的 memory-based AI 问题都不是模型不够聪明而是记忆数据写错、没写进去或没被查出来。6. 最佳实践与扩展方向6.1 记忆架构设计原则第一记忆写入和读取必须分离。不要在对话生成函数里直接写 SQL而是通过独立服务或模块管理记忆这样方便扩展、测试和故障隔离。第二记忆必须有生命周期。每条记忆都应包含创建时间、更新时间、置信度、状态和来源否则无法做遗忘、更新和隐私删除。第三检索结果必须可解释。至少提供“模型为什么引用这条记忆”的追踪信息否则用户一旦指出错误很难定位是写入问题、检索问题还是生成问题。第四降低对模型的记忆依赖。模型的能力是语言理解和生成不是可靠的数据库。重要事实和业务状态应该用结构化存储保证一致性模型只负责把检索结果组织成自然语言。6.2 从原型到生产的检查清单在把最小原型推向生产前可以参考以下清单记忆表是否包含用户 ID、作用域、状态、时间戳、置信度和来源字段。是否实现记忆去重和覆盖更新。是否支持按用户隔离检索避免跨用户串忆。是否支持软删除和物理删除。是否记录记忆写入、检索、更新日志。是否设置检索超时和 top_k 上限。是否评估向量数据库或 embedding 模型的成本与性能。是否有回滚机制允许用户撤销最近的记忆变更。是否对敏感记忆做权限控制。是否编写记忆回归测试用例。这些条目并不需要一开始全部完成但它们决定了系统能否长期运行。6.3 MIRaS 后续值得关注的扩展方向MIRaS 作为新类别后续方向可能围绕更精细的记忆管理展开。值得重点观察的方向包括分层记忆工作记忆、短期记忆、长期记忆之间如何自动迁移。结构性记忆从纯文本记忆向知识图谱、事件图、状态机升级。记忆压缩如何把长时间对话压缩成更抽象的摘要而不是保留全部原文。主动回忆系统在需要时自动检索历史而不是只等用户询问。协作记忆多个用户共享项目记忆同时保持权限隔离。记忆评估标准是否会把“记忆能力”作为大模型应用的独立评测维度。从工程实践看MIRaS 这类方案会逐步把 AI 应用从“一次性问答工具”推向“可持续协作的工作系统”。技术团队现在最应该做的不是等待某个具体框架成熟而是先把记忆生命周期抽象清楚用最小原型验证闭环再根据业务场景逐步加深记忆层的设计和治理。最终要记住一个核心判断模型的上下文窗口再大也只是工作台真正的长期记忆系统必须是一个可以被写入、检索、更新和遗忘的独立基础设施。MIRaS 带来的启示不是又一个新名词而是提醒开发者AI 应用工程化的下一站很可能就是记忆工程。