Skip to content

第 12 章 · RAG 检索增强

本章目标:掌握 Mastra 的 RAG 管线:文档切片、向量化、入库、检索与重排,把私有知识注入 Agent 上下文。

12.1 RAG 五步管线

模型不知道你的内部文档。RAG(Retrieval-Augmented Generation)的标准流程:

  1. 加载——把 Markdown/PDF/HTML 转成 MDocument
  2. 切片——按 token 或语义边界切成 chunk;
  3. 向量化——用 embedding 模型把 chunk 变成向量并写入向量库;
  4. 检索——查询时把问题也向量化,取最相似的 top-K;
  5. 注入——把检索结果拼进提示词让模型回答。

12.2 文档切片与向量化

typescript
// src/rag/ingest.ts
import { MDocument } from '@mastra/rag'
import { embedMany } from 'ai'
import { openai } from '@ai-sdk/openai'

// 从 Markdown 文本创建文档对象(也支持 fromPDF / fromHTML 等)
const doc = MDocument.fromText(`
# 退款政策
下单后 7 天内可无理由退款。
# 发货时效
现货商品 48 小时内发出。
`)

// 按标题结构切片,每块不超过 512 token,块间重叠 50
const chunks = await doc.chunk({
  strategy: 'markdown',
  size: 512,
  overlap: 50,
})

// 批量生成向量(embedding 维度由模型决定)
const { embeddings } = await embedMany({
  model: openai.embedding('text-embedding-3-small'),
  values: chunks.map((c) => c.text),
})

12.3 写入向量库

Mastra 提供多种 Vector 适配器,这里用 LibSQL 的向量扩展:

typescript
// src/rag/store.ts
import { LibSQLVector } from '@mastra/libsql'

const vector = new LibSQLVector({ url: 'file:./vectors.db' })

// 建表:指定维度必须与 embedding 模型输出一致
await vector.createIndex({
  indexName: 'kb_docs',
  dimension: 1536, // text-embedding-3-small 输出 1536 维
})

// 插入向量与原文元数据
await vector.upsert({
  indexName: 'kb_docs',
  vectors: embeddings,
  metadata: chunks.map((c) => ({ text: c.text })),
})

12.4 检索并注入上下文

typescript
// src/rag/query.ts
import { embed } from 'ai'
import { openai } from '@ai-sdk/openai'

export async function retrieve(question: string): Promise<string[]> {
  // 问题转向量
  const { embedding } = await embed({
    model: openai.embedding('text-embedding-3-small'),
    value: question,
  })

  // 余弦相似度取前 3 条
  const results = await vector.query({
    indexName: 'kb_docs',
    queryVector: embedding,
    topK: 3,
  })

  return results.map((r) => r.metadata?.text ?? '')
}

// 用法:把检索结果作为上下文传给 Agent
const context = (await retrieve('退货要多久处理?')).join('\n---\n')
const answer = await agent.generate(
  `参考以下资料回答客户问题:\n${context}\n\n问题:退货要多久处理?`,
)

12.5 重排与进阶

  • rerank() 可用交叉编码器对初筛结果二次排序,显著提升精度;
  • Metadata Filters 支持按类别、时间等字段过滤;

重排示例

向量初筛快但糙,用交叉编码器精排 top-20 再取前 3:

typescript
// src/rag/rerank.ts
import { rerank } from '@mastra/rag'

const candidates = await retrieveAll(question, 20) // 初筛扩大召回
const reranked = await rerank({
  query: question,
  results: candidates,
  topK: 3, // 只保留精排后的前三名注入上下文
})
  • 元数据过滤(Metadata Filters)支持按类别、时间等字段收窄检索范围;
  • 数据量大时可探索 GraphRAG(知识图谱增强检索)。

本章小结

  • 数据量大时可探索 GraphRAG(知识图谱增强检索)。

本章小结

  • RAG = 切片 → 向量化 → 入库 → 检索 → 注入五步;
  • MDocument.chunk() 支持按 markdown 结构智能切分;
  • 向量维度必须与 embedding 模型匹配(1536 for text-embedding-3-small);
  • 检索质量决定回答上限,必要时加 rerank 与元数据过滤。

🧪 随堂测验

点击你认为正确的选项。答错时会展示正确答案与原因解析。

1. RAG 流程中"切片(chunk)"的主要目的是?

2. 创建向量索引时 dimension 参数必须满足什么条件?

3. vector.query() 中 topK: 3 表示什么?

4. rerank() 在 RAG 管线中的作用是?

🛠️ 动手实践

  1. 把你的一份真实产品 FAQ 文档做成 RAG 管线,问 5 个问题检验命中率。
  2. 对比 size: 256size: 1024 两种切片对回答质量的影响。
  3. 在检索后加入 rerank 步骤,观察 top-3 结果顺序变化。