RAG与LLM
date
Mar 8, 2024
slug
RAG和LangchainChatchat
status
Published
tags
LLM
summary
type
Post
引言
今天重点讨论RAG,首先我会根据实际例子引出RAG是什么,然后介绍一个很火的project:
RAG是什么
LLM火起来之后,一个常见想法就是打造一个私有化的LLM,作为自己的第二个大脑。比如说把LLM应用在我们的笔记上面,就可以围绕着笔记来做一些讨论;把LLM跟公司业务文档集合,就能打造一个公司的chatgpt,可以回答各种业务问题。
因为LLM训练的时候并没有用到私有化的资料,想要让LLM学习到私有化的知识,我们可以做微调,但是微调的成本太高了,而且很难把握微调的效果,于是就有另一种想法,就是把文档“外挂”在LLM上,让LLM回答的时候参考这些文档,这让就能让LLM间接的掌握私有化的知识。
比如说chatgpt并不知道我们公司业务,但是我们可以要求它回答的时候参考若干个文档,LLM就能用自己的一般(general)能力来检索出相关的知识,然后给你一个贴切的回答。
市面上已经有很多类似的产品和功能了,例如chatPDF,回答的时候可以参考你指定PDF文件。
上述这个过程,专业的角度来说叫做Retrieval Augmented Generation (RAG) 。其实现阶段的LLM有几个很突出的缺点:
- hallucination,幻觉,胡编乱造答案
- domain knowledge gaps,缺乏领域知识,可能是常识的专家,但是对特定领域的问题能力有限
- factuality issues,误导性问题,例如生成非常具有误导性的新闻和历史
这几个问题都很致命,目前业界有两个解决方案:
- Long context:把LLM可以容纳的上下文做得无比巨大,当这个上下文趋于无限的时候,上述的缺点不复存在。例如最近的Calude-3能容纳200k的context。
- RAG:提供一系列的文档,让LLM的回答重点参考这些文档。
简单来说,RAG通过将文档整理知识库,然后通过适当的prompt来要求LLM在推理的时候检索这些知识库,这样就能在不重新训练的情况下让LLM掌握特定知识,对以上三个缺点都有缓解。
如上图所示,我们先准备好Document store,也就是我们的知识库。然后通过prompt的方式来要求LLM对知识库进行检索,大概检索到相关的文章后,从这些文章中生成一个自然的回答给用户。
如果熟悉推荐系统的话,应该能想到整个检索过程其实和召回、粗排、精排很相似,都是筛选出高度相关的物品。
下面是一个例子:
用户提问LLM,怎么看待Altman突然从OpenAI离职,如果是常规的LLM,它并不能做出这个回答,因为离职这件事情发生在它训练完成之后,LLM并没有相关知识,也就无法进行推理。但是我们如果使用RAG,可以提供一些相关的新闻或者资料给LLM,LLM如果从中检索出重点信息,那么就可以根据这些重点信息来做出回答。
Langchain-Chatchat
chatchat做了很多工程工作,能够用最低成本的方式实现私有化的LLM。支持多种开源的LLM,默认是清华出品的ChatGLM3-6B。
它在最基础的部署LLM并进行回答的工作上,进一步提供了一个知识库问答,而且还支持API调用,可以说是非常好用的。
如上图所示,这个项目里面有一个知识库对话的功能,其实就是最基础的RAG,它可以让LLM从给定文本中回答问题。
我们可以看一下它的实现思路:
首先这是基本处理:
- 使用Unstructured Loader得到文本(Text)
- 在文本的基础上使用Splitter进行分块
- 将文本块(Chunks)进行embedding,得到向量
- 将向量存储到向量数据库中
然后当用户询问的时候:
- 将询问向量化
- 向量化后,得到需要查询的表示,即Query Vector
- 计算向量相似度
- 检索到相关的文本块
- 使用prompt来将这些文本块包含进去,让LLM参考这些回答
- LLM在相关文本块的基础上进行推理,回答问题
从文档角度来看:
本地配置了一下,效果还是不错的,可以在它上面二次开发。
代码
来研究一下代码吧。
整个项目使用了FastAPI来创建的,快速启动的入口在
startup.py
,下面是主函数入口:其中
create_tables()
是用来创建DB的,这里用了sqlalchemy这个包,DB我们后面再看,先看看整个服务是怎么启动的。start_main_server
负责启动整个服务,下面是流程图:这里一共启动了两个server,分别是API server和WebUI Server,UI使用streamlit生成的。
然后还有三个process,用于响应请求,分别是:
- OpenAI API process
- Model Worker process
- API Worker process
先看两个server:
API server
我太久没写python了,这种随时随地import的操作正常吗?
uvicorn 实现了轻量的ASGI服务,ASGI是 Asynchronous Server Gateway Interface,我没接触过,感觉就是一个高性能的异步服务器吧。
看了一眼,
set_httpx_config()
是用来设置httpx的,设置了超时时间,proxy等内容。httpx
是一个python3的HTTP客户端。接下来到
create_app()
,就是创建一个FastAPI的实例,其中又引用了MakeFastAPIOffline
和mount_app_routes
MakeFastAPIOffline
太长了,让gpt总结一下:The
MakeFastAPIOffline
function modifies a FastAPI application to serve its documentation pages (Swagger UI and ReDoc) from local static files instead of fetching them from a Content Delivery Network (CDN). 把需要联网获取的资源全部换成静态生成的。另外这里用了ReDoc,,可以根据OpenAPI生成漂亮的文档。
redoc
Redocly • Updated Mar 10, 2024
看下一个函数:
mount_app_routes
This
mount_app_routes
function is used to define the routes (or endpoints) for a FastAPI application. Each route corresponds to a URL path and an HTTP method (like GET or POST). When the application receives a request at a given path with a given method, it will call the associated function.画风大概是这样:
比方说最后一个函数,就是定义一个函数,然后使用FastAPI包装成一个接口,响应用户请求。
ok,我们到这里基本看完了API Server,让gpt总结一下它做了什么工作:
Based on the routes defined in the
mount_app_routes
function, here's a summary of the tasks likely performed by the run_api_server
function:- Initialize the FastAPI application: The first step is to create an instance of the FastAPI application which will be used to define the routes and start the server.
- Mount Routes: The
mount_app_routes
function is called to define the routes for the application. These routes include endpoints for chat, knowledge base, file summary, LLM model management, server state, and other functionalities.
- Start the Server: Once the routes are defined, the FastAPI server is started. This makes the application available to receive HTTP requests at the defined routes.
…
Web UI server
let’s see this server.
这个函数就是用来启动streamlit的web user interface:
接下来看下API的代码:
OpenAI API process