微信扫码
与创始人交个朋友
我要投稿
用于提交知识(文本)的接口,该接口用于把网站或客户端的知识集中到后端服务中向量化存储
用于提问/查询的接口,该接口用于从知识库中获得结果
用于列出所有提问的接口
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-01-13
哈啰:构建智能出行RAG,ES还是向量数据库?
2025-01-13
企业级LLM独角兽 Cohere 发布 North:集成 RAG、搜索及 Agent 的企业级 AI 工作空间
2025-01-13
2025年这7种用于构建Agentic RAG系统的架构不可或缺
2025-01-13
RAG四种进阶方式
2025-01-13
使用RAG技术构建企业级文档问答系统:切分(3)使用Jina API进行语义切分
2025-01-13
使用RAG技术构建企业级文档问答系统:切分(2)使用Embedding进行语义切分
2025-01-12
RAG在智能问答系统中的应用
2025-01-12
CAG能取代RAG吗?别被表面现象迷惑!
2024-07-18
2024-09-04
2024-05-05
2024-06-20
2024-05-19
2024-07-09
2024-07-09
2024-06-13
2024-07-07
2024-07-07
2025-01-13
2025-01-09
2025-01-09
2025-01-09
2025-01-06
2025-01-04
2024-12-30
2024-12-27