主页 > 其他  > 

langchain学习笔记之langserve服务部署

langchain学习笔记之langserve服务部署

langchain学习笔记之langserve服务部署 引言 LangServe \text{LangServe} LangServe简单介绍安装过程示例应用调用模型接口实现交互使用 Requests \text{Requests} Requests方式进行交互 附: server.py \text{server.py} server.py完整代码

引言

本节将介绍 LangServe \text{LangServe} LangServe服务部署。

LangServe \text{LangServe} LangServe简单介绍

LangServe \text{LangServe} LangServe帮助开发者将langchain可运行的chain部署为REST API,该库底层使用FAST API并使用pydantic进行数据验证。

关于pydantic,它是一个 Python \text{Python} Python中用于数据验证和数据解析的第三方库,并且可以从模型中生成 JSON \text{JSON} JSON架构,并提供自动生成文档等功能,从而轻松与其他模型进行集成。

安装过程 对于客户端与服务器, LangServe \text{LangServe} LangServe的安装指令 pip install --upgrade "langserve[all]"

单独对于客户端或者服务器端代码,则执行:

pip install "langchain[client]" pip install "langchain[server]" LangChain CLI \text{LangChain CLI} LangChain CLI安装:该工具可以快速启动LangServe项目。安装指令: pip install -U langchain-cli

安装完毕后,在terminal中输入langchain,可以查看其相关操作: 在项目路径下,使用指令:

langchain app new [app_name] # app_name 为想要设置的app名称

在当前路径下会生成一个文件夹: 接下来,安装pipx:

pip install pipx

将pipx添加到环境变量,执行完该操作后,需重启 PyCharm \text{PyCharm} PyCharm:

pipx ensurepath

正确的返回结果:

Success! Added C:\Users\dell\.local\bin to the PATH environment variable. Consider adding shell completions for pipx. Run 'pipx completions' for instructions. You will need to open a new terminal or re-login for the PATH changes to take effect. Alternatively, you can source your shell's config file with e.g. 'source ~/.bashrc'. Otherwise pipx is ready to go! ✨ 🌟 ✨

接下来,使用pipx安装poetry:

pipx install poetry

值得庆幸的是,pipx同样会使用PyPI作为包的来源,因而可以使用镜像源进行安装。例如:

pipx install poetry -i mirrors.aliyun /pypi/simple/

正确的返回结果:

installed package poetry 2.0.1, installed using Python 3.10.1 These apps are now globally available - poetry.exe done! ✨ 🌟

同样,在terminal中输入poetry,可以查看其相关操作: 接下来,使用poetry指令安装相关库。根据上一节使用的 Qwen \text{Qwen} Qwen模型,这里安装的是与 Qwen \text{Qwen} Qwen模型相关的库: 先装着,用不用得到以后再说~

poetry add langchain poetry add langchain-community poetry add langchain-core poetry add dashvector

如果安装成功,刚才安装的APP文件夹中的pyproject.toml文件中,会将安装库的版本显示在内:

[tool.poetry.dependencies] python = "^3.11" uvicorn = "^0.23.2" langserve = {extras = ["server"], version = ">=0.0.30"} pydantic = ">=2.7.4" langchain = "^0.3.18" langchain-community = "^0.3.17" langchain-core = "^0.3.35" dashvector = "^1.0.18"

一个小坑:上面部分初始状态下的结果展示如下:

[tool.poetry.dependencies] python = "^3.11" uvicorn = "^0.23.2" langserve = {extras = ["server"], version = ">=0.0.30"} pydantic = "<2"

其中python版本我们按照它的要求,将当前conda环境内的版本升级到了3.11.11;但在使用poetry安装上述包时,依然出现错误。主要原因是:当前的langchain版本0.3.18与上述pydantic = "<2"产生版本冲突,需要手动将上面的pydantic版本改成与langchain相匹配的版本,我这里设置的是pydantic = ">=2.7.4"。

设置环境变量: 关于 Qwen \text{Qwen} Qwen模型,依然要导入申请的dashscope-api-key:

export DASHSCOPE_API_KEY = "sk-..." # 自行申请的api-key

若将api-key写死在本机中(计算机右键属性,高级系统设置,环境变量,添加api-key),此条可以忽略

启动应用。在terminal中使用poetry执行如下指令:

poetry run langchain serve --port=8000 # 端口号可自行分配

需要注意的是,需要在pyproject.toml所在文件夹位置执行该指令,即上步骤中创建app文件夹的路径内。正确返回结果如下: 打开上述链接,得到如下结果: 这里包含创建的routes以及相应的功能接口。

示例应用

想要部署一个 Qwen \text{Qwen} Qwen聊天模型,在使用langchain app new [app_name]指令时,在app文件夹中,自动生成一个server.py文件:

from fastapi import FastAPI from fastapi.responses import RedirectResponse from langserve import add_routes app = FastAPI() @app.get("/") async def redirect_root_to_docs(): return RedirectResponse("/docs") # Edit this to add the chain you want to add add_routes(app, NotImplemented) if __name__ == "__main__": import uvicorn uvicorn.run(app, host="0.0.0.0", port=8000)

可以在该界面中对app自身参数信息进行完善。例如:

app = FastAPI( title="langchain server", version="0.1.0", description="A server for running Langchain with FastAPI", )

对应的效果如下:

调用模型接口实现交互

第一步:在server.py文件中,添加一个routes:

llm = Tongyi( model_name="tongyi-7b-chinese", temperature=0.5, max_tokens=100) add_routes( app, llm, path="/tongyi-7b-chinese")

请记住这个path,在创建routes完毕之后,若重新执行server.py或者在terminal执行指令poetry run langchain serve --port=8000,都可以看见一个playgound被生成: 进入该路径:http://localhost:8000/tongyi-7b-chinese/playground/可以得到一个在线的基于tongyi-7b-chinese模型的问答系统~ 言归正传~ 创建一个client.py文件,代码如下:

from langserve import RemoteRunnable p = 8000 model_loc = "tongyi-7b-chinese" # http://127.0.0.1:8000/docs#/tongyi-7b-chinese/ def get_remote_runnable_url(port_input, model_loc_input): url_output = 'http://localhost:{}/{}/'.format(str(port_input), model_loc_input) return url_output tongyi = RemoteRunnable( get_remote_runnable_url(port_input=p, model_loc_input=model_loc) )

引入一个类RemoteRunnable来远程调用模型,这里的model_loc之前add_routes中的path相同。此时变量tongyi可看作是一个模型,在构建chain的过程中,调用RannableMap类对变量进行描述:

from langchain.schema.runnable import RunnableMap chain = prompt | RunnableMap( { "tongyi": tongyi } )

其余部分,如prompt,chain的调用均正常。client.py完整代码如下:

from langchain.schema.runnable import RunnableMap from langchain_core.prompts import ChatPromptTemplate from langserve import RemoteRunnable p = 8000 model_loc = "tongyi-7b-chinese" # http://127.0.0.1:8000/docs#/tongyi-7b-chinese/ def get_remote_runnable_url(port_input, model_loc_input): url_output = 'http://localhost:{}/{}/'.format(str(port_input), model_loc_input) return url_output tongyi = RemoteRunnable( get_remote_runnable_url(port_input=p, model_loc_input=model_loc) ) prompt = ChatPromptTemplate( [ ("system", "你的名字是静静的喝酒,你是一个喜欢做科普的助手"), ("user", "简单介绍一下{input},字数限制:200") ] ) chain = prompt | RunnableMap( { "tongyi": tongyi } ) response = chain.stream({ "input": "林徽因" }) for chunk in response: print(chunk["tongyi"], end="", flush=True)

返回结果如下,通过流式调用stream生成出的结果:

林徽因(1904-1955),原名林徽音,中国著名建筑师、诗人、作家。她出生于浙江杭州,父亲林长民是民国时期的重要官员。林徽因自幼聪慧过人,16岁赴英留学,后转入美国宾夕法尼亚大学学习建筑。 她与梁思成共同致力于中国古代建筑的研究和保护工作,参与了多处重要古建筑的考察与测绘。同时她也是中国第一位女性建筑学家,在建筑设计领域有着卓越贡献。 林徽因在文学上也有很高造诣,著有散文集《你是人间四月天》等作品,诗文优美动人。她的才华横溢,不仅体现在专业领域,在社交场合也备受尊敬,是中国现代文化史上一位杰出的女性。 Process finished with exit code 0

当然,也可以使用invoke等其他方式进行调用,这里不再展示。

使用 Requests \text{Requests} Requests方式进行交互

相比于使用Runnable调用模型进行交互,还可以使用requests直接进行交互,这种交互方式的特点是:调用部分客户端代码异常简单。仅需要知道url路径以及相应格式的输入即可。创建一个新的文件:client_req.py,分别以invoke调用与stream调用为例:

在此之前,需要在server.py中添加一个新的routes,并创建一个新的path:

prompt = PromptTemplate.from_template("请介绍一下{topic},200字") add_routes( app, prompt | llm, path="/tongyi-7b-chinese-chain" )

和调用模型接口交互的操作相比,主要区别是:将prompt,chain的部分交给了server.py去做。

invoke调用。对应代码如下: response_invoke = requests.post( url="http://localhost:8000/tongyi-7b-chinese-chain/invoke", json={ "input": { "topic": "林徽因" } } )

可以发现:url路径中多了\invoke,这是langchain serve提供的接口: 将其展开,可以得到要求输入的格式以及输出格式: 上面的json格式就是基于该输入格式构建的。输入结果也为json格式。基于上述输入,输入结果如下。由于格式已经确定,完全可以获取更精确的信息(例如:只展示output部分),这里不再赘述。

print(response_invoke.json()) {'output': '林徽因(1904年6月10日-1955年4月1日),原名林徽音,中国著名建筑师、诗人、作家。她出生于福建闽侯,在文学和建筑领域均有所建树。\n\n林徽因是第一位女性建筑学家,曾参与设计了中华人民共和国国徽和人民英雄纪念碑。她的建筑设计融合了传统与现代元素,独具匠心。同时,她也是新月派诗人群体中唯一的女性成员,著有《你是人间四月天》等脍炙人口的诗作。其作品风格清新婉约,充满灵性,表达了对生活的热爱以及对理想的追求。\n\n林徽因以其卓越才华和独特魅力,在近现代中国文化史上留下了浓墨重彩的一笔。', 'metadata': {'run_id': '093d5777-d19c-4bc5-9b53-d1e508765661', 'feedback_tokens': []}} stream调用:同上,url路径中同样包含\stream接口,以及输入、输出格式。对应代码表示如下: response_stream = requests.post( url="http://localhost:8000/tongyi-7b-chinese-chain/stream", json={ "input": { "topic": "林徽因" } } )

比较特殊的是,stream调用返回的结果类型是string,在处理数据上,例如:将返回结果有效信息提取出来,需要消耗一些代码:

for l in response_stream.iter_lines(): line = l.decode("utf-8") if line.startswith("data:"): if "run_id" in line: continue else: print(line[len("data: ") + 1:-1], end="", flush=True)

返回结果如下:

林徽因(1904年6月10日-1955年4月1日),原名林徽音,中国著名建筑师、诗人、作家。她出生于福建闽侯,成长于一个书香门第,自幼聪慧过人。1920年随父游历欧洲,深受西方文化影响。1924年赴美留学,主修建筑学,后与梁思成结为夫妇,共同致力于中国古代建筑的研究和保护。\n\n林徽因不仅在建筑领域成就斐然,还是一位才华横溢的文学家。她的诗歌清新脱俗,散文优美动人,代表作有《你是人间四月天》等。此外,她积极参与社会活动,为中国古代建筑艺术的传承与发展做出了重要贡献。林徽因的一生短暂而辉煌,她的思想和作品至今仍被广泛传颂。

client_req.py完整代码如下:

import requests import json # invoke_method response_invoke = requests.post( url="http://localhost:8000/tongyi-7b-chinese-chain/invoke", json={ "input": { "topic": "林徽因" } } ) print(response_invoke.json()) # stream_method response_stream = requests.post( url="http://localhost:8000/tongyi-7b-chinese-chain/stream", json={ "input": { "topic": "林徽因" } } ) print("stream result..") # 只是一个相应结果,200表示成功;422表示失败 print(response_stream) for l in response_stream.iter_lines(): line = l.decode("utf-8") if line.startswith("data:"): if "run_id" in line: continue else: print(line[len("data: ") + 1:-1], end="", flush=True)

一个小坑:若修改client_req.py文件(例如修改topic或者其他地方)后再执行代码时,可能出现这样的错误:

requests.exceptions.ChunkedEncodingError: Response ended prematurely

观察一下langchain serve的执行情况: 报错的最终原因是:client_req.py存在修改,并重新加载;从而导致ASGI可调用对象返回但未完成响应。再执行一次就好了~

综上,两种交互方式介绍完毕。两者各有特色:

模型调用交互:仅使用Runnable远程调用模型,如prompt,chain需要client自己构建,但这些步骤偏向简单,调用相关类就可以了;request交互:仅需要知晓url以及数据输入格式即可;但输出格式需要手动处理(invoke,stream等其他调用方式),比较乐观的是,输出格式也是确定的,在langchain serve中的相应接口中可以查到;上面的小坑也只有在request交互中出现,因为每一次修改代码,都会影响langchain serve的执行,但模型调用交互不会出现这种情况原因是:修改代码并不影响模型的调用,除非去修改模型- -~

个人更偏向于模型调用交互。

附: server.py \text{server.py} server.py完整代码

至此,代码就齐了,大家一起努力呀~

from fastapi import FastAPI from langchain_community.llms import Tongyi from fastapi.responses import RedirectResponse from langchain_core.prompts import ChatPromptTemplate, PromptTemplate from langserve import add_routes app = FastAPI( title="langchain server", version="0.1.0", description="A server for running Langchain with FastAPI", ) llm = Tongyi( model_name="tongyi-7b-chinese", temperature=0.5, max_tokens=100, ) @app.get("/") async def redirect_root_to_docs(): return RedirectResponse("/docs") # Edit this to add the chain you want to add add_routes( app, llm, path="/tongyi-7b-chinese" ) prompt = PromptTemplate.from_template("请介绍一下{topic},200字") add_routes( app, prompt | llm, path="/tongyi-7b-chinese-chain" ) if __name__ == "__main__": import uvicorn uvicorn.run(app, host="0.0.0.0", port=8000)
标签:

langchain学习笔记之langserve服务部署由讯客互联其他栏目发布,感谢您对讯客互联的认可,以及对我们原创作品以及文章的青睐,非常欢迎各位朋友分享到个人网站或者朋友圈,但转载请说明文章出处“langchain学习笔记之langserve服务部署