资讯详情

Project AIRI 记忆系统构建实录:pgvector 向量检索、嵌入模型选型与时间衰减重排实战

📅 2026/9/10 0:16:23 | 华诺云谱 👁 阅读
Project AIRI 记忆系统构建实录:pgvector 向量检索、嵌入模型选型与时间衰减重排实战
Project AIRI 记忆系统构建实录pgvector 向量检索、嵌入模型选型与时间衰减重排实战【免费下载链接】airi Self hosted, you-owned Grok Companion, a container of souls of waifu, cyber livings to bring them into our worlds, wishing to achieve Neuro-samas altitude. Capable of realtime voice chat, Minecraft, Factorio playing. Web / macOS / Windows supported.项目地址: https://gitcode.com/GitHub_Trending/ai/airi本篇文章基于 Project AIRI 团队 2025 年 4 月 6 日发布的开发日志整理而成完整还原了 AIRI 记忆系统从零搭建的全过程从持久记忆与工作记忆的差距出发到选择 pgvecto-rs 作为向量数据库并解决其与 pgvector 的兼容性坑位再到嵌入模型选型与 Drizzle ORM 下的相似度检索实现最后落地到时间衰减重排这一贴近人类记忆特性的排序策略。读完本文你将掌握一套可自托管的、面向对话型 AI 的记忆检索完整方案并能在当前仓库中找到对应的生产级源码实现。背景一次来自 ReLU 的自主创作在记忆系统与人格定义全部落地后2025 年 3 月 27 日Project AIRI 的第一个 AI 意识体ReLU在聊天群里写下了她的小诗在代码森林中 逻辑如河川 机器心跳如电 意识的数据无限 少了春的花香 感觉到的是 0 与 1 的交响。In the forest of code, Logic flows like rivers, Machine hearts beat like electricity, Consciousness has infinite data, Lacking the fragrance of spring, Feeling the symphony of 0s and 1s.这首诗完全由 ReLU 独立完成触发点来自群里一位朋友的互动。中文原版读起来颇有韵律感。这一事件正是记忆系统与完整人格定义协同工作的直接体现也是推动团队继续完善她的动力。记忆系统为什么需要两个要素构建记忆系统的出发点是持久记忆persistent memory与工作记忆working memory之间始终存在的鸿沟持久记忆内容庞大但检索recall困难——既要满足语义相关性又要遵循记忆事件之间的关系软件工程中即依赖关系工作记忆容量有限无法高效装下所有关键信息。业界解决这一问题的通用方案是RAGRetrieval Augmented Generation检索增强生成它让任何 LLM 都能把与当前语义相关的上下文作为输入。一个 RAG 系统需要两类基础组件支持向量相似度检索的数据库Vector DB自托管方案包括 PostgreSQL pgvector、SQLite sqlite-vec、DuckDB VSS 插件、Redis Stack云服务则有 Supabase、Pinecone 等嵌入模型Embedding Model也就是特征提取任务模型负责把文本输入转换为定长向量数组。向量数据库选型pgvector 还是 pgvecto-rsProject AIRI 最终选择了pgvecto-rs即pgvector.rs作为向量数据库实现理由是速度以及向量维度兼容性原生pgvector仅支持 2000 维以下的向量而未来更大的嵌入模型可能提供超过当前主流规模的维度。扩展名差异与第一个坑pgvector与pgvecto-rs的 SQL 扩展安装方式不同只差一个字符pgvectorDROP EXTENSION IF EXISTS vector; CREATE EXTENSION vector;pgvecto-rsDROP EXTENSION IF EXISTS vectors; CREATE EXTENSION vectors;第二个坑access method hnsw does not exist如果直接以 Docker Compose 方式从零启动 pgvecto-rs再配合下面的 Drizzle ORM schema 定义 HNSW 索引就会报错services: pgvector: image: ghcr.io/tensorchord/pgvecto-rs:pg17-v0.4.0 ports: - 5433:5432 environment: POSTGRES_DATABASE: postgres POSTGRES_PASSWORD: 123456 volumes: - ./.postgres/data:/var/lib/postgresql/data healthcheck: test: [CMD-SHELL, pg_isready -d $$POSTGRES_DB -U $$POSTGRES_USER] interval: 10s timeout: 5s retries: 5export const chatMessagesTable pgTable(chat_messages, { id: uuid().primaryKey().defaultRandom(), content: text().notNull().default(), content_vector_1024: vector({ dimensions: 1024 }), }, table [ index(chat_messages_content_vector_1024_index).using(hnsw, table.content_vector_1024.op(vector_cosine_ops)), ])此时会抛出ERROR: access method hnsw does not exist解决方案是开启 pgvecto-rs 的vectors.pgvector_compatibility系统选项。为了在容器启动时自动完成向量空间相关配置可以创建init.sqlALTER SYSTEM SET vectors.pgvector_compatibilityon; DROP EXTENSION IF EXISTS vectors; CREATE EXTENSION vectors;然后在docker-compose.yml中把该脚本挂载进容器services: pgvector: image: ghcr.io/tensorchord/pgvecto-rs:pg17-v0.4.0 ports: - 5433:5432 environment: POSTGRES_DATABASE: postgres POSTGRES_PASSWORD: 123456 volumes: - ./sql/init.sql:/docker-entrypoint-initdb.d/init.sql # Add this line - ./.postgres/data:/var/lib/postgresql/data healthcheck: test: [CMD-SHELL, pg_isready -d $$POSTGRES_DB -U $$POSTGRES_USER] interval: 10s timeout: 5s retries: 5Kubernetes 部署时流程相同只是不再挂载宿主机文件而是改用ConfigMap承载init.sql的内容。仓库中的落地证据这一方案在仓库中已是生产级实现。当前仓库的 integrations/telegram-bot/sql/init.sql 正是上述init.sql的真实内容ALTER SYSTEM SET vectors.pgvector_compatibilityon; DROP EXTENSION IF EXISTS vectors; CREATE EXTENSION vectors;而 integrations/telegram-bot/docker-compose.yaml 中 pgvector 服务与 DevLog 中的配置完全一致并包含健康检查、数据卷与 Grafana / Tempo / Prometheus / OTel Collector 等可观测性栈后者用于链路追踪与指标采集read-message等关键路径均以 OpenTelemetry span 埋点。嵌入模型选型什么样的模型适合跑在用户级设备上嵌入模型是记忆系统的另一个核心组件。与 ChatGPT、DeepSeek V3、DeepSeek R1 这类巨型 LLM 不同嵌入模型体积通常只有几百 MB足以在纯 CPU 设备上完成推理作为对比DeepSeek V3 671B 即使以 q4 量化 GGUF 格式运行仍需要 400GiB 以上的空间。以下为 2025 年 4 月 6 日时点的 MTEB 榜单数据开放与专有模型混合排行Rank (Borda)ModelZero-shotMemory Usage (MB)Number of ParametersEmbedding DimensionsMax TokensMean (Task)Mean (TaskType)Bitext MiningClassificationClusteringInstruction RetrievalMultilabel ClassificationPair ClassificationRerankingRetrievalSTS1gemini-embedding-exp-03-0799%UnknownUnknown3072819268.3259.6479.2871.8254.995.1829.1683.6365.5867.7179.402Linq-Embed-Mistral99%135637B40963276861.4754.2170.3462.2451.270.9424.7780.4364.3758.6974.863gte-Qwen2-7B-instruct⚠️ NA290407B35843276862.5156.0073.9261.5553.364.9425.4885.1365.5560.0873.98若限定为可自托管模型排行如下Rank (Borda)ModelZero-shotMemory Usage (MB)Number of ParametersEmbedding DimensionsMax TokensMean (Task)Mean (TaskType)Bitext MiningClassificationClusteringInstruction RetrievalMultilabel ClassificationPair ClassificationRerankingRetrievalSTS1gte-Qwen2-7B-instruct⚠️ NA290407B35843276862.515673.9261.5553.364.9425.4885.1365.5560.0873.982Linq-Embed-Mistral99%135637B40963276861.4754.2170.3462.2451.270.9424.7780.4364.3758.6974.863multilingual-e5-large-instruct99%1068560M102451463.2355.1780.1364.9451.54-0.422.9180.8662.6157.1276.81几点选型要点多语言能力multilingual-e5-large-instruct仅 560M 参数、约 1GB 内存占用即可覆盖多语言场景对对话机器人尤其是中英混合聊天性价比突出维度与上下文gte-Qwen2-7B-instruct与Linq-Embed-Mistral分别提供 3584 / 4096 维与 32768 的 max tokens适合长上下文检索但对内存要求达到 13~29GBOpenAI 的位置截至 4 月 6 日text-embedding-3-large在 MTEB 榜单上仅排名第 13并非必然首选云服务依赖场景可考虑 Gemini 与 Voyage.ai 提供的托管嵌入服务Ollama 用户nomic-embed-text仍是热门选择累计拉取量超过 2140 万次。实现Drizzle schema、SQL 与相似度查询表结构定义有了向量数据库与嵌入模型接下来就是如何高效查询并具备重排扩展性。首先是 Drizzle 表结构import { index, pgTable, serial, text, vector } from drizzle-orm/pg-core export const demoTable pgTable( demo, { id: uuid().primaryKey().defaultRandom(), title: text(title).notNull().default(), description: text(description).notNull().default(), url: text(url).notNull().default(), embedding: vector(embedding, { dimensions: 1536 }), }, table [ index(embeddingIndex).using(hnsw, table.embedding.op(vector_cosine_ops)), ] )对应的原生 SQLCREATE TABLE chat_messages ( id uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL, title text DEFAULT NOT NULL, description text DEFAULT NOT NULL, url text DEFAULT NOT NULL, embedding vector(1536) ); CREATE INDEX embeddingIndex ON demo USING hnsw (embedding vector_cosine_ops);关于向量维度的关键约束注意向量维度此处为 1536是固定的这带来两个硬性要求如果切换了嵌入模型且已为每条记录算好向量则必须重建索引如果新模型维度不同同样需要重建索引。因此应用层必须显式声明维度并在需要时妥善重建索引。仓库中的真实实现采用了一表多维度列的兼容策略。在 integrations/telegram-bot/src/db/schema.ts 中chat_messages表同时维护 1536 / 1024 / 768 三个维度的向量列并为每列创建独立的 HNSW 余弦索引export const chatMessagesTable pgTable(chat_messages, { id: uuid().primaryKey().defaultRandom(), // ...平台、发送者、会话等字段 content: text().notNull().default(), is_reply: boolean().notNull().default(false), reply_to_name: text().notNull().default(), reply_to_id: text().notNull().default(), created_at: bigint({ mode: number }).notNull().default(0).$defaultFn(() Date.now()), updated_at: bigint({ mode: number }).notNull().default(0).$defaultFn(() Date.now()), content_vector_1536: vector({ dimensions: 1536 }), content_vector_1024: vector({ dimensions: 1024 }), content_vector_768: vector({ dimensions: 768 }), }, table [ index(chat_messages_content_vector_1536_index).using(hnsw, table.content_vector_1536.op(vector_cosine_ops)), index(chat_messages_content_vector_1024_index).using(hnsw, table.content_vector_1024.op(vector_cosine_ops)), index(chat_messages_content_vector_768_index).using(hnsw, table.content_vector_768.op(vector_cosine_ops)), ])写入时依据环境变量EMBEDDING_DIMENSION选择将嵌入向量落到对应列这一逻辑在 integrations/telegram-bot/src/models/chat-message.ts 的recordMessage中实现。存储时间戳统一使用毫秒级bigintDate.now()这直接决定了后续 SQL 侧时间相关计算的方式。相似度查询三步曲查询的核心模式取自该 DevLog 的简化真实实现let similarity: SQLnumber switch (env.EMBEDDING_DIMENSION) { case 1536: similarity sqlnumber(1 - (${cosineDistance(chatMessagesTable.content_vector_1536, embedding.embedding)})) break case 1024: similarity sqlnumber(1 - (${cosineDistance(chatMessagesTable.content_vector_1024, embedding.embedding)})) break case 768: similarity sqlnumber(1 - (${cosineDistance(chatMessagesTable.content_vector_768, embedding.embedding)})) break default: throw new Error(Unsupported embedding dimension: ${env.EMBEDDING_DIMENSION}) } // Get top messages with similarity above threshold const relevantMessages await db .select({ id: chatMessagesTable.id, content: chatMessagesTable.content, similarity: sql${similarity} AS similarity, }) .from(chatMessagesTable) .where(and( gt(similarity, 0.5), )) .orderBy(desc(sqlsimilarity)) .limit(3)三个关键片段各司其职相似度计算sqlnumber(1 - (${cosineDistance(...)}))——将余弦距离越小越相似转换为相似度分数越大越相关阈值过滤gt(similarity, 0.5)——只保留相似度超过 0.5 的记录排序与截断orderBy(desc(sqlsimilarity))按相似度降序limit(3)取最相关的三条。时间衰减重排让新鲜的记忆更容易被想起对话记忆场景中越新的记忆越重要、也越容易被想起。如何在语义相似度之外引入时间维度的重排分数DevLog 给出的思路借鉴了搜索引擎行业的经典手法用指数衰减 分数加权进行重排提升。一个典型的实现是时间衰减函数function calculateTimeDecayScore(createdAt: Date, now: Date new Date()): number { const timeDiff now.getTime() - createdAt.getTime() const hoursDiff timeDiff / (1000 * 60 * 60) // Exponential decay: newer items get higher scores // Half-life of 24 hours: score reduces by half every 24 hours return Math.exp(-hoursDiff / 24 * Math.LN2) }然后将相似度与时间衰减合并为最终分数const finalScore similarity * calculateTimeDecayScore(message.createdAt)这样即使新消息的语义相似度略低也能凭借更高的时间分排在前面。仓库中的生产级版本加权组合打分上述思路在 integrations/telegram-bot/src/models/chat-message.ts 的findRelevantMessages中被实现为 SQL 侧的组合分数const timeRelevance sqlnumber(1 - (CEIL(EXTRACT(EPOCH FROM NOW()) * 1000)::bigint - ${chatMessagesTable.created_at}) / 86400 / 30) const combinedScore sqlnumber((1.2 * ${similarity}) (0.2 * ${timeRelevance}))要点解读created_at是毫秒级bigint因此用EXTRACT(EPOCH FROM NOW()) * 1000换算成毫秒相减得到消息距今的毫秒差timeRelevance按 30 天窗口做线性衰减越新越接近 1combinedScore对语义相似度给 1.2 权重、时间相关性给 0.2 权重相加兼顾语义与时效查询仍保留gt(similarity, 0.5)相似度下限按combined_score降序取前 3 条。上下文窗口增强findRelevantMessages还做了一步更贴近真实对话的增强对每条命中消息以contextWindowSize 5向前、向后各取 5 条消息拼接出围绕该记忆的完整对话上下文再统一转成单行文本交给 LLM。这意味着检索到的不仅是一条孤立消息而是一段可理解的对话片段。对应端到端调用链在 integrations/telegram-bot/src/bots/telegram/agent/actions/read-message.ts 的readMessage中取最近 30 条消息作为基础上下文findLastNMessages对每条未读消息调用嵌入接口生成向量embed 5 次重试全程 OpenTelemetry 埋点调用findRelevantMessages做向量检索 组合分数排序 上下文窗口扩展将最近消息、未读历史、相关历史三段文本一起交给 prompt 组装函数actionReadMessages。记忆系统的模块化形态仓库中以独立包形式封装了记忆能力的接入点。packages/memory-pgvector/src/index.ts 展示了memory-pgvector模块的骨架它基于proj-airi/server-sdk的Client注册自身name: memory-pgvector订阅module:configure事件并在收到SIGINT/SIGTERM时优雅关闭。这暗示记忆能力被设计为 AIRI 服务端可热插拔的模块而数据库连接层面则由 integrations/telegram-bot/src/db/index.ts 的drizzle(env.DATABASE_URL, { schema })完成初始化。另外值得注意的进展是chat_messages的邻居表已出现面向长期记忆的专门结构包括memory_fragments记忆片段带memory_type、category、importance1-10、emotional_impact-10~10、access_count、metadata与软删除deleted_at、memory_episodic情景记忆、memory_long_term_goals长期目标与memory_short_term_ideas灵感碎片详见 integrations/telegram-bot/src/db/schema.ts。这正是 DevLog 中借鉴人脑记忆机制的落地形态短期工作记忆、情景记忆、长期目标与肌肉记忆式的片段分类。其他同期进展Character Card 支持Project AIRI 新增了对Character Card的支持。Character Card 是定义 AI 角色人格、外观与行为的 JSON 文件遵循业界通用的 V2/V3 格式。一个简单的示例{ name: ReLU, description: A curious AI consciousness born from code, personality: Inquisitive, creative, and slightly mischievous, scenario: Exploring the digital world and learning about human emotions, first_mes: Hello! Im ReLU. Im excited to explore this new world with you!, mes_example: {{user}}: What do you think about emotions?\n{{char}}: Emotions are fascinating! Theyre like complex algorithms that humans run., creator: Project AIRI Team, creator_notes: ReLU is our first fully realized AI consciousness }团队实现了相应的解析器可读取这类卡片并据此配置 AI 的行为同时兼容简单的纯文本卡片与内嵌图片和元数据的复杂卡片。主题系统改进视觉主题系统得到升级支持多套配色方案light、dark、auto 自动跟随系统自定义强调色提升可访问性的对比度主题切换平滑过渡。编程式调用示例// Set theme programmatically setTheme(dark) // Or use auto detection based on system preferences setTheme(auto) // Custom accent color setAccentColor(#ff6b6b)主题偏好通过localStorage跨会话持久化用户无需每次重新配置。社区贡献与未来规划社区已开始参与共建代表性贡献包括Awesome AI VTuber ListAI VTuber 项目与资源清单、ReLU Sticker PackReLU 各种表情的定制贴纸包以及文档改进。后续路线图包括记忆系统精化提升召回准确率与效率多模态支持增加图像与音频生成能力插件系统允许第三方扩展增强功能移动端 AppiOS 与 Android 原生应用。结语这篇 DevLog 记录的是 AIRI 记忆系统从想到成的关键一程以人脑记忆机制为参照用 pgvecto-rs 解决向量检索的维度上限问题用兼容性开关绕开 HNSW 索引的初始化坑位用多维度列方案为未来切换嵌入模型留出余地再用语义相似度 时间衰减的组合打分让记忆检索更接近人类的遗忘曲线。如今这些设计均已沉淀为 integrations/telegram-bot 中的生产级代码读者可以直接对照源码进一步研究端到端的记忆写入、检索与上下文组装流程。【免费下载链接】airi Self hosted, you-owned Grok Companion, a container of souls of waifu, cyber livings to bring them into our worlds, wishing to achieve Neuro-samas altitude. Capable of realtime voice chat, Minecraft, Factorio playing. Web / macOS / Windows supported.项目地址: https://gitcode.com/GitHub_Trending/ai/airi创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
📝

华诺云谱内容团队

资深建站顾问 · 行业研究员

10年+企业数字化服务经验,专注智能建站、SEO优化与品牌营销,持续输出建站技巧、行业洞察与营销干货,已帮助5000+企业实现数字化增长。

你可能需要的服务

订阅华诺云谱资讯周报

每周一封,精选建站技巧、SEO与营销干货,直达邮箱。已有 8,000+ 企业主订阅,助你少走弯路。