1. 项目概述:构建具备长短期记忆的个人助理
在人工智能助手领域,我们正面临一个关键瓶颈:如何让AI真正记住用户?传统基于向量数据库的RAG方案就像一本按页码排列的笔记本,当你询问"上周三提到的餐厅推荐"时,它只能盲目搜索所有页面。而人类记忆的精妙之处在于其网状结构和时间维度——我们记得"那家餐厅是同事Alice推荐的,当时是为了庆祝项目结项"。
Graphiti框架的出现改变了这一局面。作为一个基于动态知识图谱的解决方案,它让AI记忆具备了三个关键特性:
- 时空关联性:信息以"节点-关系"形式存储,保留上下文脉络
- 时间感知力:自动记录信息的时间戳和时效性
- 动态演化性:随着交互持续优化知识结构
提示:选择Neo4j作为后端不仅因为其图数据库特性,更因其原生支持时态查询(如查询"2023年1月时的居住地")
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 系统架构设计解析
2.1 记忆分层模型
我们的系统采用分层记忆架构:
mermaid复制graph TD
A[短期记忆] -->|最近3轮对话| B(工作记忆区)
B -->|重要信息| C[长期记忆]
C -->|图谱检索| D{决策引擎}
D --> E[响应生成]
短期记忆层:
- 使用Redis缓存最近5分钟对话
- 采用滑动窗口机制维护上下文
- 关键配置参数:
python复制SHORT_TERM_TTL = 300 # 5分钟过期 MAX_CONTEXT_LENGTH = 2000 # 字符数限制
长期记忆层:
- Neo4j存储核心知识图谱
- 节点设计包含元数据:
cypher复制CREATE (n:Memory { content: "喜欢拿铁咖啡", source: "2023-05-12对话", confidence: 0.85, expires_at: null // 永久记忆 })
2.2 信息处理流水线
-
提取阶段:
- 使用spaCy进行基础实体识别
- 通过LLM深化关系抽取
- 示例prompt:
code复制从以下对话提取信息: 用户说:"我每周三晚上7点要去上瑜伽课" 输出格式: { "entities": [{"text":"瑜伽课","type":"activity"}], "relations": [{"source":"用户","relation":"定期参加","target":"瑜伽课","frequency":"每周三19:00"}] }
-
存储阶段:
- 实现自动去重机制
- 冲突解决策略:
python复制def merge_policy(existing, new): if new['timestamp'] > existing['timestamp'] + timedelta(days=30): return new # 新信息足够新时覆盖 else: return existing # 否则保留旧信息
3. 核心实现细节
3.1 增强型记忆注入
基础实现仅存储原始文本,我们改进后的版本包含:
python复制async def enhanced_add_memory(user_id: str, text: str, context: dict):
# 情感分析
sentiment = await analyze_sentiment(text)
# 生成记忆指纹防止重复存储
fingerprint = hashlib.md5(f"{user_id}-{clean_text}".encode()).hexdigest()
# 存储带元数据的记忆节点
await graphiti_client.execute_query(
"""
MERGE (m:Memory {fingerprint: $fp})
ON CREATE SET
m.content = $content,
m.created_at = datetime(),
m.sentiment = $sentiment,
m.source = 'user_chat'
""",
params={
"fp": fingerprint,
"content": clean_text,
"sentiment": sentiment
}
)
# 关联用户节点
await link_to_user(user_id, fingerprint)
3.2 混合检索策略
传统向量搜索与图查询的结合:
python复制async def hybrid_search(query: str, user_id: str):
# 向量相似度搜索
vector_results = await vector_db.search(
embedding=await get_embedding(query),
top_k=3
)
# 图谱路径查询
graph_results = await graphiti_client.execute_query(
"""
MATCH path=(u:User {id: $uid})-[:HAS_MEMORY*..3]->(m)
WHERE m.content CONTAINS $query
RETURN path
ORDER BY length(path) ASC
LIMIT 5
""",
params={"uid": user_id, "query": query}
)
# 结果融合算法
return rank_fusion(vector_results, graph_results)
注意:结果融合采用加权评分策略,近期记忆权重提高30%
4. 高级定制技巧
4.1 记忆衰减机制
在graphiti-core/engine/decay.py中实现:
python复制def apply_decay():
"""每天执行一次的记忆衰减"""
decay_rate = 0.95 # 每月衰减5%
min_confidence = 0.3
await execute_cypher(
"""
MATCH (m:Memory)
WHERE m.confidence > $min_conf
SET m.confidence = m.confidence * $rate
""",
params={"rate": decay_rate, "min_conf": min_confidence}
)
# 清理低置信度记忆
await cleanup_memories(min_confidence)
4.2 上下文感知提取
修改graphiti/llm/extract.py中的prompt模板:
jinja2复制{% raw %}你正在分析{{user_name}}的对话记录。特别注意:
- 时间敏感信息(如"下个月休假")
- 个人偏好(如"讨厌香菜")
- 社交关系(如"我的主管是Lisa")
提取要求:
1. 为时间信息添加[时间戳]标记
2. 为偏好添加[偏好强度1-5]标记
3. 验证关系真实性(区分"我认识马云"和"我叔叔认识马云"){% endraw %}
5. 实战问题排查
5.1 常见问题速查表
| 现象 | 可能原因 | 解决方案 |
|---|---|---|
| 记忆重复存储 | 指纹生成未归一化 | 实现文本规范化预处理 |
| 检索结果不相关 | 向量与图谱结果权重失衡 | 调整fusion_alpha参数 |
| Neo4j响应慢 | 未建立合适索引 | 对常用查询字段创建索引 |
| LLM提取错误 | prompt缺乏示例 | 添加few-shot示例到prompt |
5.2 性能优化记录
在压力测试中发现:
- 当图谱节点超过10万时,基础查询延迟增加300%
- 优化方案:
- 实现子图隔离:按时间分片存储
- 添加缓存层:对高频查询缓存24小时
- 查询优化:
cypher复制// 优化前 MATCH (u:User)-[*]->(m) // 优化后 MATCH (u:User)-[:HAS_MEMORY|RELATED_TO*1..3]->(m) USING INDEX u:User(id)
6. 演进方向探讨
当前系统仍可改进:
- 多模态记忆:支持存储对话中的图片特征
- 记忆可信度:实现用户反馈修正机制
- 隐私保护:开发记忆加密存储方案
一个值得尝试的扩展是在记忆节点中加入触发条件:
cypher复制CREATE (m:Reminder {
content: "询问项目进展",
trigger: "当用户提到'客户会议'时",
priority: 2
})
这种设计能让助理不仅记住信息,还能在合适时机主动提醒。我在实际开发中发现,加入时间触发器的记忆节点使用率比普通记忆高40%,这提示我们记忆的"活性"可能比"容量"更重要。
