共计 1903 个字符,预计需要花费 5 分钟才能阅读完成。
同步调用的陷阱
最近在开发客服工单分类系统时,我们最初采用简单的同步调用 ChatGPT API:

response = openai.ChatCompletion.create(
model="gpt-3.5-turbo",
messages=[{"role": "user", "content": prompt}]
)
很快遇到三个典型问题:
- 超时雪崩 :当 API 延迟从平均 800ms 突增到 5s 时,整个系统线程池被占满
- Token 浪费 :每个请求独立携带 15 轮历史对话,重复传输消耗 30% 额度
- 错误扩散 :单个请求失败导致用户流程中断,需要人工介入恢复
架构选型对比
方案 1:纯队列模式
flowchart LR
A[生产者] --> B[Redis 队列]
B --> C[消费者集群]
C --> D[ChatGPT API]
- 优点:实现简单,易于水平扩展
- 缺点:上下文状态维护困难,错误恢复成本高
方案 2:事件驱动架构
flowchart TB
A[事件源] -->| 发布 | B[消息队列]
B --> C[处理器 1]
B --> D[处理器 2]
C --> E[状态存储]
- 优点:松耦合,适合异构系统
- 缺点:开发调试复杂度高
方案 3:状态机引擎
class ConversationState(Enum):
INIT = 0
PROCESSING = 1
RETRYING = 2
COMPLETED = 3
最终选择状态机方案,因其:
– 显式管理对话生命周期
– 内置错误恢复路径
– 方便审计追踪
核心实现
异步编排引擎
async def dispatch_worker(task_queue: asyncio.Queue):
"""带速率限制的任务分发器"""
semaphore = asyncio.Semaphore(100) # 并发控制
while True:
task = await task_queue.get()
async with semaphore:
await handle_task(task)
async def handle_task(task: Task):
retry_policy = ExponentialBackoff()
while retry_policy.should_retry():
try:
await execute_api_call(task)
break
except RateLimitError:
await retry_policy.wait()
上下文缓存设计
def get_context_hash(user_id: str) -> str:
"""生成对话指纹避免重复存储"""
return sha256(f"{user_id}:{timestamp//300}".encode()).hexdigest()
async def cached_completion(prompt: str, user_id: str):
cache_key = f"ctx:{get_context_hash(user_id)}"
if cached := await redis.get(cache_key):
return cached
response = await openai_chat(prompt)
await redis.setex(cache_key, 3600, response) # 1 小时过期
return response
智能退避算法
class ExponentialBackoff:
def __init__(self):
self.attempts = 0
self.max_retries = 5
async def wait(self):
self.attempts += 1
delay = min(2 ** self.attempts + random.uniform(0, 1), 10)
await asyncio.sleep(delay)
def should_retry(self):
return self.attempts < self.max_retries
性能数据
| 批处理大小 | QPS | 平均延迟 |
|---|---|---|
| 1 | 12 | 850ms |
| 10 | 85 | 920ms |
| 50 | 320 | 1100ms |
错误率随重试次数变化曲线:
重试 1 次:错误率下降 60%
重试 3 次:累计下降 85%
超过 3 次:收益递减明显
生产环境避坑指南
限流自适应策略
- 实时监控 429 状态码频率
- 动态调整并发 Semaphore 值
- 紧急情况降级到本地模型
敏感信息过滤
- 输入层:使用正则过滤银行卡号等模式
- 输出层:强制审核关键动作(如转账确认)
状态持久化
- 每次状态变更立即写入 Redis
- 后台进程每 5 分钟同步到 PostgreSQL
- 启动时恢复未完成任务
开放性问题
实际测试发现:
– 批处理 50 条时吞吐量最大
– 但 P99 延迟突破 1.5 秒
在金融客服场景中,应该:
1. 优先保证 200ms 内的实时响应?
2. 还是追求整体吞吐最大化?
欢迎在评论区分享您的场景选择策略。
正文完
