别让你的GLM-4.5在本地"吃灰"!三步教你用FastAPI把它变成一个能赚钱的API服务
当一个强大的语言模型GLM-4.5躺在你的硬盘里时,它的价值是有限的。只有当它变成一个稳定、可调用的API服务时,才能真正赋能万千应用。本文将手把手教你如何实现这一转变,将本地运行的GLM-4.5模型转化为一个生产级的API服务。
技术栈选型与环境准备
为什么选择FastAPI?
FastAPI是一个现代、快速(高性能)的Web框架,用于构建API。选择它的理由包括:
- 极快的性能:基于Starlette和Pydantic,性能接近NodeJS和Go
- 自动文档生成:自动生成Swagger UI和ReDoc文档
- 类型提示支持:完整的Python类型提示,提供更好的开发体验
- 异步支持:原生支持async/await,适合高并发场景
环境依赖
创建requirements.txt文件:
fastapi==0.104.1
uvicorn[standard]==0.24.0
transformers==4.40.0
torch==2.2.0
accelerate==0.27.0
sentencepiece==0.1.99
protobuf==4.25.3
安装依赖:
pip install -r requirements.txt
核心逻辑封装:适配GLM-4.5的推理函数
模型加载函数
import torch
from transformers import AutoModelForCausalLM, AutoTokenizer
from typing import Optional
import logging
# 配置日志
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)
class GLM45Model:
def __init__(self, model_name: str = "zai-org/GLM-4.5-Air", device: str = "auto"):
"""
初始化GLM-4.5模型
Args:
model_name: 模型名称或路径,默认为GLM-4.5-Air轻量版
device: 设备类型,auto表示自动选择
"""
self.model_name = model_name
self.device = device
self.model = None
self.tokenizer = None
self.is_loaded = False
def load_model(self):
"""加载模型和分词器"""
try:
logger.info(f"正在加载模型: {self.model_name}")
# 加载分词器
self.tokenizer = AutoTokenizer.from_pretrained(
self.model_name,
trust_remote_code=True
)
# 加载模型
self.model = AutoModelForCausalLM.from_pretrained(
self.model_name,
torch_dtype=torch.bfloat16,
device_map=self.device,
trust_remote_code=True
)
self.is_loaded = True
logger.info("模型加载成功")
except Exception as e:
logger.error(f"模型加载失败: {str(e)}")
raise
def generate_response(
self,
prompt: str,
max_length: int = 2048,
temperature: float = 0.7,
top_p: float = 0.9,
enable_thinking: bool = True
) -> str:
"""
生成模型响应
Args:
prompt: 输入提示文本
max_length: 最大生成长度
temperature: 温度参数,控制随机性
top_p: 核采样参数
enable_thinking: 是否启用思考模式
Returns:
str: 模型生成的文本
"""
if not self.is_loaded:
self.load_model()
try:
# 构建对话格式
messages = [{"role": "user", "content": prompt}]
# 编码输入
inputs = self.tokenizer.apply_chat_template(
messages,
tokenize=True,
add_generation_prompt=True,
return_tensors="pt"
).to(self.model.device)
# 生成参数配置
generation_config = {
"max_length": max_length,
"temperature": temperature,
"top_p": top_p,
"do_sample": True,
"pad_token_id": self.tokenizer.eos_token_id,
# GLM-4.5特有参数:控制思考模式
"chat_template_kwargs": {"enable_thinking": enable_thinking}
}
# 生成文本
with torch.no_grad():
outputs = self.model.generate(
inputs,
**generation_config
)
# 解码输出
response = self.tokenizer.decode(
outputs[0][len(inputs[0]):],
skip_special_tokens=True
)
return response
except Exception as e:
logger.error(f"生成失败: {str(e)}")
return f"生成错误: {str(e)}"
关键代码解析
- 模型加载:使用
AutoModelForCausalLM和AutoTokenizer自动处理GLM-4.5的特殊架构 - 设备映射:
device_map="auto"让transformers自动分配模型到可用GPU - 对话模板:
apply_chat_template方法正确处理GLM-4.5的对话格式 - 思考模式:通过
chat_template_kwargs参数控制是否启用复杂的推理过程
API接口设计:优雅地处理输入与输出
完整的FastAPI服务
from fastapi import FastAPI, HTTPException
from pydantic import BaseModel, Field
from typing import List, Optional
import uvicorn
from glm45_model import GLM45Model
app = FastAPI(
title="GLM-4.5 API服务",
description="基于GLM-4.5大语言模型的API服务",
version="1.0.0"
)
# 全局模型实例
model_handler = GLM45Model()
class ChatMessage(BaseModel):
role: str = Field(..., description="消息角色:user或assistant")
content: str = Field(..., description="消息内容")
class ChatRequest(BaseModel):
messages: List[ChatMessage] = Field(..., description="对话消息列表")
max_tokens: Optional[int] = Field(1024, description="最大生成token数")
temperature: Optional[float] = Field(0.7, description="温度参数")
top_p: Optional[float] = Field(0.9, description="核采样参数")
enable_thinking: Optional[bool] = Field(True, description="是否启用思考模式")
stream: Optional[bool] = Field(False, description="是否流式输出")
class ChatResponse(BaseModel):
id: str = Field(..., description="请求ID")
object: str = Field("chat.completion", description="对象类型")
created: int = Field(..., description="创建时间戳")
model: str = Field(..., description="模型名称")
choices: List[dict] = Field(..., description="生成选项")
usage: dict = Field(..., description="使用统计")
@app.on_event("startup")
async def startup_event():
"""启动时加载模型"""
try:
model_handler.load_model()
except Exception as e:
raise RuntimeError(f"模型加载失败: {str(e)}")
@app.get("/")
async def root():
"""健康检查端点"""
return {"status": "healthy", "model_loaded": model_handler.is_loaded}
@app.post("/v1/chat/completions", response_model=ChatResponse)
async def chat_completion(request: ChatRequest):
"""
兼容的聊天补全接口
支持标准的API格式,方便现有应用迁移
"""
try:
# 提取用户最后一条消息
user_messages = [msg for msg in request.messages if msg.role == "user"]
if not user_messages:
raise HTTPException(status_code=400, detail="需要用户消息")
last_user_message = user_messages[-1].content
# 生成响应
response_text = model_handler.generate_response(
prompt=last_user_message,
max_length=request.max_tokens,
temperature=request.temperature,
top_p=request.top_p,
enable_thinking=request.enable_thinking
)
# 构建兼容的响应格式
import time
response_data = {
"id": f"chatcmpl-{int(time.time())}",
"object": "chat.completion",
"created": int(time.time()),
"model": model_handler.model_name,
"choices": [
{
"index": 0,
"message": {
"role": "assistant",
"content": response_text
},
"finish_reason": "stop"
}
],
"usage": {
"prompt_tokens": len(last_user_message) // 4, # 近似估算
"completion_tokens": len(response_text) // 4,
"total_tokens": (len(last_user_message) + len(response_text)) // 4
}
}
return response_data
except Exception as e:
raise HTTPException(status_code=500, detail=f"生成错误: {str(e)}")
@app.post("/v1/generate")
async def generate_text(prompt: str, max_length: int = 1024):
"""
简化的文本生成接口
适用于简单的文本生成需求
"""
try:
response = model_handler.generate_response(
prompt=prompt,
max_length=max_length
)
return {"generated_text": response}
except Exception as e:
raise HTTPException(status_code=500, detail=str(e))
if __name__ == "__main__":
uvicorn.run(
app,
host="0.0.0.0",
port=8000,
reload=True
)
API设计理念
- 兼容性:采用标准的API格式,降低迁移成本
- 双接口设计:提供标准的
/v1/chat/completions和简化的/v1/generate接口 - 错误处理:完善的异常捕获和HTTP状态码返回
- 使用统计:返回token使用情况,便于成本控制
实战测试:验证你的API服务
使用curl测试
# 测试健康检查
curl http://localhost:8000/
# 测试文本生成接口
curl -X POST "http://localhost:8000/v1/generate" \
-H "Content-Type: application/json" \
-d '{"prompt": "请用Python写一个快速排序算法", "max_length": 500}'
# 测试兼容接口
curl -X POST "http://localhost:8000/v1/chat/completions" \
-H "Content-Type: application/json" \
-d '{
"messages": [
{"role": "user", "content": "请解释一下机器学习中的过拟合现象"}
],
"max_tokens": 300,
"temperature": 0.7
}'
使用Python requests测试
import requests
import json
def test_glm45_api():
# 测试基础生成
url = "http://localhost:8000/v1/generate"
data = {
"prompt": "写一个关于人工智能的简短故事",
"max_length": 200
}
response = requests.post(url, json=data)
print("生成结果:", response.json())
# 测试聊天接口
chat_url = "http://localhost:8000/v1/chat/completions"
chat_data = {
"messages": [
{"role": "user", "content": "如何预防编程中的安全漏洞?"}
],
"max_tokens": 400,
"temperature": 0.8,
"enable_thinking": True
}
chat_response = requests.post(chat_url, json=chat_data)
result = chat_response.json()
print("AI回复:", result["choices"][0]["message"]["content"])
print("Token使用:", result["usage"])
if __name__ == "__main__":
test_glm45_api()
生产化部署与优化考量
部署方案
使用Gunicorn + Uvicorn Workers
# 安装Gunicorn
pip install gunicorn
# 启动生产服务器
gunicorn -w 4 -k uvicorn.workers.UvicornWorker -b 0.0.0.0:8000 main:app
Docker部署
创建Dockerfile:
FROM python:3.9-slim
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY . .
EXPOSE 8000
CMD ["gunicorn", "-w", "4", "-k", "uvicorn.workers.UvicornWorker", "-b", "0.0.0.0:8000", "main:app"]
性能优化建议
- 模型量化:使用4-bit或8-bit量化减少显存占用
from transformers import BitsAndBytesConfig
quantization_config = BitsAndBytesConfig(
load_in_4bit=True,
bnb_4bit_compute_dtype=torch.bfloat16
)
- KV缓存优化:启用KV缓存加速重复查询
generation_config = {
"use_cache": True,
"past_key_values": None # 可以复用之前的KV缓存
}
- 批量推理:支持批量请求处理提高吞吐量
def batch_generate(self, prompts: List[str], **kwargs):
"""批量生成响应"""
# 实现批量处理逻辑
- 异步处理:使用FastAPI的异步特性处理并发请求
监控与日志
添加监控:
from prometheus_fastapi_instrumentator import Instrumentator
Instrumentator().instrument(app).expose(app)
配置结构化日志:
import json
import logging
class JSONFormatter(logging.Formatter):
def format(self, record):
log_data = {
"timestamp": self.formatTime(record),
"level": record.levelname,
"message": record.getMessage(),
"module": record.module
}
return json.dumps(log_data)
# 配置日志处理器
handler = logging.StreamHandler()
handler.setFormatter(JSONFormatter())
logger.addHandler(handler)
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考



