RAG 深入:从关键词 Demo 到真实检索系统
问题场景
Demo 05 用关键词重叠模拟 RAG,能讲清楚「先检索、再生成」。但真实资料库会遇到更多问题:
- 文档太长,chunk 切得不好会丢上下文。
- embedding 维度和向量索引不匹配。
- topK 太小漏资料,太大浪费上下文。
- 向量相似度命中相关但不够精确。
- 检索结果没有来源,回答无法验证。
Mastra RAG 文档把这些拆成文档处理、chunk、embedding、vector store、retrieval、rerank。
完整流程
mermaid
flowchart LR
A[原始资料] --> B[MDocument]
B --> C[chunk 策略]
C --> D[embedding model]
D --> E[vector store index]
Q[用户问题] --> F[query embedding]
F --> G[vector query topK]
E --> G
G --> H[metadata filter]
H --> I[rerank]
I --> J[context package]
J --> K[Agent 回答 + 引用来源]Chunk 策略怎么选
官方文档列出多种 chunk 策略:recursive、character、token、markdown、semantic-markdown、html、json、sentence 等。
| 资料类型 | 推荐策略 | 原因 |
|---|---|---|
| 普通文本 | recursive | 尽量按结构分割 |
| Markdown 教程 | markdown 或 semantic-markdown | 保留标题层级 |
| FAQ 或短句 | sentence | 保留句子完整性 |
| JSON 配置 | json | 避免破坏结构 |
| HTML 文档 | html | 按页面结构切分 |
chunk 参数不是越大越好。maxSize 太大,检索结果占满上下文;太小,答案缺前后文。overlap 能保留跨 chunk 的连续信息,但会增加存储和检索噪音。
Embedding 和向量库
官方文档提醒:向量索引维度必须和 embedding 模型输出维度一致。换模型时,不能直接复用旧 index。
ts
await store.createIndex({
indexName: 'course_materials',
dimension: 1536,
})选择向量库时先看项目约束:
| 选择 | 适合 |
|---|---|
| libSQL vector | 本地开发、轻量教学 |
| PostgreSQL + pgvector | 已有 PostgreSQL 的团队 |
| Qdrant / Pinecone | 独立向量检索服务 |
| MongoDB Atlas Vector Search | 资料已在 MongoDB |
| Cloudflare Vectorize | Edge / Cloudflare 体系 |
Retrieval 和 rerank
初始 vector query 只解决「语义相似」。如果资料多或问题细,建议分两步:
- 先用向量检索取
topK = 10或更多候选。 - 再用 rerank 选出真正要放进 prompt 的
topK = 3到5。
Mastra RAG 文档提供 rerankWithScorer 方案。教学项目不默认接入 rerank,是为了保持离线可跑;生产替换时应保留 searchCourseTool 的输入输出,只替换内部实现。
RAG Tool 的输出结构
RAG Tool 不应该只返回纯文本。建议保留可验证来源:
ts
outputSchema: z.object({
hits: z.array(
z.object({
id: z.string(),
title: z.string(),
excerpt: z.string(),
source: z.string(),
score: z.number(),
metadata: z.record(z.string(), z.unknown()).optional(),
}),
),
})Agent instructions 应要求回答引用 title 或 source,资料不足时明确说明不足。
Vibe coding 提示词
text
请把教学版 searchCourseTool 升级为真实 RAG 设计。
先不要直接改代码,先输出方案。
要求:
- 说明 chunk 策略、embedding 模型、vector store 选择。
- 说明 index dimension 如何确定。
- Tool 输入输出保持兼容。
- 输出 hits 必须包含 title、excerpt、source、score。
- 给出 ingestion 脚本和 query tool 的文件边界。
- 标明哪些步骤需要 API Key。验证方式
| 验证项 | 方法 |
|---|---|
| chunk 不破坏标题关系 | 抽样查看 chunk 文本和 metadata |
| 向量维度正确 | createIndex 维度匹配 embedding 模型 |
| 查询可解释 | 每个 hit 有 title/source/score |
| 不相关问题不硬答 | 低分或空命中时拒绝编造 |
| rerank 有收益 | 比较 rerank 前后 top3 是否更贴题 |
常见错误
| 错误 | 后果 | 修复 |
|---|---|---|
| 改 embedding 模型但不重建索引 | 查询报错或结果异常 | 删除并重建 index |
| chunk 没 metadata | 无法引用来源 | 写入 title、url、section |
| topK 固定很大 | prompt 变长、成本上升 | 按问题类型动态调整 |
| RAG 结果直接拼成回答 | 缺少模型整合 | 把检索结果作为上下文交给 Agent |
小练习
把 course-materials.ts 里的每条资料设计成可入库文档:写出 id、title、section、source、text、metadata。再判断使用 recursive 还是 markdown chunk 策略。