使用 FastAPI 部署 QWEN 接口的 Python 流式传输服务
QWEN 接口介绍(阿里百炼平台获取)
QWEN 的功能和特点
https://bailian.console.aliyun.com/
QWEN 是一款基于大型语言模型(LLM)的对话式人工智能系统。它能够理解自然语言,并生成流畅、连贯的文本回复。QWEN 的主要功能包括:
- 问答: 回答用户提出的各种问题,涵盖不同领域和主题。
- 对话: 与用户进行自然流畅的对话,能够理解上下文和语义。
- 文本生成: 根据用户的提示生成各种类型的文本内容,例如故事、诗歌、文章等。
- 文本摘要: 将长篇文本压缩成简短的摘要,提取关键信息。
QWEN 的特点:
- 高精度: 基于强大的 LLM,能够提供准确可靠的答案和回复。
- 自然流畅: 生成的文本风格自然,易于理解。
- 多功能: 支持多种应用场景,例如客服、教育、娱乐等。
QWEN API 文档
QWEN 提供 RESTful API 接口,方便开发者集成到各种应用程序中。API 文档详细介绍了每个接口的请求参数、响应格式以及示例代码。
注意: 具体的 API 文档地址和版本信息请参考 QWEN 官方网站或相关文档。
FastAPI 基础知识
FastAPI 简介
FastAPI 是一个现代、快速(高性能)的 Python Web 框架,用于构建 API。它基于标准类型提示,例如那些由 Pydantic 提供的,以提供数据验证和自动文档生成。
主要特点:
- 高性能: 基于 Starlette 和 Pydantic,FastAPI 具有出色的性能,能够处理大量请求。
- 自动文档: 通过 OpenAPI 和 Swagger UI 自动生成 API 文档,方便测试和使用。
- 数据验证: 利用 Pydantic 进行数据类型检查和验证,确保传入的数据符合预期格式。
- 异步支持: 使用 async/await 语法实现异步编程,提高效率。
路由和请求处理
FastAPI 使用装饰器来定义路由,例如 @app.get("/")
用于处理 GET 请求到根路径 /
。
from fastapi import FastAPI
app = FastAPI()
@app.get("/")
async def root():
return {"message": "Hello World"}
在上面的例子中,@app.get("/")
定义了一个路由,当收到 GET 请求到 /
路径时,会执行 root()
函数并返回一个 JSON 响应。
异步编程
FastAPI 支持异步编程,使用 async
/await
语法实现高效的并发处理。例如:
import asyncio
async def my_task():
print("Starting task")
await asyncio.sleep(1) # 模拟耗时操作
print("Task completed")
async def main():
await asyncio.gather(my_task(), my_task())
asyncio.run(main())
在这个例子中,my_task()
是一个异步函数,它会打印信息并等待 1 秒。main()
函数使用 asyncio.gather()
并行执行两个 my_task()
实例。
代码解析
依赖库导入
from fastapi import FastAPI, HTTPException
from pydantic import BaseModel, Field
from typing import List, Dict
from openai import OpenAI
from fastapi.responses import StreamingResponse
import asyncio
from starlette.middleware.cors import CORSMiddleware
fastapi
: 用于构建 Web API 的框架。HTTPException
: 用于抛出 HTTP 错误异常。pydantic
: 用于数据验证和解析的库。BaseModel
: 定义数据模型的基础类。List
,Dict
: Python 内置类型,用于表示列表和字典。openai
: 用于与 OpenAI API 交互的库。StreamingResponse
: 用于返回流式响应的 FastAPI 组件。asyncio
: 用于实现异步编程的库。CORSMiddleware
: 用于配置跨域资源共享 (CORS) 的中间件。
Pydantic 模型定义
class Message(BaseModel):
role: str
content: str
class RequestBody(BaseModel):
model: str
messages: List[Message]
Message
: 定义了消息的结构,包含role
(角色) 和content
(内容) 两个字段。RequestBody
: 定义了请求体的结构,包含model
(模型名称) 和messages
(消息列表) 两个字段。
OpenAI 客户端配置
client = OpenAI(
api_key='sk-8b0****************bb', # 这个需要去阿里的百炼注册获取
base_url="https://dashscope.aliyuncs.com/compatible-mode/v1"
)
- 创建一个
OpenAI
客户端对象,用于与 OpenAI API 交互。 - 设置 API 密钥和基础 URL。
流式传输函数实现
async def generate_response_stream(completion):
try:
for chunk in completion:
yield chunk.model_dump_json()
await asyncio.sleep(0)
except Exception as e:
raise HTTPException(status_code=500, detail=str(e))
generate_response_stream
函数用于将 OpenAI API 返回的流式响应转换为 JSON 格式并逐个发送。- 使用
yield
关键字实现生成器函数,每次返回一个 JSON 字符串。 asyncio.sleep(0)
用于确保异步操作不会阻塞主线程。
API 路由定义
@app.post("/ask")
async def ask_question(request_body: RequestBody):
try:
completion = client.chat.completions.create(
model=request_body.model,
messages=[message.dict() for message in request_body.messages],
stream=True,
stream_options={"include_usage": True}
)
return StreamingResponse(generate_response_stream(completion), media_type="application/json")
except Exception as e:
raise HTTPException(status_code=500, detail=str(e))
- 定义了一个
/ask
路由,接受 POST 请求。 - 使用
RequestBody
模型验证请求体数据。 - 调用 OpenAI API 的
chat.completions.create
方法生成响应,并设置stream=True
以启用流式模式。 - 将流式响应传递给
StreamingResponse
并返回给客户端。
本地部署
Uvicorn 运行服务器
Uvicorn 是一个快速、高效的 ASGI(Asynchronous Server Gateway Interface)服务器,用于运行 FastAPI 应用。在代码中,使用 uvicorn.run(app, host="0.0.0.0", port=5003)
启动 Uvicorn 服务器:
app
: FastAPI 实例。host="0.0.0.0"
: 绑定到所有网络接口,使服务可从任何位置访问。port=5003
: 指定服务器监听的端口号。
端口配置
代码中将 FastAPI 应用部署在 port=5003
端口上。您可以根据需要修改此端口号以适应您的环境。
使用示例
发送请求
您可以使用工具如 curl
或 Python 的 requests
库发送 POST 请求到 /ask
端点。请求体应为 JSON 格式,包含以下字段:
-
model
: 指定使用的 OpenAI 模型名称 (例如: “gpt-3.5-turbo”) -
messages
: 一个消息列表,每个消息都是一个字典,包含role
和content
字段。role
: 消息的角色,可以是 “system”、“user” 或 “assistant”。content
: 消息的内容,是一个字符串。
示例代码 (Python):
import requests
data = {
"model": "gpt-3.5-turbo",
"messages": [
{"role": "system", "content": "You are a helpful assistant."},
{"role": "user", "content": "What is the capital of France?"}
]
}
response = requests.post("http://localhost:5003/ask", json=data)
if response.status_code == 200:
for chunk in response.iter_content():
print(chunk.decode('utf-8'))
else:
print(f"Error: {response.status_code}")
接收响应
服务将返回一个流式 JSON 响应,其中包含模型生成的每个标记。您可以使用 Python 的 StreamingResponse
或其他工具来处理流式数据。
示例代码 (Python):
async def handle_response(response):
async for chunk in response.iter_chunks():
data = json.loads(chunk)
print(data['choices'][0]['text'])
# 处理模型生成的文本
async def main():
async with aiohttp.ClientSession() as session:
async with session.post('http://localhost:5003/ask', json=data) as response:
await handle_response(response)
asyncio.run(main())
完整代码
from fastapi import FastAPI, HTTPException
from pydantic import BaseModel, Field
from typing import List, Dict
from openai import OpenAI
from fastapi.responses import StreamingResponse
import asyncio
from starlette.middleware.cors import CORSMiddleware
app = FastAPI()
# Add CORS middleware
app.add_middleware(
CORSMiddleware,
allow_origins=["*"], # Allows all origins
allow_credentials=True,
allow_methods=["*"], # Allows all methods
allow_headers=["*"], # Allows all headers
)
# Define a Pydantic model for the request body
class Message(BaseModel):
role: str
content: str
class RequestBody(BaseModel):
model: str
messages: List[Message]
async def generate_response_stream(completion):
try:
for chunk in completion:
yield chunk.model_dump_json()
await asyncio.sleep(0)
except Exception as e:
raise HTTPException(status_code=500, detail=str(e))
@app.post("/ask")
async def ask_question(request_body: RequestBody):
try:
client = OpenAI(
api_key='sk-8b0*****************bb',
base_url="https://dashscope.aliyuncs.com/compatible-mode/v1",
)
completion = client.chat.completions.create(
model=request_body.model,
messages=[message.dict() for message in request_body.messages],
stream=True,
stream_options={"include_usage": True}
)
return StreamingResponse(generate_response_stream(completion), media_type="application/json")
except Exception as e:
raise HTTPException(status_code=500, detail=str(e))
if __name__ == '__main__':
import uvicorn
uvicorn.run(app, host="0.0.0.0", port=5003)