微信扫码
添加专属顾问
我要投稿
用于提交知识(文本)的接口,该接口用于把网站或客户端的知识集中到后端服务中向量化存储
用于提问/查询的接口,该接口用于从知识库中获得结果
用于列出所有提问的接口
import { ChatOllama } from "@langchain/ollama";
export function initOllamaChatModel() {
const llm = new ChatOllama({
model: "llama3",
temperature: 0,
maxRetries: 2,
// other params...
});
return llm;
}
import { OllamaEmbeddings } from "@langchain/ollama";
epxort function initOllamaEmbeddings() {
const embeddings = new OllamaEmbeddings({
model: "mxbai-embed-large", // Default value
baseUrl: "http://localhost:11434", // Default value
});
return embeddings;
}
同样的道理,ollama把对应的模型跑起来。
import { Document } from "@langchain/core/documents";
export async function loadPureText(text, metadata) {
const docs = [
new Document({
pageContent: text,
metadata,
}),
];
return docs;
}
import { RecursiveCharacterTextSplitter } from 'langchain/text_splitter';
export async function splitTextDocuments(docs) {
const textSplitter = new RecursiveCharacterTextSplitter({
chunkSize: 1000,
chunkOverlap: 200,
});
return await textSplitter.splitDocuments(docs);
}
import { FaissStore } from "@langchain/community/vectorstores/faiss";
export function createFaissVectorStore(embeddings) {
return new FaissStore(embeddings, {});
}
export async function searchFromFaissVectorStore(vectorStore, searchText, count) {
const results = await vectorStore.similaritySearch(searchText, count);
return results;
}
export async function addDocumentsToFaissVectorStore(vectorStore, docs, ids) {
return await vectorStore.addDocuments(docs, { ids });
}
export async function deleteFromFaissVectorStore(vectorStore, ids) {
return await vectorStore.delete({ ids });
}
/** 备份 */
export async function saveFaissVectorStoreToDir(vectorStore, dir) {
await vectorStore.save(dir);
}
/** 恢复 */
export async function loadFaissVectorStoreFromDir(dir, embeddings) {
const vectorStore = await FaissStore.load(dir, embeddings);
return vectorStore;
}
Prompt
import { ChatPromptTemplate } from "@langchain/core/prompts";
export function createRagPromptTemplate() {
const promptTemplate = ChatPromptTemplate.fromTemplate(
`You are an assistant for question-answering tasks. Use the following pieces of retrieved context to answer the question. If you don't know the answer, just say that you don't know. Use three sentences maximum and keep the answer concise.
Question: {question}
Context: {context}
Answer:`
);
return promptTemplate;
}
import { createStuffDocumentsChain } from "langchain/chains/combine_documents";
import { StringOutputParser } from "@langchain/core/output_parsers";
import path from 'path';
import fs from 'fs';
export async function createRagApplication() {
const llm = initOllamaChatModel();
const embeddings = initOllamaEmbeddings();
const faissStoragePath = path.join(__dirname, 'faiss');
const isFaissExist = fs.existsSync(faissStoragePath);
const vectorStore = isFaissExist ? await loadFaissVectorStoreFromDir(faissStoragePath, embeddings) : createFaissVectorStore(embeddings);
const saveTo = () => {
if (!isFaissExist) {
fs.mkdirSync(faissStoragePath, { recursive: true });
}
return saveFaissVectorStoreToDir(vectorStore, faissStoragePath);
};
const retriever = vectorStore.asRetriever(options.retriever);
const prompt = createRagPromptTemplate();
const chain = await createStuffDocumentsChain({
llm,
prompt,
outputParser: new StringOutputParser(),
});
/**
*
* @param {string} text
* @param {{
* type: string; // 类型
* id: string | number; // 标识
* }} meta
*/
const addText = async (text, meta) => {
const docs = await loadPureText(text, meta);
const docsToAdd = await splitTextDocuments(docs);
const { type, id } = meta;
const ids = docsToAdd.map((_, i) => `${type}_${id}_${i}`);
await addDocumentsToFaissVectorStore(vectorStore, docsToAdd, ids);
await saveTo(); // 每次新增向量之后,自动保存到目录中
return ids;
};
/**
* @param {string[]} ids
*/
const remove = async (ids) => {
await deleteFromFaissVectorStore(vectorStore, ids);
await saveTo();
};
const query = async (question) => {
const context = await retriever.invoke(question);
const results = await chain.invoke({
question,
context,
})
return results;
};
const stream = async (question) => {
const context = await retriever.invoke(question);
const results = await chain.stream({
question,
context,
})
return results;
};
return {
addText,
remove,
query,
stream,
};
}
const rag = await createRagApplication();
await rag.addText(`
RAG 检索的底座:向量数据库,我的博客 www.tangshuang.net 中有专门的内容对向量数据库做介绍。
在业界实践中,RAG 检索通常与向量数据库密切结合,也催生了基于 ChatGPT + Vector Database + Prompt 的 RAG 解决方案,简称为 CVP 技术栈。这一解决方案依赖于向量数据库高效检索相关信息以增强大型语言模型(LLMs),通过将 LLMs 生成的查询转换为向量,使得 RAG 系统能在向量数据库中迅速定位到相应的知识条目。这种检索机制使 LLMs 在面对具体问题时,能够利用存储在向量数据库中的最新信息,有效解决 LLMs 固有的知识更新延迟和幻觉的问题。
尽管信息检索领域也存在选择众多的存储与检索技术,包括搜索引擎、关系型数据库和文档数据库等,向量数据库在 RAG 场景下却成为了业界首选。这一选择的背后,是向量数据库在高效地存储和检索大量嵌入向量方面的出色能力。这些嵌入向量由机器学习模型生成,不仅能够表征文本和图像等多种数据类型,还能够捕获它们深层的语义信息。在 RAG 系统中,检索的任务是快速且精确地找出与输入查询语义上最匹配的信息,而向量数据库正因其在处理高维向量数据和进行快速相似性搜索方面的显著优势而脱颖而出。
以下是对以向量检索为代表的向量数据库与其他技术选项的横向比较,以及它在 RAG 场景中成为主流选择的关键因素分析:
`, { type: 'text', id: 0 });
const results = await rag.stream('RAG检索的底座是什么?');
for await (const chunk of results) {
process.stdout.write(chunk);
}
53AI,企业落地大模型首选服务商
产品:场景落地咨询+大模型应用平台+行业解决方案
承诺:免费场景POC验证,效果验证后签署服务协议。零风险落地应用大模型,已交付160+中大型企业
2025-04-30
聊聊AI智能体框架MetaGPT下的RAG实践
2025-04-30
如何用大模型+RAG给宠物做一个AI健康助手(干货分享)?
2025-04-30
HiRAG:基于层级知识索引和检索的高精度RAG
2025-04-29
教程|通义Qwen 3 +Milvus,混合推理模型才是优化RAG成本的最佳范式
2025-04-29
RAG开发框架LangChain与LlamaIndex对比解析:谁更适合你的AI应用?
2025-04-29
RAG性能暴增20%!清华等推出“以笔记为中心”的深度检索增强生成框架,复杂问答效果飙升
2025-04-29
超神了,ChatWiki 支持GraphRAG,让 AI 具备垂直深度推理能力!
2025-04-29
AI 产品思维:我如何把一个 AI 应用从基础 RAG 升级到 multi-agent 架构
2024-10-27
2024-09-04
2024-07-18
2024-05-05
2024-06-20
2024-06-13
2024-07-09
2024-07-09
2024-05-19
2024-07-07
2025-04-30
2025-04-29
2025-04-29
2025-04-26
2025-04-25
2025-04-22
2025-04-22
2025-04-20