前面介绍的是 Operator X02 的记忆设计理念。这一节进一步从源码角度说明整个召回流程。
X02 的 AI 记忆相关代码主要位于:
src/ide/aiAssistant/
X02 并不是把完整历史记录直接发给模型,而是将「聊天显示」和「AI 记忆召回」分成两个不同的系统。
STEP 01完整聊天与长期记忆分开保存
X02 使用两个独立的本地存储。
完整对话
聊天侧边栏、历史会话显示、重新打开旧聊天。
= 完整会议录像记忆索引
不是完整聊天副本,而是面向搜索的记忆索引。
- userMessage
- assistantResponse
- keywords
- topics
- timestamp
- referenceCount
- summary
- decayScore
用户需要查看完整对话时,X02 打开「录像」;AI 需要回忆旧知识时,X02 搜索「索引卡」。这种分离可以避免每次请求都重新加载全部历史内容。
STEP 02先判断这次是否真的需要回忆
引擎挂在全局 window.aiHistorySearch,apiProviderManager.ts 调用它的 intelligentHistorySearch(message)。整个过程分两个阶段 —— 这也是省 token 的关键。
记忆搜索并不会在每一条消息中执行。X02 首先通过 aiHistorySearch.ts 中的 shouldSearchHistory(message) 判断当前用户输入是否依赖过去的信息。
例如「继续之前那个功能。」「和上一次的方法比较。」「昨天那个 CAN Bug 怎么解决的?」这些语句包含:
- 显式历史引用
- 模糊代词
- 追问表达
- 比较表达
- 项目引用
因此会触发长期记忆搜索。但如果用户只是问「什么是 Python?」或「生成一个 Hello World。」,系统通常不会搜索历史。
图 · 触发不中 → 直接跳过(零开销);命中才本地搜索并注入
这是 X02 节省 Context 的一个重要设计 —— 不是每一轮都搜索全部记忆,而是只有真正需要时才回忆。如果触发条件没有命中,系统会返回:
这意味着本轮不会加入额外的历史记忆,也不会浪费相关 Token。
阶段一:触发门shouldSearchHistory(message)
- 先排除机器生成内容(
[X02 TOOL RESULT]、[PROJECT CONTEXT]、banner),否则会误触发、让 reference count 失控。 - 依次匹配 5 类正则:
explicitReferences·ambiguousPronouns·followUpPatterns·comparisonPatterns·projectReferences(仅当消息 < 150 字符)。 - 命中任一 → 继续搜索;都不中 → 不搜、不注入(零 token)。
阶段二:混合搜索hybridSearch(query)
- 对每条历史算加权分数,阈值
≥ 0.25,取 top 5(见下一节)。
STEP 03通过混合评分寻找最相关的记忆
确认需要回忆后,X02 会调用 hybridSearch(query) 对历史记忆进行评分。当前快速模式主要考虑三个维度:
总相关度
关键词 · 主题 · 时效的加权组合
例如用户问:「继续之前的 CAN Timeout 修复。」
╎ 过滤线 0.25 —— 低于此值的结果直接丢弃
过滤之后按得分从高到低排列,最终最多保留 Top 5。也就是说,即使本地存储了 150 条历史记录,本轮也不会全部放入 Prompt —— 它只选择最相关的几条。
STEP 04旧记忆会自然衰减
像人一样会遗忘:相关度随时间指数衰减,但被引用越多衰减越慢。calculateDecayScore(entry) 会根据记忆年龄和引用次数调整权重,当前设计采用大约 14 天的衰减周期。
图 · e^(-age/14) 指数衰减;referenceBoost 让常用记忆「不会忘」
markAsReferenced(id):每次某条被召回就 referenceCount++ → 常用的几乎不衰减,没人碰的沉底、到 150 上限被裁掉。
Reinforced
项目一直使用 AUTOSAR经常被搜索和引用,referenceCount 增加并得到额外加成,长期保持较高权重。
Faded
测试一个已经删除的 UI 按钮很久以前、再也没有使用过,逐渐沉到记忆列表底部,并可能在达到 150 条存储上限时被裁剪。
这使 X02 的长期记忆更像人类:经常使用的知识越来越牢固,不再使用的信息逐渐淡化。
STEP 05把记忆重新注入当前 Prompt
完成搜索之后,apiProviderManager.ts 开始构建最终请求。最近对话使用 historyMsgs.slice(-10),每条消息再限制到大约 600 字符 —— 这部分相当于 X02 的 Working Memory。
然后系统调用 window.aiHistorySearch.intelligentHistorySearch(message)。如果 shouldSearch = true 并且找到相关记忆,X02 就把这些结果组合成一条新的 system message。单条召回结果还会进一步截断:用户消息最多约 200 字符,助手回答最多约 400 字符。
这意味着 X02 不会让长期记忆直接取代当前对话。长期记忆只是作为辅助背景加入,让模型知道:之前发生了什么、过去作出了什么决定、当前任务和哪个旧项目有关。
EXAMPLE一个完整例子
假设昨天用户和 X02 讨论:CAN Driver 在高负载下出现 Timeout,最终决定增加重试机制,并将等待时间调整到 100 ms。今天用户只输入「继续昨天那个问题。」X02 的内部流程大致如下:
- 收到用户消息「继续昨天那个问题」
- shouldSearchHistory()识别到「继续」和「昨天」 → 需要搜索历史
- hybridSearch()搜索关键词、主题和近期记录
- 找到高相关记忆「CAN Driver Timeout」「增加重试机制」「等待时间调整到 100 ms」
- 取 Top 结果并截断只保留最相关的几条
- apiProviderManager.ts 组装 Prompt系统提示 + 最近聊天 + 相关记忆 + 当前请求
- 发送给模型Final Prompt
- 模型继续昨天的工程工作用户不需要重新解释整个问题,模型也不需要重新阅读全部历史聊天。
WHY为什么这比单纯 Token Window 更像工程师?
Token Window 主要解决「模型最多可以接收多少内容」。而 X02 的记忆系统解决的是:当前应该记住什么、什么时候需要回忆、哪些旧知识更重要、哪些内容可以逐渐忘记。因此 X02 的架构可以分为两个层次:
认知层
- Working Memory
- Long-term Memory
- Recall Engine
决定「该想什么」
资源层
- Token Budget Manager
决定「最多能放多少」
这两者结合后,才会形成一个完整的 AI 工程记忆系统。
TUNING想改哪里就改哪个
这套记忆系统的每一个行为都对应一处具体的符号 —— fork 之后想调整,直接改这些:
STATUS当前源码设计的技术特点
- 完整聊天与搜索索引分离
- 本地存储,不依赖云端向量数据库
- 先判断是否需要历史
- 只注入 Top-5 相关结果
- 低相关结果自动过滤
- 旧记忆具有时间衰减
- 常用记忆具有引用加成
- 最近对话采用固定 Working Memory
- 长期记忆优先使用摘要和截断内容
未来可以升级的方向
- 当前主要使用关键词和主题搜索
- Embedding 搜索代码存在,但默认关闭
- 最近窗口仍以消息数量和字符长度管理
- Context Analytics 目前是估算值,不是真实 Token
- Prompt 装配逻辑在多个请求路径中存在重复
目前的 X02 设计已经建立了一个清晰的工程记忆基础。下一步不是推翻当前架构,而是在其上加入:真实 Token Budget、本地语义重排、记忆类型分类、冲突检测、Memory Inspector。
这样 X02 就能同时拥有:类人的记忆管理方式,以及机器级的精确资源控制。
Operator X02 · Dev Log 系列