AI agent四层记忆架构实践,治疗大模型"金鱼脑"

源码已跑通,本文不讲虚的概念,只讲我们在半导体 Fab(晶圆厂)场景落地 Agent 记忆时,怎么避免把 Prompt 撑爆。
github:https://github.com/BumbleBee-ZDS/fab_memory_agent


一、背景:Fab 工程师不需要"聊天",需要"查档"

在半导体工厂,工艺工程师(PE)最常问的问题是这类:

“EQP-01 又报警了,上次是怎么处理的?”
“这批 Lot 的刻蚀参数,跟标准值差多少?”

传统的做法是:翻 EES 系统、查维修工单、翻 PDF 标准配方。

我也试过直接上 RAG:把几百份 SOP、几万条工单扔进向量库,每次全量召回塞给大模型。结果有两个问题:

  1. 参数冲突:SOP 写的是标准值,工单里是异常值,模型经常一本正经地混着编。
  2. 上下文浪费:用户明明只问 EQP-01,我却把 CVD 机台的文档也召回了一堆,128K 上下文照样不够用。

痛定思痛,我重构了一套分层记忆系统,做了一个最小可运行 Demo(Streamlit + DeepSeek)。核心思想只有一句:

Context Window 是寄存器,不是硬盘。


二、架构总览:四层记忆,各司其职

我把记忆拆成四层,每一层都有明确的"存什么、活多久、怎么取"。

层级 技术实现 Fab 场景里存什么 生命周期
感知层 Streamlit Input 工程师的原始自然语言 毫秒
工作记忆 st.session_state 最近 5 轮对话(CoT 中间结果) 单次会话
短期记忆 Dict + TTL(模拟 Redis) 当前正在查的 EQP-IDLot-ID 30 分钟
长期记忆 KV 热库 + ChromaDB 冷库 机台标准参数 / 历史维修工单 永久

关键区别

  • 机台的标准气压(500mTorr)是精确事实,放 KV,直接读,绝不向量化。
  • 维修工单的描述(“valve 堵塞、工程师 A 更换”)是非结构化经验,放向量库,语义搜。

三、大脑:Memory Manager 调度逻辑

很多 Demo 里,记忆是个被动数据库。我的设计里,MemoryManager 是主动调度器,它干了三件最核心的事。

1. 路由:用规则,不用大模型

在 Fab 场景,用户说话很直接,没必要上 BERT 甚至 GPT-4o 做意图识别。

# router.py(简化版)
def route(self, query: str):
    if "上次" in query or "历史" in query or "报警" in query:
        return {"action": "retrieve"}
    if query.startswith("/"):
        return {"action": "system_cmd"}
    return {"action": "direct_chat"}

为什么这么粗暴?
因为工程师不会跟机台谈哲学。看到"上次"“报警”,100% 是要查历史。规则延迟 0ms,比调 API 省下的钱够买两杯咖啡。

2. 短期记忆自动追踪

用户输入 EQP-01 又报警了,系统自动提取机台号:

# short_term.py
def update_state(self, query):
    import re
    match = re.search(r'(EQP-\d+|CVD-\d+)', query)
    if match:
        self.store['current_eqp'] = match.group(1)

这样,后续不管用户说"把参数调回去"还是"看看压力",系统都知道指的是哪台机台——这就是短期记忆维持"当前语境"的能力

3. 冷热双路召回

长期记忆模块同时查两个地方:

# long_term.py
def search(self, eqp_id, query):
    hot = self.kv.get(eqp_id)          # 毫秒级,标准参数
    cold = self.vector.query(query)    # 语义,历史工单
    return f"{hot}\n{cold}"

热数据在前,冷数据在后。Prompt 里永远是"精确事实 + 相关经验",模型不再乱猜。


四、工作记忆:为什么只留 5 轮?

我在 main.py 里硬性限制工作记忆长度:

MAX_HISTORY = 5
working_memory.append(msg)
if len(working_memory) > MAX_HISTORY:
    working_memory.pop(0)

原因很现实:
工程师查问题时,对话通常是"发散-收敛"的。前两轮可能是闲扯,第三轮锁定机台,后面才是调参。超过 5 轮前的上下文,90% 是噪音。砍掉它们,不仅省 Token,还能强迫模型依赖外部记忆,而不是靠自己"硬背"上文。


五、降级设计:LLM 不是唯一依赖

工业现场最怕断网或 API 挂。我在 utils/mock_llm.py 里做了一层兜底:

  • 优先:调用 DeepSeek API,把检索结果组织成自然语言。
  • 降级:如果 API Key 没配或超时,直接拼接检索结果返回。
[来源: 长期记忆]
机台 EQP-01 标准气压 500mTorr。
历史工单:Lot1234 气压异常,调整 valve 后恢复。

虽然生硬,但信息是对的。在 Fab,准确性永远大于文采。


六、跑起来的样子

Streamlit 左侧 Sidebar 实时展示"记忆快照":

  • 🟢 短期记忆:EQP-01(剩余 28min)
  • 📚 长期记忆:KV(12条) + 工单(10条)
  • 💬 工作记忆:3/5 轮

用户问完,回复下方会打标签:

  • [来源: 长期记忆] → 说明走了检索
  • [来源: 直接回复] → 纯闲聊
  • [来源: 系统] → 执行了 reset

/reset,短期记忆清零,相当于工程师下班交班,下一班从零开始。
在这里插入图片描述


七、踩过的坑(重点)

1. 别把结构化参数向量化

最早我把"500mTorr"也嵌成向量,检索回来变成了"大约 400-600"。现在一律 KV 直读。

2. 工单数据要带时间戳

这次 MVP 还没加,但下个版本每条工单必须带时间,用来解决"新 SOP 覆盖旧工单"的冲突。

3. TTL 比手动删重要

工程师查完一个机台,半小时后大概率切到别的。TTL 自动过期,避免"上个案子的记忆串到下一个案子"。


八、总结:给工业 AI 开发者的建议

做 To C 的 Agent,可以靠模型"猜";做 Fab 这种工业场景,确定性比聪明更重要

好的记忆系统不是拼 Prompt 有多长,而是:

  • 短期记住"你在查哪台机台"
  • 长期存好"这台机台到底该怎么修"
  • 中间用规则和小模块,低成本地把两者连起来

更多推荐