河北省建设厅管网站,企业门户网站管理要求,中航鑫源建设集团有限公司网站,wordpress留言群发1. 引言
最近由于工作需要#xff0c;又去了解了一下简单的python服务搭建的相关工作#xff0c;主要是为了自己开发的模型或者工具给同组的人使用。之前介绍的针对于数据科学研究比较友好的一个可以展示的前端框架Streamlit可以说是一个利器。不过#xff0c;随着ChatGPT的…1. 引言
最近由于工作需要又去了解了一下简单的python服务搭建的相关工作主要是为了自己开发的模型或者工具给同组的人使用。之前介绍的针对于数据科学研究比较友好的一个可以展示的前端框架Streamlit可以说是一个利器。不过随着ChatGPT的流行基于chat的服务越来越多了起来streamlit有一个chat衍生物streamlit-chat但是它能提供的只是一个简单的聊天功能并不能具有更高级显示例如支持markdown和流式输出等。因此更加适合大模型前端的FastChat可能是更好的选择。
话说回来前端只是一个展示的界面而真正提供服务的需要后端才行。严格意义上讲后端都是提供数据服务的也就是对数据进行增删查改的本质是离不开的但是现在不是和数据库链接而是和模型或者模型服务链接了。
这时候当我去搜索python的后端库的时候最长用的中小应用web框架Flask出现在我面前。但是我感觉还是有点复杂于是我想到了一个基于Starlette二次开发的FastAPI也许更适合。这里有他们之间的比较介绍介绍1介绍2介绍3介绍4。当然也有人指出用Flask与FastAPI比较式不公平的就像是比较苹果和橙汁哪个更甜一样。Flask作为通用web框架应该和Starlette比。我关于这点也是认同的。但这更能说明在一个需要快速开发而需求不是那么重的时候FastAPI是一个更好的选择。(当然我也用Flask开发过多线程支持的服务两者各有千秋。)
2. Hello world
正如任何语言的第一个案例一样我们首先用一个非常简单的例子介绍使用FastAPI提供服务的。
第一步准备代码app.py
from fastapi import FastAPI
from pydantic import BaseModel
from gpt import GPTapp FastAPI()
model GPT()class Message(BaseModel):new_message: strrole: str args: dict {}app.post(/gpt)
def gpt_endpoint(message: Message):new_message message.new_messagerole message.roleargs message.argsresponse model.call(new_messagenew_message, rolerole, argsargs)return response从这段不足50行的代码里凸显出了大部分的FastAPI的特性。可以看到主体app自己的资源model以及一个数据类Message一个post接口服务(名为/gpt)并做了一些操作返回response。
这里的GPT是简化的类其处理代码可以写到另一个文件中。
第二步安装依赖库
pip install fastapi
pip install uvicorn
pip install pydantic第三步运行服务
uvicorn app:app --host 0.0.0.0 --port 8000经历以上三步一个FastAPI服务就搭建好了访问地址例如http://localhost:8000/gpt如何快速的测试它呢FastAPI自带了Docs可以通过URL访问(http://localhost:8000/docs)如下图所示
如果代码中注释写的足够多的话都不用另写手册了。
但是刚才的代码只是向我们简单的介绍了一下FastAPI的特性如果我需要构建更加复杂的服务呢下面我们通过2个实战例子更加全面的介绍FastAPI的使用。
3. 扩充实战
3.1 使用FastAPI提供增删查改的例子
假设我们正在开发一个简单的待办事项管理应用程序。我们希望实现以下功能
获取所有待办事项的列表创建一个新的待办事项更新特定待办事项的内容标记特定待办事项为已完成删除特定待办事项
我们可以使用FastAPI来实现这些功能。以下是使用不同类型装饰器定义的API端点的示例代码
from fastapi import FastAPI
from pydantic import BaseModelapp FastAPI()# 待办事项数据模型
class TodoItem(BaseModel):id: inttitle: strcompleted: bool False# 模拟的待办事项存储
todo_items []# 获取所有待办事项的列表
app.get(/todos)
def get_todo_list():return todo_items# 创建一个新的待办事项
app.post(/todos)
def create_todo_item(item: TodoItem):todo_items.append(item)return item# 更新特定待办事项的内容
app.put(/todos/{item_id})
def update_todo_item(item_id: int, item: TodoItem):for i in range(len(todo_items)):if todo_items[i].id item_id:todo_items[i] itemreturn item# 标记特定待办事项为已完成
app.patch(/todos/{item_id})
def complete_todo_item(item_id: int):for item in todo_items:if item.id item_id:item.completed Truereturn item# 删除特定待办事项
app.delete(/todos/{item_id})
def delete_todo_item(item_id: int):for item in todo_items:if item.id item_id:todo_items.remove(item)return {message: Item deleted}
在上面的示例中我们使用了以下不同类型的装饰器 app.get(path: str)定义GET请求的端点。使用app.get(“/todos”)装饰器定义了获取所有待办事项的列表的端点。这个端点不需要接收任何参数直接返回待办事项列表。 app.post(path: str)定义POST请求的端点。使用app.post(“/todos”)装饰器定义了创建新待办事项的端点。这个端点接收一个请求体参数item它是一个TodoItem模型的实例包含了待办事项的内容。在处理函数中我们将新的待办事项添加到todo_items列表中并返回添加的待办事项。 app.put(path: str)定义PUT请求的端点。使用app.put(“/todos/{item_id}”)装饰器定义了更新待办事项的端点。这个端点接收一个路径参数item_id用于指定待办事项的ID以及一个请求体参数item它是一个TodoItem模型的实例包含了待办事项的新内容。在处理函数中我们遍历todo_items列表找到对应ID的待办事项将其更新为新的内容并返回更新后的待办事项。 app.patch(path: str)定义PATCH请求的端点。使用app.patch(“/todos/{item_id}”)装饰器定义了标记待办事项为已完成的端点。这个端点接收一个路径参数item_id用于指定待办事项的ID。在处理函数中我们遍历todo_items列表找到对应ID的待办事项并将其标记为已完成item.completed True然后返回已更新的待办事项。 app.delete(path: str)定义DELETE请求的端点。使用app.delete(“/todos/{item_id}”)装饰器定义了删除待办事项的端点。这个端点接收一个路径参数item_id用于指定待办事项的ID。在处理函数中我们遍历todo_items列表找到对应ID的待办事项并将其从列表中删除然后返回一个简单的消息表示删除成功。 这些装饰器允许我们根据HTTP方法GET、POST、PUT、PATCH、DELETE来定义不同类型的端点并通过路径参数和请求体参数来接收和处理不同的数据。这样我们可以使用统一的应用程序来处理各种操作并根据RESTful API的原则设计我们的API。
这里我们注意到PUT和PATCH方法是很相似的都是用于更新的但是两者有以下不同 PUT请求用于完全替换Replace服务器上的资源或实体。当客户端发送一个PUT请求时它需要提供完整的资源表示包括要更新的所有字段。 如果资源不存在则会创建一个新的资源如果资源存在则会完全替换覆盖现有资源的内容。 客户端通常应该提供完整的资源表示即使只有部分字段发生更改。这意味着客户端需要提供所有字段而不仅仅是要更改的字段。 PUT请求是幂等的多次执行相同的PUT请求不会对资源产生额外的影响。 PATCH请求用于对服务器上的资源或实体进行部分更新。当客户端发送一个PATCH请求时它只需要提供要更新的部分字段或属性。 PATCH请求可以用于增量更新资源的特定字段而不需要发送整个资源的表示。这使得它适用于只更新部分字段的情况。 服务器根据客户端提供的更新内容选择性地更新资源的相应字段。未提供的字段将保持不变。 PATCH请求可以是幂等的但也可以是非幂等的这取决于具体实现和使用情况。 在实际应用中可以根据具体的业务需求和设计准则来选择使用PUT还是PATCH请求。如果要更新整个资源或实体应该使用PUT请求。如果只需更新部分字段或属性应该使用PATCH请求。
3.2 使用FastAPI提供GPU加载语言模型的例子
import uvicorn
import torch
from fastapi import FastAPI, BackgroundTasks
from pydantic import BaseModel# 示例GPT模型类
class GPTModel:def __init__(self):# 这里是你的GPT模型的初始化代码# ...def generate_text(self, input_text):# 这里是生成文本的代码# ...# 示例后台任务函数
def load_model(background_tasks):# 加载和初始化GPT模型model GPTModel()device torch.device(cuda if torch.cuda.is_available() else cpu)model.to(device)model.eval()# 将模型保存到FastAPI应用的state中app.state.model model# 示例请求模型的输入
class TextRequest(BaseModel):text: strapp FastAPI()app.post(/generate)
def generate_text(request: TextRequest, background_tasks: BackgroundTasks):# 在后台任务中加载模型background_tasks.add_task(load_model, background_tasks)# 从应用程序状态中获取模型model app.state.model# 在GPU上进行推理input_text request.textgenerated_text model.generate_text(input_text)# 返回生成的文本结果return {generated_text: generated_text}if __name__ __main__:uvicorn.run(app, host0.0.0.0, port8000)
4. 小结
本文主要简单的介绍了FastAPI的一些简单用法方便快速搭建原型服务。
随着ChatGPT等大模型逐渐渗透到我们的生活中可能如此记录技术细节的博客愈发的没有用了。因为不会的东西ChatGPT可以交互式的给予指导而不需要再这样看博客了。尽管这博客也离不开ChatGPT的指示我还是看了一些相关博客也自己亲手试了ChatGPT给的代码多少增加一些可信度。