RAG2026.08.26 · 7 分钟阅读
agent之RAG初识
RAG(Retrieval-Augmented Generation,检索增强生成)= 检索 + 生成**:不让模型凭记忆瞎编,先从文档库里按语义找出最相关的片段,把片段拼进 Prompt,再让模型基于片段回答
L
Leo
2026.08.26 · 更新于 2026.09.13
3 次浏览
RAG(Retrieval-Augmented Generation,检索增强生成)= 检索 + 生成:不让模型凭记忆瞎编,先从文档库里按语义找出最相关的片段,把片段拼进 Prompt,再让模型基于片段回答。
核心链路:
准备文档 → 向量化建库 → 构建检索器 → 检索 Top-K 片段 → 片段入 Prompt → 模型生成回答
1. 两个模型各管一摊
| 模型 | 类 | 负责什么 |
|---|---|---|
model | ChatOpenAI | 生成最终回答(temperature: 0 让回答更确定) |
embeddings | OpenAIEmbeddings | 把文本转成向量,用于检索 |
2. 文档切块(Document)
- 一个
Document=pageContent(正文)+metadata(标签:章节 / 角色 / 类型 / 心情)。 - 文档要切成小块:整本书一个大 Document 检索不精确;按主题切成小段,检索才能精准命中,也方便把相关片段塞进 Prompt。
3. 建索引:文档 → 向量
const vectorStore = await MemoryVectorStore.fromDocuments(documents, embeddings);
const retriever = vectorStore.asRetriever({ k: 3 });
fromDocuments:把每个文档转成向量存进内存向量库(MemoryVectorStore 纯内存,不落盘)。asRetriever({ k: 3 }):检索器,每次返回最相似的 3 个。
4. 两种检索姿势
const retrievedDocs = await retriever.invoke(question); // 只拿文档
const scoredResults = await vectorStore.similaritySearchWithScore(question, 3); // 文档 + 分数
retriever.invoke():上层封装,返回 Document 数组,不带分数。similaritySearchWithScore():底层方法,返回[[doc, score], ...]。- 坑:
score是「距离」不是「相似度」,越小越相似(OpenAI 用余弦距离)。显示时用1 - score转成相似度。 - 示例里同时跑两遍,再用
pageContent当 key 把两边结果对上,就是为了"既能拿文档、又能打印分数"。
5. 片段入 Prompt,模型基于片段回答
const context = retrievedDocs.map((doc, i) => `[片段${i+1}]\n${doc.pageContent}`).join("\n\n━━━━━\n\n");
const prompt = `你是一个讲友情故事的老师。基于以下故事片段回答问题...\n故事片段:\n${context}\n问题: ${question}`;
const response = await model.invoke(prompt);
context:把检索到的 Top-K 片段编号拼成上下文(也就是 133 行选中的那个变量)。- Prompt 里明确指示"只能基于片段回答,没提到的就说没提到"——这是防止模型瞎编(幻觉)的关键。
- 检索(拿事实)和生成(写回答)分开,就是 RAG 的全部。
6. 一句话总结
RAG = 切块建索引 → 语义检索最相关片段 → 片段 + 问题一起交给模型。模型看到的不是整个库,而是和问题最相关的几小段,回答有据可依、可控不瞎编。
7. RAG 流程图(Mermaid)
图与代码对照:fromDocuments 建库(89 行)→ retriever.invoke()(106 行)→ similaritySearchWithScore()(109 行)→ context 拼 Prompt(127–138 行)→ model.invoke()(141 行)。
读图要点:左边建索引(离线一次),右边跑问答(每次在线)。检索把"与问题最相关的片段"捞出来,模型只基于这些片段回答,不瞎编。
标签 / TAGSRAG
读者留言
COMMENTS · 0发表留言