1. 项目概述
在当今的AI应用开发中,如何高效地将大模型能力集成到Web服务中是一个常见需求。本文将详细介绍如何使用FastAPI构建一个Web API接口来调用ollama服务,实现模型推理的标准化接入。
ollama是一个流行的本地大模型运行工具,而FastAPI作为Python生态中高性能的Web框架,二者结合可以快速搭建起AI服务接口。这个方案特别适合需要将大模型能力集成到现有系统中的开发者,或者希望构建自定义AI接口的团队。
2. 环境准备与依赖安装
2.1 基础环境配置
首先确保你的开发环境满足以下要求:
- Python 3.7或更高版本
- pip包管理工具
- 已安装ollama并配置好模型(如llama2、mistral等)
提示:ollama的安装可以参考其官方文档,通常只需要一行命令即可完成基础安装。
2.2 安装必要的Python包
在项目目录下创建并激活虚拟环境后,安装以下依赖:
bash复制pip install fastapi uvicorn pydantic httpx
各包的作用说明:
fastapi: 核心Web框架uvicorn: ASGI服务器,用于运行FastAPI应用pydantic: 数据验证和设置管理httpx: 异步HTTP客户端,用于调用ollama接口
3. FastAPI应用基础搭建
3.1 初始化FastAPI应用
创建一个名为main.py的文件,初始化FastAPI应用:
python复制from fastapi import FastAPI
from fastapi.middleware.cors import CORSMiddleware
app = FastAPI()
# 配置CORS中间件
app.add_middleware(
CORSMiddleware,
allow_origins=["*"],
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
)
CORS配置说明:
allow_origins=["*"]: 允许所有来源访问(生产环境应限制为特定域名)allow_credentials=True: 允许携带凭证(如cookies)allow_methods=["*"]: 允许所有HTTP方法allow_headers=["*"]: 允许所有请求头
3.2 定义请求参数模型
使用Pydantic定义输入数据的结构和验证规则:
python复制from pydantic import BaseModel
from typing import Optional
class ChatRequest(BaseModel):
prompt: str
model: str = "llama2" # 默认模型
stream: Optional[bool] = False # 是否流式响应
这个模型将自动验证传入的JSON数据,确保:
prompt字段必须存在且为字符串model字段可选,默认为"llama2"stream字段可选,默认为False
4. ollama接口调用实现
4.1 基础API调用方法
实现调用ollama的基础方法:
python复制import httpx
OLLAMA_URL = "http://localhost:11434/api/generate"
async def ollama_api(url: str, data: dict):
async with httpx.AsyncClient() as client:
try:
response = await client.post(url, json=data, timeout=60.0)
response.raise_for_status()
return response
except httpx.HTTPStatusError as e:
raise HTTPException(
status_code=e.response.status_code,
detail=f"Ollama API error: {str(e)}"
)
关键点说明:
- 使用
httpx.AsyncClient实现异步HTTP调用 - 设置60秒超时防止长时间阻塞
- 对HTTP错误状态码进行统一处理
4.2 流式响应处理
对于需要流式输出的场景,实现专门的流式处理方法:
python复制from fastapi.responses import StreamingResponse
async def ollama_stream_response(response):
async for chunk in response.aiter_lines():
if chunk:
yield f"data: {chunk}\n\n"
这个方法:
- 使用
aiter_lines()逐行读取响应内容 - 按照Server-Sent Events(SSE)格式封装数据
- 通过生成器逐步返回数据,实现流式传输
5. 核心路由实现
5.1 聊天接口实现
将上述组件组合起来实现完整的聊天接口:
python复制from fastapi import HTTPException
from fastapi.responses import StreamingResponse
@app.post("/api/chat")
async def chat(chat_request: ChatRequest):
data = {
"model": chat_request.model,
"prompt": chat_request.prompt,
"stream": chat_request.stream
}
if chat_request.stream:
response = await ollama_api(OLLAMA_URL, data)
return StreamingResponse(
ollama_stream_response(response),
media_type="text/event-stream"
)
else:
response = await ollama_api(OLLAMA_URL, data)
return response.json()
接口逻辑说明:
- 接收符合
ChatRequest模型的JSON数据 - 根据
stream参数决定返回普通响应还是流式响应 - 流式响应使用
StreamingResponse包装,设置正确的媒体类型
5.2 异常处理
统一处理可能出现的异常:
python复制from fastapi import HTTPException
from fastapi.responses import JSONResponse
@app.exception_handler(HTTPException)
async def http_exception_handler(request, exc):
return JSONResponse(
status_code=exc.status_code,
content={"error": exc.detail}
)
@app.exception_handler(Exception)
async def generic_exception_handler(request, exc):
return JSONResponse(
status_code=500,
content={"error": "Internal server error"}
)
这种处理方式确保:
- 已知的HTTP异常返回具体的错误信息
- 未知异常返回500状态码和通用错误信息
- 所有错误响应保持一致的格式
6. 测试与验证
6.1 启动服务
使用uvicorn启动开发服务器:
bash复制uvicorn main:app --reload
服务默认运行在http://127.0.0.1:8000,可以通过/docs访问自动生成的交互式API文档。
6.2 测试普通请求
使用curl测试普通请求:
bash复制curl -X POST "http://127.0.0.1:8000/api/chat" \
-H "Content-Type: application/json" \
-d '{"prompt":"解释量子计算的基本原理","model":"llama2"}'
预期返回完整的JSON响应。
6.3 测试流式请求
测试流式响应:
bash复制curl -X POST "http://127.0.0.1:8000/api/chat" \
-H "Content-Type: application/json" \
-d '{"prompt":"用简单的语言解释区块链","model":"llama2","stream":true}'
预期看到数据逐步返回的效果。
7. 生产环境部署建议
7.1 性能优化配置
对于生产环境,建议调整uvicorn配置:
bash复制uvicorn main:app --host 0.0.0.0 --port 8000 --workers 4
关键参数:
--workers 4: 根据CPU核心数设置工作进程数--host 0.0.0.0: 监听所有网络接口- 考虑添加
--timeout-keep-alive等参数优化长连接
7.2 安全加固措施
- 限制CORS来源:
python复制origins = [
"https://yourdomain.com",
"https://app.yourdomain.com"
]
app.add_middleware(
CORSMiddleware,
allow_origins=origins,
allow_credentials=True,
allow_methods=["POST"],
allow_headers=["Content-Type"],
)
- 添加API密钥认证:
python复制from fastapi.security import APIKeyHeader
api_key_header = APIKeyHeader(name="X-API-Key")
async def get_api_key(api_key: str = Depends(api_key_header)):
if api_key != "your_secret_key":
raise HTTPException(
status_code=401,
detail="Invalid API Key"
)
return api_key
@app.post("/api/chat")
async def chat(chat_request: ChatRequest, api_key: str = Depends(get_api_key)):
# 原有逻辑
8. 常见问题与解决方案
8.1 ollama服务连接问题
问题现象:连接ollama服务超时或拒绝连接
排查步骤:
- 确认ollama服务正在运行:
ollama list - 检查服务端口(默认11434)是否监听:
netstat -tulnp | grep 11434 - 测试直接访问ollama API:
curl http://localhost:11434/api/generate -d '{"model":"llama2","prompt":"hello"}'
解决方案:
- 确保ollama服务已正确启动
- 检查防火墙设置,确保端口可访问
- 如果服务运行在容器中,确保网络配置正确
8.2 流式响应中断问题
问题现象:流式响应中途断开
可能原因:
- 网络不稳定
- 客户端过早关闭连接
- ollama服务响应慢导致超时
解决方案:
- 增加超时时间:
python复制async with httpx.AsyncClient(timeout=300.0) as client:
# 调用逻辑
- 客户端实现重试机制
- 添加心跳保持连接
8.3 性能优化技巧
- 连接池配置:
python复制client = httpx.AsyncClient(limits=httpx.Limits(
max_connections=100,
max_keepalive_connections=20
))
- 响应缓存:对常见prompt的响应进行缓存
python复制from fastapi_cache import FastAPICache
from fastapi_cache.backends.redis import RedisBackend
from fastapi_cache.decorator import cache
@app.post("/api/chat")
@cache(expire=300) # 缓存5分钟
async def chat(chat_request: ChatRequest):
# 原有逻辑
- 批量处理请求:对于可以批量处理的场景,实现批量接口
9. 扩展功能建议
9.1 添加对话历史支持
扩展ChatRequest模型以支持多轮对话:
python复制class Message(BaseModel):
role: str # "user" 或 "assistant"
content: str
class ChatRequest(BaseModel):
messages: List[Message]
model: str = "llama2"
stream: Optional[bool] = False
9.2 实现速率限制
使用FastAPI的中间件实现API速率限制:
python复制from fastapi import Request
from fastapi.middleware import Middleware
from slowapi import Limiter
from slowapi.util import get_remote_address
limiter = Limiter(key_func=get_remote_address)
app.state.limiter = limiter
@app.post("/api/chat")
@limiter.limit("5/minute")
async def chat(request: Request, chat_request: ChatRequest):
# 原有逻辑
9.3 添加健康检查端点
python复制@app.get("/health")
async def health_check():
try:
async with httpx.AsyncClient() as client:
response = await client.get("http://localhost:11434")
response.raise_for_status()
return {"status": "healthy"}
except Exception:
raise HTTPException(status_code=503, detail="Service unavailable")
10. 项目结构优化建议
对于更复杂的项目,建议采用模块化结构:
code复制project/
├── app/
│ ├── __init__.py
│ ├── main.py # FastAPI应用创建和配置
│ ├── routes/ # 路由模块
│ │ ├── chat.py # 聊天相关路由
│ │ └── health.py # 健康检查路由
│ ├── models/ # Pydantic模型
│ ├── services/ # 业务逻辑
│ │ └── ollama.py # ollama服务封装
│ └── utils/ # 工具函数
│ └── exceptions.py # 异常处理
├── tests/ # 测试代码
├── requirements.txt # 依赖文件
└── README.md # 项目文档
这种结构使得代码更易于维护和扩展,特别适合团队协作开发。