Mastra RAG 实战指南:用 @mastra/rag 完成文档切分、重排序与图检索
2026/9/15 14:21:40 网站建设 项目流程

Mastra RAG 实战指南:用 @mastra/rag 完成文档切分、重排序与图检索

【免费下载链接】mastraMastra is the modern TypeScript framework for AI-powered applications and agents.项目地址: https://gitcode.com/GitHub_Trending/ma/mastra

导读

@mastra/rag是 Mastra 面向检索增强生成(Retrieval-Augmented Generation)场景的 TypeScript 工具包,封装了 RAG 管线的三个核心环节:文档切分(chunking)、相关性重排序(reranking)与基于图的检索(graph-based retrieval)。本文将以 packages/rag/README.md 为主线,结合仓库源码逐层讲解如何用MDocument准备语料、用多策略切分器控制分块粒度、用元数据提取器增强上下文,以及如何用重排序与 GraphRAG 在生成前筛选出最相关的上下文,帮助你在 Mastra Agent 或 Workflow 中搭建一套可落地的 RAG 知识库检索链路。

一、包概览与安装

1.1 包定位

@mastra/rag的核心职责是"在 Agent 生成响应之前,准备好语料并筛选最相关的上下文"。它提供的三大能力与源码目录一一对应(见 packages/rag/src/index.ts):

  • 文档切分MDocument类及若干 Transformer(src/document/);
  • 重排序rerankrerankWithScorer函数与相关性打分器(src/rerank/);
  • 图检索GraphRAG类(src/graph-rag/)。

此外,src/tools/还提供了可直接接入 Agent 的检索工具,例如createVectorQueryToolcreateDocumentChunkerTool,让 RAG 能力可以像普通工具一样被 Agent 调用。

1.2 安装

npm install @mastra/rag

从 packages/rag/package.json 可以看到,该包的运行时依赖包括big.js(重排序权重计算)、js-tiktoken(token 级切分)、node-html-better-parser(HTML 解析)等,peer 依赖为@mastra/corezod,Node 版本要求>=22.13.0。包同时提供 ESM 与 CJS 产物,可在 CommonJS 项目中直接require使用。

二、文档对象 MDocument:RAG 管线的起点

2.1 四种文档构造入口

MDocument提供了四种静态工厂方法(document.ts),分别对应不同的源材料类型,并在内部记录type字段,用于后续自动选择默认切分策略:

import { MDocument } from '@mastra/rag'; const textDoc = MDocument.fromText('纯文本内容', { source: 'notes.txt' }); const htmlDoc = MDocument.fromHTML('<html><body><p>HTML 内容</p></body></html>'); const mdDoc = MDocument.fromMarkdown('# 标题\n\nMarkdown 正文'); const jsonDoc = MDocument.fromJSON('{"key": "value"}');

每个文档都可以附带metadata元数据,元数据会随切分后的 chunk 一起保留,成为后续向量检索过滤(filter)与来源追溯(source)的基础。

2.2 基于文档类型的默认策略

当调用chunk()而未显式指定策略时,defaultStrategy()(document.ts)会根据文档类型自动推断:

文档类型默认切分策略
htmlhtml
markdownmarkdown
jsonjson
latexlatex
其他(含 text)recursive

也就是说,MDocument.fromMarkdown(...).chunk()与显式传入strategy: 'markdown'等价,这让最简单的调用方式也能获得与内容结构匹配的切分效果。

三、文档切分:九种策略与参数详解

3.1 策略总览

chunk()方法的完整签名定义在 document/types.ts,共支持 9 种切分策略:

type ChunkStrategy = | 'recursive' | 'character' | 'token' | 'markdown' | 'html' | 'json' | 'latex' | 'sentence' | 'semantic-markdown';

每种策略的可用参数由StrategyOptions联合类型约束(document/types.ts),TypeScript 会在编译期阻止传入非法组合。所有策略共享的通用参数(BaseChunkOptions)如下:

参数类型说明
maxSizenumber单个 chunk 的最大长度(长度单位由lengthFunction决定,默认按字符计数)
overlapnumber相邻 chunk 之间的重叠长度,用于保持上下文连贯、避免关键信息被切断
lengthFunction(text: string) => number自定义长度计算函数,例如按 token 数而非字符数计数
separatorPosition'start' \| 'end'分隔符保留在 chunk 的起始还是末尾(character/recursive策略有效)
addStartIndexboolean是否为每个 chunk 记录起始字符索引
stripWhitespaceboolean是否去除 chunk 首尾空白

3.2 recursive:通用首选,支持 26 种编程语言

递归切分按"从大到小"的分隔符优先级逐级拆分,默认分隔符序列为['\n\n', '\n', ' ', ''](见 character.ts)。它会先用段落分隔,段落过长再按换行、空格逐级细分,最后兜底到单字符,因此对混合结构的自然语言文本效果最好。

该策略的独特能力是language参数:通过Language枚举(document/types.ts)指定代码语言后,RecursiveCharacterTransformer.fromLanguage()会加载该语言专属的分隔符列表(character.ts),从而在classfunctioninterface等语法边界处切分,避免把代码块拦腰截断:

import { MDocument, Language } from '@mastra/rag'; const code = MDocument.fromText(` class UserService { async findById(id: string) { ... } } `); const chunks = await code.chunk({ strategy: 'recursive', language: Language.TS, // 按 TypeScript 语法边界切分 maxSize: 512, overlap: 50, });

Language枚举覆盖了 TS/JS、Python、Go、Rust、Java、C/C++、Kotlin、Swift、PHP、Ruby、Lua、Perl、Haskell、Elixir、PowerShell、Scala、COBOL、Solidity、Protobuf、Markdown、LaTeX、HTML 等 26 种语言。此外还支持separators自定义分隔符序列与isSeparatorRegex指定分隔符为正则表达式。

实现上,CharacterTransformer还专门处理了 Unicode 代理对(surrogate pair)边界问题:切分时会先记录完整码点边界,确保 chunk 永远不会从一个 emoji 或生僻字的中间切开(character.ts),对中文等多字节文本同样安全。

3.3 character:按固定分隔符切分

适用于分隔符明确、结构规整的文本(如日志、CSV 风格的记录)。默认分隔符为'\n\n',支持separatorisSeparatorRegex参数;若某个片段仍超过maxSize,会自动带 overlap 二次细分:

const chunks = await doc.chunk({ strategy: 'character', separator: '\n', // 按行切分 maxSize: 512, overlap: 50, });

3.4 token:以 token 为单位的精确切分

基于js-tiktoken实现(document.ts),适合对上下文窗口预算敏感的场景(如把内容切进固定 token 上限的 prompt)。支持encodingName(tiktoken 编码名,如cl100k_base)或modelName(按模型自动选择对应编码)两种指定方式,以及allowedSpecial/disallowedSpecial控制特殊 token:

const chunks = await doc.chunk({ strategy: 'token', modelName: 'gpt-4o', // 或 encodingName: 'cl100k_base' maxSize: 512, // 按 token 数而非字符数 overlap: 50, });

3.5 markdown:保留文档结构的切分

Markdown 文档优先按标题层级切分:传入headers(标题级别与分隔符的映射数组)即可使用MarkdownHeaderTransformer,每个标题及其下内容成为一个 chunk,stripHeaders可控制是否剥离标题行、returnEachLine可让每一行单独成块(document.ts):

const chunks = await mdDoc.chunk({ strategy: 'markdown', headers: [ ['#', 'Header 1'], ['##', 'Header 2'], ], maxSize: 512, overlap: 50, });

3.6 html:按标题或区块切分

HTML 切分要求必须显式提供headers(标签名与语义名映射)或sections(标签对与区块名映射)二者之一,否则会抛出HTML chunking requires either headers or sections to be specified(document.ts)。按区块切分后再用RecursiveCharacterTransformer兜底控制maxSize

const chunks = await htmlDoc.chunk({ strategy: 'html', headers: [ ['h1', 'Header 1'], ['h2', 'Header 2'], ], maxSize: 512, });

3.7 json:结构化数据的递归切分

JSON 必须显式指定maxSizeRecursiveJsonTransformer会沿 JSON 的嵌套结构递归切分,尽量保持每个 chunk 是完整的 JSON 片段,并提供minSizeensureAscii(转义非 ASCII 字符)、convertLists(数组转文本列表)等控制项(document.ts):

const chunks = await jsonDoc.chunk({ strategy: 'json', maxSize: 512, minSize: 64, ensureAscii: false, convertLists: true, });

3.8 latex 与 sentence

  • latex:按\section\subsection\begin{...}等 LaTeX 结构边界切分(LatexTransformer);
  • sentence:按句子边界切分,maxSize为必填参数,支持minSizetargetSizesentenceEnders(自定义句末标点)、fallbackToWords(句子过长时退回按词切分)、fallbackToCharacters(再退回按字符切分)等(document.ts)。

3.9 semantic-markdown:语义级智能切分

SemanticMarkdownTransformer同样基于 tiktoken,在 Markdown 结构基础上引入joinThreshold(token 阈值,低于该阈值时相邻小节会合并成一个 chunk),从而避免产生大量内容稀疏的碎片化小节,适合模型输出或 LLM 生成的文档(document.ts):

const chunks = await mdDoc.chunk({ strategy: 'semantic-markdown', joinThreshold: 512, modelName: 'gpt-4o', maxSize: 1024, });

3.10 一次调用同时完成元数据提取

chunk()支持可选的extract参数(document.ts),在切分后立刻对每个 chunk 执行 LLM 元数据提取,一次调用得到"已切分 + 已增强"的结果。提取器共五种,均定义在 src/document/extractors/:

提取器参数要点提取内容
titlenodesnodeTemplatecombineTemplate为每个 chunk 生成标题,并建立指向原文档的SOURCE关系
summarysummaries(预置摘要列表)、promptTemplate生成 chunk 摘要
questionsquestions(生成数量)、embeddingOnly生成该 chunk 可能回答的问题
keywordskeywords(关键词数量)、promptTemplate提取关键词
schemaschema(Zod Schema)、instructionsmetadataKey按 Zod Schema 抽取结构化字段写入元数据
const chunks = await doc.chunk({ strategy: 'recursive', maxSize: 512, overlap: 50, extract: { title: true, summary: { summaries: ['本段讨论 RAG 的切分策略'] }, keywords: { keywords: 5 }, questions: { questions: 3 }, schema: z.object({ topic: z.string() }), }, });

从 extractors/types.ts 可见,提取器默认使用openai('gpt-4o')(读取OPENAI_API_KEY环境变量),也可通过每个提取器的llm字段传入自定义的 Mastra 语言模型。注意schema提取器无布尔形式,必须显式传入 Zod Schema;titletrue时会为带docId元数据的 chunk 建立SOURCE关系链(document.ts)。提取出的内容会合并写回对应 chunk 的metadata(如titlesummaryquestionskeywords、自定义 key),这些元数据随后可直接用于向量检索的过滤与重排序。

3.11 切分结果访问

chunk()返回Chunk[](内部结构定义见 src/document/schema/),也可以通过三个便捷方法读取(document.ts):

const chunks = await document.chunk({ strategy: 'recursive', maxSize: 512, overlap: 50 }); document.getDocs(); // Chunk[],含 text 与 metadata document.getText(); // string[],纯文本列表 document.getMetadata(); // Record<string, any>[],元数据列表

四、重排序:让最相关的上下文排到最前

4.1 为什么需要重排序

向量检索返回的前 K 条结果不一定与用户问题语义最贴合——向量相似度可能被主题相似但未正面回答问题的文本干扰。重排序阶段用一个更强的打分器对候选结果逐条评估,再按综合得分取 Top-K,能显著提升送入 Agent 的上下文质量。

4.2 三因子加权打分模型

rerank的实现位于 src/rerank/index.ts,每个候选结果的综合得分由三个因子加权求和:

finalScore = weights.semantic * semanticScore + weights.vector * vectorScore + weights.position * positionScore
因子来源说明
semanticLLM / 专用重排模型打分对 query 与 chunk 文本的相关性评分(0~1)
vector向量库返回的相似度候选结果在向量检索阶段的原始得分
position候选在原始列表中的位置1 - position / totalChunks,保留向量检索的初始排序信号

默认权重为semantic: 0.4vector: 0.4position: 0.2(index.ts),可通过weights自定义,但三项权重之和必须严格等于 1,否则会抛出Weights must add up to 1异常(使用big.js做精确浮点校验)。若提供了queryEmbedding,还会对查询向量的模长与主特征做微调(adjustScores),提升对长查询的敏感度。topK默认取 3,控制最终返回的结果数。

4.3 两种重排序入口

入口一:rerank—— 基于语言模型自动选择打分器(index.ts):

import { rerank } from '@mastra/rag'; import { createOpenAI } from '@ai-sdk/openai'; const openai = createOpenAI({ apiKey: process.env.OPENAI_API_KEY }); const model = openai('gpt-4o'); const ranked = await rerank(vectorResults, queryText, model, { topK: 5, weights: { semantic: 0.5, vector: 0.3, position: 0.2 }, });

rerank会根据模型 ID 自动选择语义打分器:modelId === 'rerank-v3.5'时使用CohereRelevanceScorer(调用 Cohere Rerank API),否则使用MastraAgentRelevanceScorer(用传入的语言模型包装一个专门的"相关性打分 Agent")。

入口二:rerankWithScorer—— 显式传入打分器(index.ts):

import { rerankWithScorer, CohereRelevanceScorer } from '@mastra/rag'; const cohereScorer = new CohereRelevanceScorer('rerank-v3.5', process.env.COHERE_API_KEY); const ranked = await rerankWithScorer({ results: vectorResults, query: queryText, scorer: cohereScorer, options: { topK: 5 }, });

4.4 三种语义打分器

打分器统一实现RelevanceScoreProvider接口(来自@mastra/core/relevance),位于 src/rerank/relevance/:

  1. MastraAgentRelevanceScorer(mastra-agent/index.ts):将传入的 Mastra 语言模型包装成relevance-scorer-<name>Agent,指令要求它仅输出 0~1 之间的一个数字;parseRelevanceScore会严格校验返回值必须在[0, 1]内,非法输出直接抛错。这是开箱即用、无需额外 API Key 的默认方案。
  2. CohereRelevanceScorer(cohere/index.ts):调用api.cohere.com/v2/rerank,API Key 通过构造参数或COHERE_API_KEY环境变量提供;适合需要专用重排模型、追求更高相关性的场景。
  3. ZeroEntropy(zeroentropy/index.ts):基于 ZeroEntropy 的语义打分实现。

rerank()返回RerankResult[],每项包含result(原始向量结果)、score(综合得分)以及details(semantic/vector/position 各因子得分,便于调试分析)。该函数还支持传入observabilityContext,切分与重排序过程都会以rag rerankrag chunk等 span 的形式写入 Mastra 的可观测性追踪(index.ts)。

五、GraphRAG:基于图的检索与关联挖掘

5.1 图结构的建立

GraphRAG类(src/graph-rag/index.ts)以"语义相似即连边"的方式把 chunk 组织成图:构造时指定dimension(embedding 维度,默认 1536)与threshold(连边相似度阈值,默认 0.7)。createGraph(chunks, embeddings)会为每个 chunk 创建一个节点,并对任意两节点计算余弦相似度,超过阈值则建立一条semantic类型的无向边(自动补上反向边),边的权重即为相似度(index.ts):

import { GraphRAG } from '@mastra/rag'; const graph = new GraphRAG({ dimension: 1536, threshold: 0.7 }); graph.createGraph(chunkList, embeddingList); // 两数组长度必须一致

5.2 混合检索:稠密检索 + 随机游走重排

query()采用"稠密检索 + 随机游走重排(Random Walk with Restart)"的混合策略(index.ts):

  1. 先用余弦相似度从图中筛出与查询向量最相似的 Top-K 候选节点;
  2. 对每个候选节点执行带重启的随机游走(默认 100 步、重启概率 0.15),按边的权重概率游走到邻居节点,统计访问频次并归一化;
  3. 将"稠密相似度 × 游走得分"叠加作为最终分,排序后返回 Top-K 的RankedNode[](含idcontentmetadatascore)。
const results = graph.query({ query: queryEmbedding, // number[],维度必须与 dimension 一致 topK: 10, randomWalkSteps: 100, // 游走步数 restartProb: 0.15, // 重启概率,须在 (0, 1) 之间 filter: { category: 'health' }, // 可选的严格元数据过滤 });

随机游走的价值在于:即使某节点与查询的直接相似度不高,只要它被多个高相关节点高频引用(处于图的"枢纽"位置),其最终得分也会被抬升——这正是图检索区别于纯向量检索、擅长挖掘关联与模式的地方。query()的参数有严格校验:查询向量维度必须匹配、topK >= 1randomWalkSteps >= 1restartProb必须落在(0, 1),违规都会抛出明确错误。filter提供严格等值匹配的元数据过滤,且过滤时随机游走只会发生在被过滤后的节点子集内。

5.3 图的生命周期管理

GraphRAG还提供完整的管理 API(index.ts):

  • addNode/addEdge:手动增补节点与边(节点必须携带与dimension匹配的 embedding);
  • getNodes/getEdges/getEdgesByType:遍历图结构;
  • updateNodeContent:更新节点内容;
  • clear:清空整个图;
  • serialize()/GraphRAG.deserialize():图的 JSON 快照持久化与恢复。快照带有版本号(当前为 1),恢复时校验版本、节点 embedding 维度与边引用的节点完整性。源码注释提示:由于快照会包含每个节点的完整 embedding,大图的 JSON 快照可能达到数 MB,持久化时需注意体积(index.ts)。

从源码中的 TODO 注释可以推断,当前仅支持semantic一种边类型,后续计划扩展 sequential、hierarchical、citation 等更多边类型与自定义边(index.ts)。

六、开箱即用的 RAG 工具

@mastra/rag在 src/tools/ 提供了可直接注册给 Agent 的工具:

6.1 createDocumentChunkerTool

把"文档切分"封装成 Agent 可调用工具(document-chunker.ts),默认参数为recursive策略、maxSize: 512overlap: 50,可在创建时覆盖:

import { createDocumentChunkerTool, MDocument } from '@mastra/rag'; const doc = MDocument.fromText(longText); const chunkerTool = createDocumentChunkerTool({ doc, params: { strategy: 'recursive', maxSize: 512, overlap: 50 }, });

6.2 createVectorQueryTool

把"向量检索 + 重排序"封装成 Agent 工具(vector-query.ts),内部完成:解析queryText/topK/filter→ 从向量库检索 → 使用配置的reranker(模型或打分器)对结果重排序 → 返回relevantContextsources。工具描述与参数说明集中在 utils/default-settings.ts,其中对 Agent 的提示词明确了queryText不可为空、topK默认 10、filter必须是合法 JSON 等约束,确保 Agent 生成的工具调用参数符合预期。该工具还支持通过requestContext在运行时覆盖indexNamevectorStoreNamemodelreranker等配置,并内置了向量库缺失时的优雅降级(返回空结果而非报错)。

除此之外,src/tools/bedrock-knowledge-base.ts 还提供了 AWS Bedrock 托管知识库(Managed KB)的接入工具,使用说明见 tools/BEDROCK_MANAGED_KB.md,适合已有 Bedrock 知识库资产、想在 Mastra Agent 中直接复用的团队。

七、把三件套串成一条完整 RAG 管线

综合上述能力,一条典型的 Mastra RAG 检索链路可以这样组织:

import { MDocument, GraphRAG, rerank, createVectorQueryTool } from '@mastra/rag'; import { createOpenAI } from '@ai-sdk/openai'; // 1. 语料准备:切分 + 元数据提取 const doc = MDocument.fromMarkdown(rawMarkdown, { docId: 'guide-001' }); const chunks = await doc.chunk({ strategy: 'markdown', maxSize: 512, overlap: 50, extract: { title: true, summary: true, keywords: { keywords: 5 } }, }); // 2. 生成 embedding 并写入向量库(示意,具体取决于所选向量库) // await vectorStore.upsert(chunks.map(c => ({ id: c.id, vector: embed(c.text), metadata: c.metadata }))); // 3. 可选:构建图索引用于关联检索 const graph = new GraphRAG({ dimension: 1536, threshold: 0.7 }); graph.createGraph(chunks, await Promise.all(chunks.map(c => embed(c.text)))); // 4. 检索 + 重排序(在 Agent 生成前筛选最相关上下文) const openai = createOpenAI({ apiKey: process.env.OPENAI_API_KEY }); const results = await rerank(rawVectorResults, userQuestion, openai('gpt-4o'), { topK: 5, weights: { semantic: 0.4, vector: 0.4, position: 0.2 }, });

八、总结与延伸阅读

@mastra/rag用一套简洁的 TypeScript API 覆盖了 RAG 从"语料准备"到"上下文筛选"的完整闭环:

  • MDocument + 9 种切分策略解决"怎么切":从通用递归切分、token 精确切分,到 Markdown/HTML/JSON/LaTeX 的结构感知切分,再到 26 种编程语言的语法边界切分;
  • 五种 LLM 元数据提取器解决"怎么增强":标题、摘要、问题、关键词、结构化 Schema 在切分时一次提取;
  • 三因子重排序解决"怎么选":语义 + 向量 + 位置加权,配合 Cohere / Mastra Agent / ZeroEntropy 三种打分器;
  • GraphRAG解决"怎么关联":语义图 + 随机游走,挖掘跨 chunk 的关联与模式;
  • 内置工具让 Agent 直接获得切分与检索能力,并全程接入 Mastra 可观测性。

仓库中还提供了完整的测试用例可作进一步参考,例如切分行为的测试在 document.test.ts 与各 transformers/ 目录下的测试文件,重排序测试在 rerank/index.test.ts,图检索测试在 graph-rag/index.test.ts;如果你使用 Docker 开发环境,packages/rag/docker-compose.yaml 可用于启动测试所需的依赖服务。版本历史与发布说明见 packages/rag/CHANGELOG.md。

【免费下载链接】mastraMastra is the modern TypeScript framework for AI-powered applications and agents.项目地址: https://gitcode.com/GitHub_Trending/ma/mastra

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询