统一记忆与 LLM 分析
学习目标
理解 CrewAI 的统一记忆系统如何通过 EncodingFlow 和 RecallFlow 两条管线实现 LLM 驱动的记忆管理,以及存储后端的选择策略。
前置知识
下文假设你已理解上述概念,直接聚焦 CrewAI 的具体实现。
项目实践
层次化 Scope
CrewAI 的记忆使用路径式的层次化 Scope:
# 伪代码scope = MemoryScope("/company/team/user")slice = MemorySlice([scope1, scope2]) # 组合多个 scopeMemoryScope 支持 .bind() 方法用于检查点恢复。
复合评分
每条记忆通过三维加权评分决定重要性:
score = semantic_weight × 语义相关性(0.5) + recency_weight × 时间衰减因子(0.3, 指数衰减) + importance_weight × LLM 评估的重要性(0.2)EncodingFlow:5 步批量编码
batch_embed → intra_batch_dedup → parallel_find_similar → parallel_analyze → execute_plansA/B/C/D 分组优化:将记忆项按相似度分为四组,相似的项批量处理,减少 LLM 调用次数。去重在批量内部进行,而非逐条比较。
RecallFlow:查询蒸馏 + 并行多路搜索
- LLM 将用户查询蒸馏为更适合向量搜索的形式
- 生成多个变体查询,并行搜索多个 scope
- 基于置信度的路由决策(
decide_depth):synthesize:结果足够,直接综合返回explore_deeper:结果不足,迭代使用不同搜索策略
- 迭代探索预算:限制最大探索次数
存储后端
| 后端 | 特点 | 适用场景 |
|---|---|---|
| LanceDB | 基于文件的向量存储,跨进程锁,乐观并发重试(5 次) | 本地部署,小规模 |
| Qdrant Edge | 写本地/同步中心模式,每进程独立分片 | 多进程,边缘部署 |
后台写入队列
Memory 类使用后台线程池处理写入(_submit_save、drain_writes),避免阻塞主流程。
问题与规避
进程退出时未 flush 的记忆丢失
- 后台写入队列中的记忆在进程退出时可能未写入存储
- 对策:在进程退出前调用
drain_writes()清空队列
复合评分权重调优困难
- 默认的 0.5/0.3/0.2 权重不一定适合所有场景
- 对策:根据实际召回质量调整权重——如果时间因素更重要,提高
recency_weight
查询蒸馏可能丢失原始查询的细微差别
- LLM 蒸馏过程可能简化原始查询,丢失关键细节
- 对策:在 RecallFlow 中同时使用原始查询和蒸馏查询进行多路搜索
设计取舍
EncodingFlow 批量处理 vs 逐条编码
- 批量处理的优势是减少 LLM 调用次数(A/B/C/D 分组),降低延迟
- 代价是需要积累一定量的记忆后才触发,不适合需要即时编码的场景
LanceDB vs Qdrant
- LanceDB 更轻量(零依赖,基于文件),适合本地和小规模部署
- Qdrant 提供更好的并发支持和多进程隔离,适合生产环境
参考来源
- 源码验证:
lib/crewai/src/crewai/memory/encoding_flow.py - 源码验证:
lib/crewai/src/crewai/memory/recall_flow.py - 源码验证:
lib/crewai/src/crewai/memory/storage/lancedb_storage.py