为什么挑选这三个组合
- OpenAI官方SDK是Python,此开发言语首选Python
- FastAPI是Python言语编写的高性能的现代化Web结构
- LangChain是AI运用开发的干流结构,能便利的组合各种AI技能进行运用开发
- MemFire Cloud供给Supabase保管,LangChain原生支撑Supabase API
- MemFire Cloud供给向量数据库支撑,向量数据库是开发常识库运用的必选项
FastAPI介绍
FastAPI 是一个用于构建 API 的现代、快速(高性能)的 web 结构,运用 Python 3.6+ 开发。
要害特性:
- 快速:可与 NodeJS 和 Go 并肩的极高性能。最快的 Python web 结构之一。
- 高效编码:进步功用开发速度约 200% 至 300%。
- 更少 bug:削减约 40% 的人为(开发者)导致过错。
- 智能:极佳的编辑器支撑。处处皆可主动补全,削减调试时刻。
- 简略:规划的易于运用和学习,阅览文档的时刻更短。
- 简短:使代码重复最小化。经过不同的参数声明完成丰厚功用。bug 更少。
- 强健:出产可用等级的代码。还有主动生成的交互式文档。
- 规范化:依据(并彻底兼容)API 的相关开放规范:OpenAPI(曾经被称为 Swagger) 和 JSON Schema。
官方文档:fastapi.tiangolo.com/zh/
ChatGPT介绍
准确来说ChatGPT只是openai依据GPT模型开发的一个运用,只不过这个词更盛行,更广为人知。对于开发者来说,更准确的说法是GPT模型。现在openai供给的模型包括:
模型 | 描述 |
---|---|
GPT-4 | GPT-3.5的优化版别,了解才能和生成自然言语和代码的才能更好 |
GPT-3.5 | GPT-3的优化版别 |
DALLE | 文生图AI模型 |
Whisper | 音频转文本AI模型 |
Embeddings | 文本转向量模型 |
Moderation | 灵敏词检测模型,用以检查用户的输入是否合规 |
每个模型下面又别离有许多细分的模型,在文本和代码生成场景,官方引荐运用两个模型:gpt-3.5-turbo
or gpt-4
,本文运用现在本钱更优的gpt-3.5-turbo
。相对应的, gpt-4
能了解更杂乱的指令,也会尽可能不胡说八道,但是 gpt-4
本钱要高一些,推理速度要慢一些。
GPT模型的运用场景:
- 编写各种类型的文档,包括学术论文
- 生成代码
- 依据常识库进行问题答复
- 剖析文本,包括阅览论文、解读代码等都能胜任
- 对话东西,比方客服
- 为软件供给自然言语接口
- 教育导师
- 翻译
- 游戏人物AI
openai的更多资源请参阅:
platform.openai.com/docs
github.com/openai/open…
MemFire Cloud介绍
本文首要运用了MemFire Cloud的BaaS服务供给的数据库主动生成API以及向量数据库才能,用以存储和检索embedding之后的向量数据。MemFire Cloud的BaaS服务还供给了其他一些便利开发者进行运用开发的功用:
- 完好的用户注册、登录、权限办理API,web前端和App开发人员能够直接调用,几行代码完结用户注册登录功用。
- 微信小程序、手机短信验证码、github、apple等第三方用户接入支撑。
- 最盛行的postgres联系数据库支撑,主动生成增删查改API接口,支撑向量化插件pgvector。
- 供给目标存储接口,便利运用开发者上传下载图片、文档等。
- 静态保管和自界说域名服务,能够让开发者不需求为布置前端代码专门购买云服务器。
- 云函数能够便利处理杂乱和灵敏的业务逻辑,也能够用来保管完好的api服务。
MemFire Cloud更多信息,请参阅:memfiredb.com
GPT初体验
下面是openai官方的一个比方:
importos
importopenai
openai.organization="org-kjUiGhsu6S3CI2MUch25dMOF"
openai.api_key=os.getenv("OPENAI_API_KEY")
openai.ChatCompletion.create(
model="gpt-3.5-turbo",
messages=[
{"role":"system","content":"Youareahelpfulassistant."},
{"role":"user","content":"Whowontheworldseriesin2020?"},
{"role":"assistant","content":"TheLosAngelesDodgerswontheWorldSeriesin2020."},
{"role":"user","content":"Wherewasitplayed?"}
]
)
参数必填项:
- model:模型挑选,这儿用的是gpt-3.5-turbo
- messages:信息,能够只包括一个问题,也能够包括更多上下文,以协助AI更准确的答复问题。
AI开发的最首要的工作便是组装合适的messages,以到达更准确的答复用户问题的意图。一条message包括role和content两个元素。其中role包括:
- system:相当于体系设定,一般用来告诉ai他要扮演什么人物,怎么答复问题等。
- assistant:辅佐信息,协助AI更好的了解用户的当时问题。一般用于包括多轮对话的上下文信息,也能够用来给出一些问答样例。
- user:用户的发问内容。
FastAPI编写后端服务
假如你运用过FastAPI,能够越过本节内容。
假如你有flask或django的根底,强烈主张阅览官方的这两个教程,能够协助你快速了解fastapi的运用办法。
fastapi.tiangolo.com/zh/tutorial…
fastapi.tiangolo.com/zh/tutorial…
假如你是web服务的新手,主张从头阅览fastapi的教程文档,写的十分好。
fastapi.tiangolo.com/zh/tutorial…
咱们先以上面openai的官方示例,来看一下怎么运用fastapi来编写服务端代码,完结与openai的交互,并暴露接口给你的web或app客户端运用。为了便利,咱们将一切代码放在一个main.py中演示,假如你要完成一个完好的运用,主张参阅大型运用开发这篇教程,模块化安排你的代码。
接口界说
一个简略的可运转的接口界说:
fromfastapiimportFastAPI
app=FastAPI()
@app.get("/hello")
asyncdefhello():
return{"message":"你好"}
装置下列依靠就能够运转了:
#装置依靠
pipinstall"uvicorn[standard]==0.23.1""fastapi[all]==0.99.1"
#运转服务
uvicornmain:app--reload
拜访接口:
curlhttp://127.0.0.1:8000/hello
封装openai调用接口
了解了fastapi的接口界说办法,咱们就能很快将openai的官方示例封装成接口:
fromfastapiimportFastAPI
importopenai
app=FastAPI()
openai.organization="org-kjUiGhsu6S3CI2MUch25dMOF"
openai.api_key=os.getenv("OPENAI_API_KEY")
@app.get("/openai")
asyncdefopenai():
returnopenai.ChatCompletion.create(
model="gpt-3.5-turbo",
messages=[
{"role":"system","content":"Youareahelpfulassistant."},
{"role":"user","content":"Whowontheworldseriesin2020?"},
{"role":"assistant","content":"TheLosAngelesDodgerswontheWorldSeriesin2020."},
{"role":"user","content":"Wherewasitplayed?"}
]
)
过错处理
在调用openai的接口时,假如产生过错,openai会抛出反常。在FastAPI的服务代码中,假如咱们不处理openai的反常,FastAPI会将反常抛出,并回来客户端500 Internal Server Error。一般咱们需求以更结构化的办法将详细的过错信息回来给接口调用者。
FastAPI供给了反常处理的回调接口,以openai的反常为例,能够经过如下办法注册反常处理函数,以更友爱、一致的结构回来过错信息:
fromfastapiimportFastAPI,Request
fromfastapi.responsesimportJSONResponse
fromopenaiimportOpenAIError
app=FastAPI()
#捕获反常,并回来JSON格局的过错信息
@app.exception_handler(OpenAIError)
asyncdefopenai_exception_handler(request:Request,exc:OpenAIError):
returnJSONResponse(
status_code=exc.http_status,
content={'code':exc.code,'message':exc.error},
)
了解了FastAPI的api界说办法和过错处理办法,咱们根本上能够完结一个简略的web服务程序了。
依据openai开发常识库运用的根本原理
LangChain的文档中对QA场景的AI运用开发有比较详细的讲解,感兴趣的能够深入阅览:
python.langchain.com/docs/use_ca…
下图是常识库类AI运用的根本开发流程:
从图中能够看到,大致能够分为如下过程:
- 文档处理:将pdf、docx、数据库中的数据转换成文本内容。现在openai的chat模型接纳的都是文本数据。
- 数据切分:将处理过后的文本内容进行切分。切分的意图是便利语义化检索,让检索内容更准确,别的也是为了适配AI接口的约束。gpt-3.5-turbo的最大tokens数量是4k,gpt-3.5-turbo-16k也只要16k,因而文档切分是必需的过程。
- 向量化:openai供给了embedding模型,专门用来将文本转换成向量化数据,向量化的意图是便利后续依据用户的输入来检索相似度高的常识。这儿就需求用到向量数据库来存储embedding模型的输出。 以上是常识库处理过程,这一部分一般是运转在运用的后台,需求继续不断的获取最新的常识(比方最新的产品文档、技能手册),并更新向量数据库中的数据。
接下来是用户问答的处理流程:
- 常识检索:将用户的问题向量化,然后到向量化数据库中检索相似度最高的常识(能够依据需求选取相似度高的前n项)。
- AI辅佐解答:将检索到的常识以及设定好的prompt信息一同发送给ai模型,ai模型会结合自己已有的常识以及常识库中检索到的常识进行终究的答案生成。
LangChain根本用法
依据上面的流程,咱们彻底能够自主的完成一个ai运用了,为什么要引进LangChain呢?
假如你通读了LangChain的文档(python.langchain.com/docs/use_ca…
- 数据加载才能
在上面的过程中,咱们首先要完结的是将已有的常识文档处理成文本数据。LangChain现在现已内置类十分多的文档类型处理才能,包括常见的pdf、docx、markdown、html、json、csv等,同时兼容了一百多种数据源,几乎包括了市面上一切最常用的服务,包括S3、Bilibili、EverNote、Github、Hacker News、Slack、Telegram等等。
下面是加载Web数据的WebBaseLoader的运用办法:
fromlangchain.document_loadersimportWebBaseLoader
loader=WebBaseLoader("https://lilianweng.github.io/posts/2023-06-23-agent/")
data=loader.load()
- 数据切分才能
LangChain供给了文本切分东西,能够便利的将加载后的文本进行切分处理。上面将网页内容加载到data目标之后,能够运用RecursiveCharacterTextSplitter进行文本切分:
fromlangchain.text_splitterimportRecursiveCharacterTextSplitter
text_splitter=RecursiveCharacterTextSplitter(chunk_size=500,chunk_overlap=0)
all_splits=text_splitter.split_documents(data)
- 向量化才能
LangChain支撑常见的向量化数据库以及embedding模型接入才能,以MemFire Cloud保管的SupaBase和openai embedding模型为例(参阅python.langchain.com/docs/integr…
importos
fromsupabase.clientimportClient,create_client
fromlangchain.vectorstoresimportSupabaseVectorStore
fromlangchain.embeddingsimportOpenAIEmbeddings
supabase_url=os.environ.get("SUPABASE_URL")
supabase_key=os.environ.get("SUPABASE_SERVICE_KEY")
client:Client=create_client(supabase_url,supabase_key)
vector_store=SupabaseVectorStore.from_documents(
all_splits,OpenAIEmbeddings(),client=client)
要运用LangChain + pgvector的向量化数据库才能,需求在MemFire Cloud上创立运用,并开启vector扩展,然后创立documents表和函数。能够运用SQL语句完结这些操作:
--Enablethepgvectorextensiontoworkwithembeddingvectors
createextensionvector;
--Createatabletostoreyourdocuments
createtabledocuments(
idbigserialprimarykey,
contenttext,--correspondstoDocument.pageContent
metadatajsonb,--correspondstoDocument.metadata
embeddingvector(1536)--1536worksforOpenAIembeddings,changeifneeded
);
CREATEFUNCTIONmatch_documents(query_embeddingvector(1536),match_countint)
RETURNSTABLE(
iduuid,
contenttext,
metadatajsonb,
--wereturnmatchedvectorstoenablemaximalmarginalrelevancesearches
embeddingvector(1536),
similarityfloat)
LANGUAGEplpgsql
AS$$
#variable_conflictuse_column
BEGIN
RETURNquery
SELECT
id,
content,
metadata,
embedding,
1-(documents.embedding<=>query_embedding)ASsimilarity
FROM
documents
ORDERBY
documents.embedding<=>query_embedding
LIMITmatch_count;
END;
$$;
- 常识检索
上面介绍LangChain组合openai embedding 和 pgvector进行向量化处理和存储,LangChain的vectorstore能够直接完成向量化检索功用,将用户的问题转换为最切近的常识库数据:
query="Howtobuildllmautoagent"
matched_docs=vector_store.similarity_search(query)
matched_docs便是与用户发问相关性最高的的常识库内容。
接下来就能够将matched_docs + 用户的发问打包提交给AI,让AI帮咱们生成终究的答案了。
怎么将常识库和用户问题打包提交给openai
在GPT初体验章节,咱们现已介绍了GPT接口的运用办法和参数含义,这儿咱们能够运用assistant人物将咱们的常识库打包成messages,然后将用户的问题以user人物打包到messages中,最终调用openai的接口:
messages=[
{"role":"assistant","content":doc.page_content}fordocinmatched_docs
]
messages.append({"role":"user","content":query})
response=openai.ChatCompletion.create("gpt-3.5-turbo",messages=messages)
你也能够将文档和问题悉数打包成user人物的message,大概的格局:
content='\n'.join[doc.page_contentfordocinmatched_docs]
content+=f'\n问题:{query}'
messages=[
{"role":"user","content":content}
]
response=openai.ChatCompletion.create("gpt-3.5-turbo",messages=messages)
怎么让AI生成更契合预期的结果
咱们都知道,ChatGPT有的时分会胡说八道,一般会产生在GPT模型中没有相关的常识的时分。为了让AI答复的更谨慎,咱们能够给AI一些清晰的指令,比方:
docs='\n'.join[doc.page_contentfordocinmatched_docs]
content=f"'''{docs}'''"
content+=f'\n问题:{query}'
messages=[
{"role":"system","content":"我会将文档内容以三引号(''')引起来发送给你,假如你无法从我供给的文档内容中找到答案,请答复:"我无法找到该问题的答案"。请运用中文答复问题。"},
{"role":"user","content":content}
]
response=openai.ChatCompletion.create("gpt-3.5-turbo",messages=messages)
这儿有一份GPT的最佳实践(platform.openai.com/docs/guides…
运用MemFire Cloud作为AI的记忆体
openai的GPT模型本身是没有记忆力的,假如咱们期望常识库运用能像ChatGPT相同跟运用者进行接连的对话,需求让咱们的运用有记忆才能,并将记忆的信息在下一次对话时发送给openai的模型,以便模型了解前面跟用户聊了些什么。
别的openai的接口是有token约束的,当接连对话的内容超出了一次api调用的token约束时,需求压缩前史对话信息。有两种压缩办法:
办法1:让openai提炼前史对话的概要信息,然后运用概要信息加最新问题进行问答;
办法2:从前史对话中检索与最新问题相关性比较高的内容,发送给openai; 不管哪种办法,你都需求对当时会话的前史数据进行记载:
- 办法1: 需求记载不断迭代的摘要信息。因为token数量约束,你不能一次性获得一切前史对话的摘要,因而需求不断的叠加前史摘要和最新对话数据,生成新的摘要并以会话id为标识,记载到数据库中。
- 办法2: 需求将前史对话的信息经过embedding模型向量化,要并为每个会话构建上下文常识库索引,检索的时分,只检索当时会话的相似内容。
Supabase SDK供给了十分便利的操作数据库的接口,以下为记载会话前史音讯的表以及根本的操作办法:
--前史音讯表界说
createtablehistory_messages(
idbigserialprimarykey,
session_idtext,--会话id
roletext,--会话人物(userorai)
messagetext,--会话信息
create_attimestampdefault(now())--创立时刻
)
操作前史音讯表的办法:
importos
fromsupabase.clientimportClient,create_client
fromlangchain.vectorstoresimportSupabaseVectorStore
#初始化客户端
url=os.environ.get("SUPABASE_URL")
key=os.environ.get("SUPABASE_SERVICE_KEY")
client:Client=create_client(url,key)
#往会话xxxxx插入一条前史音讯
client.table("history_messages").insert({"session_id":"xxxxx","role":"user","message":"你好"}).execute()
#查询会话id是xxxxx的一切前史音讯
client.table("history_messages").select("*").eq("session_id","xxxxx").execute()
完好代码
前面咱们讲解了运用FastAPI+SupaBase+LangChain进行GPT常识库开发的根本原理和要害途径的代码完成。完好的完成代码现已上传到了github,感兴趣的能够自己玩一下:
GitHub – iswarezwp/supabase-qa: 运用FastAPI+Supabase+LangChain开发GPT运用(地址:github.com/iswarezwp/s…)
代码首要完结了如下一些根本功用:
- 运用FastAPI作为Web服务端结构完结了根本的Web服务端开发
- 运用MemFire Cloud作为向量数据和个人文档数据存储
- 运用LangChain进行AI运用开发,加载本地磁盘目录上的文档,计算embedding、存储到向量数据库
- 运用OpenAI的GPT模型,完结问答功用完成
- 运用Next.js开发了一个简略的UI界面用于问答演示
下面首要介绍一下怎么布置运用该代码。
准备工作
- 首先咱们需求一个SupaBase运用
MemFire Cloud供给了SupaBase运用的保管,因而能够先在MemFire Cloud上创立运用,后边需求用到运用的API URL和Service Role Key。能够在运用的运用设置->API页面找到相应的装备。
- 创立运用后,在运用的SQL执行器页面执行如下脚本
--Enablethepgvectorextensiontoworkwithembeddingvectors
createextensionvector;
--Createatabletostoreyourdocuments
createtabledocuments(
iduuidprimarykey,
contenttext,--correspondstoDocument.pageContent
metadatajsonb,--correspondstoDocument.metadata
embeddingvector(1536)--1536worksforOpenAIembeddings,changeifneeded
);
CREATEFUNCTIONmatch_documents(query_embeddingvector(1536),match_countint)
RETURNSTABLE(
iduuid,
contenttext,
metadatajsonb,
--wereturnmatchedvectorstoenablemaximalmarginalrelevancesearches
embeddingvector(1536),
similarityfloat)
LANGUAGEplpgsql
AS$$
#variable_conflictuse_column
BEGIN
RETURNquery
SELECT
id,
content,
metadata,
embedding,
1-(documents.embedding<=>query_embedding)ASsimilarity
FROM
documents
ORDERBY
documents.embedding<=>query_embedding
LIMITmatch_count;
END;
$$;
- 准备好用来测试的文档目录
默认需求将文档放到app/docs
下,能够经过环境变量指定其他目录
- 准备好openai的账号 请参阅网上教程请求一个openai账号,后边代码运转需求用到openai的API KEY
怎么运转
linux 下运转
- 装置依靠
pipinstall-rapp/requirements.txt
- 设置参数 SUPABASE_URL/SUPABASE_KEY别离对应
运用URL
和service_role
密钥。注意service_role秘钥具有比较高的数据库操作权限,只能用于服务端装备,不要走漏。
exportDOCS_PATH=./docs
exportSUPABASE_URL="your-api-url"
exportSUPABASE_KEY="your-service-role-key"
exportOPENAI_API_KEY="your-openai-api-key"
- 运转
uvicornmain:app--reload--host0.0.0.0
- docker运转
dockerbuild-tmemfirecloud-qa:v1.
dockerrun-p8000:80\
-eSUPABASE_URL="your-api-url"\
-eSUPABASE_KEY="your-service-role-key"\
-eOPENAI_API_KEY="your-openai-api-key"\
-v./docs:/docs\
memfirecloud-qa:v1
windows下运转
与linux相似,设置相关环境变量,然后运转:
uvicornmain:app--reload--host0.0.0.0
怎么拜访
用浏览器拜访: http://your-ip:8000/
能够显示一个简陋的问答页面
支撑的参数装备
#本地文档途径
exportDOCS_PATH=./docs
#memfirecloud运用的APIURL和Servicerolekey
exportSUPABASE_URL="your-api-url"
exportSUPABASE_KEY="your-service-role-key"
#运用openai/baidu的大模型
exportQA_BACKEND="openai"#默认值
#openai相关装备(QA_BACKEND=openai是需求)
exportOPENAI_ORGANIZATION="your-openai-organization"
exportOPENAI_API_KEY="your-openai-api-key"
exportOPENAI_MODEL="gpt-3.5-turbo"#默认值
#百度相关装备(QA_BACKEND=baidu时需求)
exportBAIDU_API_KEY="your-baidu-api-key"
exportBAIDU_API_SECRET="your-baidu-api-secret"
exportBAIDU_MODEL="ERNIE-Bot-turbo"#默认值
接下来能够做的事情
- 过滤掉重复文档,防止运用重启或者增加重复文档是重新计算embedding
- 程序运转中支撑增量增加新文档,实时更新常识库
- 支撑对话(chat),现在只是问答(QA),不能接连对话
- 支撑百度文心一言接口(已完结api的封装)