【大模型调用编排】Task A流式输出,Task B/C/D并发执行
启动任务
一、你现在的明确需求(确认)
你要的是:
-
Task A
- 调用大模型
- 流式输出
-
Task B / C / D
- 与 Task A 异步并发执行
- 非流式调用
- 并发跑
- 结果缓存(内存即可)
-
输出顺序
- Task A 先流式输出
- Task A 完成后
- 再把 Task B / C / D 的结果依次流式输出
-
接口
- FastAPI
- EventSourceResponse(SSE)
- 可用 Postman 调用
⚠️ 注意:
Postman 支持 SSE(EventStream),但需要用Send(不是 Send and Download)
二、整体架构(推荐)
┌──────────────┐
│ FastAPI POST │
└──────┬───────┘
│
▼
┌──────────────────────────┐
│ stream_controller │ ← 统一调度
│ │
│ create_task(Task A) │
│ create_task(Task B) │
│ create_task(Task C) │
│ create_task(Task D) │
│ │
│ await Task A (stream) │
│ await B/C/D (result) │
│ stream B/C/D result │
└──────────────────────────┘
三、关键技术点
| 需求 | 方案 |
|---|---|
| 并发 | asyncio.create_task |
| 多任务结果收集 | asyncio.gather |
| 流式输出 | async generator |
| SSE | EventSourceResponse |
| 顺序控制 | await |
四、完整可运行示例
1️⃣ 模拟大模型接口
Task A(流式)
import asyncio
from typing import AsyncGenerator
async def llm_stream_call(name: str) -> AsyncGenerator[str, None]:
for i in range(5):
await asyncio.sleep(0.6)
yield f"{name} token {i}"
Task B / C / D(非流式)
async def llm_call(name: str) -> str:
await asyncio.sleep(2)
return f"{name} final result"
2️⃣ Task 定义
Task A(流式)
async def task_a():
async for chunk in llm_stream_call("TaskA"):
yield chunk
Task B / C / D(并发、非流式)
async def task_b():
return await llm_call("TaskB")
async def task_c():
return await llm_call("TaskC")
async def task_d():
return await llm_call("TaskD")
3️⃣ 核心:流式调度器(重点)
from sse_starlette.sse import EventSourceResponse
async def stream_controller(payload: dict):
# 1️⃣ 并发启动 B / C / D(不阻塞)
b_task = asyncio.create_task(task_b())
c_task = asyncio.create_task(task_c())
d_task = asyncio.create_task(task_d())
# 2️⃣ 先流式输出 Task A
async for chunk in task_a():
yield {
"event": "task_a",
"data": chunk
}
# 3️⃣ Task A 完成后,等待 B/C/D 结果
results = await asyncio.gather(
b_task,
c_task,
d_task,
return_exceptions=True
)
# 4️⃣ 再把 B / C / D 的结果流式吐给客户端
for name, result in zip(["task_b", "task_c", "task_d"], results):
if isinstance(result, Exception):
yield {
"event": "error",
"data": f"{name} failed: {result}"
}
else:
yield {
"event": name,
"data": result
}
4️⃣ FastAPI POST 接口(SSE)
from fastapi import FastAPI, Request
app = FastAPI()
@app.post("/stream")
async def stream_endpoint(request: Request):
payload = await request.json()
return EventSourceResponse(
stream_controller(payload),
media_type="text/event-stream"
)
五、Postman 调用方式(非常关键)
1️⃣ 请求方式
- Method:
POST - URL:
http://localhost:8000/stream
2️⃣ Headers
Content-Type: application/json
Accept: text/event-stream
3️⃣ Body(raw / JSON)
{
"query": "test"
}
4️⃣ Postman 现象
你会看到类似:
event: task_a
data: TaskA token 0
event: task_a
data: TaskA token 1
...
event: task_b
data: TaskB final result
event: task_c
data: TaskC final result
event: task_d
data: TaskD final result
六、为什么这个方案是「工业级正确解」
✅ Task A 与 B/C/D 真正并发
- B/C/D 在 Task A 输出时已经跑完大半
✅ 无流式阻塞
- Task A 的 token 不会被 B/C/D 卡住
✅ SSE 标准
- 前端 / Postman / 浏览器都支持
✅ 极易扩展
-
后续你可以:
- Task DAG
- 条件分支
- 多阶段 stream
七、常见坑提醒(你很可能会踩)
❌ 错误:在 generator 里 await 太久
会卡死流式输出
❌ 错误:B/C/D 直接 yield
会乱序
✅ 正确:并发算,顺序吐
八、如果你愿意,我可以继续帮你
我可以直接帮你:
- 改成 真实 OpenAI / Azure / 通义 / Claude 流式 SDK
- 封装成 通用 Task Orchestrator
- 设计 多阶段 + 条件分支 LLM pipeline
- 增加 心跳 / keep-alive / 超时取消
你可以直接说下一步你想要哪一个。

浙公网安备 33010602011771号