AI Agent 的并发调度工程实战:任务队列、并发限制与 Fan-out/Fan-in 模式
Datadog 2026 年的 AI 工程现状报告显示:5% 的 LLM 调用 span 报错,其中 60% 是 rate limit 触发的。而这 60% 里,绝大多数源自同一个根因------并发没有控制好,重试风暴引发系统雪崩。
我在生产中遇到过这样的场景:一个 AI Agent 接到需求,并发启动 20 个子 Agent 去爬取、总结、比较数据。前 3 秒看起来很快,第 4 秒 API 开始 429,第 5 秒重试进来,第 8 秒整个 LLM 调用队列爆了,服务不可用。
无脑并发,是 AI Agent 在生产中最常见的工程事故。
这篇文章系统讲清楚三件事:
- 为什么 AI Agent 的并发比普通微服务更危险
- 三种核心并发模式:任务队列、并发限制、Fan-out/Fan-in
- 生产级实现:Python asyncio + TypeScript,含背压、超时、Hedging
一、AI Agent 并发的特殊性:不是普通的 HTTP 并发
普通微服务的并发问题,核心是数据库连接池和线程池耗尽。AI Agent 的并发问题,有三个不一样的地方:
1.1 Token 消耗是非线性的
10 个并发请求,不只是 10 倍的 QPS,还是 10 倍的 token 消耗。每个 LLM call 的输入 token 数取决于 context 长度,Agent 任务越复杂,context 越长,token 越多。触发 rate limit 的往往不是请求数,而是 TPM(tokens per minute)上限。
OpenAI 的 TPM 上限按 tier 划分:Tier 1 是 200K TPM,Tier 5 才有 10M TPM。一个中等 Agent 任务可能每次消耗 2000-5000 tokens,20 个并发瞬间打满 Tier 1 的 TPM 上限。
1.2 Fan-out 下的尾延迟放大效应
这是很多人没意识到的数学问题。
假设一个 Agent 任务需要并发调用 N 个子任务,每个子任务完成时间满足均值 2s、标准差 0.5s 的正态分布。
Fan-out 的整体延迟 = max(子任务延迟),而不是 mean(子任务延迟)
N=1: 期望延迟 ≈ 2.0s
N=5: 期望 max ≈ 2.84s(+42%)
N=10: 期望 max ≈ 3.24s(+62%)
N=20: 期望 max ≈ 3.60s(+80%)
10 个并发子任务,哪怕 9 个都在 1.5s 完成,只要有 1 个跑了 8s,整批就等 8s。InfoQ 的研究(2026-01)证实:Fan-out 架构中,stragglers(慢节点)而非失败,是 p99 延迟的主要驱动因素。
1.3 重试风暴的正反馈
LLM API 被 rate limit 之后,正常的做法是重试。但如果 20 个并发任务同时被 429,同时开始退避重试,退避结束后又同时打进来------这就是雷群效应(Thundering Herd),会反复触发 rate limit,形成正反馈循环。
Datadog 的报告明确指出:rate limit 触发重试,重试增加负载,进一步触发 rate limit,"evolve the issue into a sustained system failure"。
二、基础模式:并发限制(Concurrency Limiter)
在处理 Fan-out 之前,先解决最基础的问题:限制同时进行的 LLM 调用数量。
2.1 Python asyncio.Semaphore
python
import asyncio
import time
from openai import AsyncOpenAI
client = AsyncOpenAI()
# 生产建议:根据你的 API tier 和 TPM 上限调整
# Tier 1: max_concurrent=5, Tier 2: max_concurrent=10
MAX_CONCURRENT = 8
semaphore = asyncio.Semaphore(MAX_CONCURRENT)
async def call_llm_with_limit(prompt: str, task_id: str) -> dict:
"""带并发限制的 LLM 调用"""
async with semaphore: # 超过 MAX_CONCURRENT 的请求会在这里等待
start = time.monotonic()
try:
response = await client.chat.completions.create(
model="gpt-4o-mini",
messages=[{"role": "user", "content": prompt}],
timeout=30.0 # 重要:必须设置超时
)
elapsed = time.monotonic() - start
return {
"task_id": task_id,
"status": "success",
"content": response.choices[0].message.content,
"elapsed_s": elapsed,
"tokens": response.usage.total_tokens
}
except Exception as e:
return {
"task_id": task_id,
"status": "error",
"error": str(e),
"elapsed_s": time.monotonic() - start
}
async def process_tasks_batch(prompts: list[dict]) -> list[dict]:
"""并发处理一批任务,带全局并发限制"""
tasks = [
call_llm_with_limit(item["prompt"], item["id"])
for item in prompts
]
# return_exceptions=True 确保单个失败不会中断整批
results = await asyncio.gather(*tasks, return_exceptions=True)
# 处理异常情况
processed = []
for i, result in enumerate(results):
if isinstance(result, Exception):
processed.append({
"task_id": prompts[i]["id"],
"status": "exception",
"error": str(result)
})
else:
processed.append(result)
return processed
关键点 :async with semaphore 的语义是"同时最多 MAX_CONCURRENT 个协程持有信号量"。第 9 个请求会在 async with 这里 await,直到有一个持有者释放。
2.2 为什么不用 asyncio.BoundedSemaphore?
BoundedSemaphore 在 release 超过 initial value 时会抛出 ValueError。在正常 acquire/release 配对使用时,两者等价。但如果你的代码有复杂的 cancel/timeout 路径,BoundedSemaphore 能帮你发现异常的释放行为,适合调试阶段。生产中用标准 Semaphore 即可。
2.3 TypeScript 版本:p-limit
typescript
import pLimit from 'p-limit';
import OpenAI from 'openai';
const client = new OpenAI();
const limit = pLimit(8); // 同时最多 8 个并发
interface TaskResult {
taskId: string;
status: 'success' | 'error';
content?: string;
error?: string;
elapsedMs: number;
}
async function callLLMWithLimit(
prompt: string,
taskId: string
): Promise<TaskResult> {
const start = Date.now();
try {
const response = await client.chat.completions.create({
model: 'gpt-4o-mini',
messages: [{ role: 'user', content: prompt }],
});
return {
taskId,
status: 'success',
content: response.choices[0].message.content ?? '',
elapsedMs: Date.now() - start,
};
} catch (err) {
return {
taskId,
status: 'error',
error: String(err),
elapsedMs: Date.now() - start,
};
}
}
async function processTasksBatch(
tasks: Array<{ id: string; prompt: string }>
): Promise<TaskResult[]> {
// limit() 会自动排队,超过并发限制的任务等待前面的完成
return Promise.all(
tasks.map((task) =>
limit(() => callLLMWithLimit(task.prompt, task.id))
)
);
}
p-limit 是 Node.js 生态里最简洁的并发限制库,内部基于 Promise 队列,无需手动管理 semaphore。
三、任务队列:处理大批量 + 背压
并发限制解决了"同时请求不超限",但当任务总数很大(比如 1000 个文档),还需要任务队列来:
- 背压:防止任务无限堆积在内存里
- 优先级:紧急任务先处理
- 持久化:程序崩溃后可恢复
3.1 内存队列(适合中等规模)
python
import asyncio
import time
from dataclasses import dataclass, field
from typing import Any
@dataclass(order=True)
class Task:
priority: int # 数字越小优先级越高
task_id: str = field(compare=False)
prompt: str = field(compare=False)
created_at: float = field(default_factory=time.time, compare=False)
async def worker(
queue: asyncio.PriorityQueue,
semaphore: asyncio.Semaphore,
results: dict,
worker_id: int
):
"""工作协程:从队列取任务,受 semaphore 限制执行"""
while True:
try:
# 从优先队列取任务(阻塞直到有任务)
task: Task = await queue.get()
wait_time = time.time() - task.created_at
print(f"[Worker-{worker_id}] task={task.task_id} waited={wait_time:.2f}s")
async with semaphore:
result = await execute_llm_task(task)
results[task.task_id] = result
queue.task_done() # 必须调用,否则 queue.join() 不会结束
except asyncio.CancelledError:
break
async def execute_llm_task(task: Task) -> dict:
"""实际执行 LLM 调用"""
# 这里调用 LLM API
await asyncio.sleep(0.1) # 模拟 LLM 调用
return {"task_id": task.task_id, "status": "success"}
async def run_pipeline(task_list: list[dict], max_workers: int = 4, max_concurrent_llm: int = 8):
"""
任务队列 + 工作者池 + 信号量三层控制
max_workers: 工作者协程数(控制内存中并发协程数)
max_concurrent_llm: 实际 LLM 调用并发数
"""
queue: asyncio.PriorityQueue = asyncio.PriorityQueue(maxsize=50) # maxsize=背压控制
semaphore = asyncio.Semaphore(max_concurrent_llm)
results = {}
# 启动工作者池
workers = [
asyncio.create_task(worker(queue, semaphore, results, i))
for i in range(max_workers)
]
# 生产者:往队列里放任务(受 maxsize 背压限制)
for item in task_list:
task = Task(
priority=item.get("priority", 5),
task_id=item["id"],
prompt=item["prompt"]
)
await queue.put(task) # 队列满时会在这里 await(背压生效)
# 等待所有任务处理完
await queue.join()
# 取消工作者
for w in workers:
w.cancel()
await asyncio.gather(*workers, return_exceptions=True)
return results
关键设计 :PriorityQueue(maxsize=50) 是背压的关键------当队列满时,await queue.put() 会阻塞生产者,自然限制了内存中待处理任务的数量。
3.2 关键参数设定指南
| 参数 | 建议值 | 说明 |
|---|---|---|
max_concurrent_llm |
5-15 | 根据 API tier 的 RPM 上限;Tier 1 建议 ≤8 |
max_workers |
max_concurrent_llm * 1.5 |
工作者数略多于并发数,避免等 IO 时工作者全部等待 |
queue maxsize |
max_workers * 3 |
队列缓冲,太小会频繁阻塞生产者,太大耗内存 |
| 任务超时 | 30-60s | 单个 LLM 调用的超时,防止一个慢任务卡住工作者 |
四、Fan-out/Fan-in:并行子任务的结果聚合
Fan-out/Fan-in(也叫 scatter-gather、map-reduce)是 AI Agent 里最常见的并发模式:协调者(Coordinator)将一个大任务分解为 N 个子任务并发执行,收集结果后聚合。
4.1 基础 Fan-out:asyncio.gather
python
import asyncio
from typing import Optional
from openai import AsyncOpenAI
client = AsyncOpenAI()
async def analyze_document(doc_id: str, content: str, angle: str) -> dict:
"""单个分析子任务"""
try:
async with asyncio.timeout(30): # Python 3.11+ 的超时语法
response = await client.chat.completions.create(
model="gpt-4o-mini",
messages=[{
"role": "user",
"content": f"从{angle}角度分析以下文档:\n\n{content}"
}]
)
return {
"doc_id": doc_id,
"angle": angle,
"analysis": response.choices[0].message.content,
"status": "success"
}
except asyncio.TimeoutError:
return {"doc_id": doc_id, "angle": angle, "status": "timeout", "analysis": None}
except Exception as e:
return {"doc_id": doc_id, "angle": angle, "status": "error", "error": str(e), "analysis": None}
async def multi_angle_analysis(document: str, angles: list[str]) -> dict:
"""
Fan-out: 从多个角度并发分析同一文档
Fan-in: 收集所有角度的分析结果,聚合摘要
"""
semaphore = asyncio.Semaphore(5) # 限制并发
async def bounded_analyze(angle: str) -> dict:
async with semaphore:
return await analyze_document("doc-001", document, angle)
# ===== Fan-out =====
subtasks = [bounded_analyze(angle) for angle in angles]
# return_exceptions=True: 单个失败不影响其他子任务
raw_results = await asyncio.gather(*subtasks, return_exceptions=True)
# ===== Fan-in =====
successful = []
failed = []
for i, result in enumerate(raw_results):
if isinstance(result, Exception):
failed.append({"angle": angles[i], "error": str(result)})
elif result["status"] == "success":
successful.append(result)
else:
failed.append(result)
# 决策:是否有足够的成功结果来生成汇总?
if len(successful) < len(angles) * 0.5: # 超过 50% 失败,返回错误
return {
"status": "insufficient_results",
"successful_count": len(successful),
"failed_count": len(failed),
"failed_details": failed
}
# 汇总成功的分析结果
combined_analyses = "\n\n".join([
f"【{r['angle']}】\n{r['analysis']}"
for r in successful
])
# 第二阶段:用 LLM 汇总(Fan-in 聚合)
summary_response = await client.chat.completions.create(
model="gpt-4o", # 汇总用更强的模型
messages=[{
"role": "user",
"content": f"综合以下多角度分析,给出总体结论:\n\n{combined_analyses}"
}]
)
return {
"status": "success",
"summary": summary_response.choices[0].message.content,
"successful_analyses": successful,
"failed_analyses": failed,
"coverage": f"{len(successful)}/{len(angles)}"
}
4.2 动态 Fan-out:结果驱动的子任务展开
更复杂的场景:Agent 不知道子任务数量,根据中间结果动态决定展开多少子任务。
python
import asyncio
from openai import AsyncOpenAI
client = AsyncOpenAI()
class DynamicFanoutAgent:
"""
动态 Fan-out Agent:
1. 先运行"分解"步骤,获得子任务列表
2. 并发执行子任务(带并发限制)
3. 聚合结果
"""
def __init__(self, max_concurrent: int = 6, max_fanout: int = 10):
self.semaphore = asyncio.Semaphore(max_concurrent)
self.max_fanout = max_fanout # 防止无限展开
async def decompose(self, goal: str) -> list[str]:
"""第一步:分解目标为子任务"""
response = await client.chat.completions.create(
model="gpt-4o-mini",
messages=[{
"role": "user",
"content": f"""将以下目标分解为独立的并行子任务(最多{self.max_fanout}个)。
目标:{goal}
以 JSON 数组返回,每个元素是一个子任务描述字符串。
格式:["子任务1", "子任务2", ...]"""
}],
response_format={"type": "json_object"}
)
import json
result = json.loads(response.choices[0].message.content)
subtasks = result.get("subtasks", result.get("tasks", []))
# 安全:限制最大展开数
return subtasks[:self.max_fanout]
async def execute_subtask(self, subtask: str, idx: int) -> dict:
"""执行单个子任务"""
async with self.semaphore:
try:
async with asyncio.timeout(45):
response = await client.chat.completions.create(
model="gpt-4o-mini",
messages=[{"role": "user", "content": subtask}]
)
return {
"idx": idx,
"subtask": subtask[:50] + "...",
"result": response.choices[0].message.content,
"status": "success"
}
except asyncio.TimeoutError:
return {"idx": idx, "subtask": subtask[:50], "status": "timeout"}
except Exception as e:
return {"idx": idx, "subtask": subtask[:50], "status": "error", "error": str(e)}
async def run(self, goal: str) -> dict:
# 分解
subtasks = await self.decompose(goal)
print(f"Fan-out: {len(subtasks)} 个子任务")
# 并发执行(Fan-out)
coroutines = [self.execute_subtask(task, i) for i, task in enumerate(subtasks)]
results = await asyncio.gather(*coroutines, return_exceptions=True)
# 聚合(Fan-in)
successful = [r for r in results if isinstance(r, dict) and r["status"] == "success"]
failed = [r for r in results if isinstance(r, Exception) or (isinstance(r, dict) and r["status"] != "success")]
print(f"Fan-in: {len(successful)} 成功, {len(failed)} 失败")
return {"goal": goal, "results": successful, "failed": failed}
五、生产级陷阱:Straggler 与 Hedging
5.1 Straggler 问题
Fan-out 最大的生产陷阱:一个慢子任务导致整批超时。
场景:并发 10 个子任务,9 个在 2s 内完成,第 10 个因为 LLM 服务器负载高跑了 12s。用户等了 12s,而不是 2s。
解决方案一:超时 + 降级
python
async def fanout_with_timeout(subtasks: list, timeout_per_task: float = 10.0) -> list:
"""每个子任务独立超时,超时的返回 None 而不是等待"""
async def task_with_timeout(coro, idx):
try:
async with asyncio.timeout(timeout_per_task):
return await coro
except asyncio.TimeoutError:
return {"idx": idx, "status": "timeout", "result": None}
coroutines = [task_with_timeout(subtask, i) for i, subtask in enumerate(subtasks)]
return await asyncio.gather(*coroutines, return_exceptions=True)
解决方案二:Hedging(对冲请求)
InfoQ 2026 年的研究显示,Adaptive Hedging 可将 p99 延迟降低 74%。原理:当某个子任务超过阈值时间还没完成,发起一个"对冲"请求,用最先完成的那个结果。
python
import asyncio
import time
async def hedged_llm_call(
prompt: str,
hedge_after_seconds: float = 3.0,
max_hedges: int = 1
) -> dict:
"""
对冲请求:如果第一个请求超过 hedge_after_seconds 还没完成,
发起第二个请求,取最先完成的结果。
"""
async def single_call(attempt_id: int) -> dict:
start = time.monotonic()
response = await client.chat.completions.create(
model="gpt-4o-mini",
messages=[{"role": "user", "content": prompt}],
timeout=30.0
)
return {
"attempt_id": attempt_id,
"content": response.choices[0].message.content,
"elapsed_s": time.monotonic() - start
}
# 创建第一个请求
tasks = {asyncio.create_task(single_call(0))}
hedge_count = 0
while True:
# 等待任意一个完成,或者超过对冲阈值
done, pending = await asyncio.wait(
tasks,
timeout=hedge_after_seconds if hedge_count < max_hedges else None,
return_when=asyncio.FIRST_COMPLETED
)
if done:
# 有任务完成,取第一个成功的结果
result = done.pop().result()
# 取消其他进行中的请求(节省 token)
for task in pending:
task.cancel()
return result
# 超时还没完成,发起对冲请求
if hedge_count < max_hedges:
hedge_count += 1
tasks.add(asyncio.create_task(single_call(hedge_count)))
# 继续等待,不设超时(等任意一个完成)
hedge_after_seconds = float('inf')
注意:Hedging 会增加 token 消耗(两个请求都会计费,即使取消了一个)。对冲阈值应该根据你的 p50 延迟设定(通常 p50 的 1.5-2 倍)。
5.2 Rate Limit 的指数退避
当遇到 429 时,正确的退避策略:
python
import asyncio
import random
import time
from openai import RateLimitError, AsyncOpenAI
client = AsyncOpenAI()
async def call_with_exponential_backoff(
prompt: str,
max_retries: int = 5,
base_delay: float = 1.0,
max_delay: float = 60.0
) -> dict:
"""指数退避 + 全量抖动,防止雷群效应"""
for attempt in range(max_retries):
try:
response = await client.chat.completions.create(
model="gpt-4o-mini",
messages=[{"role": "user", "content": prompt}]
)
return {"status": "success", "content": response.choices[0].message.content}
except RateLimitError as e:
if attempt == max_retries - 1:
return {"status": "rate_limited", "error": str(e)}
# 指数退避 + 全量抖动(Full Jitter)
# Full Jitter: sleep = random(0, min(cap, base * 2^attempt))
# 比固定指数退避更能分散请求,防止雷群
cap = min(max_delay, base_delay * (2 ** attempt))
sleep_time = random.uniform(0, cap)
print(f"Rate limited (attempt {attempt+1}/{max_retries}), sleeping {sleep_time:.1f}s")
await asyncio.sleep(sleep_time)
except Exception as e:
return {"status": "error", "error": str(e)}
return {"status": "max_retries_exceeded"}
Full Jitter(全量抖动):这是 AWS 在 2015 年总结的最佳实践,比"指数退避 + 小抖动"更能分散并发重试,在高并发场景下降低服务器负载。
六、LangGraph 中的并行节点
如果你用 LangGraph,并发调度有原生支持:
python
from langgraph.graph import StateGraph, START, END
from typing import TypedDict, Annotated
import operator
class AgentState(TypedDict):
query: str
research_results: Annotated[list, operator.add] # 多个节点写入,用 add reducer
synthesis: str
async def research_node_a(state: AgentState) -> dict:
"""并行节点 A:从数据库搜索"""
result = await search_database(state["query"])
return {"research_results": [{"source": "db", "content": result}]}
async def research_node_b(state: AgentState) -> dict:
"""并行节点 B:从网络搜索"""
result = await search_web(state["query"])
return {"research_results": [{"source": "web", "content": result}]}
async def research_node_c(state: AgentState) -> dict:
"""并行节点 C:从知识库检索"""
result = await search_knowledge_base(state["query"])
return {"research_results": [{"source": "kb", "content": result}]}
async def synthesize_node(state: AgentState) -> dict:
"""Fan-in:合并所有研究结果"""
all_results = state["research_results"]
# 调用 LLM 综合所有结果
synthesis = await synthesize_results(state["query"], all_results)
return {"synthesis": synthesis}
# 构建图
builder = StateGraph(AgentState)
builder.add_node("research_a", research_node_a)
builder.add_node("research_b", research_node_b)
builder.add_node("research_c", research_node_c)
builder.add_node("synthesize", synthesize_node)
# 并行展开(Fan-out)
builder.add_edge(START, "research_a")
builder.add_edge(START, "research_b")
builder.add_edge(START, "research_c")
# 收敛(Fan-in)
builder.add_edge("research_a", "synthesize")
builder.add_edge("research_b", "synthesize")
builder.add_edge("research_c", "synthesize")
builder.add_edge("synthesize", END)
graph = builder.compile()
注意 LangGraph 的一个已知问题 :LangGraph Forum(2026-03)有用户反馈 parallel nodes 存在"sequential execution issue",即声明为并行的节点实际上顺序执行。确认并行是否真正生效的方法:加计时日志,用 asyncio.current_task() 验证是否在不同 Task 中执行。
七、生产监控:并发调度的可观测性
并发系统的 bug 往往不是报错,而是"怎么这么慢"。必须监控这几个指标:
python
import asyncio
import time
from dataclasses import dataclass, field
from collections import defaultdict
@dataclass
class ConcurrencyMetrics:
"""并发指标追踪"""
active_count: int = 0
queue_depth: int = 0
completed: int = 0
failed: int = 0
rate_limited: int = 0
latencies: list = field(default_factory=list)
def p50(self) -> float:
if not self.latencies:
return 0
sorted_l = sorted(self.latencies)
return sorted_l[len(sorted_l) // 2]
def p99(self) -> float:
if not self.latencies:
return 0
sorted_l = sorted(self.latencies)
return sorted_l[int(len(sorted_l) * 0.99)]
def report(self) -> str:
return (
f"active={self.active_count} queue={self.queue_depth} "
f"done={self.completed} failed={self.failed} "
f"rate_limited={self.rate_limited} "
f"p50={self.p50():.1f}s p99={self.p99():.1f}s"
)
metrics = ConcurrencyMetrics()
class InstrumentedSemaphore:
"""带监控的信号量"""
def __init__(self, max_concurrent: int):
self._sem = asyncio.Semaphore(max_concurrent)
self.max_concurrent = max_concurrent
async def __aenter__(self):
wait_start = time.monotonic()
await self._sem.acquire()
wait_time = time.monotonic() - wait_start
if wait_time > 0.1: # 等待超过 100ms,记录队列压力
print(f"[WARN] Semaphore wait {wait_time:.2f}s (queue pressure)")
metrics.active_count += 1
return self
async def __aexit__(self, *args):
metrics.active_count -= 1
self._sem.release()
必须追踪的关键指标:
semaphore_wait_time:排队等待时间,高于 200ms 说明并发数设置太低或任务太重p99_latency:99 分位延迟,Fan-out 的整体性能指标rate_limited_count:rate limit 次数,高了要降低并发数或升级 tierqueue_depth:队列深度,持续高意味着生产者比消费者快,需要扩容 worker
八、设计决策矩阵
| 场景 | 推荐模式 | 并发数建议 |
|---|---|---|
| 批量文档处理(无 deadline) | 任务队列 + 工作者池 | 5-10,优先稳定 |
| 用户请求的多角度分析 | asyncio.gather + timeout | 3-6,优先低延迟 |
| 实时 Agent 多步推理 | 动态 Fan-out + Hedging | 3-5,加 hedge 控 p99 |
| 大批量离线数据处理 | Queue + Semaphore + Redis 持久化 | 10-20,关注吞吐 |
| TPM 敏感场景(Tier 1) | Semaphore(3) + Full Jitter 退避 | ≤3,优先不触发 429 |
总结
AI Agent 的并发调度,坑在三个地方:
1. 无脑并发打爆 rate limit :用 asyncio.Semaphore 或 p-limit 限制同时进行的 LLM 调用数。根据你的 API tier 调整。
2. Fan-out 的尾延迟放大:p99 延迟 = max(子任务 p99)。10 个任务里 1 个慢,全部都慢。解法是给每个子任务设独立超时,对 p99 要求高的场景用 Hedging。
3. 重试风暴:429 后不要立即全量重试。用 Full Jitter 指数退避,分散重试时间,打破雷群效应。
Datadog 报告里那 60% 因为 rate limit 导致的 LLM 调用失败,大部分是可以通过上面这三点解决的。
并发不是银弹,Fan-out 也不是越多越快。在 LLM 调用的世界里,适当的并发 + 精细的控制,才能在性能和稳定性之间找到平衡。
参考资料:
- Datadog State of AI Engineering 2026: https://www.datadoghq.com/state-of-ai-engineering/
- InfoQ - Adaptive Hedged Requests: https://www.infoq.com/articles/adaptive-hedged-requests-p99-latency/
- Microsoft Azure AI Agent Orchestration Patterns: https://learn.microsoft.com/en-us/azure/architecture/ai-ml/guide/ai-agent-design-patterns
- Google ADK Multi-Agent Patterns: https://developers.googleblog.com/developers-guide-to-multi-agent-patterns-in-adk/
- arxiv AI Agent Design Patterns Framework: https://arxiv.org/html/2605.13850