Mastra 语义召回Semantic Recall高级配置完全指南topK、messageRange、scope 与 filter 深度解析【免费下载链接】mastraMastra is the modern TypeScript framework for AI-powered applications and agents.项目地址: https://gitcode.com/GitHub_Trending/ma/mastra语义召回Semantic Recall是 Mastra 记忆系统中用于跨会话检索历史消息的核心能力它通过向量相似度搜索把过去对话中语义相关的消息重新注入当前上下文让 Agent 在长周期交互中保持连贯记忆。本文以semanticRecall配置项为主线结合 Mastra 仓库中 SemanticRecall 处理器源码 与测试用例完整讲解 topK、messageRange、scope、filter 等全部配置参数、支持的操作符语法以及底层实现原理帮助你按业务场景精确调优召回行为。读完本文你将能独立配置一套检索数量可控、上下文范围可调、按线程/资源隔离、按元数据精确过滤的生产级语义记忆。一、语义召回是什么输入处理器与输出处理器的双向协作在深入配置之前先明确语义召回在 Mastra 中扮演的角色。从源码结构看SemanticRecall 类 同时实现了两种处理器职责输入处理器processInput在每次对话请求进入时用最新用户消息的文本生成查询向量到向量库中检索相似历史消息并把命中结果以memory来源注入MessageList作为模型的额外上下文。输出处理器processOutputResult在每次对话结束后为本次新增的用户消息与模型回复生成嵌入并 upsert 到向量库使后续对话可以被检索到。这意味着语义召回是一个先写入、后读取的闭环只有被输出处理器索引过的消息才可能被输入处理器召回。源码中还特别过滤了role: system的消息只对用户与助手的文本内容建索引。在使用时你既可以通过Memory的options.semanticRecall自动启用Memory 会自行装配处理器也可以像源码 JSDoc 示例那样把SemanticRecall作为独立的inputProcessors/outputProcessors手动挂到 Agent 上。本文聚焦前一种、也是文档推荐的方式。二、基础配置一行 options 启用语义召回在Memory实例中启用语义召回只需在options.semanticRecall下进行配置。以下是原文档给出的完整示例注释已保留并补充const memory new Memory({ storage: new LibSQLStore({ id: learning-memory-storage, url: file:../../memory.db, // relative path from the .mastra/output directory }), vector: new LibSQLVector({ url: file:../../vector.db, // relative path from the .mastra/output directory }), embedder: openai.embedding(text-embedding-3-small), options: { semanticRecall: { topK: 3, messageRange: { before: 2, after: 1, }, scope: resource, // Search all threads for this resource filter: { projectId: { $eq: project-a } }, }, }, })启用语义召回有一个硬性前提storage消息存储、vector向量存储与embedder嵌入模型三者缺一不可。在 memory.ts 的处理器装配逻辑 中缺少任何一个都会抛出明确的MastraError错误 ID 分别为SEMANTIC_RECALL_MISSING_STORAGE_ADAPTER、SEMANTIC_RECALL_MISSING_VECTOR_ADAPTER、SEMANTIC_RECALL_MISSING_EMBEDDER。因此配置前请先确认三个组件都已挂载。关于示例中的路径file:../../memory.db与file:../../vector.db是相对.mastra/output构建目录的 SQLite 文件路径请根据你的实际部署位置调整。三、topK控制召回消息数量topK决定每次检索返回多少条相似消息对应向量查询的topK参数semanticRecall: { topK: 3, }关键说明默认值为4。这与源码中的常量DEFAULT_TOP_K 4semantic-recall.ts完全一致。调大 topK检索到更多消息对复杂话题有帮助但可能混入相关性较低的内容同时增加 token 消耗与上下文长度。调小 topK上下文更精简、成本更低但可能遗漏关键信息。测试用例 semantic-recall.test.ts 验证了topK会被原样透传给vector.query即检索数量严格受此参数约束。四、messageRange控制命中消息的上下文包裹向量检索命中的只是单条消息但孤立的单条消息往往缺乏上下文。messageRange用来控制每条命中消息前后各补多少条相邻消息// 写法一数字形式前后各取 N 条 messageRange: 2 // 每条命中前 2 条、后 2 条 // 写法二对象形式前后分别指定 messageRange: { before: 2, after: 1 } // 前 2 条、后 1 条关键说明默认值为1即每条命中消息默认向前、向后各补充 1 条源码常量DEFAULT_MESSAGE_RANGE 1见 semantic-recall.ts。数字写法等价于{ before: n, after: n }源码构造函数会把它规范化为对象形式semantic-recall.ts。底层通过storage.listMessages的withPreviousMessages/withNextMessages参数实现semantic-recall.ts因此补出的消息来自消息存储、按时间顺序排列能还原出真实的对话流。测试用例验证了自定义messageRange: { before: 5, after: 3 }会被正确应用到消息加载semantic-recall.test.ts。五、scope限定搜索范围实现跨会话记忆scope决定语义检索在哪个范围内进行// 只搜索当前线程 scope: thread // 搜索该资源用户拥有的所有线程 —— 默认值 scope: resource关键说明默认值为resourcememory/types.ts 与 semantic-recall.ts 中的默认逻辑一致。底层实现scope: thread时向量查询的filter为{ thread_id: threadId }scope: resource时filter 为{ resource_id: resourceId }semantic-recall.ts。对应测试见 semantic-recall.test.ts。跨线程消息的特殊处理当scope: resource且命中其他线程的消息时这些跨线程消息不会与当前线程消息混在一起而是被格式化成一条带时间戳、角色标签的 system 消息并用remembered_from_other_conversation标签包裹semantic-recall.ts让 Agent 明确知道这些内容来自其他对话。相关测试见 semantic-recall.test.ts。典型场景希望 Agent 记住同一用户resource在多个会话里提过的偏好用resource希望每个会话完全隔离、互不干扰用thread。六、filter按元数据精确过滤召回结果filter允许把语义召回结果限制在匹配特定线程元数据的消息上例如项目 ID、分类等semanticRecall: { filter: { projectId: { $eq: project-a } }, }6.1 元数据的存储时机重要需要特别注意的是过滤器匹配的是消息保存时写入消息嵌入embedding上的元数据而不是线程的实时元数据。文档明确指出Filters match metadata stored on message embeddings when messages are saved. If thread metadata changes later, existing embeddings keep their previous metadata until those messages are saved or indexed again.也就是说如果某条消息被索引时线程元数据是projectId: project-a之后你把线程改成了projectId: project-b这条旧消息的嵌入上仍然是project-a在重新保存或重新索引前它不会被project-b的过滤条件召回。从源码看输出处理器在创建嵌入时写入的元数据字段包括message_id、thread_id、resource_id、role、content、created_atsemantic-recall.ts。filter中的自定义字段如projectId需要由你的线程/消息元数据在保存链路中带入。6.2 支持的操作符filter使用类似 MongoDB 的查询语法支持的逻辑与比较操作符如下完整定义见 向量过滤器基类操作符含义说明$and逻辑与连接多个查询子句全部满足才命中$or逻辑或任一子句满足即命中$eq等于值相等$ne不等于值不相等$gt大于仅支持 number / string / Date$gte大于等于仅支持 number / string / Date$lt小于仅支持 number / string / Date$lte小于等于仅支持 number / string / Date$in在数组中匹配数组中的任意一个值$nin不在数组中匹配数组之外的值6.3 常见用例示例原文档给出了三个可直接落地的过滤写法// 按项目过滤 const options { semanticRecall: { filter: { projectId: { $eq: my-project } } }, } // 按多个分类过滤 const options { semanticRecall: { filter: { category: { $in: [work, research] } } }, } // 按项目 优先级组合过滤逻辑与 const options { semanticRecall: { filter: { $and: [{ projectId: { $eq: project-a } }, { priority: { $gte: 3 } }], }, }, }$and/$or的子句以数组形式组合可以在其中混用不同字段与操作符构造复杂的业务过滤条件。filter与 scope 过滤resource_id/thread_id是叠加生效的先按 scope 划定范围再按filter进一步收紧结果memory/types.ts。七、进阶参数threshold 与 indexConfig除了原文档覆盖的四个参数SemanticRecallOptions与SemanticRecall类型还公开了两个值得了解的高级配置semantic-recall.ts、memory/types.ts7.1 threshold相似度阈值semanticRecall: { threshold: 0.7, // 相似度低于 0.7 的消息不召回 }threshold取值范围 0-1会在向量查询之后对结果做二次筛选score threshold的消息被过滤掉semantic-recall.ts。测试用例演示了设置threshold: 0.9后score: 0.85的命中被剔除semantic-recall.test.ts。当历史库中噪声较多、相似度普遍偏低时调高阈值能显著提升召回质量。7.2 indexConfig向量索引优化PostgreSQL/pgvectorsemanticRecall: { indexConfig: { type: hnsw, // ivfflat | hnsw | flat默认 ivfflat metric: dotproduct, // cosine | euclidean | dotproduct默认 cosine hnsw: { m: 16, efConstruction: 64 }, }, }indexConfig用于控制向量索引的创建方式目前主要对 PostgreSQL pgvector 生效其余向量存储Pinecone、Qdrant、Chroma 等会忽略这些设置并使用各自的默认配置。可配置项包括索引类型ivfflat/hnsw/flat、距离度量cosine/euclidean/dotproduct、IVFFlat 的聚类列表数ivf.lists以及 HNSW 的m与efConstruction。在 memory.ts 的索引创建逻辑 中可以看到这些参数只会在设置了type/ivf/hnsw时才会被透传给向量存储同时该逻辑还会为thread_id、resource_id请求 btree 索引避免大表上按元数据过滤时的全表扫描。八、底层实现原理与设计细节为了让配置真正可控理解几个源码层面的实现细节会很有帮助1. 向量索引命名。若未显式指定indexName处理器会基于嵌入模型 ID 自动生成索引名mastra_memory_${sanitizedModel}并将特殊字符替换为下划线、截断至 63 字符semantic-recall.ts。而在Memory类一侧默认索引名为memory_messages嵌入维度 1536 时或memory_messages_${dimensions}memory.ts。若你手动指定了indexName请确保它与向量库中实际创建的索引一致。2. 索引维度校验。处理器会在检索前通过createIndex幂等操作校验索引维度并使用进程内缓存indexValidationCache避免重复校验调用semantic-recall.ts。这保证了更换嵌入模型维度变化时不会静默出错。3. 去重与注入。召回结果中已存在于当前MessageList的消息会被去重semantic-recall.ts避免上下文重复同线程消息直接以memory来源加入跨线程消息则如上文所述格式化为 system 消息。4. 稳定排序。召回消息会按createdAt → threadId → roleuser → assistant → tool → system→ id的顺序稳定排序semantic-recall.ts避免向量查询结果顺序波动导致同样的提示词在不同运行间渲染不一致。5. 失败降级。无论是检索阶段还是建索引阶段出错处理器都只记录日志而不会中断请求semantic-recall.ts保证语义召回异常时对话仍能正常进行——但相应地当次请求会丢失召回上下文。6. 嵌入缓存。查询文本会先经过 xxhash 内容哈希命中全局嵌入缓存则直接复用向量减少重复调用嵌入 API 的开销semantic-recall.ts。九、配置建议与最佳实践综合以上参数针对不同场景给出如下配置参考跨会话记忆助手默认推荐topK: 3、messageRange: { before: 2, after: 1 }、scope: resource必要时加filter限定业务范围。这是原文档示例的默认形态兼顾召回质量与 token 成本。多租户/多项目隔离务必使用filter如projectId: { $eq }并结合scope: resource避免不同项目之间的消息互相污染。对时效敏感、会话相互独立改用scope: thread此时messageRange会基于同线程消息补上下文跨线程消息不会被注入。知识库噪声较大在topK基础上叠加threshold如0.7用相似度下限剔除低质量命中。生产环境 PostgreSQL 用户通过indexConfig将索引类型从默认ivfflat调整为hnsw通常能获得更优的查询性能相关类型与默认值见 memory/types.ts。最后提醒由于过滤依赖的是消息保存时的嵌入元数据任何会影响元数据归属的业务变更例如线程迁移项目、修改分类发生后都应考虑重新保存或重新索引相关消息否则旧嵌入仍按旧元数据参与召回。参考文件本文配置示例继承自 语义召回进阶配置课程文档实现细节可进一步查阅 SemanticRecall 处理器实现、处理器测试用例、Memory 类型定义、Memory 类的处理器装配与索引逻辑 以及 向量过滤操作符定义。【免费下载链接】mastraMastra is the modern TypeScript framework for AI-powered applications and agents.项目地址: https://gitcode.com/GitHub_Trending/ma/mastra创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
