AI Agent 的并发调度工程实战:任务队列、并发限制与 Fan-out/Fan-in 模式

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 在生产中最常见的工程事故。

这篇文章系统讲清楚三件事:

  1. 为什么 AI Agent 的并发比普通微服务更危险
  2. 三种核心并发模式:任务队列、并发限制、Fan-out/Fan-in
  3. 生产级实现: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 个文档),还需要任务队列来:

  1. 背压:防止任务无限堆积在内存里
  2. 优先级:紧急任务先处理
  3. 持久化:程序崩溃后可恢复

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 次数,高了要降低并发数或升级 tier
  • queue_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.Semaphorep-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 调用的世界里,适当的并发 + 精细的控制,才能在性能和稳定性之间找到平衡。


参考资料:

相关推荐
nuo5342021 小时前
人工智能生成内容 (AIGC) 期末复习资料
人工智能·aigc
zhangfeng11331 小时前
DNN Transformer SNN 这几个模型的对比和应用场景 前景
人工智能·transformer·dnn
V搜xhliang02461 小时前
告别SPSS卡顿:用AI智能体自动跑回归、生存曲线、生成方法学段落
运维·人工智能·数据挖掘·回归·机器人·自动化·飞书
Triv20251 小时前
边缘计算新选择:Kvaser Edge 搭载容器化 OS,四路隔离 CAN-FD + Wi-Fi 6 + GNSS/IMU 全解析
人工智能·边缘计算·远程管理·socketcan·linux容器·ip67、gnss、imu·can-fd
nix.gnehc1 小时前
CLI 入门:从终端命令到 AI Agent 调用
人工智能·cli
大象说1 小时前
朱雀大模型检测对降AI改写内容的适配性实测与原理拆解
人工智能
kisdiem1 小时前
GAN(Generative Adversarial Network)生成对抗网络
人工智能·神经网络·生成对抗网络
咖啡星人k1 小时前
AI友好的全栈架构设计:接口规范、状态管理与组件拆分的最佳实践
人工智能
财迅通Ai2 小时前
智迪科技斥资1.52亿元收购越南工厂:当“租赁出海”走向“资产出海”
人工智能·科技·智迪科技