智能体记忆整理:Dreaming机制如何把日志沉淀为可复用技能

发布时间:2026/10/21 15:13:19
智能体记忆整理:Dreaming机制如何把日志沉淀为可复用技能 如果你维护过一个长时间运行的智能体系统一定见过这种场面跑了一整天积累了上千条交互日志里面既有用户随口说的闲话也有最终成功解决某个问题的完整链路。你很想把后者沉淀下来作为后续任务的经验但直接把这些日志全扔给模型去学——既贵又慢还会让长期记忆变成一锅粥。OpenClaw项目早期就是这样。我们后来专门实现了Dreaming功能在低负载窗口自动做“记忆整理”把当天的原始交互片段重放、聚类、提炼摘要再归纳成可复用的技能模板。这篇文章就详细拆解它的设计思路和落地过程适合正在做智能体记忆系统或长时间运行知识库的同学参考。1. 持续运行的智能体缺的往往不是算力而是记忆整理1.1 从“日志堆积”到“记忆混乱”的转变OpenClaw 是我们内部维护的一个智能体平台主要形态是“对话 工具调用”日常处理客服工单、知识问答和跨天任务续接。平台刚上线那阵记忆模块非常简单所有交互记录默认保留24小时之后全部清理。后来我们发现很多真实场景根本绕不过历史上下文——用户周一咨询一个方案周三补充材料周五追问结果如果每天清理记录第三天的追问就完全对不上前面的语境。于是我们把日志保留时间改成了30天。结果新问题又来了记忆库越来越大在线推理时检索到的内容越来越杂。用户随口问一句“你们周末上班吗”三天后都可能被当作用户画像的一部分检索出来。整个系统的行为变得飘忽不定同一类问题有时答得对有时答得离谱。问题不是出在记忆容量而是出在记忆的整理方式。原始日志是流水账它记录的是“发生了什么”但没有回答“这件事意味着什么”“以后遇到类似情况该怎么做”。如果不做离线整理记忆库就会变成一间没有分类架的仓库——东西越多越难找到有用的那件。1.2 Dreaming 定位给记忆系统装一个“睡眠整理器”很多人把智能体的记忆等同于“存储”和“检索”但真正跑过长时间系统就会发现这两个环节之间缺了一部非常重要的中间处理把碎片化的事件变成可复用的知识。我习惯把这个过程类比成人类睡眠时的记忆巩固——白天经历的大量事情在睡眠中被大脑筛选、重放、强化最后沉淀成长时记忆。OpenClaw 的 Dreaming 机制干的就是这件事。它不是用户请求关键路径上的环节也不是一个被动的数据清理脚本。它是一套主动的、周期性的后台任务在系统负载较低的窗口被触发把短期事件缓冲区里积累的原始交互数据捞出来经过重放、聚类、摘要、技能提取、整合写回五个阶段最后生成两类核心产物——语义摘要和技能模板。语义摘要负责回答“这段经历到底发生了什么”技能模板负责回答“以后遇到什么条件我可以直接使用什么动作”。这两类产物会被写入长期语义库供在线推理时检索使用。整个过程完全异步不会阻塞用户对话也不占用在线服务的推理资源。2. 记忆系统的基础架构短期缓冲、工作记忆与长期语义库要理解 Dreaming 的运作方式得先看清 OpenClaw 的记忆系统整体长什么样。我们没有做一套很玄的“统一记忆网络”而是老老实实分了三层短期事件缓冲、工作记忆、长期语义库。每一层的职责不同数据格式不同淘汰策略也不同。2.1 短期事件缓冲所有事件的临时寄存区短期事件缓冲EventBuffer是 Dreaming 的原料仓库。系统里每一次用户消息、工具调用、工具返回、系统内部状态变化都会实时写进缓冲每条记录包含事件ID、会话ID、发生时间、事件类型、文本内容、embedding向量和重要度分数。缓冲不是无限长的。我们设置了一个组合上限最多保留2000条事件或者最多保留24小时哪个先到就触发淘汰。淘汰策略也不是简单的FIFO而是走“重要度加新鲜度”的加热模型。一个重要性很低、很久没被访问的事件会被优先清掉一个虽然时间久但明显涉及用户核心目标的工具调用记录会被多留一会儿。这里有个很关键的细节Buffer 里的 raw event 是不可修改的。即使后来发现某条记录是错误示范或者跟某个任务无关Dreaming 在整理时也不会去篡改原始事件只会在整理完成后给这条事件打一个 archived 标记。这个设计让所有后续分析都有据可查不至于出现“为了修数据反而把历史搞脏”的尴尬情况。2.2 工作记忆在线推理时真正看得见的那一小块上下文工作记忆是智能体在每轮对话中实际使用的上下文片段。它不做全量存储而是从长期语义库里检索 top-K 条相关内容拼接到系统提示词里。OpenClaw 默认取12条用向量相似度排序时间衰减作为辅助权重。这一层跟 Buffer 是两种完全不同的形态。Buffer 是流式的新事件不断追加旧事件不断淘汰工作记忆是快照式的每轮推理临时生成用完即弃。Dreaming 存在的意义就是把流式的 Buffer 数据整理成快照式工作记忆能够高效检索的形态。没有 Dreaming工作记忆的每一次检索都相当于在一片原始沼泽里捞东西有了 Dreaming检索面对的是一个有目录、有摘要、有技能索引的图书馆。2.3 长期语义库摘要与技能模板的最终归宿长期语义库里存两类记录。第一类是摘要记录DigestRecord它保存的是从原始事件链中提炼出来的语义摘要同时挂着一个非常关键的字段——source_chain也就是来源事件链。这条链可以一路回溯到当天发生的具体事件ID保证任何摘要都不是凭空产生的。第二类是技能模板SkillTemplate格式是“条件 动作 结果”的三元组。比如“当用户询问订单状态且已经拿到订单号 → 调用查询订单接口 → 92%的概率成功返回物流信息”。技能模板带有 confidence 置信度和 hit_count 命中次数只有连续命中或人工确认后置信度才会提升。索引方面我们以向量索引为主、时间索引为辅。向量索引用余弦相似度做近似检索时间索引用于支撑“最近一周发生了什么”这类带时间窗的查询。source_chain 字段不仅是可回溯的凭证也是后面排查异常行为的救命稻草。提示很多团队一开始会把 Buffer 直接当长期库用不区分原始事件与整理后的知识。短期跑起来没问题一两个月后会明显感觉到检索质量和响应速度同步下降。三层结构多出的那点存储成本相比后续排查问题的代价完全是值得的。3. Dreaming 五个阶段的完整执行流程3.1 召回不是全量回放而是带着“价值”去捞Dreaming 的第一阶段是召回。最容易犯的错误是把 Buffer 里所有事件一股脑拿出来处理一遍——这样既慢又会让低价值片段喧宾夺主。我们的做法是构造一个候选池然后按分数排序截取前200条作为本轮处理对象。召回时的打分公式并不复杂score importance_score * exp(-0.05 * age_hours) / (replay_count 1)三个因子各管一件事importance_score 决定这条事件本身是否重要指数衰减项决定近期性一天前的事件衰减到约30%权重replay_count 加1后取倒数是为了防止一条事件被反复回放、反复强化导致某个热点被无限放大。只处理未被 archived 的事件是硬条件已经被 Dreaming 整理过的原始事件直接跳过。这轮捞出来的候选事件会带着各自的 embedding 进入下一阶段。3.2 重放与事件链重组把碎片还原成完整经历召回之后是重放。注意这里的“重放”不是直接把事件文本塞给模型而是先做一步关键的重组对候选事件按 embedding 相似度做聚类簇内再按时间戳升序排列形成一条条“事件链”。为什么要做这件事因为原始事件在 Buffer 里是散落的同一次任务可能穿插着好几段无关对话。比如用户在处理订单问题时中间问了一句“今天天气怎么样”如果不做聚类事件链就会变得支离破碎。聚类后订单问题相关的工具调用、报错信息、成功返回会聚成一条完整链路天气闲聊则被归到另一个簇互不干扰。聚类完成后我们会在事件链上做一个简单的状态标注工具调用是否成功、用户目标是否达成、链路是否中途放弃。这些标注是后续摘要和技能提取的骨架没有状态标注的摘要只是流水账有了状态标注的摘要才具备学习价值。3.3 提炼与摘要不是压缩而是可验证的结论对每条事件链Dreaming 会调用一次大模型生成摘要。这里的 prompt 约束很严格不能写“这次对话讨论了……”必须回答三个问题用户原本想要什么、系统实际做了什么、最终结果如何。同时要求摘要保留关键实体、数字目标和明确的成败结论。生成后的摘要不能直接落库先过一道一致性校验。我们会从源事件里抽取关键实体和数值然后检查摘要是否完整保留了这些信息。实体保留率低于80%的摘要会被降级处理只保留关键词索引用于检索摘要正文不写入长期库。这一步看起来有些“浪费”但非常必要。模型生成摘要时出现幻觉的概率并不低如果不做校验一条错误的摘要进入长期库之后所有相关查询都会被污染。宁可少存几条有用信息也不能让错误信息进库。3.4 技能提取从成功经验里挖出“条件-动作-结果”摘要整理完之后就该归纳可复用的技能了。技能提取的目标不是覆盖所有事件而是聚焦“成功完成目标”的事件链。我们会在这些链路之间做序列模式挖掘找出反复出现的“条件-动作-结果”组合。典型产出长这样条件用户提供订单号且询问物流状态动作调用查询物流接口返回轨迹列表结果成功展示物流信息用户未继续追问技能模板在落库之前会有一个额外的验证步骤拿最近三个相关的历史会话做模拟推导预测的动作如果与真实动作一致才允许落库。初始置信度设为0.5后续每命中一次加权重连续命中3次后升到0.8如果出现错误触发置信度会降到0.2并送入观察区等待人工复核。3.5 整合与索引重建写入长期库时想清楚怎么“不污染”最后一个阶段是整合。在把摘要和技能写入长期语义库之前要先做相似度去重——新摘要与已有摘要的向量余弦相似度超过0.9时不新建记录而是把新的 source_chain 追加到旧记录上同时刷新 last_refreshed_at。这样既保住了知识的连续性又不会让长期库无限膨胀。整合完成后所有参与本轮 Dreaming 的原始事件都会被标记为 archived防止下一轮召回时再次进入候选池。最后执行一次向量索引重建保证新写入的内容能被在线检索及时命中。注意如果跳过 archived 标记或者只做摘要不做一致性校验短期看不出问题运行一周后就会出现语义漂移和重复回放。这两个点属于“不做一定会踩”的坑后面第6章会详细展开。4. 调度策略与资源控制什么时候做梦怎么不做过头Dreaming 是一个消耗资源的功能尤其在生产环境里调用大模型生成摘要是有成本和延时的。如果调度不当它会反过来拖累在线服务。我们在调度上花了不少功夫核心思路是该跑的时候绝不偷懒不该跑的时候绝不硬来。4.1 触发条件空闲窗口、积压度与强制周期三路并行我们设计了三条触发路径命中任意一条就会启动一轮 Dreaming。第一条是空闲窗口系统连续10分钟没有任何新的用户事件进入 Buffer就认为当前处于低负载时段这时候适合做后台整理。第二条是积压度Buffer 里未归档事件数超过200同时事件进入速率持续高于平均处理速率说明日志快堆满淘汰线了等不到空闲也必须处理。第三条是强制周期无论有没有空闲每4小时兜底执行一次防止极端情况下长时间不整理。三条路径并不互斥。实际执行时会取优先级最高的那条如果积压度非常严重即使系统还在交互中也会在确保不抢占在线资源的前提下启动一轮精简版 Dreaming——只做摘要不做技能提取以最快速度压降未归档事件数量。4.2 资源预算与并发隔离绝不让后台任务拖垮在线推理Resource 控制是我们踩过最深的一轮坑。早期版本里Dreaming 进程和在线推理共享同一批计算资源结果在线会话一多后台摘要任务就开始抢显存用户明显感觉响应变慢。后来我们做了严格的资源隔离。Dreaming 任务只在一个 worker 上运行CPU 限制为1核内存上限设死不使用在线服务的 GPU/NPU 资源所有大模型摘要调用走独立的低优先级队列超时机制设得比在线推理短一倍。同时整个 Pipeline 是异步执行的用户只要产生新输入系统会立刻抢占后台任务把计算资源还给出主对话链路。所有写操作都走事务Dreaming 正在写长期库的时候即使遇到进程被抢占或异常退出也不会留下半个半成品摘要。等下一轮调度进来事务回滚后重新执行。4.3 睡眠窗口与退避策略别让 Dreaming 自己变成干扰源调度器还需要防“抖”。我们加了一个最小运行间隔同一轮 Dreaming 结束后的15分钟内不允许再次触发。如果一轮 Dreaming 执行超时或频繁失败说明系统可能正处于异常状态调度器会开启指数退避——从12分钟开始逐次翻倍最多退避到2小时。另外还有一个硬性上限每天最多执行8轮。这个数字不是拍脑袋定的而是根据线上数据算出来的。正常情况下一天沉底的成功经验大约在300到500条8轮 Dreaming 足够处理超过8轮说明系统正在高频交互此时更应该让资源服务在线用户而不是加速整理记忆。5. 核心代码骨架与关键数据结构5.1 数据结构定义先看数据结构。用 Python dataclass 定义清晰直观dataclass class EventRecord: event_id: str session_id: str created_at: datetime event_type: str # user_message / tool_call / tool_result / system_note content: str embedding: np.ndarray importance: float archived: bool False replay_count: int 0 dataclass class DigestRecord: digest_id: str summary_text: str source_chain: list[str] # 原始事件ID列表可一路回溯 first_seen_at: datetime last_refreshed_at: datetime dataclass class SkillTemplate: skill_id: str condition: str # 触发条件描述 action: str # 动作描述 outcome: str # 预期结果描述 confidence: float 0.5 hit_count: int 0三个数据结构分别对应三层记忆的核心实体。EventRecord 是流式写入的DigestRecord 和 SkillTemplate 是 Dreaming 产出的。注意 DigestRecord 里的 source_chain 是列表类型承载的是事件ID链而不是直接存文本避免重复存储导致的一致性问题。5.2 Dreaming 管线的伪代码实现下面是 Pipeline 的主体骨架我简化了异常处理和日志埋点保留核心逻辑class DreamingPipeline: def __init__(self, buffer: EventBuffer, store: LongTermStore, embedder, summarizer): self.buffer buffer self.store store self.embedder embedder self.summarizer summarizer async def run(self): # 加锁防止多轮 Dreaming 并发执行 async with self._lock: candidates await self._retrieve() if len(candidates) 10: return clusters await self._cluster(candidates) summaries await self._summarize(clusters) skills await self._discover_skills(summaries) await self._integrate(summaries, skills) # 核心把已处理事件标记为 archived防止重复回放 for event in candidates: event.archived True event.replay_count 1 await self.buffer.flush() async def _retrieve(self) - list[EventRecord]: events self.buffer.unarchived(limit500) scored sorted(events, keylambda e: e.importance * math.exp(-0.05 * age_hours(e.created_at)) / (e.replay_count 1), reverseTrue) return scored[:200] async def _cluster(self, candidates): # 用 embedding 做聚类阈值 0.82返回事件簇列表 return cluster_by_embedding(candidates, threshold0.82) async def _summarize(self, clusters): results [] for cluster in clusters: summary await self.summarizer.summarize(cluster) if consistency_check(summary, cluster): results.append(DigestRecord( digest_iduuid4().hex, summary_textsummary, source_chain[e.event_id for e in cluster], first_seen_atnow(), last_refreshed_atnow(), )) return results async def _discover_skills(self, summaries): # 优先从成功事件链中挖掘条件-动作-结果模式 return await skill_miner.mine(summaries) async def _integrate(self, summaries, skills): # 相似度 0.9 视为重复追加 source_chain 而不是新建 for item in summaries: await self.store.upsert_digest(item) for skill in skills: await self.store.upsert_skill(skill) await self.store.rebuild_index()代码里最关键的是_retrieve方法里的打分排序以及_integrate完成后统一标记 archived。这两个细节直接决定了长期库会不会越跑越乱。5.3 集成方式与调用约定在实际的智能体主循环里我们是这样接入的async def main_loop(): while True: new_event await wait_event() await buffer.append(new_event) if should_trigger_dreaming(): # 异步启动不阻塞用户请求 asyncio.create_task(pipeline.run())调度器只负责判断“该不该跑”和“能不能跑”具体怎么跑完全交给 Pipeline 内部。Pipeline 的 run 方法自带互斥锁如果上一轮还没跑完新的触发会被直接丢弃而不是排队堆积。这样最坏情况下损失一轮整理但绝不让后台任务排起长队。调用约定里还有一条run 方法结束时会在调度器里记录 last_run_at下次调度时至少间隔15分钟。这个字段由 Pipeline 自己写调度器不关心中间状态。6. 踩坑实录与效果调优哪些坑必须避开6.1 重复回放导致语义漂移源头不堵越整越偏第一轮上线时我们栽在了一个很隐蔽的问题上语义漂移。现象是某个热点问题在摘要库里被反复强化摘要内容逐渐偏离原始事实甚至开始出现原事件里根本没有的细节。排查了很久才找到根因。问题出在整合环节——我们当时没有在 Dreaming 结束后给原始事件打 archived 标记。于是同一批事件下一轮又被召回摘要被二次提炼、三次提炼每提炼一次都有概率引入轻微失真跑一周之后摘要已经变成了“原始事件的二次创作”。修复方式就是前面的event.archived True和replay_count上限。我们把 replay_count 上限设为3超过3次的事件不再进入候选集。这个修复上线后漂移现象基本消失。这条经验让我明白一个道理所有摘要必须可回溯原始事件source_chain否则漂移发生的时候你连排查的方向都没有。6.2 摘要噪声污染长期记忆一致性校验不是可选项另一个高频坑是摘要噪声。有一轮测试中模型把一个“用户抱怨响应慢”的对话摘要成了“用户要求必须当日发货”。摘要落库后后续所有涉及物流的查询都检索到了这条错误信息系统连续回答了三天错误答案。从那以后一致性校验从“建议项”改成了“硬性门槛”。我们写了一个简单的实体保留率检查def consistency_check(summary: str, source_events: list[EventRecord]) - bool: key_entities extract_entities(source_events) if not key_entities: return True coverage sum(1 for e in key_entities if e in summary) / len(key_entities) return coverage 0.8覆盖率不足0.8的摘要不进长期库只保留关键词索引用于向量检索。这个妥协挺聪明系统仍能通过关键词索引找到相关事件的蛛丝马迹但错误文本不会被当作知识库正文带进上下文。6.3 技能过度泛化把“特殊情况”当成了“普适规律”技能提取模块也出过一轮问题属于过度泛化。早期技能模板的条件写得过于抽象比如“用户询问价格相关问题就调用库存查询接口”。结果在“用户问发货时间”这种场景下也被触发了返回的内容完全不相关。后续我们加了两个约束。一是条件部分必须包含至少一个具体槽位比如 API 名称、实体类型或明确的意图标签不允许纯抽象描述。二是技能落库前的模拟验证用最近三个真实会话做一次深推预测动作与真实动作一致才放行。同时置信度更新规则也定了死线初始0.5命中3次才升到0.8一旦错误触发立即降到0.2并送入观察区等待人工确认。这两条约束上线后技能误触发率从早期的12%降到了2%以下。也让我意识到技能提取的价值恰恰来自“克制”不是挖掘得越多越好。6.4 效果评估收益指标与消融对照最后看一下实际效果。我们内部跑过两周对照实验数据样本主要是客服工单和知识问答场景。为了排除偶然性做了三组对照关闭 Dreaming、只做重放不做摘要与技能提取、完整 Dreaming 流程。指标关闭 Dreaming仅重放完整 Dreaming任务成功率71.2%74.5%82.3%平均交互轮次6.86.34.9跨天召回命中率21.4%23.1%65.8%在线检索平均时延390ms395ms310ms完整 Dreaming 流程的优势非常明显。单独做重放环节收益很小说明“把历史再放一遍”本身没有意义真正带来提升的是摘要整理和技能提取——它们把原始经验压缩成了在线推理真正用得上的形态。另外在线检索时延下降也符合预期长期库里的摘要记录比原始日志短得多向量检索的候选集更干净排序阶段的计算量大幅降低。顺带说一句消融实验这类测试建议每两到三周做一次。智能体系统的行为会随着用户群体和使用场景漂移一个月前有效的整理策略一个月后可能不再匹配。Dreaming 不能做成一次上线就永久不变的静态工具。最后说一点我个人的体会。Dreaming 这种机制真正难的不是实现而是克制——克制不要做全量回放克制不要让摘要过长克制不要让技能泛化得太野。如果你准备在自家智能体上做类似功能我建议先用一周的日志做离线可视化看看用户提问到底集中在哪几类再决定要不要上完整的 Dreaming 链路。有个小技巧不管摘要还是技能模板一定把 source_chain 挂在上面以后排查任何奇怪行为都能顺着链条找到根因。我在 OpenClaw 上被这类问题折磨过好几次把这条经验写在这希望你能少走弯路。

关于本文作者

来自尧图内容编辑团队

尧图内容编辑团队 内容团队

尧图内容编辑团队

本文由尧图网络内容编辑团队执笔。团队由资深项目经理、前端工程师与设计师组成,所有内容均来自亲手交付的真实项目,先讲清问题、再给出可落地的解法。尧图深耕北京网站建设十年,服务过京华建材集团、智造科技等各行业客户,把一线经验沉淀为可复用的行业观察。

  • 十年建站经验,覆盖建材、制造、服务、文创等
  • 项目经理把关选题与事实准确性
  • 工程师与设计师联合撰写专业细节
  • 统一编辑规范,保证文风与排版一致
  • 每月复盘转化数据,迭代选题方向

延伸阅读

相关资讯与近期热门内容

深度阅读推荐

建站决策前值得细读的三篇

网站改版的5个关键决策
2024-08-12

网站改版的5个关键决策

什么时候该改版、改到什么程度、如何避免流量掉光,京华建材集团改版复盘给出答案。

获取专属建站方案

看完文章,把您的行业与预算告诉我们,免费获取一份量身定制的官网建设方案与报价。

立即免费咨询