微信扫码
与创始人交个朋友
我要投稿
用于提交知识(文本)的接口,该接口用于把网站或客户端的知识集中到后端服务中向量化存储
用于提问/查询的接口,该接口用于从知识库中获得结果
用于列出所有提问的接口
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+中大型企业
2024-11-25
RAG的2024—随需而变,从狂热到理性(下)
2024-11-25
RAG的2024—随需而变,从狂热到理性(下)
2024-11-25
糟糕!LLM输出半截Json的答案,还有救吗!
2024-11-24
解读GraphRAG
2024-11-24
RAGChecker:显著超越RAGAS,一个精细化评估和诊断 RAG 系统的创新框架
2024-11-23
FastRAG半结构化RAG实现思路及OpenAI O1-long COT蒸馏路线思考
2024-11-23
检索增强生成(RAG):解密AI如何融合记忆与搜索
2024-11-23
如何提高RAG系统准确率?12大常见痛点及巧妙解!
2024-07-18
2024-05-05
2024-07-09
2024-05-19
2024-07-09
2024-06-20
2024-07-07
2024-07-07
2024-07-08
2024-07-09
2024-11-06
2024-11-06
2024-11-05
2024-11-04
2024-10-27
2024-10-25
2024-10-21
2024-10-21