WeClaw_59_AI的"失忆症"处方:跨会话经验知识图谱让AI越用越聪明
系列文章第 59 篇 - 从"每次修 Bug 都从零开始"到"越用越聪明的个人助手"
📚 专栏信息
《从零到一构建跨平台 AI 助手:WeClaw 实战指南》专栏
本文是模块十第 2 篇,探讨如何用 SQLite + 向量检索为 AI 构建"经验记忆",让每一次自我修正都能复用上一次的智慧。
👨💻 作者与项目
作者简介:翁勇刚 WENG YONGGANG
新概念龙虾-WeClaw 开发团队负责人,一群专注于跨平台 AI 应用的实践者
理念:"再复杂的技术,也能用代码讲清楚"
📝 摘要
本文结构概览: 本文从两个"表面不同、根因相同"的真实 Bug 修复场景切入,揭示 AI 助手的核心体验缺陷——跨会话失忆症。然后设计 ExperienceStore(经验知识图谱)的 SQLite + 向量双存储架构,展示"记录经验 → 语义检索 → 上下文注入"的完整数据流,探讨经验泛化这一核心难点,最后给出经验积累系统的 Do's/Don'ts 清单。
背景:WeClaw 已经具备自我反思闭环(感知 → 诊断 → 修复 → 重启),但每次对话是无状态的。第 1 周花了 30 分钟修复 stock_query 的浮点解析 Bug,第 3 周 log_viewer 出现类似的编码问题时,AI 完全不记得上次的"防御性数据解析"经验,又从零分析了一遍。
核心问题:如何让 AI 在跨会话场景下积累经验,实现"越用越聪明"而非"每次从零开始"?
解决方案:构建 ExperienceStore,在每次自我修正闭环完成后自动记录结构化经验(trigger / diagnosis / fix / outcome),下次遇到类似问题时通过向量检索召回历史经验,注入 System Prompt 供 LLM 参考。
关键成果:
- 设计了 experience 数据模型(7 字段 + 向量 embedding)
- 复用现有 embedding 模型和 SQLite 基础设施,零额外依赖
- 提出"经验泛化"问题的 LLM 辅助抽象方案
- 明确经验积累 vs 自主目标设定的投入产出比判断
适合读者:对 AI Agent 记忆系统、向量检索、知识图谱感兴趣的开发者
阅读时长:约 14 分钟
关键词:经验积累、向量检索、跨会话记忆、知识图谱、Embedding、语义搜索、System Prompt
一、AI 的"失忆症"——一个被忽视的核心体验缺陷
1.1 两个场景,同一个根因
想象这两个真实场景:
第 1 周:
用户:"stock_query 查询港股价格时报错了。"
AI 花 30 分钟排查,发现问题根因:外部 API 返回的价格字段有时是字符串
"N/A",直接float()转换导致 ValueError。修复方案:引入
float_or_none()安全转换函数,对无法解析的值返回 None 而非抛异常。
第 3 周:
用户:"log_viewer 读取日志时出现编码异常。"
AI 从零开始分析,花了 20 分钟排查。发现根因:日志文件中的某些行包含非 UTF-8 字符,直接读取导致 UnicodeDecodeError。
修复方案:
open()时添加errors="replace"参数。
发现问题了吗?
┌──────────────────────────────────────────────────────┐
│ │
│ stock_query Bug log_viewer Bug │
│ ────────────── ────────────── │
│ 外部 API 返回 "N/A" 日志文件含非 UTF-8 字符 │
│ ↓ ↓ │
│ 直接 float() 转换 直接 open() 读取 │
│ ↓ ↓ │
│ ValueError UnicodeDecodeError │
│ ↓ ↓ │
│ 修复:防御性转换 修复:防御性读取 │
│ │
│ 共同根因:外部数据未做防御性解析 │
│ │
│ 但 AI 在第 3 周完全不知道第 1 周的经验! │
└──────────────────────────────────────────────────────┘
1.2 失忆症的本质:无状态对话架构
WeClaw 当前每次对话是无状态的。虽然 chat_history 可以检索历史对话文本,但它只存储原始聊天消息,不存储结构化的"诊断经验"。
| 维度 | chat_history(已有) | ExperienceStore(需要) |
|---|---|---|
| 存储内容 | 原始对话文本 | 结构化经验记录 |
| 检索方式 | 关键词匹配 | 向量语义检索 |
| 信息密度 | 低(大量闲聊夹杂少量技术信息) | 高(每条记录都是提炼后的诊断结论) |
| 跨会话可用 | 部分(需手动翻阅) | 自动注入 |
| 适合场景 | "我们上周聊了什么?" | "上次类似的 Bug 是怎么修的?" |
chat_history 不是不能用,而是用不好。让 AI 从几百条对话记录中找到"上次类似的 Bug 修复经验",就像让你在微信聊天记录里找"上次那个修水管师傅的电话"——技术上可行,体验上痛苦。
1.3 产品差异化:"越用越聪明"
这其实是 WeClaw 与普通 AI 助手的核心差异点。用户会感受到:
"它记得上次帮我修了什么,这次直接告诉我可能是类似的问题——这个 AI 在成长。"
这种体验粘性远强于每次对话都从零开始的竞品。
二、ExperienceStore 架构设计——SQLite + 向量的双存储方案
2.1 核心数据模型
每条经验记录包含 7 个结构化字段 + 1 个向量 embedding:
┌─────────────────────────────────────────────────────┐
│ Experience 数据模型 │
│ │
│ ┌─────────────┐ ┌─────────────────────────────┐ │
│ │ 结构化字段 │ │ trigger: "tool_audit错误率上升" │ │
│ │ (SQLite) │ │ diagnosis: "浮点解析bug" │ │
│ │ │ │ fix: "添加float_or_none()" │ │
│ │ │ │ outcome: "success" │ │
│ │ │ │ related_files: ["stock.py"] │ │
│ │ │ │ pattern: "防御性数据解析" │ │ ← 抽象化标签
│ │ │ │ created: "2026-05-29" │ │
│ └─────────────┘ └─────────────────────────────┘ │
│ │
│ ┌─────────────┐ ┌─────────────────────────────┐ │
│ │ 向量字段 │ │ embedding: [0.12, -0.34, │ │
│ │ (向量索引) │ │ 0.56, ...] (384维) │ │
│ │ │ │ │ │
│ │ │ │ 编码内容: trigger + diagnosis │ │
│ │ │ │ + fix + pattern │ │
│ └─────────────┘ └─────────────────────────────┘ │
└─────────────────────────────────────────────────────┘
2.2 完整数据流
自我修正闭环完成
│
▼
┌─────────────────────┐
│ ExperienceStore │
│ .record() │
│ │
│ 1. 提取结构化字段 │
│ 2. LLM 抽象化 │
│ → pattern 标签 │
│ 3. Embedding 编码 │
│ 4. 写入 SQLite │
└────────┬────────────┘
│
│ 下次遇到类似问题
▼
┌─────────────────────┐
│ ExperienceStore │
│ .recall() │
│ │
│ 1. 编码当前问题 │
│ 2. 向量相似度检索 │
│ 3. 返回 Top-K 经验 │
└────────┬────────────┘
│
▼
┌─────────────────────┐
│ System Prompt │
│ 注入相关经验 │
│ │
│ "历史经验参考: │
│ 3周前修复过类似 │
│ 的防御性解析问题..." │
└─────────────────────┘
2.3 为什么选择 SQLite + 向量而非纯向量数据库?
| 方案 | 优势 | 劣势 | 适合场景 |
|---|---|---|---|
| 纯向量数据库(如 Milvus) | 高性能检索 | 额外依赖、运维成本高 | 大规模 SaaS |
| 纯 SQLite + 全文搜索 | 零依赖、简单 | 只能关键词匹配,无法语义检索 | 简单日志 |
| SQLite + 内嵌向量 | 零额外依赖、语义检索、事务安全 | 向量维度受限 | 个人桌面应用 |
WeClaw 选择第三种方案。已有的基础设施几乎可以直接复用:
resources/embedding_models/:本地 embedding 模型(已部署)- SQLite:项目核心数据库引擎(已使用)
- System Prompt 注入:已有的上下文管理机制
三、实战代码详解——ExperienceStore 的概念实现
3.1 经验记录与检索
以下是 ExperienceStore 的核心逻辑(概念版,展示设计思路):
class ExperienceStore:
"""跨会话经验知识图谱。
在每次自我修正闭环完成后自动记录经验,
下次遇到类似问题时通过向量检索召回历史经验。
"""
def record(self, trigger, diagnosis, fix, outcome, files):
"""记录一次自我修正经验。"""
# Step 1: 用 LLM 做抽象化,提取通用 pattern
pattern = self._extract_pattern(trigger, diagnosis, fix)
# 例:trigger="stock_query浮点错误", fix="float_or_none()"
# → pattern="防御性数据解析"
# Step 2: 生成语义向量
text = f"{trigger} {diagnosis} {fix} {pattern}"
embedding = self.embedder.encode(text)
# Step 3: 写入 SQLite
self.db.execute("""
INSERT INTO experiences
(trigger, diagnosis, fix, outcome, pattern,
related_files, embedding, created)
VALUES (?, ?, ?, ?, ?, ?, ?, ?)
""", (trigger, diagnosis, fix, outcome, pattern,
json.dumps(files), embedding, datetime.now()))
def recall(self, current_problem: str, top_k: int = 5):
"""检索与当前问题最相似的历史经验。"""
query_emb = self.embedder.encode(current_problem)
# 向量余弦相似度检索
return self.db.vector_search(
table="experiences",
query_vector=query_emb,
top_k=top_k
)
3.2 经验抽象化:从具体 Fix 到通用 Pattern
_extract_pattern() 是整个系统最关键的一步。如果只记录"stock_query 的 float() 改成了 float_or_none()",那这条经验只对 stock_query 有用。我们需要 LLM 做抽象:
def _extract_pattern(self, trigger, diagnosis, fix) -> str:
"""用 LLM 从具体修复中提炼通用模式。"""
prompt = f"""
以下是 AI 系统的一次自我修复记录:
- 触发:{trigger}
- 诊断:{diagnosis}
- 修复:{fix}
请用 3-5 个字概括这类问题的通用模式(如"防御性数据解析"、
"并发竞态保护"、"编码兼容处理"),只输出模式名称。
"""
return self.llm.generate(prompt, max_tokens=20)
这个抽象化步骤的价值:
具体记录(低泛化):
"stock_query 的 float() 改为 float_or_none()"
→ 只对 stock_query 有用
抽象记录(高泛化):
pattern = "防御性数据解析"
→ 对 log_viewer 编码问题、API 响应解析问题都有参考价值
3.3 与 System Prompt 的集成
在每次新对话开始或自我反思触发时,自动将相关经验注入上下文:
async def inject_experience_context(self, current_problem: str):
"""将相关历史经验注入 LLM 上下文。"""
experiences = self.recall(current_problem, top_k=3)
if not experiences:
return ""
context_lines = ["\n## 历史经验参考(自动检索)"]
for i, exp in enumerate(experiences, 1):
context_lines.append(
f"\n{i}. [{exp.created[:10]}] {exp.pattern}\n"
f" 触发:{exp.trigger}\n"
f" 诊断:{exp.diagnosis}\n"
f" 修复:{exp.fix}\n"
f" 结果:{exp.outcome}"
)
return "\n".join(context_lines)
AI 看到的上下文注入效果:
## 历史经验参考(自动检索)
1. [2026-05-01] 防御性数据解析
触发:stock_query 查询港股价格报错
诊断:外部 API 返回 "N/A",直接 float() 转换失败
修复:引入 float_or_none() 安全转换函数
结果:success
2. [2026-05-15] 编码兼容处理
触发:文件读取出现 UnicodeDecodeError
诊断:日志文件含非 UTF-8 字符
修复:open() 添加 errors="replace"
结果:success
有了这段上下文,当 log_viewer 再次出现类似问题时,AI 会直接联想到"防御性数据解析"这个通用模式,而不是从零开始分析。
四、问题诊断与修复——经验系统的三个核心挑战
4.1 挑战一:经验噪声——"假经验"干扰决策
问题:并非所有修复都值得记录。如果 AI 修了一个 typo(变量名拼写错误),这条"经验"几乎没有复用价值,反而会在后续检索中成为噪声。
解决思路:引入经验质量评分机制。
# 记录时由 LLM 评估经验的复用价值
quality_prompt = """
评估以下修复经验的复用价值(1-5分):
- 1分:一次性问题(typo、配置错误)
- 3分:同类模块可能遇到(特定数据格式问题)
- 5分:通用模式(防御性解析、并发保护、编码兼容)
"""
# 只保存 quality >= 3 的经验
4.2 挑战二:经验膨胀——数据库越来越大,检索越来越慢
问题:随着使用时间增长,经验记录越来越多,向量检索的性能会下降,注入 System Prompt 的内容也会膨胀。
解决思路:
┌──────────────────────────────────────────────┐
│ 经验生命周期管理 │
│ │
│ 新增经验 → 活跃期(30天)→ 沉淀期 → 归档/淘汰 │
│ │
│ 活跃期:全量参与检索 │
│ 沉淀期:仅 pattern 参与检索(摘要级别) │
│ 归档:outcome=failed 的经验 90 天后自动归档 │
│ 淘汰:相似度 > 0.95 的经验合并去重 │
└──────────────────────────────────────────────┘
关键设计:相似经验合并。如果 3 条经验的 pattern 都是"防御性数据解析"且相似度 > 0.95,合并为一条"超级经验",记录多次修复的共同模式。
4.3 挑战三:经验误导——历史经验可能是错误的
问题:如果一次修复的 outcome 后来被证明是失败的(重启后问题依然存在),但经验已经被记录,后续可能会被错误地召回参考。
解决思路:
# 闭环验证:重启后检查修复是否真正生效
async def verify_experience(self, experience_id: str):
"""重启后验证修复是否生效。"""
exp = self.get(experience_id)
# 检查相关错误是否不再出现
recent_errors = await tool_audit.get_recent_errors(
keyword=exp.trigger, hours=1
)
if recent_errors:
# 修复失败,标记经验为无效
self.update(experience_id, outcome="failed")
else:
# 修复成功,确认经验有效
self.update(experience_id, outcome="verified")
教训:经验不是"记录了就完事",必须有闭环验证机制。一条被标记为 failed 的经验不会参与后续检索。
五、最佳实践——经验积累系统的设计原则
5.1 Do's / Don'ts
Do's(推荐做法):
- ✅ 修复成功后才记录:只在自我修正闭环完成且验证成功后才调用
record() - ✅ 抽象化 pattern 标签:不只记具体 fix,更记通用 pattern
- ✅ 限制注入数量:System Prompt 中最多注入 3-5 条经验,避免上下文膨胀
- ✅ 经验去重合并:相似度 > 0.95 的经验自动合并,防止数据库膨胀
- ✅ 闭环验证 outcome:重启后检查修复是否真正生效,失败经验标记为
failed - ✅ 复用现有基础设施:embedding 模型、SQLite 引擎、System Prompt 机制都是现成的
Don'ts(避免做法):
- ❌ 不要记录 typo 类一次性修复(quality < 3)
- ❌ 不要把完整代码 diff 存入经验——只存诊断思路和修复方案
- ❌ 不要让经验注入占用过多 System Prompt 预算(建议 < 500 tokens)
- ❌ 不要跳过闭环验证——未验证的经验可能是"毒药"
- ❌ 不要在经验中存储用户敏感信息(遵循与 tool_audit 相同的脱敏原则)
5.2 投入产出比分析
在自我反思能力体系的后三项中,经验积累的投入产出比是最优的:
| 能力 | 实现周期 | 核心价值 | 适用场景 | 推荐优先级 |
|---|---|---|---|---|
| 跨会话经验积累 | 1-2 周 | 解决失忆症核心痛点 | 所有对话场景 | P3(推荐) |
| 自主设定改进目标 | 2-4 周 | 信号量不足(单用户) | 高并发 SaaS | 暂缓 |
为什么经验积累是刚需,自主目标是奢侈品?
WeClaw 是单用户桌面应用,日均交互量在几十到几百次级别,错误信号量不足以支撑"空闲自省"的统计显著性。每小时跑一次 LLM 分析,可能得到的结论是"今天一切正常"——白白消耗 API 费用。
而经验积累是被动触发的——只在自我修正闭环完成时才记录,零额外 API 消耗,零噪声产生。
5.3 黄金法则
让 AI "记住过去"比让 AI "主动思考未来"更实际。 对个人助手而言,经验积累是刚需,自主目标是奢侈品。先把"记忆力"做好,再考虑"想象力"。
六、总结与展望
6.1 核心要点回顾
本文从"失忆症"切入,设计了跨会话经验知识图谱:
3 个关键设计:
- SQLite + 向量双存储:结构化字段用 SQLite 管理,语义检索用向量编码,零额外依赖
- LLM 辅助抽象化:不只记具体 fix,更记通用 pattern(如"防御性数据解析")
- 闭环验证机制:修复成功才记录,失败经验自动标记,未验证经验不参与检索
1 个核心公式:
经验积累 = 结构化记录 + 语义检索 + 上下文注入 + 闭环验证
6.2 下一步学习方向
前置知识:
- ✅ 上一篇:《当 AI 学会"照镜子":自我反思闭环架构》
- ✅ Embedding 模型原理与余弦相似度计算
- ✅ System Prompt 上下文管理机制
后续主题:
- 📖 下一篇:《能力越强,护栏越硬:AI 自主进化的安全边界工程与核能类比》
6.3 互动环节
思考题:
- 如果两条经验的 pattern 相同但 fix 不同(一个成功一个失败),系统应该如何处理?
- 经验注入 System Prompt 时,应该按时间排序还是按相似度排序?各有什么利弊?
讨论话题:
你认为 AI 助手应该"记住"多少历史经验?如果记忆太多导致信息过载,该如何取舍?欢迎在评论区讨论。
下期预告:《能力越强,护栏越硬:AI 自主进化的安全边界工程与核能类比》
- AI "突破沙箱"需要的六项能力,WeClaw 具备了几项?
- 为什么说"权限提升"和"隐蔽行动"是真正的安全红线?
- 能力-安全共演化:每扩展一项能力,需要多少安全护栏?
敬请期待!
附录 A:技术栈对比
| 组件 | 已有基础设施 | ExperienceStore 复用方式 |
|---|---|---|
| 数据库引擎 | SQLite(tool_audit.db) | 新建 experience.db,同模式 |
| 向量编码 | resources/embedding_models/ | 直接调用本地 embedding 模型 |
| 上下文注入 | System Prompt 构建机制 | 在 prompt 组装阶段注入经验 |
| 触发时机 | 自我反思闭环 | 闭环最后一步自动 record() |
| 检索时机 | 自我反思第一步 | 自动 recall() 并注入 |
附录 B:参考资料
- Sentence-BERT: Sentence Embeddings using Siamese BERT-Networks
- SQLite Vector Search Extensions: sqlite-vec
- 上一篇:《当 AI 学会"照镜子":自我反思闭环架构从哲学之问到工程实践》
- 下一篇:《能力越强,护栏越硬:AI 自主进化的安全边界工程与核能类比》
版权声明:本文为 CSDN 博主「yweng18」的原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接及本声明。