高并发异步与 AI 流式实战

在大语言模型(LLM)与智能体(Agent)开发中,流式传输 JSON Lines、Server-Sent Events (SSE)、WebSocket 双向长连接以及底层异步调度是构建现代 AI 微服务的核心。

Mermaid 图表

FastAPI 异步并发执行机制深度剖析

避免在 async def 中调用同步阻塞代码

切勿在 async def 中调用同步阻塞代码(如 time.sleep(5) 或 requests.get),否则会导致整个主线程的事件循环被完全卡死!


流式传输 JSON Lines (Stream JSON Lines)

FastAPI 官方文档展示了通过 StreamingResponse 逐行推送 JSON Lines (.jsonl) 数据流的模式:

import asyncio
import json
from typing import AsyncGenerator
from fastapi import FastAPI
from fastapi.responses import StreamingResponse

app = FastAPI()

async def json_lines_generator() -> AsyncGenerator[str, None]:
    for i in range(1, 6):
        record = {"event_id": i, "status": "processing", "progress": i * 20}
        # 每行输出一个独立的 JSON 字符串并以 \n 换行
        yield json.dumps(record, ensure_ascii=False) + "\n"
        await asyncio.sleep(0.5)

@app.get("/stream/jsonl")
async def stream_json_lines():
    return StreamingResponse(
        json_lines_generator(),
        media_type="application/x-ndjson"
    )

Server-Sent Events (SSE) 服务端推送

import asyncio
import json
from typing import AsyncGenerator
from fastapi import FastAPI
from fastapi.responses import StreamingResponse

app = FastAPI()

async def sse_event_generator() -> AsyncGenerator[str, None]:
    for i in range(1, 6):
        event_data = {"step": i, "message": f"思考步骤 {i}..."}
        # SSE 标准数据行以 data: 开头,以 \n\n 结尾
        yield f"data: {json.dumps(event_data, ensure_ascii=False)}\n\n"
        await asyncio.sleep(0.5)
    yield "data: [DONE]\n\n"

@app.get("/api/v1/sse/events")
async def stream_sse():
    return StreamingResponse(
        sse_event_generator(),
        media_type="text/event-stream",
        headers={"Cache-Control": "no-cache", "Connection": "keep-alive"}
    )

大模型 (LLM) 真实流式输出实战

from typing import AsyncGenerator
from fastapi import FastAPI
from fastapi.responses import StreamingResponse
from pydantic import BaseModel
from openai import AsyncOpenAI
import os

app = FastAPI()

client = AsyncOpenAI(
    api_key=os.getenv("OPENAI_API_KEY", "your-key"),
    base_url=os.getenv("OPENAI_BASE_URL", "https://api.openai.com/v1")
)

class ChatRequest(BaseModel):
    prompt: str
    model: str = "gpt-4o"

async def llm_stream_generator(prompt: str, model: str) -> AsyncGenerator[str, None]:
    stream = await client.chat.completions.create(
        model=model,
        messages=[{"role": "user", "content": prompt}],
        stream=True
    )
    async for chunk in stream:
        content = chunk.choices[0].delta.content
        if content:
            yield f"data: {content}\n\n"
    yield "data: [DONE]\n\n"

@app.post("/api/v1/chat/stream")
async def chat_stream(request: ChatRequest):
    return StreamingResponse(
        llm_stream_generator(request.prompt, request.model),
        media_type="text/event-stream",
        headers={"Cache-Control": "no-cache", "Connection": "keep-alive"}
    )

WebSocket 全双工长连接实战

from typing import list
from fastapi import FastAPI, WebSocket, WebSocketDisconnect

app = FastAPI()

class ConnectionManager:
    def __init__(self):
        self.active_connections: list[WebSocket] = []

    async def connect(self, websocket: WebSocket):
        await websocket.accept()
        self.active_connections.append(websocket)

    def disconnect(self, websocket: WebSocket):
        self.active_connections.remove(websocket)

    async def broadcast(self, message: str):
        for connection in self.active_connections:
            await connection.send_text(message)

manager = ConnectionManager()

@app.websocket("/ws/chat/{client_id}")
async def websocket_chat(websocket: WebSocket, client_id: str):
    await manager.connect(websocket)
    await manager.broadcast(f"用户 [{client_id}] 进入了房间")
    try:
        while True:
            data = await websocket.receive_text()
            await manager.broadcast(f"[{client_id}]: {data}")
    except WebSocketDisconnect:
        manager.disconnect(websocket)
        await manager.broadcast(f"用户 [{client_id}] 离开了房间")