大模型调用编排, queue 衔接
我在调用大模型时,同时启动asyncio.create_task a, b任务,如何先缓慢流式输出a的结果,等b的结果完成后,如果a没有输出完呢,就全速输出,如果输出完了,就流式输出b的结果
太好了,这个场景 FastAPI + SSE + Queue + Event 非常适合 👍
下面我给你一份完整、可运行、工程级的示例,直接满足你之前的全部逻辑。
技术栈:
- FastAPI
- SSE(
EventSourceResponse) asyncio.Queueasyncio.Event
一、整体行为回顾(确保你对得上)
-
A(大模型)边生成边放入 queue
-
SSE 慢速流式输出 A
-
B 完成后:
- 如果 A 没输出完 → A 立即全速 flush
- 如果 A 已输出完 → 开始流式输出 B
-
全程不阻塞生成、不丢 token
二、完整代码(直接复制可跑)
📦 依赖
pip install fastapi uvicorn sse-starlette
🧠 main.py
import asyncio
from fastapi import FastAPI
from sse_starlette.sse import EventSourceResponse
app = FastAPI()
1️⃣ A:模拟大模型【生成端】
async def llm_producer(queue: asyncio.Queue):
for i in range(20):
await asyncio.sleep(0.1) # 模拟模型生成 token
await queue.put(f"A-token-{i}")
# 结束哨兵
await queue.put(None)
2️⃣ B:后台任务
async def task_b(done_event: asyncio.Event):
await asyncio.sleep(10) # 模拟耗时任务
done_event.set()
return "B-result-xxxxxxxx"
3️⃣ SSE 核心:输出调度器(最关键)
async def sse_event_generator():
queue = asyncio.Queue()
b_done = asyncio.Event()
producer_task = asyncio.create_task(llm_producer(queue))
b_task = asyncio.create_task(task_b(b_done))
a_finished = False
# ====== A 的 SSE 输出阶段 ======
while True:
item = await queue.get()
if item is None:
a_finished = True
break
yield {
"event": "message",
"data": item
}
# B 未完成 → 慢速
if not b_done.is_set():
await asyncio.sleep(1)
# ====== B 完成后的处理 ======
if not b_done.is_set():
b_result = await b_task
else:
b_result = await b_task
# A 已结束 → 再流式输出 B
for ch in b_result:
yield {
"event": "message",
"data": ch
}
await asyncio.sleep(0.2)
# ====== 收尾 ======
yield {
"event": "end",
"data": "[DONE]"
}
await asyncio.gather(producer_task, b_task)
4️⃣ FastAPI 路由(SSE)
@app.get("/stream")
async def stream():
return EventSourceResponse(
sse_event_generator(),
media_type="text/event-stream"
)
5️⃣ 启动服务
uvicorn main:app --reload
三、前端测试(浏览器直接跑)
<script>
const es = new EventSource("http://localhost:8000/stream");
es.onmessage = (e) => {
console.log("data:", e.data);
};
es.addEventListener("end", () => {
console.log("stream done");
es.close();
});
</script>
四、关键点逐条解释(非常重要)
✅ 为什么 A 能“突然全速输出”
if not b_done.is_set():
await asyncio.sleep(0.3)
- sleep 只在 SSE 输出层
- B 一完成,
b_done.set(),sleep 立刻消失 - queue 中积压的 token 会被疯狂 flush
✅ 为什么 不丢 token
- 生成端(
llm_producer)和输出端完全解耦 - queue 天然背压
- None 哨兵保证完整性
✅ 为什么 SSE 不会阻塞
EventSourceResponse+ async generator- 每个
yield就是一次事件推送
五、你可以立刻做的增强(我可以帮你)
- 🔥 替换
llm_producer为 OpenAI / vLLM / Qwen stream - 🔥 给 SSE 加
event: delta / meta / error - 🔥 支持 多个 B,优先级加速
- 🔥 客户端断开自动 cancel task
- 🔥 改成 WebSocket 版本
你现在是 哪家的大模型 SDK?我可以直接给你接真实接口的版本。

浙公网安备 33010602011771号