尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

AI智能体记忆系统:Cognee开源平台架构与LangChain集成实战

AI智能体记忆系统:Cognee开源平台架构与LangChain集成实战 1. 项目概述当AI智能体开始拥有“记忆”最近在AI智能体Agent的圈子里一个词被频繁提起记忆。无论是构建一个能持续对话的客服助手还是一个能根据历史交互调整策略的自动化流程智能体如何记住过去发生的事情并利用这些“记忆”来指导未来的行为成了决定其智能程度的关键。这就像我们人类如果没有记忆每一次对话都是全新的开始每一次任务都要从头学起那效率将低得无法想象。正是在这个背景下我注意到了Cognee这个开源项目。它不只是一个工具库更是一个专门为AI智能体设计的“记忆平台”。简单来说它试图解决的核心问题是如何让AI智能体像人一样拥有一个结构化、可查询、可推理的长期记忆系统。Cognee的目标用户非常明确所有正在或计划开发复杂AI智能体的工程师和研究者。如果你正在用LangChain、AutoGPT、Dify或者任何自定义框架搭建智能体并且头疼于如何让它们记住上下文、学习用户偏好、或者基于历史数据做出更优决策那么Cognee就是你值得深入研究的组件。它不是一个完整的智能体框架而是一个专注于“记忆”这一子系统的平台你可以把它无缝集成到你现有的架构中。我花了一些时间深入研究它的设计理念、源码和实际应用发现它确实提出了一些颇具启发性的思路尤其是在记忆的表示、存储和检索这几个核心环节上。接下来我就结合自己的理解和实践拆解一下Cognee到底做了什么以及我们该如何用它来为自己的智能体赋能。2. 核心设计理念记忆不是简单的日志在深入代码之前理解Cognee的设计哲学至关重要。很多初涉智能体开发的团队容易把“记忆”简单等同于“聊天历史记录”或者“日志文件”。这种思路下的实现往往就是一个向量数据库把所有对话的文本切片存进去需要的时候做一下语义搜索。这种做法在简单场景下可行但面对复杂的、多回合的、目标导向的智能体任务时就显得力不从心了。Cognee的出发点更高一层。它将智能体的记忆视为一个知识图谱Knowledge Graph与事件流Event Stream的结合体。这意味着记忆不仅仅是文本片段而是结构化的实体Entities、关系Relationships以及随时间发生的事件Events。2.1 记忆的层次化结构Cognee将记忆大致分为几个层次这种分层设计是其强大检索能力的基础情景记忆Episodic Memory这是最直接的一层记录了智能体与用户或环境交互的“事件”。例如“用户A在2023-10-27 14:30询问了产品X的价格”、“智能体执行了数据查询任务Y并返回了结果Z”。这些记忆带有明确的时间戳和上下文像日记一样。语义记忆Semantic Memory这是从情景记忆中抽象出来的知识。例如从多次“询问产品价格”的事件中可以抽象出“用户A对价格敏感”或“产品X属于高端品类”这样的语义事实。这层记忆更稳定是智能体对世界认知的体现。程序性记忆Procedural Memory这关乎“如何做”。智能体通过多次成功或失败的任务执行会形成一些经验性的规则或策略。例如“当用户问题涉及复杂计算时优先调用工具Calculator而非直接生成答案”。这部分记忆直接指导智能体的行为决策。Cognee通过内部的数据模型试图将这些不同层次的记忆关联起来。一个“用户询问”事件情景可以链接到“用户画像”实体语义而该画像又可以关联到一系列“推荐策略”程序性。2.2 基于图的记忆表示与检索这是Cognee最核心的技术亮点。它使用图数据库如Neo4j、Memgraph或兼容的存储后端作为记忆的存储引擎。为什么是图关系查询能力传统向量检索擅长找“相似的文本”但图检索擅长回答“谁在什么时候做了什么导致了什么结果”这类关系型问题。例如你可以轻松查询“在过去一周所有对‘预算’话题表现出兴趣的用户中谁曾收到过我们的促销邮件但未下单”这种查询在向量库中几乎无法直接实现。动态关联新的记忆节点或边可以自然地链接到已有的记忆网络上形成一个不断生长和演化的知识体系这非常符合记忆的本质。推理潜力基于图结构可以运行图算法来进行社区发现、影响力分析或路径推理从而让智能体发现记忆之间隐藏的模式。在实际操作中Cognee会利用大语言模型LLM的抽取能力将一段自然语言文本如对话记录自动解析成结构化的图数据。例如从句子“张三昨天在官网用信用卡购买了旗舰版软件”中可以提取出实体Person:张三、Product:旗舰版软件、Action:购买、PaymentMethod:信用卡以及它们之间的关系(张三)-[PERFORMED]-(购买)、(购买)-[TARGET]-(旗舰版软件)、(购买)-[USED]-(信用卡)并为整个事件节点打上时间戳昨天。注意这个信息抽取的准确度高度依赖于你使用的LLM的能力和提示词Prompt工程。Cognee提供了默认的抽取逻辑但在生产环境中你可能需要根据你的领域数据对其进行微调或替换更强大的模型。3. 系统架构与核心模块拆解理解了理念我们来看Cognee是如何落地的。它的代码结构清晰主要围绕以下几个核心模块构建我们可以将其想象成一个记忆处理流水线。3.1 记忆摄取层Ingestion Layer这是记忆的入口。任何需要被记住的信息无论是来自用户消息、工具执行结果、系统日志还是外部API回调都需要通过这一层进行处理。标准化接口Cognee定义了统一的记忆摄入接口。你不需要关心数据最初是什么格式最终都需要转换成平台内部的“记忆事件”数据结构。这通常包含事件内容、元数据如来源、时间、会话ID、关联的实体列表等。异步处理摄入通常是异步的避免阻塞智能体的主响应流程。智能体在给出响应后可以将需要记忆的上下文抛给Cognee的后台任务去处理。去重与融合简单的摄入可能会产生大量冗余记忆。Cognee的摄取层应具备初步的去重和融合能力。例如连续两次“用户说你好”可能被融合为一个“用户进行了问候”的语义记忆并更新其发生频率而不是存储两个独立的事件。在集成时你需要在你智能体的关键“钩子”Hooks处调用Cognee的摄入API。例如在LangChain的AgentExecutor中你可以在每次Agent行动Action和观察Observation之后将相关记录发送给Cognee。3.2 记忆处理与编码层Processing Encoding Layer这是记忆“从生到熟”的关键环节。原始事件在这里被深度加工。信息抽取与图构建如前所述利用LLM将非结构化文本转换为结构化的图元素节点和边。Cognee可能会使用多个LLM调用先进行实体识别再进行关系抽取。向量化嵌入尽管核心是图但文本的语义相似性检索依然重要。Cognee会为记忆事件的文本内容、以及抽取出的实体名称和属性生成向量嵌入例如使用OpenAI的text-embedding-3-small或开源的BGE模型。这些向量将被存储并与图中的节点关联实现“图检索”与“向量检索”的混合查询。记忆分类与分级不是所有记忆都同等重要。处理层会根据规则或学习到的策略为记忆打上重要性标签或确定其存储的“温度”访问频率。高频、重要的记忆可能被缓存在更快的存储中。# 伪代码示例展示Cognee处理层可能的工作流程 async def process_memory_event(raw_event): # 1. 文本嵌入 embedding await embed_text(raw_event.content) # 2. LLM信息抽取生成图数据 graph_data await llm_extract_entities_and_relations(raw_event.content) # 3. 与现有图融合 memory_graph.merge(graph_data) # 4. 关联向量与图节点 memory_index.link_embedding_to_node(embedding, graph_data.main_node_id) # 5. 评估并设置记忆属性 importance assess_importance(raw_event) memory_graph.set_node_property(graph_data.main_node_id, importance, importance)3.3 记忆存储层Storage LayerCognee采用了混合存储策略这也是其设计精妙之处。图数据库存储核心的记忆结构关系。这是“主数据库”负责记忆的逻辑关联和复杂查询。向量数据库存储文本嵌入向量。用于支持快速的语义相似性搜索。通常可以选择Chroma、Weaviate、Qdrant等。文档存储/对象存储对于一些原始的、非结构化的附件或详细日志可能存储在S3、MinIO或简单的文件系统中并在图数据库中保存其引用指针。缓存如Redis用于存储热门的、需要快速访问的记忆片段或用户会话上下文。这种混合架构保证了系统既能做复杂的关联推理又能进行快速的语义召回还能处理海量的原始数据。Cognee的价值之一就是封装了这些不同存储后端的交互细节对外提供统一的抽象API。3.4 记忆检索与推理层Retrieval Reasoning Layer这是记忆被“唤醒”和使用的环节。智能体在决策时会向Cognee发起查询。混合查询接口你可以进行纯向量搜索“找到和‘退款政策’相关的记忆”也可以进行纯图查询“找到用户A所有‘投诉’事件并按时间排序”更强大的是进行混合查询“找到和‘服务器故障’语义相关并且发生在最近24小时内的所有事件并列出受影响的用户”。Cognee的查询引擎会分解查询分别调用向量库和图数据库然后合并、排序结果。上下文组装检索到的记忆通常是分散的节点和事件。Cognee会根据查询的意图将这些碎片化的记忆组装成一段连贯的、对LLM友好的上下文文本然后注入到智能体本次决策的提示词Prompt中。这个过程可能包括总结相关事件、描述实体关系、突出关键变化等。记忆链Memory Chain对于复杂的任务Cognee支持构建“记忆链”即根据当前任务目标主动、多步地检索相关记忆形成一条推理链条。例如要回答“为什么用户B取消了订阅”系统可能先检索“用户B的订阅记录”再检索“订阅取消前一周的用户反馈”最后检索“同期是否有服务中断报告”并将这些记忆链整合后提供给LLM分析。4. 实战集成为LangChain智能体添加Cognee记忆理论说再多不如动手试一下。假设我们有一个基于LangChain构建的客服智能体现在希望为其集成Cognee使其能记住用户偏好和历史问题。以下是关键步骤和代码思路。4.1 环境搭建与初始化首先你需要选择并启动存储后端。这里以Neo4j图数据库和Chroma向量库内存模式为例。# 使用Docker快速启动一个Neo4j实例 docker run -d \ --name cognee-neo4j \ -p 7474:7474 -p 7687:7687 \ -e NEO4J_AUTHneo4j/your_password \ neo4j:latest # 在Python项目中安装Cognee假设已发布到PyPI # pip install cognee接下来初始化Cognee客户端。你需要配置各个组件的连接信息。import os from cognee import Cognee from cognee.backend.graph import Neo4jGraphBackend from cognee.backend.vector import ChromaVectorBackend # 配置存储后端 graph_backend Neo4jGraphBackend( uribolt://localhost:7687, usernameneo4j, passwordyour_password ) vector_backend ChromaVectorBackend( persist_directory./chroma_db # 向量数据持久化路径 ) # 创建Cognee实例 cognee Cognee( graph_backendgraph_backend, vector_backendvector_backend, llm_provideropenai, # 指定用于信息抽取的LLM llm_api_keyos.getenv(OPENAI_API_KEY) ) # 初始化系统创建数据库索引等 await cognee.initialize()4.2 创建自定义记忆类并集成到LangChain AgentLangChain有BaseChatMessageHistory和BaseMemory等抽象类用于管理记忆。我们需要创建一个适配器将消息历史记录同步到Cognee。from langchain.memory import ChatMessageHistory from langchain.schema import BaseMessage, HumanMessage, AIMessage from typing import List class CogneeEnhancedMessageHistory(ChatMessageHistory): 扩展LangChain的消息历史自动同步到Cognee def __init__(self, cognee_client: Cognee, session_id: str): super().__init__() self.cognee cognee_client self.session_id session_id # 用于关联同一会话的记忆 async def add_message(self, message: BaseMessage) - None: # 1. 先调用父类方法保存在本地列表 super().add_message(message) # 2. 构建Cognee记忆事件 memory_event { content: message.content, role: human if isinstance(message, HumanMessage) else assistant, timestamp: datetime.utcnow().isoformat(), session_id: self.session_id, metadata: { message_type: message.type } } # 3. 异步提交到Cognee进行处理和存储 # 注意这里使用asyncio.create_task避免阻塞主线程 import asyncio asyncio.create_task(self.cognee.add_memory(memory_event)) async def get_relevant_memories(self, query: str, k: int 5) - List[str]: 根据当前查询从Cognee检索相关记忆 results await self.cognee.search_memories( query_textquery, filters{session_id: self.session_id}, # 限定在本会话内 limitk ) # 将检索到的记忆片段格式化成文本准备注入Prompt formatted [] for mem in results: # 简单格式化可根据需要调整 formatted.append(f[{mem[role]} at {mem[timestamp]}]: {mem[content]}) return formatted然后在构建你的Agent时使用这个自定义的历史类。from langchain.agents import AgentExecutor, create_openai_tools_agent from langchain_openai import ChatOpenAI from langchain.prompts import ChatPromptTemplate, MessagesPlaceholder # 1. 创建带有Cognee的记忆历史 session_id user_123_chat_session message_history CogneeEnhancedMessageHistory(cognee, session_id) # 2. 构建Prompt预留位置注入记忆 prompt ChatPromptTemplate.from_messages([ (system, 你是一个拥有记忆的客服助手。以下是你与当前用户的历史对话中与当前问题可能相关的片段供你参考 {relevant_memories} 请基于以上信息和你的知识回答问题。如果历史信息不足请直接根据你的知识回答。), MessagesPlaceholder(variable_namechat_history), (human, {input}), MessagesPlaceholder(variable_nameagent_scratchpad), ]) # 3. 创建LLM和工具 llm ChatOpenAI(modelgpt-4-turbo, temperature0) tools [...] # 你的工具列表 # 4. 创建Agent agent create_openai_tools_agent(llm, tools, prompt) # 5. 在创建Executor时需要动态获取相关记忆 async def run_agent_with_memory(user_input: str): # 在每次调用前先检索相关记忆 relevant_mems await message_history.get_relevant_memories(user_input) # 将记忆格式化成字符串传入Prompt的relevant_memories变量 memory_context \n.join(relevant_mems) # 运行Agent agent_executor AgentExecutor(agentagent, toolstools, verboseTrue) result await agent_executor.ainvoke({ input: user_input, chat_history: message_history.messages, # 标准的对话历史 relevant_memories: memory_context, # 注入的Cognee相关记忆 }) return result4.3 高级功能触发基于记忆的主动服务记忆不仅用于回答问题还可以用于触发工作流。例如当系统通过记忆分析发现某个用户多次抱怨同一个问题时可以自动创建一张工单或通知人工客服。这可以通过在Cognee的检索层或后处理层添加“监听器”Listener来实现。# 示例一个简单的记忆分析后处理钩子 async def memory_analysis_hook(new_memory, cognee_client): 分析新添加的记忆并触发相应动作 # 1. 检查这是否是一个“用户投诉”类记忆 # 这里可以用一个分类器LLM来判断或者基于关键词规则 if is_complaint(new_memory[content]): user_id extract_user_id(new_memory) # 从记忆或session_id中提取用户 # 2. 查询该用户近期的投诉次数 past_complaints await cognee_client.search_memories( query_text投诉 不满 问题, filters{ session_id: {$regex: f^user_{user_id}}, # 匹配该用户的所有会话 timestamp: {$gt: 30 days ago} } ) # 3. 如果短期投诉超过阈值触发动作 if len(past_complaints) 3: await create_support_ticket(user_id, new_memory[content], priorityhigh) # 甚至可以自动在记忆中记录“已创建工单”这一系统事件 await cognee_client.add_memory({ content: f系统检测到用户{user_id}多次投诉已自动创建高优先级工单#XXX。, role: system, event_type: alert_triggered })5. 性能调优与生产环境注意事项将Cognee用于生产环境除了基本功能还需要考虑性能、成本和稳定性。5.1 存储成本与优化图数据库规模控制图数据库的查询性能与图的规模节点和边的数量密切相关。需要制定记忆的留存和归档策略。定期归档将旧的、不活跃的记忆从主图数据库迁移到成本更低的文档存储如S3只在图库中保留一个索引指针。记忆摘要对于长时间、高频率的会话可以定期如每50轮对话使用LLM生成一个“会话摘要”将大量细粒度的事件记忆压缩为几条语义记忆然后归档原始事件。向量数据库索引选择Chroma的默认索引在数据量超过百万级后可能性能下降。生产环境应考虑使用支持更高效索引的向量库如Weaviate基于HNSW或PgVector与PostgreSQL集成。Cognee通常支持配置不同的后端。嵌入模型选择嵌入模型直接影响向量检索质量和速度。text-embedding-3-small在质量和成本间取得了很好平衡。对于中文场景BGE-M3是优秀的开源选择。需要在你的数据集上做召回率测试。5.2 检索延迟与缓存策略记忆检索位于智能体的关键路径上延迟必须可控。分级缓存会话级缓存当前会话的最近10-20轮对话可以直接缓存在应用内存中无需每次查询数据库。用户级缓存用户的长期偏好如“喜欢用邮件沟通”、“是VIP客户”这类低频变化的信息可以缓存在Redis中设置较长的TTL。热点记忆缓存被全系统频繁查询的公共记忆如“公司退货政策”可以缓存在Redis中。异步索引与最终一致性记忆的添加尤其是需要LLM处理和图融合的可以完全异步化。智能体写入记忆后立即返回无需等待索引完成。这遵循最终一致性模型对于绝大多数应用场景是可接受的。限制检索范围每次检索时务必使用过滤器如session_id,user_id,time_range来缩小搜索范围避免在全量数据中扫描这是提升性能最有效的手段之一。5.3 信息抽取的准确性与可靠性这是整个系统质量的瓶颈。LLM抽取的实体和关系可能有误。后处理与验证对于关键领域如产品名、订单号可以在LLM抽取后用正则表达式或业务规则进行二次验证和修正。微调抽取模型如果领域专业性很强如医疗、法律可以考虑用标注数据对一个小型开源模型如Qwen或Llama进行微调专门用于信息抽取这比依赖通用大模型更准确、成本更低。提供反馈闭环当智能体基于记忆做出了错误决策应设计机制让用户或管理员可以标记错误。这些反馈可以用于重新评估和修正相关的记忆数据甚至用于重新训练抽取模型。5.4 隐私与安全考量记忆平台存储了大量交互数据安全至关重要。数据脱敏在记忆摄入前对敏感信息如身份证号、银行卡号、密码进行脱敏处理。脱敏规则最好在进入Cognee之前完成。访问控制Cognee本身可能不提供细粒度的访问控制。需要在应用层实现确保用户只能检索到自己权限范围内的记忆例如通过向查询强制添加user_id过滤器。记忆遗忘权必须实现“记忆删除”功能以响应合规要求如GDPR的被遗忘权。这不仅要在向量库和图库中删除数据还要考虑备份和日志中的残留。6. 常见问题与排查实录在实际集成和测试中我遇到了一些典型问题这里记录下来供大家参考。问题1检索到的记忆不相关干扰了智能体判断。现象智能体回答问题时引用了完全不相关的历史对话片段。排查检查向量检索首先确认向量搜索本身是否准确。可以单独测试用同样的查询语句去向量库搜索看返回的top-k结果是否相关。如果不相关问题可能出在嵌入模型不适合你的领域或者文本清洗去除无意义符号、统一格式没做好。检查混合查询逻辑如果使用了混合查询图向量检查两者的结果是如何融合和排序的。默认的融合策略如加权平均可能不适合你的场景。可以尝试调整权重或者先做图过滤再做向量检索。检查记忆粒度存储的记忆事件是否过于冗长或过于琐碎一个包含多轮对话的“会话”作为一个记忆单元可能太大导致向量表征模糊。尝试将记忆切分成更小、意图更明确的单元如“用户询问价格”、“助手提供方案A”。解决我们最终调整了记忆的切分策略从“按会话”改为“按对话回合和意图边界”并使用更领域化的嵌入模型进行微调相关性显著提升。问题2图数据库查询超时响应缓慢。现象涉及多跳关系查询的请求响应时间很长甚至超时。排查查询复杂度检查生成的CypherNeo4j查询语言语句。是否涉及了过多的可变长度路径[*..]查询是否没有使用索引数据规模检查图中节点和边的数量。如果超过百万级复杂查询必然变慢。数据库配置检查Neo4j的内存配置dbms.memory.heap.*是否分配给JVM的堆内存不足解决优化查询为常用查询属性如user_id,timestamp,event_type创建索引。限制查询范围在应用层为所有查询强制加上时间范围限制如最近90天。数据归档实施上述提到的记忆摘要与归档策略将主图数据库的规模控制在百万节点以内。升级硬件对于核心生产系统为图数据库单独配置高性能SSD和充足内存。问题3LLM信息抽取的API调用成本过高。现象随着交互量增长用于将文本转换成图数据的LLM API调用费用激增。排查分析记忆摄入的日志看是否每条消息无论长短、重要性都调用了LLM进行全量抽取。解决重要性过滤在摄入层增加一个轻量级分类器甚至可以用规则判断一条消息是否值得进行深度抽取。例如简单的问候语“你好”、“在吗”可能就不需要。缓存抽取结果对于相同或高度相似的文本内容例如标准化的系统消息、常见问题可以缓存其抽取出的图结构避免重复调用LLM。使用小型/本地模型对于非关键路径或对准确性要求不极高的场景可以尝试使用量化后的中小型开源模型如Qwen2.5-7B-Instruct在本地进行信息抽取大幅降低成本。问题4记忆冲突与信息不一致。现象用户说“我喜欢蓝色”但后续检索时系统却提供了“用户喜欢红色”的矛盾记忆。排查检查记忆的更新机制。是新增了一条矛盾记忆还是错误地修改了原有记忆解决Cognee需要更完善的记忆融合与冲突解决策略。时间戳与信源加权更近的记忆、来自更权威信源如用户明确声明 vs 智能体推测的记忆权重更高。显式记忆更新提供API让智能体可以显式地“修正”或“确认”某条记忆。例如当用户说“我之前说喜欢红色是错的其实我喜欢蓝色”系统应能定位到“喜欢红色”那条记忆并将其标记为过时或低权重同时创建“喜欢蓝色”的新记忆并建立“纠正”关系。提供不确定性在将记忆注入Prompt时可以附带置信度分数。例如“[推测]用户可能喜欢蓝色置信度0.7”让LLM知道这并非确定事实。为智能体赋予记忆是一个充满挑战但回报巨大的方向。Cognee作为一个开源平台提供了一个高起点的设计框架和实现参考。它最大的价值不在于开箱即用的完美解决方案而在于它清晰地定义了问题域并展示了一个混合存储、图向量结合、可扩展的架构范式。在实际项目中你很可能无法直接照搬Cognee而是需要借鉴其思想根据自身业务的数据规模、查询模式、成本预算和合规要求构建一个定制化的记忆系统。从简单的向量缓存开始逐步引入图关系再完善记忆的生命周期管理是一个稳妥的演进路径。记住目标是让智能体变得更“聪明”和“贴心”而一个高效、准确的记忆系统正是通往这个目标的核心基石。
返回列表