Tensory:为AI智能体构建原生记忆系统的四层架构与实战指南
1. 项目概述:为AI智能体构建原生记忆系统
如果你正在开发一个需要长期记忆的AI智能体,比如一个能记住用户偏好的客服机器人,或者一个能持续跟踪项目进展的研究助手,那么你肯定遇到过“记忆”这个核心难题。传统的做法要么是把所有对话历史一股脑塞进上下文窗口(很快就会超限),要么是用向量数据库做RAG检索(检索到的经常是零碎的文本片段,缺乏逻辑关联)。这两种方式都让智能体像个“金鱼”——只有7秒记忆,每次对话都要重新认识你。
最近我在实际项目中尝试了tensory,一个开源的AI智能体记忆库,它的设计理念让我眼前一亮。tensory不存储原始文本块,而是像人脑一样,从信息中提取“主张”(Claim)——那些原子化的、可验证的事实陈述。比如从“谷歌宣布与EigenLayer合作开展云再质押”这句话中,它会提取出“谷歌与EigenLayer建立了合作伙伴关系”和“合作领域是云再质押”两个独立的主张。这种“主张原生”的存储方式,配合内置的矛盾检测和时序推理能力,让智能体真正拥有了连贯的、无冲突的长期记忆。
最让我省心的是它的零基础设施依赖。整个系统——从原始文本存储、主张提取、实体关系到混合搜索——全部塞进一个SQLite文件里。没有Docker容器要维护,没有Neo4j图数据库要搭建,更没有外部服务要调用。对于像我这样喜欢轻量级部署的开发者来说,这简直是福音。安装就是一句pip install tensory,然后你的智能体就能开始“记住”事情了。
2. 核心架构解析:四层认知模型与单文件哲学
tensory的架构清晰得让人感动,它模拟了一个简化的认知栈,全部封装在单个SQLite数据库中。理解这四层模型,是有效使用它的关键。
2.1 第0层:原始记录层——永不删除的“经历”
这一层存储的是未经处理的原始文本,tensory称之为“片段”(Episodes)。无论是用户的一条消息、一篇爬取的文章,还是一次API调用的结果,都会原封不动地存到这里。一个重要的设计原则是:这一层的数据永远不会被删除。这保证了记忆的可追溯性。即使上层提取的主张后续被修正或推翻,你总能回到最初的“经历”去核查信息来源。在实际操作中,我建议为每个片段附加丰富的元数据,比如source(来源URL或会话ID)、timestamp(精确到毫秒)、author(如果可知)。这些元数据在后续的矛盾检测和时序推理中会起到关键作用。
2.2 第1层:主张层——原子化的事实仓库
这是tensory的核心。原始文本通过LLM被分解成一个独立的“主张”。每个主张必须是一个原子化的、可验证的陈述句。例如,“项目A的团队有50人”是一个好主张;“项目A很厉害且团队庞大”则不是,因为它包含了主观判断且未原子化。
每个主张会生成向量嵌入(用于语义搜索),并计算一个“显著性”分数。这个分数不是静态的,它会根据“衰减”算法随时间推移而降低,模拟人脑对不常用记忆的淡忘。同时,当新输入的主张与已有主张高度相似时,系统会触发“惊喜”评分,这有助于识别信息更新或矛盾。这里的一个实操要点是:主张提取的质量直接取决于你提供的“上下文镜头”。同一段关于科技公司的文本,在“追踪融资动态”的上下文中,可能提取出“B轮融资1亿美元”的主张;而在“追踪技术栈”的上下文中,则可能提取出“后端主要使用Go语言”的主张。这意味着你需要为智能体不同的“关注领域”创建不同的上下文。
2.3 第2层:图谱层——实体与关系的网络
系统会自动从主张中抽取实体(如人名、组织名、项目名)和它们之间的关系,构建一个轻量级的知识图谱。这个图谱并不像Neo4j那样承载复杂的图遍历算法,而是作为搜索的“路标”。在混合搜索时,如果你搜索“EigenLayer”,系统不仅会返回直接匹配的主张,还会通过图谱找到与“EigenLayer”相关的实体(如“谷歌”、“再质押”),并检索与这些实体相关的主张,从而实现简单的关联推理。我的经验是,要定期通过内置的仪表盘查看实体图谱,它能直观地揭示你的智能体“知识”的结构和潜在的联系缺失。
2.4 第3层:上下文层——赋予记忆以目的
这是最体现设计智慧的一层。上下文(Context)在这里被定义为“研究目标”或“关注视角”。它本质上是一个用于主张提取和搜索加权的过滤器。你为智能体创建一个上下文,比如“监控DeFi领域的团队动态与合作协议”,那么之后所有的文本提取和搜索都会透过这个“镜头”进行,确保记忆的内容高度服务于特定目标。在实际项目中,我通常会为我的研究型智能体创建3-4个核心上下文,分别对应不同的分析维度,这让它的记忆和回忆变得非常有针对性。
注意:这四层全部通过精心设计的SQLite表结构和索引实现关联,所有操作(增删改查)都保证ACID事务性。这意味着即使在并发写入时,你的记忆库也不会出现数据错乱。
3. 从安装到实战:三种集成模式详解
tensory提供了多种接入方式,适应不同的开发场景。下面我结合自己的使用经验,详细拆解每一种。
3.1 模式一:Claude Code插件(无感集成,推荐首选)
这是最优雅的集成方式,特别适合基于Claude Code构建的编码助手或自动化工作流。
# 安装插件 claude plugin install --source github kryptogrib/tensory --path plugins/claude-code安装过程中,Claude Code会提示你输入必要的API密钥(如OpenAI的)。完成之后,记忆功能会在每一次会话中自动生效。你不需要在提示词中描述工具,也不需要手动调用搜索函数。当智能体在处理任务时,它会自动将对话中的关键信息存入tensory,并在需要时自动检索相关记忆。
它的工作原理是:插件通过拦截和分析Claude Code与模型之间的通信,自动识别哪些是应该被记住的“事实”,哪些是普通的指令或代码。然后它在后台调用tensory的API进行存储和查询,再将结果无缝融入上下文中。对于开发者来说,这完全是“黑盒”式的体验,你获得了一个拥有持久记忆的智能体,而无需改动任何业务逻辑代码。
3.2 模式二:Python库直接调用(最大灵活性)
当你需要将tensory深度集成到自己的Python应用中,或者需要更精细的控制时,直接使用其Python库是最佳选择。
# 安装核心库 pip install tensory # 按需安装扩展功能 pip install "tensory[mcp]" # 启用MCP服务器功能 pip install "tensory[ui]" # 启用Web仪表盘 pip install "tensory[all]" # 一次性安装所有功能基础使用的代码结构非常清晰:
import asyncio from tensory import Tensory, Claim async def main(): # 1. 创建或连接记忆库 store = await Tensory.create("memory.db") # 2. 添加主张(最基础的方式) claim = Claim(text="EigenLayer的核心团队目前约有50名成员", entities=["EigenLayer", "团队"]) await store.add_claims([claim]) # 3. 搜索 results = await store.search("EigenLayer团队规模") for r in result: print(f"- {r.claim.text} (相关性: {r.score:.3f})") # 4. 使用上下文进行高级操作 from tensory import Context research_ctx = await store.create_context( goal="追踪加密货币项目团队构成与融资信息", domain="crypto_research" ) # 在上下文中添加文本,自动提取相关主张 addition_result = await store.add( "据TechCrunch报道,EigenLayer在最新一轮融资中获得了1亿美元,估值达到50亿。", source="techcrunch_article_123", context=research_ctx ) print(f"提取了 {len(addition_result.claims)} 个新主张。") if addition_result.collisions: print(f"发现 {len(addition_result.collisions)} 处潜在矛盾,已标记。") asyncio.run(main())这里有几个关键细节:
- 异步接口:所有核心方法都是
async的,确保在高并发场景下的性能。请确保在你的异步事件循环中调用。 - Claim对象:手动创建
Claim时,entities字段尽量填全。这能极大帮助图谱层的构建和后续的关联搜索。 addvsadd_claims:add方法是“智能”的,它接受原始文本和上下文,自动完成提取、去重、矛盾检测等一系列操作。add_claims则是“手动”的,用于直接插入已结构化的主张。在大多数自动化场景下,使用add更省心。
3.3 模式三:MCP服务器(通用桥接)
MCP(Model Context Protocol)正在成为AI应用间通信的标准协议。tensory的MCP服务器模式让你能将记忆能力暴露给任何支持MCP的客户端,如Claude Desktop、Cursor、Windsurf等。
配置通常放在客户端的配置文件中(如Claude Desktop的claude_desktop_config.json):
{ "mcpServers": { "tensory_memory": { "command": "uvx", "args": [ "--from", "tensory[mcp]", "tensory-mcp" ], "env": { "TENSORY_DB": "/path/to/your/memory.db", "OPENAI_API_KEY": "sk-your-key-here" } } } }配置成功后,客户端内的AI助手就能调用tensory_search、tensory_remember等7个工具。但请注意:与Claude Code插件相比,MCP模式需要智能体“主动”去调用这些工具,并且工具的描述会占用宝贵的上下文令牌。因此,插件模式在自动化程度和效率上更优,MCP模式则提供了跨平台的通用性。
4. 核心功能实战:矛盾检测、混合搜索与仪表盘
4.1 矛盾检测:让记忆保持一致性
这是tensory区别于其他内存库的杀手级功能。想象一下,你的智能体先读到“项目X使用Python开发”,后来又读到“项目X的主要语言是Go”。一个简单的记忆系统会同时存储这两条矛盾信息,导致检索结果混乱。tensory会自动检测这种矛盾。
它的原理是基于主张的向量嵌入和实体关系进行相似度计算。当新主张与已有主张的语义高度相似但关键事实(如谓语部分)冲突时,系统会将其标记为“碰撞”。处理策略不是简单地覆盖或删除,而是:
- 标记时间戳:新旧主张都会被保留,但会打上时间标签。
- 评估信源:如果配置了信源可靠性权重,系统会更倾向于相信高权重信源的主张。
- 提供给上层逻辑:
add方法的返回结果中包含collisions列表,由开发者或智能体的决策逻辑来决定如何处理(例如,向用户请求澄清,或根据时间戳采用最新信息)。
在我的实践中,我会在添加重要信息后检查collisions,并编写简单的规则,例如“对于技术栈描述,总是采用时间最新的非冲突主张”。
4.2 混合搜索管道:向量、全文与图谱的融合
简单的向量搜索在事实性检索上容易“失焦”。tensory的搜索是一个精心设计的管道:
- 多路召回:
- 向量搜索:计算查询语句与主张嵌入的余弦相似度。
- 全文搜索:利用SQLite内置的FTS5引擎进行关键词匹配,对专有名词、缩写特别有效。
- 图谱扩展:通过实体关系网络,召回与查询实体相关联的其他主张。
- 结果合并(RRR):使用倒数排序融合算法,将三路召回的结果列表进行加权合并。这避免了单一搜索方式的偏差。
- 多样性排序(MMR):对合并后的结果使用最大边际相关性算法进行重排,在保证相关性的同时,确保返回的结果在语义上具有多样性,避免信息冗余。
调用搜索时,你可以通过参数精细控制:
results = await store.search( query="EigenLayer的合作伙伴", context=my_research_ctx, # 限定在特定上下文下搜索 limit=10, deduplicate=True, # 启用去重 search_mode="hybrid" # 可选 "vector", "fts", "graph", "hybrid" )4.3 Web仪表盘:可视化你的智能体“脑图”
对于调试和理解智能体的记忆状态,没有什么比可视化工具更有效了。
# 启动仪表盘 uvx --from "tensory[ui]" tensory-dashboard --db ./memory.db --port 7770访问http://localhost:7770,你会看到三个核心面板:
- 实体图谱浏览器:以节点图形式展示实体及其关系,点击节点可查看所有相关主张。这是发现知识网络结构的利器。
- 主张浏览器:以表格形式列出所有主张,支持按实体、时间、来源过滤。可以直观看到主张的显著性分数和冲突标记。
- 内存统计:展示主张总数、实体数、不同上下文下的数据分布等。帮助你量化智能体的“知识量”。
在开发中期,我习惯每周用仪表盘快速浏览一次,它能帮我发现一些意料之外的知识关联或矛盾集中点,这些往往是需要优化主张提取逻辑或上下文定义的地方。
5. 配置、调优与避坑指南
5.1 环境配置与模型选择
复制项目中的.env.example文件并配置你的环境变量是第一步。模型的选择直接影响成本、速度和提取质量。
# .env 文件示例 OPENAI_API_KEY=sk-... # 如果使用OpenAI的嵌入或LLM ANTHROPIC_API_KEY=sk-ant-... # 如果使用Claude系列模型 TENSORY_MODEL=claude-haiku-4-5-20251001 # 默认使用Haiku进行主张提取- 主张提取模型:默认的Claude Haiku在成本、速度和准确性上取得了很好的平衡。对于极高准确率要求的场景,可以尝试切换到
claude-sonnet。不建议使用GPT-4等重型模型,因为提取是高频操作,成本会急剧上升。 - 嵌入模型:如果使用OpenAI,默认是
text-embedding-3-small。你也可以通过实现BaseEmbedder接口接入其他模型,如本地运行的BGE-M3或nomic-embed-text。关键是要保证嵌入模型的维度与tensory的schema匹配(默认是1536维,对应OpenAI的small模型)。 - 成本控制:主张提取是主要成本来源。监控你的使用量,并为
store.add()操作添加频率限制或批量处理逻辑,避免对长文档进行过于频繁的片段式添加。
5.2 上下文设计的艺术
上下文是tensory的灵魂,设计好坏直接决定记忆的效用。
- 目标要具体:“分析加密货币”太宽泛,“追踪DeFi协议TVL变化、安全事件和治理提案”则是一个好的上下文目标。具体的目标能让LLM在提取主张时更有焦点。
- 领域标签有用:
domain字段虽然可选,但建议填写。它可以在内部作为辅助分类的标签,未来也可能用于跨上下文的搜索。 - 不要创建过多上下文:每个上下文都会在搜索时引入额外的计算开销(上下文加权)。为智能体的核心任务创建3-5个主要上下文通常就够了。对于边缘或一次性任务,可以不指定上下文或使用一个通用的“暂存”上下文。
5.3 性能调优与规模化
虽然tensory使用SQLite,但在数据量增长后仍需注意性能。
- 索引优化:tensory会自动创建关键索引。但如果你有特定的高频查询模式(例如总是按某个实体+时间范围搜索),可以考虑在数据库连接后手动添加复合索引。
- 批量操作:无论是
add_claims还是add,都尽量以列表形式批量提交数据,而不是单条循环插入,这能减少事务开销。 - 定期VACUUM:SQLite在频繁增删后会产生存储碎片。可以定期在业务低峰期执行
PRAGMA vacuum;来优化数据库文件大小和查询性能。 - 连接池:在Web服务等多线程环境中,不要共享同一个
Tensory实例。应为每个请求或线程创建独立的实例(它们可以指向同一个数据库文件),或者使用连接池管理数据库连接。
5.4 常见问题与排查
主张提取为空或质量差
- 检查:确认LLM API密钥和网络连通性。查看原始文本是否过于模糊或非陈述性。尝试在
store.add()时提供一个更具体、更聚焦的context。 - 调试:tensory的日志级别默认为INFO。你可以通过环境变量
TENSORY_LOG_LEVEL=DEBUG来获取更详细的提取过程日志,看看LLM返回的原始解析结果是什么。
- 检查:确认LLM API密钥和网络连通性。查看原始文本是否过于模糊或非陈述性。尝试在
搜索结果不相关
- 检查搜索模式:如果你搜索的是精确名称(如“EigenLayer”),尝试加入
search_mode="fts"或search_mode="hybrid"。纯向量搜索对专有名词有时效果不佳。 - 检查上下文:确保搜索时使用的
context与当初添加信息时的context一致或高度相关,否则加权会失效。 - 调整MMR参数:如果结果多样但最相关的没排第一,可能是MMR的多样性权重过高。目前这部分参数还未完全暴露,但你可以尝试通过
limit先获取更多结果,在应用层再做排序。
- 检查搜索模式:如果你搜索的是精确名称(如“EigenLayer”),尝试加入
数据库文件增长过快
- 分析:第0层的原始文本是主要增长源。如果你的原始文本非常长(如整篇论文),考虑在添加前进行适度的预处理或摘要,或者定期归档旧的、不再需要的上下文数据。
- 清理:tensory目前没有提供自动清理旧数据的机制。你可以通过编写脚本,基于时间戳或上下文,手动删除
episodes表中的记录。注意:删除原始片段不会自动删除其衍生出的主张,这可能导致“孤儿主张”,需要谨慎处理。
与现有应用集成困难
- 异步兼容:确保你的应用框架支持异步(
asyncio)。如果是在同步的Flask或Django视图中使用,你需要用asyncio.run()或类似方法封装调用,但这可能带来性能问题。更佳实践是将tensory操作封装成独立的异步任务,通过消息队列(如Celery)来执行。 - 数据同步:如果你的应用是多实例部署,需要确保所有实例的tensory都指向一个共享的数据库文件(例如放在网络存储上),并注意处理文件锁问题。对于高并发写入场景,可能需要考虑使用SQLite的WAL模式或引入一个轻量级的读写锁机制。
- 异步兼容:确保你的应用框架支持异步(
tensory作为一个处于Alpha阶段但设计理念先进的项目,已经为AI智能体提供了一套强大且实用的记忆基础设施。它的“主张原生”和“单文件”特性,尤其适合那些追求轻量化、高可控性、需要复杂记忆推理的AI应用场景。将它融入你的智能体工作流,就像是给AI装上了可靠的海马体,让每一次交互都建立在过去所有认知的基础之上。
