在使用coding Agent时,我一直惊艳于AI能无缝的帮我回忆出我需要的context,所以对一些操作过程中的memory的管理机制十分好奇,主动通过AI和一些资料完成了这次学习。
开篇:理解记忆机制为何重要
在高频使用 Openclaw、CodeBuddy 这类 AI Agent 工具中,还是会遇到过这些问题:
Agent 突然"失忆",忘记了几分钟前刚讨论过的内容
Agent 回答跑题,因为它不知道该调取哪段历史上下文
上下文窗口塞太满后,Agent 变得"降智",只会说大框架
这些问题的根源,都在于 Agent 的记忆系统缺乏优雅的路由与决策机制。理解这套机制,不仅能帮助更好地使用现有工具,还能为规划未来的 Agent 工作流打下基础。
第一部分:重新认识 AI Agent 的记忆本质
1.1 大模型本身没有记忆
这是最核心的认知前提。
大模型(LLM)是一个无状态(Stateless)的计算函数。它不知道"过去"发生了什么——每一次你向它提问,对它来说都是"宇宙诞生后的第一秒"。
它之所以能表现出"记得",是因为 Agent 的外围代码在每次请求前,把过去的聊天记录或相关文档,悄悄拼接在了你的新问题前面。
打个比方:大模型就像一个失忆症患者,每次问诊时你都得把病历本递给他,他才能"想起来"。
1.2 上下文窗口就是"信封"
大模型的上下文窗口(Context Window)是一个固定大小的空间,就像一个物理信封。所有信息——系统提示词、历史对话、检索到的记忆、外部工具返回的结果——都得往这个信封里塞。
当项目变大、对话变长时,会发生两种糟糕的情况:
Token 成本爆炸:每多说一句话,都要付出一整本书的钱
注意力分散(Loss in the Middle):大模型像人一样,太长的内容会记住开头结尾,忘掉中间最关键的信息
因此,Agent 的记忆管理,本质上是一个信息流检索与组装的工程问题。
1.3 记忆管理的本质是什么
Agent 的记忆系统,就是在有限的上下文窗口里,精准地决定:
塞什么进去(内容选择)
什么时候塞(触发决策)
用什么策略找(召回策略)
这三个问题没有标准答案,但有成熟的架构思路,下面会逐一展开。
第二部分:AI Agent 记忆的三层架构
Agent 的记忆并非单一整体,而是分为三个层次,每层的传输机制、决策逻辑、存放位置完全不同。
2.1 第一层:基础设定注入(Static Profile)
内容:你是谁、你的职业背景、核心偏好等几乎不变的信息
传输机制:每次必传,强制硬编码,不依赖 AI 的自主判断
存放位置:System Prompt(系统提示词)的最顶部
类比:就像一个人的"灵魂"或"出厂说明书"。无论你问什么,这段话都跟着请求发给 AI。它是模型理解所有问题的"前置滤镜"。
实现方式:通常存放为结构化的配置文件,如 .ohmo/memory/soul.md(核心逻辑设定)、.ohmo/memory/user.md(偏好与属性)。这些文件在每次对话开头被自动注入。
2.2 第二层:即时工作记忆(Sliding Window Context)
内容:你和 Agent 在过去几分钟内刚刚说过的最后 3-10 轮对话
传输机制:每次必传,自动滑窗,由代码层面的队列(Queue)机制强制执行
存放位置:动态维护的对话历史队列,每次请求自动拼接在 System Prompt 之后
逻辑:维持对话连贯性。如果没有这一层,你刚说完"帮我写个登录页面",下一句说"再加个按钮",AI 就不知道"按钮"要加在哪里。
工作原理:
程序自动维护一个队列(Queue)每当产生一句新对话:→踢掉最老的一句→把新对话塞进队列→把整个队列塞进下一次请求的上下文
决策过程:不需要 AI 决策,由代码规则强制执行。保证了你不需要在每一句话里都重复背景。
常见配置:保留最近 5-15 轮对话(具体数字可配置),超出后自动滑出
2.3 第三层:长期记忆检索(Retrieved Memory)
内容:更早之前讨论过的内容——一个月前的产品架构方案、三个月前的跑表数据、三天前写的一段已经被滑窗"踢出去"的代码
传输机制:按需调取,非必传——只有当 AI 意识到"我断片了"时才去搜索并调取
存放位置:本地数据库(SQLite + FTS5 全文搜索)或向量数据库(ChromaDB/FAISS)
逻辑:这是整个记忆系统中"最聪明"的部分,也是 AI Agent 架构的核心难点所在
三层记忆对比一览
维度 | 基础设定注入 | 即时工作记忆 | 长期记忆检索 |
|---|
内容 | 你是谁、核心偏好 | 最近几轮对话 | 更早的历史记录 |
传输 | 每次必传,强制 | 每次必传,自动滑窗 | 按需触发 |
决策者 | 代码(硬编码) | 代码(自动队列) | AI 自主 + 代码辅助 |
存放 | System Prompt | 动态队列 | 向量库 / 全文索引 |
类比 | 出厂说明书 | 工作台上的便签 | 档案柜里的老文件 |
第三部分:AI 如何知道"何时"需要搜索记忆
这是最核心、最令人困惑的问题:大模型本身没有记忆,那它怎么知道"我需要去翻旧账了"?
答案是:通过代码规则(System 1 快思考)和 模型推理(System 2 慢思考)的协同配合来实现。这借鉴了认知科学中的"双系统"模型。
3.1 机制一:基于代码规则的"被动触发"(System 1)
这是最基础、最稳定的方式。决策权完全在代码手里,大模型不参与决策。
方式 A:生命周期拦截(Lifecycle Hooks)
在产品逻辑层面硬编码。Agent 每次开启一个新任务,代码会自动在后台执行一次 search_user_profile(),把用户偏好、项目环境变量等基础设定取出来,固定塞进 System Prompt。
用户打开新会话↓代码自动触发search_user_profile()↓用户的soul.md、user.md内容被取出↓拼接到SystemPrompt顶部↓请求发送给大模型
方式 B:状态机切换触发
当 Agent 的工作流进入特定节点时,自动触发特定检索。比如在构建应用时,一旦进入"UI 渲染阶段",代码就自动检索 ui_components_history 存储库,无需模型指示。
方式 C:正则/意图分类网关
在用户输入到达大模型之前,先经过一层轻量级的代码网关:
这种方式的特点是:稳定、零延迟、无额外 API 成本,但不够"聪明",无法处理复杂或模糊的场景。
3.2 机制二:基于 Prompt 与 Tool Calling 的"主动触发"(System 2)
这是目前高阶 Agent(如 Claude Code 的底层逻辑)最核心的机制。决策权交给了大模型本身,通过 ReAct(Reasoning and Acting)框架实现深度逻辑推演。
核心思路:不告诉模型具体怎么搜索,而是赋予它一个"工具箱",并定义好使用的"契约"。
System Prompt 示例片段:
你是一个高级研发助手。你的短期上下文中只包含最近5轮对话。如果用户提及了更早的设定、特定的代码片段,或者你发现自己缺乏足够的上下文来回答问题,你不能猜测。你必须首先调用query_long_term_memory工具来获取信息。在你获得工具返回的结果后,再进行下一步的思考和回答。
完整触发流程(以一个具体例子说明):
用户提问:"把昨天的那个登录逻辑优化一下。"┌─────────────────────────────────────────────┐│大模型内部思考(Scratchpad):││"用户提到了'昨天的登录逻辑', ││ 但我当前的上下文中没有这段代码。 ││ 我不能猜测,我需要先搜索。"│└─────────────────────────────────────────────┘↓大模型输出ToolCall(特殊格式的JSON):{"tool":"query_long_term_memory","query":"昨天的登录逻辑"}↓外围代码拦截到这个请求,暂停生成↓代码执行数据库搜索(向量检索+全文检索)↓代码将搜索结果组装成ToolResponse发回给大模型↓大模型阅读了补充的记忆后,最终输出优化后的代码这种方式的优点:极具灵活性,模型在面临复杂信息缺失时,能像人类一样停下来深度思考并主动求索。
缺点:多了一次或多次的 API 往返交互(Round-trip),增加了延迟和成本。
3.3 完整的"多级缓存查询"决策流程
当用户发起一个请求时,Agent 内部会经历一个类似"多级缓存查询"的过程:
步骤 1:第一眼扫视(即时上下文)
AI 拿到你的请求后,先看一眼当前窗口里的最后几句话。
步骤 2:意图决策(推理层)
在这个阶段,优秀的 Agent 会在内部运行一个逻辑判断。这个判断是通过 System Prompt 里的规则引导 实现的。
规则示例(在代码层面预设给 AI 的指令):
"如果你发现用户提到的某个实体(Entity)或事件(Event) 不在你当前的对话历史中, 请不要尝试猜测,请立即调用 search_history 工具。"
步骤 3:发起 Tool Call(执行层)
当 AI 意识到"我断片了",它会输出一个特定的 Tool Call 请求:
步骤 4:记忆融合
代码把带回来的"老记忆"临时拼接到这次请求的 Context 中。对 AI 来说,这一刻它就像突然"想起来了"。
第四部分:如何决定使用哪种"召回策略"
当你决定要搜索记忆时,面对海量数据,"怎么搜" 成为了决定 Agent 智商的关键。
向量检索(Vector Search)并非银弹。有时候,基于规则的模式匹配(Pattern Matching)或关键词搜索(BM25)更为有效。
这个决策过程有三种主流解法:
4.1 解法 A:大模型自主选择(Tool Routing)
思路:给大模型提供多个不同的搜索工具,让它根据自己的推理来选择。
提供两个工具:
模型如何自主决策:
用户问题 | 调用的工具 | 原因 |
|---|
"我们之前关于 AI 影响中产阶级的讨论是怎么说的?" | search_by_semantic | 这是一个宽泛的语义概念,不是精确词语 |
"帮我找出所有用到 Kailas FUGA 这个词的日志" | search_by_exact_match | 这是一个精准的专有名词,需要精确查找 |
优点:灵活性高,能处理复杂场景缺点:依赖模型的推理能力,可能选错;每次搜索都是单独一次 Tool Call
4.2 解法 B:代码层的混合检索(Hybrid Search / RRF)—— 推荐方案
思路:对大模型屏蔽搜索细节,只提供一个统一的 search_memory(query) 工具。在代码执行层,当接收到 query 时,系统在后台同时触发多种搜索,然后用数学方法融合结果。
两路搜索并行执行:
query:"昨天的登录逻辑优化"│├──→语义向量检索(Embedding+ChromaDB/FAISS)│抓取"形散神不散"的相关讨论│└──→全文关键词检索(SQLiteFTS5/Elasticsearch)抓取"包含精确字眼"的日志结果合并│└──→倒数秩融合(ReciprocalRankFusion,RRF)数学打分重排序兼顾"语义相关性"和"关键词精准度"
为什么推荐这个方案:
4.3 解法 C:基于数据类型的静态路由
思路:在架构设计时,直接对记忆的存储进行物理隔离,代码层的路由网关根据当前 Agent Loop 的阶段,自动去对应的数据库抓取数据。
数据类型 | 存储方式 | 召回策略 |
|---|
短期工作流记忆(最近 10 次报错日志) | JSON 结构化存储 | 按时间戳过滤 + 正则匹配 |
长期知识库(个人简历、博客草稿) | 向量存储(Chunking 后) | 语义相似度检索 |
第五部分:Prompt 与代码的边界在哪里
这些决策到底是通过 Prompt(提示词)传达给模型实现的,还是通过代码层面的规则实现的?
答案是:优秀的 Agent 架构是两者的精密咬合。
Prompt 负责"意图与逻辑推演"
Code 负责"流程控制与数据加工"
5.1 职责划分一览表
功能模块 | 实现层级 | 具体方式 | 核心优势 |
|---|
基础设定注入 | Code(系统规则) | 每次请求强制拼接 user_profile.md | 保证稳定性,节省 Token,防止模型遗忘基础人设 |
意识到缺乏信息 | Prompt(系统提示) | 定义 ReAct 框架,鼓励模型在不确定时使用 <think> 标签并停止生成 | 发挥模型强大的零样本推理能力,不依赖死板的 if-else |
选择搜索工具 | 混合(Prompt + Code) | Prompt 暴露统一的 search() 工具;Code 在底层实现 Hybrid Search | 降低模型决策负担,提高检索召回率 |
执行数据库查询 | Code(执行规则) | Python 调用 Embedding API、执行 SQL 或向量相似度比对 | 速度极快,大模型无法直接操作底层数据库,必须由代码代劳 |
结果的总结与提炼 | Prompt(系统提示) | 指示模型:"提取与当前问题最相关的代码段,忽略其他噪音" | 保护主对话的上下文窗口不被检索结果的噪音污染 |
第六部分:Context Window 的"零和博弈"
有个非常敏锐的问题:
如果即时工作记忆中的上一轮信息非常长(比如你传了一段 5 万行代码),已经超出了 Context Window,那是否其他需要搜索的长期记忆信息就会被挤出去?
答案是完全正确。 Context Window 是一个"零和博弈"的有限空间。
把大模型的上下文窗口比作一个物理信封:
系统提示词
长期记忆(搜索回来的结果)
即时工作记忆(最近的聊天记录)
这三类内容就是往里塞的信纸。如果最近的一轮会话把信封塞满了,长期记忆确实会因为"没地方放"而被挤出去。
6.1 四大解决方案
方案 1:优先级分层策略(Priority Ranking)
在代码组装 Prompt 的那一刻,系统会给不同信息标上优先级:
优先级 | 内容 | 处理方式 |
|---|
最高 | 当前问题 + System Prompt | 必须装进去 |
次高 | 检索到的长期记忆 | AI 认为回答当前问题的"关键钥匙" |
较低 | 即时工作记忆(历史聊天) | 空间不足时优先压缩 |
处理逻辑:如果信封快满了,代码会先从"最近上下文"里开始砍掉老的信息。即便上一轮对话很长,代码也会强制对其进行截断(Truncation),只保留开头结尾,或者只保留那轮对话的"摘要",从而为更重要的搜索结果腾出空间。
方案 2:动态摘要与压缩(Recursive Summarization)
这是目前处理"信息过载"最聪明的方法。
当 Agent 检测到即时上下文过长(超过窗口的 60%)时,它不会直接丢弃信息,而是先启动一个后台静默任务:
指令:"请用 500 字总结一下刚才那段超长的代码逻辑/讨论内容。"结果:长达几万Token的原始内容被压缩成了一个小数据包替换:下一次请求时,原始长篇大论被这个摘要替换掉信封里空出了大量空间装载搜索回来的长期记忆
这就是为什么有些 Agent 聊久了会变得"只会说大框架,不记得细节"——它在用牺牲细节换取空间。
方案 3:动态调整检索量(Adaptive K-Retrieval)
如果代码发现当前即时对话已经占据了 80% 的窗口,它在调用 search_memory 工具时会动态调整召回数量:
情况 | 召回数量 | 每个片段长度 |
|---|
正常情况 | Top 10 | 1000 字/片段 |
拥挤情况 | Top 3 | 缩短到 200 字/片段 |
极端情况 | 0(无法召回) | Agent 输出提示建议开启新话题 |
方案 4:超大上下文模型(Brute Force Approach)
这是为什么像 Gemini 1.5 Pro(100万-200万 Token 窗口)这样的模型在 Agent 领域备受青睐。
对于窗口只有 128k 或 200k 的模型(Claude 3.5 / GPT-4o),"记忆打架"是一个棘手的工程难题。但对于 200 万窗口的模型来说,这个"信封"大到了几乎可以装下一整个图书馆,"即时记忆"和"长期记忆"不再需要争抢空间。
6.2 一句话建议
如果你发现 Agent 开始因为内容太长而"降智"或"忘事",最有效的办法是手动干预:对它说——"总结一下我们刚才聊的内容,然后开启一个新的上下文区间"。这相当于手动帮它清理信封,腾出空间。
第七部分:构建好用的 Agent,最核心的四个系统
结合 Hermes Agent 和 OpenHarness 这两个开源项目的设计理念,以及你高频使用 Agent 的经验,一个真正好用的 Agent 需要搭好以下四个核心系统:
7.1 系统一:协议层的"网关代理"(无缝切换模型)
核心目标:实现模型的无缝切换
做法:构建一个统一的 API 网关层(Gateway),将所有底层模型的输入输出格式统一抹平为标准 OpenAI 格式。
这样,当需要切换模型时,Agent 的核心逻辑不需要做任何改动,只需要在配置台切换对应的 Endpoint URL 和 API Key。
7.2 系统二:分层与结构化的记忆系统
核心目标:实现完整记忆能力
做法:将记忆划分为三个维度:
维度 | 存储方式 | 更新频率 | 用途 |
|---|
基础设定(Static Profile) | 结构化 Markdown 文件 | 极低 | 出厂设定,每次必传 |
动态经历检索(Episodic Memory) | SQLite + FTS5 / 向量数据库 | 按需 | 会话历史向量化/摘要存储 |
工作区记忆(Working Memory) | 自动压缩(auto-compact) | 高频 | 长对话中定期提取总结 |
参考 OpenHarness 中 ohmo 的设计,用 soul.md 记录核心逻辑,user.md 记录偏好与属性。
7.3 系统三:"思维与表达分离"的 Agent Loop
核心目标:提高复杂任务的执行质量
做法:引入明确的 ReAct(推理与行动)机制,在系统层面给 Agent 留出一个专门的"内部思考域"(Scratchpad / 隐藏的思维链)。
让 Agent 在调用工具、查阅记忆、观察执行结果时,只在后台静默更新状态。只有当它在内部完成了完整的逻辑推演,才将最终结果精炼地输出到前端界面。
7.4 系统四:标准化的工具箱抽象(MCP)
核心目标:扩展 Agent 的能力边界
做法:采用 MCP(Model Context Protocol) 标准化协议,把对外部环境的操作封装成独立的 Tool。
不需要亲自写复杂的实现代码——你只需要用自然语言极其精确地定义好这些工具的"输入字段要求"和"输出数据格式",剩下的封装脚本完全可以交由大模型生成。
进阶形态:效仿 Hermes Agent,赋予 Agent 在发现重复性劳动后,自主编写并固化出新技能(Skill)的权限。
总结
AI Agent 的记忆系统,不是魔法,而是一场在有限上下文窗口的舞台上,由 Prompt 担任导演,由外围代码担任场务,共同完成的精准的数据调度舞蹈。
核心结论:
大模型没有真正的记忆——记忆全靠每次请求时塞进上下文窗口的内容
记忆分三层:基础设定(强制注入)→ 即时工作记忆(自动滑窗)→ 长期检索(按需触发)
"何时搜索"的决策:由代码规则(快)和模型推理(慢)协同完成
"怎么搜"的决策:可通过 Tool Routing(AI 自主)、Hybrid Search(代码融合)或静态路由(按类型分区)实现
Prompt 与代码精密咬合:Prompt 负责逻辑推演,Code 负责流程控制与数据加工
Context Window 是零和博弈:需要优先级管理、动态压缩、自适应召回等策略应对