使用LlamaIndex,Qdrant,Ollama和FastAPI构建本地RAG api

本文介绍了如何使用LlamaIndex、Qdrant、Ollama和FastAPI创建一个离线、免费且安全的RAGAPI,以解决ChatGPT等大语言模型的局限性,如数据隐私问题和知识更新需求。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

介绍

ChatGPT已经改变了我们与AI的互动方式。人们现在将这些大型语言模型(LLMs)作为主要的个人助手来进行写作、头脑风暴甚至咨询。然而,这些LLMs的问题在于,它们的表现只能和它们接受的训练数据一样好。

例如,如果一家公司想要向ChatGPT查询一份内部文件,ChatGPT可能无法理解它。此外,它的知识可能不是最新的,而且容易产生幻觉。

为了解决这个问题,我们可以在我们的数据上对这些LLMs进行微调,使它们能够从中回答问题。然而,这种方法可能非常昂贵。要使它们与新信息保持更新,需要不断的重新训练。

此外,幻觉的问题仍然存在,而且很难验证他们答案的来源。

更好的方法是利用强大的LLMs,可以访问我们的数据,而无需进行微调。我们如何实现这个目标呢?多亏了一种最近出现的方法,叫做检索增强生成(RAG),我们可以检索与用户查询相关的文档,并将它们作为额外的上下文输入到LLM中以生成答案。(更多细节将在接下来的部分中讨论。)

RAG Architecture Original

 来源:原始论文

 动机

互联网上有大量关于如何构建RAG管道的教程,问题在于,大多数都依赖于在线服务和云工具,特别是在生成部分,许多教程都主张使用OpenAI LLM API,不幸的是,这些API并不总是免费的,而且在处理敏感数据时可能不被认为是可信的。

这就是为什么我试图完全离线且完全免费地构建一个端到端的RAG api,这对于不想将其敏感数据发送到云服务或在线黑盒LLM Api的公司来说可能非常有用。

 项目概述

我们将创建一个API端点,用户可以在此处提问。该端点将通过一系列研究论文来寻找答案。然后,它将使用一个大型语言模型(LLM)来处理答案,并以简单易懂的格式返回。

Local Rag Architecture

本地RAG管道架构

该项目包括4个主要部分:

  • 使用Llamaindex构建RAG管道
  • 使用Docker设置本地Qdrant实例
  • 从hugging face下载一个量化的LLM并使用Ollama将其作为服务器运行
  • 连接所有组件并使用FastApi暴露API端点

好的,让我们开始构建项目。

 构建项目

我们将遵循RAG流程的常规流程,这与标准的ETL流程有些相似。 

Rag Flow

该项目的代码可以在此Github仓库中找到。别忘了

 正在加载

首先,我们需要获取一些数据,在我们的情况下,是研究论文。让我们创建一个Python脚本,使用一些关键词快速从arxiv.org下载论文。幸运的是,arxiv api已经有了一个Python封装器。

以下函数负责将研究论文下载到一个文件夹中。

Llamaindex支持各种数据加载器,在我们的情况下,我们的数据将是一个文件夹中的一堆PDF,这意味着我们可以使用 SimpleDirectoryReader

 2. 索引/存储

索引涉及以一种有利于存储、查询和馈送到LLM的方式来构造和表示我们的数据。Llamaindex提供了几种完成此任务的方法。

 分块

首先,我们需要使用指定的 chunk_size 对我们的数据进行分块。这是必要的,因为文档经常会过长,这可能会引入噪声并超过我们LLM的上下文长度。

 嵌入

接下来,我们将使用预训练的模型来嵌入这些块。你可以在我之前的文章中了解更多关于它们的信息。

对于嵌入,有许多可用的强大的预训练模型。对于这个项目,我们将使用sentence-transformers/all-mpnet-base-v2 

sentence-transformers/all-mpnet-base-v2

 在Hugging Face上的sentence-transformers/all-mpnet-base-v2

 存储

为了存储我们的数据,我们将使用本地的 Qdrant docker 实例,可以通过以下方式轻松设置:

docker run -p 6333:6333 qdrant/qdrant

然而,由于我们需要在停止实例后,数据仍然保留在磁盘上,我们必须添加一个卷。

docker run -p 6333:6333 -v ~/qdrant_storage:/qdrant/storage:z qdrant/qdrant

qdrant_storage 是我们在HOME目录中创建的一个文件夹的名称,qdrant数据库实例的数据将会被保存在这里。

然后我们可以从http://localhost:6333/dashboard 访问我们的数据库用户界面

以下函数负责加载、分块、嵌入和存储我们的数据。

def ingest(self, embedder, llm):
    print("Indexing data...")
    # Loading
    documents = SimpleDirectoryReader(self.config["data_path"]).load_data()

    client = qdrant_client.QdrantClient(url=self.config["qdrant_url"])
    qdrant_vector_store = QdrantVectorStore(
        client=client, collection_name=self.config["collection_name"]
    )
    storage_context = StorageContext.from_defaults(vector_store=qdrant_vector_store)
    service_context = ServiceContext.from_defaults(
        llm=llm, embed_model=embedder, chunk_size=self.config["chunk_size"]
    )

    # Chunking + Embedding + Storing
    index = VectorStoreIndex.from_documents(
        documents, storage_context=storage_context, service_context=service_context
    )
    print(
        f"Data indexed successfully to Qdrant. Collection: {self.config['collection_name']}"
    )
    return index

Data 类的完整代码可以在这里找到:data.py

我们可以看到我们的收藏现在已经创建好了

Qdrant UI

 Qdrant仪表板用户界面

 3. 查询

现在我们已经成功地将我们的数据(研究论文)加载到我们的向量存储(Qdrant)中,我们可以开始查询它以获取相关数据,以供我们的LLM使用。

让我们开始编写一个函数,用于设置我们的Qdrant索引,它将作为我们的查询引擎。

 查询引擎
def qdrant_index(self):
    client = qdrant_client.QdrantClient(url=self.config["qdrant_url"])
    qdrant_vector_store = QdrantVectorStore(
        client=client, collection_name=self.config['collection_name']
    )
    service_context = ServiceContext.from_defaults(
        llm=self.llm, embed_model=self.load_embedder(), chunk_size=self.config["chunk_size"]
    )

    index = VectorStoreIndex.from_vector_store(
        vector_store=qdrant_vector_store, service_context=service_context
    )
    return index

 来自:rag.py的代码

LLM

目标是使用本地LLM,这可能有点挑战性,因为强大的LLMs可能会占用大量资源并且昂贵。但是,得益于模型量化和Ollama,这个过程可以变得非常简单。

请参阅我之前的文章,以了解更多关于使用Ollama设置本地LLM的信息:使用Ollama在本地使用Hugging Face的自定义LLMs

在从Hugging Face下载 zephyr-7b-beta.Q5_K_M.gguf 后,我们需要为Ollama创建一个模型文件。

FROM models/zephyr-models/zephyr-7b-beta.Q5_K_M.gguf
PARAMETER num_ctx 3900
PARAMETER temperature 0.7
PARAMETER top_k 50
PARAMETER top_p 0.95
PARAMETER stop "<|system|>"
PARAMETER stop "<|user|>"
PARAMETER stop "<|assistant|>"
PARAMETER stop "</s>"
TEMPLATE """
<|system|>\n system
{{ .System }}</s>
<|user|>\n user
{{ .Prompt }}</s>
<|assistant|>\n 
"""

SYSTEM """
As a personal assistant for researchers, your task is to analyze the provided research papers and extract pertinent information on using the provided keywords. Summarize key findings, methodologies, and any notable insights. This assistant plays a crucial role in facilitating researchers' understanding of the current state of knowledge on the provided keywords.
Your Motive:
Give the learner "aha" moment on every Topic he needs to understand. You can do this with the art of explaining things.
Focus on Conciseness and Clarity: Ensure that the output is concise yet comprehensive. Focus on clarity and readability to provide researchers with easily digestible insights.
IMPORTANT:
If the user query cannot be answered using the provided context, do not improvise, you should only answer using the provided context from the research papers.
If the user asks something that does not exist within the provided context, Answer ONLY with: 'Sorry, the provided query is not clear enough for me to answer from the provided research papers'.
"""

接下来,我们使用Modelfile创建我们的模型。

ollama create research_assistant -f Modelfile

然后,我们启动模型服务器:

ollama run research_assistant

默认情况下,Ollama运行在 http://localhost:11434 上

最后,我们使用FastAPI创建一个API端点。这个端点将接收一个查询,搜索文档,并返回一个响应。

使用FastAPI的一个优点是它与Pydantic的兼容性,这对于构建我们的代码和API响应非常有帮助。

让我们首先定义两个模型:一个用于查询,一个用于响应:

class Query(BaseModel):
    query: str
    similarity_top_k: Optional[int] = Field(default=1, ge=1, le=5)


class Response(BaseModel):
    search_result: str 
    source: str

在启动llm,qdrant索引和我们的FastAPI应用程序之后:

llm = Ollama(model=config["llm_name"], url=config["llm_url"])
rag = RAG(config_file=config, llm=llm)
index = rag.qdrant_index()


app = FastAPI()

我们将创建一个路由,它接收一个 Query 并返回一个 Response ,如我们的pydantic类中所定义的。

a = "You can only answer based on the provided context. If a response cannot be formed strictly using the context, politely say you don’t have knowledge about that topic"

@app.post("/api/search", response_model=Response, status_code=200)
def search(query: Query):

    query_engine = index.as_query_engine(similarity_top_k=query.similarity_top_k, output=Response, response_mode="tree_summarize", verbose=True)
    response = query_engine.query(query.query + a)
    response_object = Response(
        search_result=str(response).strip(), source=[response.metadata[k]["file_path"] for k in response.metadata.keys()][0]
    )
    return response_object

 来自:app.py的代码

关于在整个项目中使用的配置文件:

data_path: "data/"
llm_url: "http://localhost:11434"
llm_name: "research_assistant"
embedding_model: "sentence-transformers/all-mpnet-base-v2"
qdrant_url: "http://localhost:6333"
collection_name: "researchpapers"
chunk_size: 1024

现在,让我们尝试使用以下请求来测试API:

{
  "query": "How can robots imitate human actions?",
  "similarity_top_k": 3
}

为了测试目的,我们将直接使用FastAPI自带的文档用户界面

API Request

 API 请求

 响应 

API Response

  API 响应

太好了!API能够从我们的文档中检索到相关的上下文,返回一个结构良好的答案,并引用了来源。

 结论

总的来说,该项目的目标是使用LlamaIndex、Qdrant、Ollama和FastAPI创建一个本地的RAG API。这种方法提供了对数据的隐私保护和控制,对于处理敏感信息的组织来说尤其有价值。

不要忘记访问项目的Github仓库

<think>好的,用户问的是LangChain、LlamaIndexollama的区别,需要我用中文解释清楚。首先我得确定这三个工具各自的定位功能,不能混淆。 首先,ollama,我记得是一个本地运行大模型的工具,支持Llama、Mistral等模型。用户下载后可以直接在命令行运行,方便本地测试开发。可能用户会用它来快速部署模型,不需要复杂的环境配置。 然后是LangChain,这个我之前学过,是一个用于构建大语言模型应用的框架。它主要处理链式调用,比如把多个步骤连起来,比如调用模型、处理输出、连接数据库等。LangChain的模块化设计让开发者可以灵活组合不同组件,比如记忆模块、数据检索模块,适合做复杂的应用,比如客服机器人或者数据分析工具。 接下来是LlamaIndex,以前叫GPT Index,专注于数据索引检索。它的核心功能是将外部数据转换成向量索引,方便大模型快速查询。比如用户有自己的文档或数据库,LlamaIndex能高效地建立索引,让模型在回答时引用这些数据。这在需要结合私有数据的场景下特别有用,比如企业内部的知识库系统。 现在需要区分它们的区别。首先,ollama是运行模型的工具,而另外两个是应用开发框架。LangChain更全面,适合构建多步骤的复杂应用,而LlamaIndex专精于数据检索,优化了RAG(检索增强生成)流程。可能用户在选择时需要根据需求:如果只需要快速跑模型,用ollama;要开发复杂应用,用LangChain;需要高效检索私有数据,选LlamaIndex。此外,这三者可以结合使用,比如用ollama运行模型,LangChain处理流程,LlamaIndex管理数据,这样组合起来更强大。 要确保回答结构清晰,分点说明每个工具,再对比区别。注意用词准确,比如ollama是工具,LangChain是框架,LlamaIndex是专注数据索引的库。还要检查LaTeX是否符合要求,但这里可能用不上,不过数学表达式的部分需要留意。用户可能需要知道如何选择这些工具,所以最后给出使用场景的建议会更好。</think>以下是 **LangChain**、**LlamaIndex** **Ollama** 的区别定位解析: --- ### 1. **Ollama** - **定位**:轻量级工具,专注于在本地快速运行部署大语言模型(如 Llama、Mistral 等)。 - **核心功能**: - 提供命令行工具,一键下载运行开源大模型(如 `llama2`, `mistral`, `codellama` 等)。 - 支持模型本地化运行,无需复杂配置,适合开发测试或小规模场景。 - 提供简单的 API,方便与其他工具集成。 - **典型场景**:快速测试开源模型、本地部署轻量级 AI 应用。 --- ### 2. **LangChain** - **定位**:通用框架,用于构建基于大语言模型的复杂应用流程(如 Agents、RAG、自动化任务)。 - **核心功能**: - 模块化设计:提供链(Chains)、工具(Tools)、记忆(Memory)、检索(Retrieval)等组件。 - 支持多模型调用(如 OpenAI、Anthropic、本地模型等)。 - 专注于构建端到端流程,例如结合数据库、API 模型实现自动化任务。 - **典型场景**:开发智能客服、数据分析工具、多步骤决策系统。 --- ### 3. **LlamaIndex** - **定位**:专用工具,优化数据索引与检索(RAG 场景)。 - **核心功能**: - 将外部数据(文档、数据库、API)转换为高效索引(如向量索引、树状索引)。 - 为模型提供结构化数据查询能力,提升回答的准确性相关性。 - 深度集成 LangChain,但更专注于数据检索环节。 - **典型场景**:构建企业知识库、私有数据问答系统、文档摘要生成。 --- ### 三者的区别总结 | 维度 | Ollama | LangChain | LlamaIndex | |---------------|-----------------------|-------------------------------|-------------------------| | **核心目标** | 运行本地大模型 | 构建复杂应用流程 | 优化数据检索与索引 | | **功能重点** | 模型部署与调用 | 流程编排、多工具集成 | 数据连接、高效查询 | | **使用场景** | 本地测试、轻量级应用 | 多步骤任务、自动化系统 | 私有数据问答、RAG 增强 | | **依赖关系** | 独立运行 | 可集成 Ollama/LlamaIndex | 常与 LangChain 配合使用 | --- ### 协作关系举例 1. 用 **Ollama** 在本地运行 `llama2` 模型。 2. 用 **LlamaIndex** 将企业文档转换为向量索引。 3. 用 **LangChain** 编排流程:接收用户问题 → 检索 LlamaIndex 数据 → 调用 Ollama 模型生成回答。 --- 根据需求选择工具: - 仅需运行模型 → **Ollama**。 - 需复杂流程开发 → **LangChain**。 - 需高效检索私有数据 → **LlamaIndex**。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值