当前位置: 首页 > article >正文

langchain学习笔记之langserve服务部署

langchain学习笔记之langserve服务部署

引言

本节将介绍 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架构,并提供自动生成文档等功能,从而轻松与其他模型进行集成。

安装过程

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

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

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

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

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

当前路径下会生成一个文件夹:
使用langchain生成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 https://mirrors.aliyun.com/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指令相关操作示例
接下来,使用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文件夹的路径内。正确返回结果如下:
poetry启动应用
打开上述链接,得到如下结果:
链接示例
这里包含创建的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",
)

对应的效果如下:
app参数调整

调用模型接口实现交互

第一步:在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被生成:
生成playground
进入该路径:http://localhost:8000/tongyi-7b-chinese/playground/可以得到一个在线的基于tongyi-7b-chinese模型的问答系统~
playground
言归正传~ 创建一个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"
)

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

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

可以发现:url路径中多了\invoke,这是langchain serve提供的接口:
invoke接口
将其展开,可以得到要求输入的格式以及输出格式
输入输出格式
上面的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)

返回结果如下:

林徽因(1904610日-195541日),原名林徽音,中国著名建筑师、诗人、作家。她出生于福建闽侯,成长于一个书香门第,自幼聪慧过人。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的执行情况:
langchain serve bug
报错的最终原因是:client_req.py存在修改,并重新加载;从而导致ASGI可调用对象返回但未完成响应。再执行一次就好了~

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

  • 模型调用交互:仅使用Runnable远程调用模型,如prompt,chain需要client自己构建,但这些步骤偏向简单,调用相关类就可以了;
  • request交互:仅需要知晓url以及数据输入格式即可;但输出格式需要手动处理(invokestream等其他调用方式),比较乐观的是,输出格式也是确定的,在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)

http://www.kler.cn/a/545953.html

相关文章:

  • 联核CDD20堆高型无人AGV叉车产品介绍/参数/优势/应用场景
  • 算法之 跳跃游戏
  • 深拷贝实现方法
  • 缓存三大问题及其解决方案
  • 第1章大型互联网公司的基础架构——1.5 服务发现
  • STM32HAL库快速入门教程——常用外设学习(2)
  • nginx播放视频(auth_request鉴权)
  • Unity3D 类MOBA角色控制器 开箱即用
  • 调用DeepSeek API接口:实现智能数据挖掘与分析
  • 腾讯发布混元-3D 2.0: 首个开源高质3D-DiT生成大模型
  • 机器视觉--Halcon变量的创建与赋值
  • 【ICP/EDI教程】增值电信年报网络信息安全表存档记录 申请的时候对着抄
  • 两步在 Vite 中配置 Tailwindcss
  • 【git-hub项目:YOLOs-CPP】本地实现02:跑通项目了
  • Humanoid Robot Price Break 人形机器人价格突破
  • nats 消息系统架构
  • 【个人开发】cuda12.6安装vllm安装实践【内含踩坑经验】
  • 【自学笔记】人工智能基础知识点总览-持续更新
  • xpath语法以及基本使用
  • npm安装时无法访问github域名的解决方法