RAG系列:基于 DeepSeek + Chroma + LangChain 開發(fā)一個(gè)簡單 RAG 系統(tǒng)

創(chuàng)建 Next 項(xiàng)目
首先,使用 npx create-next-app@latest 根據(jù)提示完成 Next 項(xiàng)目的創(chuàng)建:
# 創(chuàng)建 Next 項(xiàng)目
npx create-next-app@latest創(chuàng)建好項(xiàng)目之后,在 src/app 目錄下新建 rag 目錄,本次 demo 的代碼都將放在這里。
知識庫構(gòu)建
接下來,我們將構(gòu)建知識庫,主要目標(biāo)是將準(zhǔn)備好的 pdf 通過向量化存到向量數(shù)據(jù)庫中,以便后續(xù)的檢索。
由于本次 RAG 系統(tǒng)的開發(fā)都要依賴 LangChain 框架,所以我們先在項(xiàng)目中安裝 LangChain 框架和核心依賴:
# LangChain 框架和核心依賴
npm install langchain @langchain/core文檔加載
LangChain 的 DocumentLoaders[1] 提供了種類豐富的文檔加載器,可加載文件系統(tǒng)的文件也可以加載線上文件,包括 csv、docx、pdf、pptx、html、github、youtube等等。
現(xiàn)在我們使用 PDFLoader[2] 來實(shí)現(xiàn) pdf 的數(shù)據(jù)加載。
先安裝所需的依賴包:
# @langchain/community:包含第三方集成,這些集成實(shí)現(xiàn)了 LangChain Core 中定義的基本接口,如:文檔加載、文檔嵌入、向量數(shù)據(jù)庫等等
# pdf-parse:讀取 pdf 文本
npm install @langchain/community pdf-parse然后添加加載 pdf 的代碼:
import { PDFLoader } from '@langchain/community/document_loaders/fs/pdf';
const loader = new PDFLoader('public/example.pdf', { splitPages: false });
const docs = await loader.load();文檔分割
加載完成后,由于加載的文檔可能過長,不適合模型的上下文窗口,需要將文檔分割成合適的大小。
LangChain 提供了 TextSplitter[3] 組件來實(shí)現(xiàn)文檔分割:
import { RecursiveCharacterTextSplitter } from 'langchain/text_splitter';
// chunkSize:分割文檔的長度
// chunkOverlap:分割文檔間的重疊長度
const textSplitter = new RecursiveCharacterTextSplitter({
chunkSize: 1000,
chunkOverlap: 200,
});
const texts = await textSplitter.splitDocuments(docs);文檔向量嵌入
接下來我們需要對分割后的文本塊進(jìn)行向量嵌入,然后使用 Chroma 向量數(shù)據(jù)庫存儲。
向量模型使用 ollama 安裝的 nomic-embed-text 模型,可用 ollama run nomic-embed-text進(jìn)行下載和運(yùn)行,完整的代碼如下:
import {
Chroma,
ChromaLibArgs,
} from'@langchain/community/vectorstores/chroma';
import { ChatOllama, OllamaEmbeddings } from'@langchain/ollama';
import { EmbeddingsInterface } from'@langchain/core/embeddings';
// 初始化 embeddings 函數(shù)
exportfunctioninitOllamaEmbeddings(model = 'nomic-embed-text') {
returnnewOllamaEmbeddings({ model });
}
// 初始化向量數(shù)據(jù)庫
exportfunctioninitChroma(
embeddings: EmbeddingsInterface = initOllamaEmbeddings(),
args: ChromaLibArgs = {
collectionName: 'rag_collection',
url: 'http://localhost:8000',
}
) {
returnnewChroma(embeddings, args);
}
// 初始化向量數(shù)據(jù)庫
const chromadb = initChroma();
// 保存文本塊
const documents = await chromadb.addDocuments(texts);到此就構(gòu)建好了一個(gè)簡單的知識庫。
RAG 系統(tǒng)構(gòu)建
在創(chuàng)建好知識庫之后,接下來就可以開始構(gòu)建一個(gè)基礎(chǔ)的 RAG 系統(tǒng)。該系統(tǒng)包括檢索器與生成器兩部分,具體工作流程如下:對于用戶輸入的問題,檢索器先搜索與該問題相關(guān)的文檔,接著將檢索到的文檔與初始問題一起傳遞給生成器,即大語言模型,最后將模型生成的答案返回給用戶。
檢索器創(chuàng)建
我們先基于 VectorStoreRetriever 創(chuàng)建檢索器,利用向量相似度進(jìn)行檢索。
// 初始化向量數(shù)據(jù)庫
const chromadb = initChroma();
// 創(chuàng)建檢索器
const retriever = chromadb.asRetriever();生成器創(chuàng)建
接下來我們創(chuàng)建生成器,這里我們使用 Ollama 安裝的 deepseek-r1:14b 大模型作為生成器。
import { ChatOllama } from '@langchain/ollama';
export function initOllamaLLM(model = 'deepseek-r1:14b') {
return new ChatOllama({ model });
}
// 創(chuàng)建生成器(初始化大模型)
const ollamaLLM = initOllamaLLM()然后再設(shè)置提示模版:
// 設(shè)置提示模版
const prompt = PromptTemplate.fromTemplate(
'你是負(fù)責(zé)回答問題的助手。使用以下檢索到的上下文片段來回答問題。如果你不知道答案,就說你不知道。\n\n上下文:{context}\n\n問題:{question}\n\n回答:'
);RAG 鏈生成答案
最后我們通過 RAG 鏈將檢索器和生成器整合在一起,這里可以使用 LangChain 表達(dá)式語言(LangChain Execution Language,LCEL)來方便快捷地構(gòu)建一個(gè)鏈,將檢索到的文檔、構(gòu)建的輸入 Prompt 以及模型的輸出組合起來。
// 使用 LCEL 構(gòu)建 RAG 鏈
const ragChain = RunnableSequence.from([
{
context: retriever.pipe((docs) => {
// 文檔列表使用 \n\n 拼接為字符串
return docs.map((doc) => doc.pageContent).join('\n\n');
}),
question: newRunnablePassthrough(),
},
prompt,
ollamaLLM,
newStringOutputParser(),
]);
// 使用 RAG 鏈生成答案
const answer = await ragChain.invoke(question);項(xiàng)目代碼
代碼:https://github.com/laixiangran/ai-learn
啟動項(xiàng)目之后在瀏覽器輸入 http://localhost:3000/rag 即可訪問該 RAG 系統(tǒng),然后在輸入框輸入問題:互聯(lián)網(wǎng)的人才缺口有哪些

也可以通過訪問 http://localhost:3000/rag/generate?questinotallow=互聯(lián)網(wǎng)的人才缺口有哪些

通過以上步驟,我們就完成了一個(gè)基礎(chǔ) RAG 系統(tǒng)的搭建,其中借助于 LangChain 提供了一系列強(qiáng)大的工具和組件,使得構(gòu)建和整合檢索與生成過程變得簡單而高效。而借助 Ollama 我們也能夠在本地部署大語言模型和向量模型,這讓我們可以以較小的資源進(jìn)行 AI 的開發(fā)學(xué)習(xí)實(shí)踐。
引用鏈接
[1] DocumentLoaders: https://js.langchain.com/docs/concepts/document_loaders
[2] PDFLoader: https://v03.api.js.langchain.com/classes/_langchain_community.document_loaders_fs_pdf.PDFLoader.html
[3] TextSplitter: https://js.langchain.com/docs/concepts/text_splitters




































