5.2 工作记忆:上下文与飞行状态管理


5.2 工作记忆:上下文与飞行状态管理

本节摘要:工作记忆是决策窗口内的工作集——状态板的当下快照加上几十步内的来龙去脉。本节设计上下文装配器:从状态板、航图与近期日志里挑选决策所需条目,按重要性评分管理遗忘,保证并发读写下的一致性;并以"上下文缺项"排错法收尾——大部分"智能体突然变笨",病根是工作集里少了关键条目。

为什么智能体跑着跑着就"忘了"刚定的计划?排查十个这类案例,九个的答案不在算法里,而在工作集的装配清单上:决策那一刻,该在场的条目没在场——目标栈被挤掉、刚发生的失败没进上下文、约束条件躺在日志里没人捞出来。上一节把记忆分了层,本节专讲最急的一层:工作记忆怎么装配、怎么遗忘、怎么在并发读写下保持一致。它延伸自第 1 章的状态板——状态板管当下快照,工作记忆管来龙去脉,两者共同构成决策的完整视野。

上下文装配:决策的供料系统

每拍决策需要什么?不同舱段答案不同,但装配逻辑同构。规划舱重算航线需要:目标栈全貌、当前航线与过期状态、目标附近的信念快照;行动舱执行需要:当前动作、验收谓词引用的字段、近期失败记录(同一个动作刚失败过,重试策略就该变);监控需要:偏差历史与阈值带。装配器按舱段的清单从各层记忆里取料:状态板取当下、航图取相关区域、航行日志取近期相关事件。

装配的核心机制是相关性圈定:空间上圈定任务路线周边的航图区域(不必全图),时间上圈定决策相关的事件窗(不必全部历史),语义上按当前目标过滤(配货任务不必带充电桩细节)。圈定做得好,工作集小而全;做得差,要么臃肿(噪声稀释关键条目),要么缺项(关键信息不在场)——两种病都比"记忆不够大"常见得多。

遗忘策略:给工作集留呼吸位

工作集有容量上限(真实系统的内存、决策接口的输入预算),满了怎么办?遗忘不是清空,是按价值让位。每条工作集条目带两个分数:重要性(丢失的代价——目标栈顶最高、约束次之、装饰性背景最低)与新鲜度(距上次使用的时间)。淘汰时先动"低重要性加陈旧"的条目,保护"高重要性"条目永不落榜——目标与安全约束是终身豁免户。一个易犯的错误是只按时间淘汰(先进先出),结果刚立的约束被一条高频旧读数挤出去,决策当场跑偏。

import time, threading from dataclasses import dataclass, field @dataclass class WorkingItem: key: str value: object importance: float # 丢失代价 0 到 1,目标与安全约束恒近 1 last_used: float = field(default_factory=time.monotonic) class WorkingMemory: """工作记忆:装配、评分、淘汰,全程读写一致。""" CAPACITY = 64 # 工作集条目上限 def __init__(self, board, cabin_map, log): self.board = board self.map = cabin_map self.log = log self._items: dict[str, WorkingItem] = {} self._lock = threading.RLock() def assemble(self, who, context_spec): """按舱段清单装配工作集:板面、地图窗、事件窗。""" with self._lock: pack = {} for key in context_spec.board_keys: # 板面直取 reading = self.board.read(key) if reading and not getattr(reading, "stale", False): pack[key] = reading.value self._touch(key, reading.value, importance=context_spec.weights.get(key, .5)) pack["nearby_map"] = self.map.window( # 空间圈定 center=self.board.read("pose").value, radius=context_spec.map_radius) pack["recent_events"] = self.log.query( # 时间圈定 since=context_spec.event_window, limit=16) self._evict_to_fit() return pack def _touch(self, key, value, importance): item = self._items.get(key) if item: item.last_used = time.monotonic() else: self._items[key] = WorkingItem(key, value, importance) def _evict_to_fit(self): """淘汰:先低重要性加陈旧;目标与安全约束豁免。""" while len(self._items) >= self.CAPACITY: victim = min( (it for it in self._items.values() if not it.key.startswith(("goal", "safety"))), key=lambda it: (it.importance, -it.last_used), ) del self._items[victim.key]

并发一致性的处理沿袭第 1 章铁律:工作记忆的一切读写走锁、走接口,舱段不许私藏副本。装配出的工作集按拍固定——同一拍内所有舱段看到同一份工作集,避免"规划舱按旧信念决策、行动舱按新信念执行"的拍内撕裂。

淘汰策略的正确性也要可测,给一段自检:

def test_eviction_preserves_goals(memory): """淘汰自检:容量打满后,目标与安全条目必须幸存。""" memory.push(WorkingItem("goal:deliver", "装柜口", importance=1.0)) memory.push(WorkingItem("safety:forbidden_zone", "高压区", importance=1.0)) for i in range(memory.CAPACITY + 4): # 灌满再溢出 memory.push(WorkingItem(f"noise:{i}", i, importance=0.1)) keys = memory.keys() return ("goal:deliver" in keys and "safety:forbidden_zone" in keys) def test_same_tick_consistency(memory, planner, executor): """按拍固定自检:同拍两个舱段看到的工作集一致。""" pack_a = memory.assemble("planner", PLANNER_SPEC) pack_b = memory.assemble("executor", EXECUTOR_SPEC) return pack_a["nearby_map"] == pack_b["nearby_map"]

联调测试与故障排查

工作记忆的联调用缺项注入:从装配清单里故意去掉某类条目,断言下游舱段的反应符合预期(去掉近期失败记录,重试策略应退化为保守;去掉约束条目,规划应触发约束校验报警)。反向再跑清单完备性:给舱段的真实决策记录做考古——每次失误决策缺了什么,回填进装配清单。联调的产出是各舱段装配清单的初版,之后随故障史持续增补。

按症状排查:突然变笨(原本会做的事开始做错)先考古当拍工作集——八成是关键条目缺项或被淘汰,查装配清单与淘汰日志;决策漂移(同一局面反复给出不同决策)查工作集按拍固定是否失守——拍内被并发更新就是根因;容量告警频繁查圈定参数——空间半径与事件窗通常设得过于慷慨,收紧比扩容便宜。

⚠️ 常见坑:把工作记忆当第二个状态板用。工作集按拍重建、内容可淘汰,真相永远只在状态板与日志里——把易失层当真相层,等于把账记在便签上。

本节要点回顾

  • 工作集三来源:板面直取、空间圈定、时间圈定;装配清单是各舱段的契约,随故障史增补。
  • 遗忘按价值:重要性加新鲜度双评分,目标与安全约束终身豁免;只按时间淘汰会挤掉刚立的约束。
  • 按拍固定:同一拍所有舱段共用同一份工作集,杜绝拍内撕裂。
  • 一致性走铁律:一切读写走锁与接口,工作集是易失缓存,不是真相层。
  • 缺项注入法:故意去掉条目看下游反应,反向考古失误决策回填清单——装配清单是这么长出来的。

工作记忆管好了"在飞的时刻",但断电的时刻没人管就会前功尽弃。下一节上检查点与重放日志:让强制重启变成无害插曲。


作者与出处
原作者: 灏天文库
来源:灏天文库
整理: 灏天文库整理
由灏天文库平台收录,内容或由平台用户上传,仅供学习交流
发布者: 作者: 灏天文库 转发
评论区 (0)
U