GLM-4-9B-Chat-1M实战教程:用FastAPI封装GLM-4-9B-Chat-1M为微服务
想不想让一个能一口气读完200万字文档的AI大脑,变成你随时可以调用的服务?今天我们就来动手,把GLM-4-9B-Chat-1M这个“长文本处理专家”用FastAPI封装成一个标准的微服务。
你可能已经体验过它的网页界面,但真正的生产力,是把能力集成到你的系统里。想象一下,你的应用能随时调用这个AI来处理超长合同、分析整本财报,或者总结几百页的研究报告。这就是微服务化的价值。
这篇教程,我会带你从零开始,一步步搭建一个稳定、高效、可扩展的GLM-4-9B-Chat-1M API服务。我们不用复杂的架构,就用最实用的FastAPI,目标是让你看完就能跑起来,快速应用到自己的项目中。
1. 准备工作:理解我们的“工具箱”
在开始敲代码之前,我们先快速了解一下今天要用到的核心“零件”。这能帮你更好地理解每一步在做什么。
GLM-4-9B-Chat-1M:今天的主角。一个90亿参数的对话模型,最大的特点是支持1M tokens的超长上下文,约等于200万汉字。这意味着你可以把一整本小说、一份超长的技术文档或者多个合同文件一次性扔给它处理。它保持了GLM-4系列的多轮对话、代码执行和工具调用能力,但专门为长文本场景做了优化。用官方INT4量化版本,一张RTX 3090/4090显卡就能流畅运行。
FastAPI:我们的“包装盒”。一个现代、快速(高性能)的Python Web框架,专门用于构建API。它的优点非常明显:编写简单(代码像在写文档)、自动生成交互式API文档、性能接近NodeJS和Go。我们用它来接收外部的请求,调用模型,再把结果返回去。
vLLM:模型的“加速引擎”。一个专为大语言模型推理设计的高吞吐量、内存高效的推理和服务引擎。简单说,它能让模型推理得更快,同时更省显存。GLM-4-9B-Chat-1M官方就推荐使用vLLM来部署。
把它们组合起来,工作流程就很清晰了:用户通过HTTP请求调用我们的FastAPI服务 -> FastAPI将请求转发给vLLM管理的模型 -> vLLM执行推理 -> 结果通过FastAPI返回给用户。
2. 基础环境搭建与模型启动
我们先确保模型能在本地跑起来,这是后续所有工作的基础。
2.1 创建并激活Python环境
为了避免包版本冲突,我们使用独立的虚拟环境。打开你的终端(Linux/Mac)或命令提示符/PowerShell(Windows),执行以下命令:
# 创建项目目录并进入 mkdir glm4-9b-api-service && cd glm4-9b-api-service # 创建Python虚拟环境(假设你已安装Python 3.8+) python -m venv venv # 激活虚拟环境 # 在 Windows 上: venv\Scripts\activate # 在 Linux/Mac 上: source venv/bin/activate激活后,你的命令行提示符前面通常会显示(venv),表示已经在这个独立环境中了。
2.2 安装核心依赖
接下来,安装我们需要的Python包。创建一个requirements.txt文件,内容如下:
fastapi==0.104.1 uvicorn[standard]==0.24.0 pydantic==2.5.0 vllm==0.2.7 transformers==4.36.0 torch==2.1.0 sentencepiece # 分词器可能需要然后使用pip安装:
pip install -r requirements.txt注意:torch的版本可能需要根据你的CUDA版本进行调整。如果你没有GPU或使用不同版本的CUDA,可以访问PyTorch官网获取对应的安装命令。
2.3 启动vLLM模型服务
vLLM可以作为一个独立的服务运行,它本身就提供了HTTP API。这是我们模型推理的核心。我们使用官方推荐的INT4量化模型来节省显存。
在终端中运行以下命令:
# 使用 vLLM 启动模型服务 python -m vllm.entrypoints.openai.api_server \ --model THUDM/glm-4-9b-chat-1m \ --served-model-name glm-4-9b-chat-1m \ --max-model-len 1048576 \ # 1M tokens --dtype half \ # 使用半精度浮点数 --quantization awq \ # 使用AWQ量化,显存需求降至~9GB --api-key token-abc123 \ # 设置一个简单的API密钥(可选,但建议) --port 8000 # 服务端口,默认是8000,这里显式指定命令参数解释:
--model THUDM/glm-4-9b-chat-1m: 指定从HuggingFace下载的模型。--served-model-name: 给服务中的模型起个名字。--max-model-len 1048576: 这是关键!设置为1,048,576,即1M tokens,完全释放模型的长文本能力。--quantization awq: 使用AWQ量化技术,能显著降低显存占用,让24GB显存的卡(如3090/4090)就能运行。--api-key: 设置一个访问密钥,增加一点基础安全性。--port: 指定服务运行的端口。
运行这个命令后,vLLM会开始下载模型(如果第一次运行),然后启动一个服务。你会看到大量的日志输出,最后出现类似INFO: Application startup complete.和INFO: Uvicorn running on http://0.0.0.0:8000的提示,说明服务启动成功了。
保持这个终端窗口运行,不要关闭它。现在,你的模型已经作为一个“后端推理服务”在http://localhost:8000上运行了。你可以简单测试一下:
# 打开另一个终端,测试vLLM服务是否正常 curl http://localhost:8000/v1/models如果返回一个包含模型信息的JSON,说明vLLM服务运行正常。
3. 构建FastAPI微服务
现在模型服务已经就绪,我们来构建自己的FastAPI应用。这个应用将作为对外的统一接口,处理更复杂的业务逻辑、输入校验和结果格式化。
3.1 项目结构规划
我们先创建一个清晰的项目目录结构:
glm4-9b-api-service/ ├── app/ │ ├── __init__.py │ ├── main.py # FastAPI应用主文件 │ ├── config.py # 配置文件 │ ├── models.py # 数据模型定义(Pydantic) │ ├── routers/ │ │ ├── __init__.py │ │ └── chat.py # 聊天对话相关的路由 │ └── services/ │ ├── __init__.py │ └── vllm_client.py # 与vLLM服务通信的客户端 ├── requirements.txt └── README.md3.2 定义数据模型(models.py)
我们使用Pydantic来定义请求和响应的数据结构,这能帮我们自动进行数据验证和生成API文档。
创建app/models.py:
from pydantic import BaseModel, Field from typing import List, Optional, Dict, Any class Message(BaseModel): """单条消息""" role: str = Field(..., description="消息角色,如 'user', 'assistant', 'system'") content: str = Field(..., description="消息内容") class ChatRequest(BaseModel): """聊天请求体""" messages: List[Message] = Field(..., description="对话历史消息列表") model: str = Field(default="glm-4-9b-chat-1m", description="使用的模型名称") max_tokens: Optional[int] = Field(default=2048, description="生成的最大token数") temperature: Optional[float] = Field(default=0.7, description="采样温度,控制随机性") top_p: Optional[float] = Field(default=0.9, description="核采样参数") stream: Optional[bool] = Field(default=False, description="是否使用流式输出") class ChatResponseChoice(BaseModel): """响应中的单个选择""" index: int message: Message finish_reason: Optional[str] = None class ChatResponse(BaseModel): """聊天响应体""" id: Optional[str] = None object: str = "chat.completion" created: Optional[int] = None model: str choices: List[ChatResponseChoice] usage: Optional[Dict[str, int]] = None class SummaryRequest(BaseModel): """长文本总结请求体""" text: str = Field(..., description="需要总结的长文本") max_summary_length: Optional[int] = Field(default=500, description="总结的最大长度") instruction: Optional[str] = Field(default="请用中文总结以下文本的主要内容:", description="总结指令") class HealthResponse(BaseModel): """健康检查响应""" status: str model_loaded: bool vllm_status: Optional[str] = None3.3 创建vLLM客户端服务(services/vllm_client.py)
这个模块负责与后台的vLLM服务进行通信。
创建app/services/vllm_client.py:
import aiohttp import logging from typing import AsyncGenerator, Dict, Any, Optional from app.models import ChatRequest, ChatResponse logger = logging.getLogger(__name__) class VLLMClient: """vLLM服务客户端""" def __init__(self, base_url: str = "http://localhost:8000", api_key: str = "token-abc123"): self.base_url = base_url.rstrip('/') self.api_key = api_key self.headers = { "Authorization": f"Bearer {api_key}", "Content-Type": "application/json" } async def check_health(self) -> bool: """检查vLLM服务是否健康""" try: async with aiohttp.ClientSession() as session: async with session.get(f"{self.base_url}/health", timeout=10) as response: return response.status == 200 except Exception as e: logger.error(f"检查vLLM服务健康状态失败: {e}") return False async def chat_completion(self, chat_request: ChatRequest) -> ChatResponse: """调用vLLM的聊天补全接口""" # 将我们的请求格式转换为vLLM的OpenAI API格式 vllm_payload = { "model": chat_request.model, "messages": [msg.dict() for msg in chat_request.messages], "max_tokens": chat_request.max_tokens, "temperature": chat_request.temperature, "top_p": chat_request.top_p, "stream": chat_request.stream } async with aiohttp.ClientSession() as session: try: async with session.post( f"{self.base_url}/v1/chat/completions", json=vllm_payload, headers=self.headers, timeout=60 # 长文本处理可能需要更长时间 ) as response: if response.status != 200: error_text = await response.text() logger.error(f"vLLM API错误: {response.status}, {error_text}") raise Exception(f"vLLM服务错误: {error_text}") result = await response.json() return ChatResponse(**result) except aiohttp.ClientError as e: logger.error(f"调用vLLM服务网络错误: {e}") raise Exception(f"网络错误: {e}") async def chat_completion_stream(self, chat_request: ChatRequest) -> AsyncGenerator[str, None]: """流式调用vLLM的聊天补全接口""" vllm_payload = { "model": chat_request.model, "messages": [msg.dict() for msg in chat_request.messages], "max_tokens": chat_request.max_tokens, "temperature": chat_request.temperature, "top_p": chat_request.top_p, "stream": True } async with aiohttp.ClientSession() as session: try: async with session.post( f"{self.base_url}/v1/chat/completions", json=vllm_payload, headers=self.headers, timeout=60 ) as response: if response.status != 200: error_text = await response.text() logger.error(f"vLLM流式API错误: {response.status}, {error_text}") raise Exception(f"vLLM服务错误: {error_text}") # 流式读取数据 async for line in response.content: if line: decoded_line = line.decode('utf-8').strip() if decoded_line.startswith('data: '): data = decoded_line[6:] # 去掉 'data: ' 前缀 if data != '[DONE]': yield data except aiohttp.ClientError as e: logger.error(f"调用vLLM流式服务网络错误: {e}") raise Exception(f"网络错误: {e}") # 创建全局客户端实例 vllm_client = VLLMClient()3.4 创建聊天路由(routers/chat.py)
这里定义具体的API端点。
创建app/routers/chat.py:
from fastapi import APIRouter, HTTPException, Depends from fastapi.responses import StreamingResponse import json import time from typing import Optional from app.models import ChatRequest, ChatResponse, SummaryRequest from app.services.vllm_client import vllm_client router = APIRouter(prefix="/api/v1", tags=["chat"]) @router.post("/chat/completions", response_model=ChatResponse) async def chat_completion(request: ChatRequest): """ 标准聊天补全接口 - **messages**: 对话历史,例如 `[{"role": "user", "content": "你好"}]` - **model**: 模型名称,默认为 glm-4-9b-chat-1m - **max_tokens**: 最大生成token数 - **temperature**: 温度参数,控制随机性 - **stream**: 是否流式输出 """ try: # 添加系统提示词优化长文本处理(可选) if not any(msg.role == "system" for msg in request.messages): # 如果没有系统消息,可以添加一个针对长文本优化的提示 system_message = { "role": "system", "content": "你是一个擅长处理长文本的AI助手。用户可能会提供很长的文本,请仔细阅读并准确回答。" } # 注意:这里需要根据实际需求调整,不是必须的 pass response = await vllm_client.chat_completion(request) return response except Exception as e: raise HTTPException(status_code=500, detail=f"处理请求时出错: {str(e)}") @router.post("/chat/completions/stream") async def chat_completion_stream(request: ChatRequest): """ 流式聊天补全接口 返回Server-Sent Events (SSE) 流 """ async def event_generator(): try: async for chunk in vllm_client.chat_completion_stream(request): yield f"data: {chunk}\n\n" yield "data: [DONE]\n\n" except Exception as e: error_msg = json.dumps({"error": str(e)}) yield f"data: {error_msg}\n\n" return StreamingResponse( event_generator(), media_type="text/event-stream", headers={ "Cache-Control": "no-cache", "Connection": "keep-alive", } ) @router.post("/summarize") async def summarize_text(request: SummaryRequest): """ 长文本总结专用接口 针对GLM-4-9B-Chat-1M的长文本能力优化,适合总结超长文档 - **text**: 需要总结的长文本(支持最多约200万汉字) - **max_summary_length**: 总结的最大长度 - **instruction**: 自定义总结指令 """ try: # 构建针对总结优化的消息 summary_prompt = f"""{request.instruction} 文本内容: {request.text[:10000]}...""" # 这里简单示例,实际可以处理完整长文本 # 注意:实际处理超长文本时,需要确保vLLM服务的max_model_len参数足够大 chat_request = ChatRequest( messages=[ {"role": "user", "content": summary_prompt} ], max_tokens=request.max_summary_length, temperature=0.3, # 总结任务降低随机性 top_p=0.9 ) response = await vllm_client.chat_completion(chat_request) if response.choices: summary = response.choices[0].message.content return { "summary": summary, "original_length": len(request.text), "summary_length": len(summary), "model": response.model } else: raise HTTPException(status_code=500, detail="模型未生成有效总结") except Exception as e: raise HTTPException(status_code=500, detail=f"总结文本时出错: {str(e)}") @router.get("/health") async def health_check(): """健康检查端点""" vllm_healthy = await vllm_client.check_health() return { "status": "healthy" if vllm_healthy else "degraded", "model_loaded": vllm_healthy, "vllm_status": "running" if vllm_healthy else "unavailable", "timestamp": int(time.time()) }3.5 创建配置和主应用文件
创建app/config.py:
import os from pydantic_settings import BaseSettings class Settings(BaseSettings): """应用配置""" app_name: str = "GLM-4-9B-Chat-1M API Service" app_version: str = "1.0.0" # vLLM服务配置 vllm_base_url: str = os.getenv("VLLM_BASE_URL", "http://localhost:8000") vllm_api_key: str = os.getenv("VLLM_API_KEY", "token-abc123") # 服务配置 host: str = os.getenv("HOST", "0.0.0.0") port: int = int(os.getenv("PORT", "8080")) # 安全配置 cors_origins: list = ["*"] # 生产环境应该配置具体的域名 class Config: env_file = ".env" settings = Settings()创建app/main.py:
from fastapi import FastAPI from fastapi.middleware.cors import CORSMiddleware import logging from app.config import settings from app.routers import chat # 配置日志 logging.basicConfig( level=logging.INFO, format='%(asctime)s - %(name)s - %(levelname)s - %(message)s' ) logger = logging.getLogger(__name__) # 创建FastAPI应用 app = FastAPI( title=settings.app_name, version=settings.app_version, description="GLM-4-9B-Chat-1M 长文本模型API微服务", docs_url="/docs", # Swagger UI文档 redoc_url="/redoc", # ReDoc文档 ) # 添加CORS中间件 app.add_middleware( CORSMiddleware, allow_origins=settings.cors_origins, allow_credentials=True, allow_methods=["*"], allow_headers=["*"], ) # 包含路由 app.include_router(chat.router) @app.get("/") async def root(): """根端点,返回服务信息""" return { "service": settings.app_name, "version": settings.app_version, "status": "running", "model": "GLM-4-9B-Chat-1M", "endpoints": { "docs": "/docs", "health": "/api/v1/health", "chat": "/api/v1/chat/completions", "summarize": "/api/v1/summarize" } } @app.on_event("startup") async def startup_event(): """应用启动时执行""" logger.info(f"{settings.app_name} v{settings.app_version} 正在启动...") logger.info(f"vLLM服务地址: {settings.vllm_base_url}") @app.on_event("shutdown") async def shutdown_event(): """应用关闭时执行""" logger.info(f"{settings.app_name} 正在关闭...") if __name__ == "__main__": import uvicorn uvicorn.run( "app.main:app", host=settings.host, port=settings.port, reload=True, # 开发时启用热重载 log_level="info" )3.6 启动FastAPI服务
现在,在项目根目录下,运行以下命令启动FastAPI服务:
# 确保你在虚拟环境中,并且位于项目根目录 python -m app.main或者使用uvicorn直接启动:
uvicorn app.main:app --host 0.0.0.0 --port 8080 --reload服务启动后,你会看到输出信息,包括服务运行的地址(通常是http://localhost:8080)。
4. 测试与使用你的API服务
现在,你的GLM-4-9B-Chat-1M微服务已经运行起来了!让我们测试一下。
4.1 访问API文档
打开浏览器,访问http://localhost:8080/docs,你会看到自动生成的Swagger UI文档。这里可以查看所有可用的API端点,并且可以直接在页面上测试。
4.2 使用curl测试API
测试健康检查:
curl http://localhost:8080/api/v1/health测试聊天接口:
curl -X POST "http://localhost:8080/api/v1/chat/completions" \ -H "Content-Type: application/json" \ -d '{ "messages": [ {"role": "user", "content": "你好,请介绍一下你自己"} ], "max_tokens": 200 }'测试长文本总结接口:
# 准备一个长文本(这里用简短的示例) LONG_TEXT="在人工智能领域,大语言模型正在快速发展..." # 这里应该是你的长文本 curl -X POST "http://localhost:8080/api/v1/summarize" \ -H "Content-Type: application/json" \ -d "{ \"text\": \"$LONG_TEXT\", \"max_summary_length\": 300 }"4.3 使用Python客户端调用
创建一个测试客户端脚本test_client.py:
import asyncio import aiohttp import json async def test_chat(): """测试聊天接口""" url = "http://localhost:8080/api/v1/chat/completions" payload = { "messages": [ {"role": "user", "content": "用中文写一个关于春天的短诗"} ], "max_tokens": 150, "temperature": 0.8 } async with aiohttp.ClientSession() as session: async with session.post(url, json=payload) as response: result = await response.json() print("聊天响应:") print(json.dumps(result, indent=2, ensure_ascii=False)) if "choices" in result and result["choices"]: message = result["choices"][0]["message"]["content"] print(f"\nAI回复: {message}") async def test_stream_chat(): """测试流式聊天接口""" url = "http://localhost:8080/api/v1/chat/completions/stream" payload = { "messages": [ {"role": "user", "content": "解释一下什么是机器学习"} ], "max_tokens": 300, "stream": True } async with aiohttp.ClientSession() as session: async with session.post(url, json=payload) as response: print("流式响应(逐块接收):") async for line in response.content: if line: decoded = line.decode('utf-8').strip() if decoded.startswith('data: '): data = decoded[6:] if data != '[DONE]': try: chunk = json.loads(data) if "choices" in chunk and chunk["choices"]: delta = chunk["choices"][0]["delta"] if "content" in delta: print(delta["content"], end="", flush=True) except: pass print() # 换行 async def main(): print("测试GLM-4-9B-Chat-1M API服务") print("=" * 50) # 测试普通聊天 await test_chat() print("\n" + "=" * 50 + "\n") # 测试流式聊天 await test_stream_chat() if __name__ == "__main__": asyncio.run(main())运行测试脚本:
python test_client.py5. 生产环境部署建议
当你完成开发和测试,准备将服务部署到生产环境时,这里有一些建议:
5.1 使用Gunicorn运行(Linux/Unix)
对于生产环境,建议使用Gunicorn作为ASGI服务器:
# 安装gunicorn pip install gunicorn # 使用gunicorn运行(使用多个worker进程) gunicorn -w 4 -k uvicorn.workers.UvicornWorker app.main:app --bind 0.0.0.0:8080 --timeout 1205.2 使用Docker容器化
创建Dockerfile:
FROM python:3.10-slim WORKDIR /app # 复制依赖文件 COPY requirements.txt . # 安装依赖 RUN pip install --no-cache-dir -r requirements.txt # 复制应用代码 COPY app/ ./app/ # 创建非root用户 RUN useradd -m -u 1000 appuser && chown -R appuser:appuser /app USER appuser # 暴露端口 EXPOSE 8080 # 运行应用 CMD ["uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "8080"]创建docker-compose.yml来编排服务:
version: '3.8' services: vllm-service: image: vllm/vllm-openai:latest command: > --model THUDM/glm-4-9b-chat-1m --served-model-name glm-4-9b-chat-1m --max-model-len 1048576 --dtype half --quantization awq --api-key ${VLLM_API_KEY:-token-abc123} --port 8000 ports: - "8000:8000" deploy: resources: reservations: devices: - driver: nvidia count: 1 capabilities: [gpu] environment: - HF_TOKEN=${HF_TOKEN} volumes: - ~/.cache/huggingface:/root/.cache/huggingface api-service: build: . ports: - "8080:8080" environment: - VLLM_BASE_URL=http://vllm-service:8000 - VLLM_API_KEY=${VLLM_API_KEY:-token-abc123} depends_on: - vllm-service restart: unless-stopped5.3 配置反向代理(Nginx)
在生产环境中,建议使用Nginx作为反向代理:
server { listen 80; server_name your-domain.com; location / { proxy_pass http://localhost:8080; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; # WebSocket支持(用于流式响应) proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection "upgrade"; # 增加超时时间,适合长文本处理 proxy_read_timeout 300s; proxy_connect_timeout 75s; } # 静态文件服务(如果有) location /static/ { alias /path/to/static/files/; expires 30d; } }5.4 监控与日志
- 日志配置:确保日志记录到文件,并设置日志轮转
- 健康检查:我们已经实现了
/api/v1/health端点,可以用于Kubernetes就绪性和存活性探针 - 指标监控:考虑添加Prometheus指标(可以使用
prometheus-fastapi-instrumentator)
6. 总结
通过这篇教程,我们完成了一个完整的GLM-4-9B-Chat-1M微服务搭建过程。让我们回顾一下关键点:
模型选择:GLM-4-9B-Chat-1M以其1M tokens的超长上下文能力,特别适合处理长文档、多文件分析等场景,而且单卡即可部署。
架构清晰:我们采用了FastAPI + vLLM的分层架构。FastAPI负责对外提供标准API接口、输入验证和业务逻辑;vLLM专门负责高效、高性能的模型推理。
功能完整:实现了标准的聊天补全接口(支持流式和非流式)、专门的长文本总结接口、健康检查等,基本覆盖了常见的使用场景。
易于扩展:这个架构很容易扩展。你可以:
- 添加新的路由来处理特定任务(如文档问答、代码分析等)
- 集成认证和授权机制
- 添加缓存层来提高频繁请求的响应速度
- 实现请求队列和限流来管理高并发场景
生产就绪:我们讨论了从开发到生产部署的完整流程,包括Docker容器化、反向代理配置等考虑。
这个微服务现在可以轻松集成到你的任何应用中,无论是Web应用、移动应用还是企业内部系统。GLM-4-9B-Chat-1M的长文本处理能力,让它特别适合知识库问答、文档分析、研究报告总结等需要处理大量文本的场景。
获取更多AI镜像
想探索更多AI镜像和应用场景?访问 CSDN星图镜像广场,提供丰富的预置镜像,覆盖大模型推理、图像生成、视频生成、模型微调等多个领域,支持一键部署。