在高并发业务中,如何正确调用大模型?我踩过的 5 个坑

29 阅读5分钟

近一年在做 智能客服 / AI 工单 / 实时对话系统 时,我越来越强烈地意识到一件事:

在业务系统中,大模型的效果固然重要,但工程层面的稳定性与可控性同样关键。

如果你是直接在业务代码里这样调用 LLM 的:

String result = llmClient.call(prompt);

那你大概率已经或者即将踩下面这些坑。

这篇文章,我想结合真实生产场景,聊聊 在高并发业务中,为什么“直接调用大模型”一定会出问题,以及我是如何把这些问题工程化解决的。


一、坑一:同一会话被重复触发,模型被“打爆”

真实场景

在客服系统中,一个用户可能会:

  • 连续发多条消息
  • 快速修改输入
  • 页面重试 / 网络抖动

如果你的系统是:

“每次事件都直接调用一次大模型”

那么在高峰期,你会看到:

  • 同一会话 短时间内触发多次模型调用
  • 成本飙升
  • 响应变慢
  • 模型 QPS 被打满

常见但错误的解决方式

synchronized (sessionId) {
    llmClient.call(prompt);
}

问题是:

  • 单机锁,分布式失效
  • 粒度粗,吞吐急剧下降
  • 无法复用结果

二、坑二:并发请求没有去重,浪费 90% 的调用

真实问题

在某些接口中(如工单自动补全):

  • 同一个 key(用户 + 会话 + 场景)
  • 在极短时间内并发进入

但本质上:

这些请求完全可以共享一次模型调用的结果

如果不做处理:

  • 10 个并发 = 10 次模型调用
  • 但业务只需要 1 次

正确的工程思路:singleflight

同一个 key,只允许一次真实执行,其它请求等待并复用结果。

这是一个非常典型的工程问题,但在 LLM 场景下尤为重要。


三、坑三:连续触发场景,根本不该“实时调用模型”

场景举例

在实时对话或输入联想中:

  • 用户输入:我想投诉
  • 紧接着:我想投诉昨天的订单
  • 又补充:订单号是 xxx

如果每一步都实时调用 LLM:

  • 成本高
  • 效果反而不好(上下文不完整)

更合理的方式:合并触发(Merge Window)

在一个时间窗口内,把多次触发合并为一次调用。

例如:

  • 2 秒内的触发 → 只调用一次模型
  • 返回的是“最后一次”的完整上下文

四、坑四:模型超时 / 异常,直接拖垮主流程

现实情况

  • 模型接口超时
  • 网络抖动
  • Provider 限流

如果你是这样写的:

llmClient.call(prompt); // 阻塞

那你的主业务线程就会:

  • 卡死
  • 堆积
  • 最终雪崩

正确做法(工程视角)

  • 必须有 timeout
  • 必须可 retry
  • 必须有 fallback

而且这些逻辑:

不应该散落在每个业务方法里


五、坑五:治理逻辑散落在业务代码中,无法复用

这是我最终决定“重构”的根本原因。

在一个项目里,你可能还能忍受:

lock
 → check cache
 → call llm
 → retry
 → fallback

但当你有:

  • 工单系统
  • 反馈系统
  • 对话系统
  • 搜索系统

你会发现:

每个系统都在重复写一套“LLM 调用防护逻辑”

而且还不一致。


六、我的解决方案:把 LLM 调用治理“抽象出来”

在踩完这些坑之后,我做了一件事:

把 LLM 调用中的“工程问题”抽象成一个独立的运行时治理组件。

核心目标只有一个:

让业务只关心“调用模型做什么”,而不是“怎么安全地调用模型”。


七、LLM Call Guard:一个注解驱动的治理方案

最终我把这些能力整理成了一个开源项目:

👉 LLM Call Guard
GitHub:github.com/cylgdzz111/…

使用方式非常简单

@LLMGuard(scene = "ticket_autofill", mergeWindowMs = 2000)
public AiResult extract(TicketReq req) {
    return llmProvider.call(req);
}

这行注解背后做了什么?


八、它在运行时做了哪些事情?

一次 LLM 调用,会被包裹成如下执行管道:

  1. 自动 key 生成(会话 / 用户 / 场景)
  2. 并发互斥(分布式 / 本地)
  3. singleflight 去重
  4. 可选合并窗口(merge window)
  5. 超时控制
  6. 重试
  7. fallback 兜底
  8. 结构化日志

而业务代码:

  • 不需要知道这些细节
  • 不需要侵入
  • 不绑定任何具体 LLM Provider

九、一个真实效果(生产验证)

在客服 AI 自动化工单场景中:

  • 连续对话 + 高频事件
  • 引入会话级治理 + 合并触发后
  • 模型调用次数下降约 85%
  • 人工客服处理效率提升约 40%

十、为什么我认为“工程化”比“Prompt 更重要”

在我看来:

Prompt 决定上限,工程决定能不能用。

很多团队:

  • 模型效果很好
  • 但系统不可控、不稳定、成本失控

最终被迫:

  • 降级
  • 下线
  • 或只在极小流量使用

十一、写在最后

如果你正在做:

  • AI 工单
  • 智能客服
  • LLM + 业务系统
  • 高并发 / 实时对话

那么我强烈建议你 不要从“模型”开始,而是从“工程约束”开始。

LLM Call Guard 目前还在持续迭代中,如果你对这个方向感兴趣,欢迎交流、提 Issue 或 PR。

👉 GitHub:github.com/cylgdzz111/…


后记

这套方案来自真实生产场景,不是 Demo。
如果你也踩过类似的坑,欢迎在评论区聊聊你是怎么解决的。