第 17 章:并发与性能:正确性是性能的前提
17.1 工程问题的本质:快的前提是没错
并发与性能优化有两个目标,但顺序不可颠倒:先正确,后快速。并发下的错误(身份串扰、数据竞争、状态覆盖)比单线程错误更隐蔽、更难复现;性能优化的手段(缓存、并行、预检)一旦破坏正确性,优化的收益全部化为故障成本。
设计原理: 并发与性能的工程形态是"正确性结构 + 延迟预算":先通过结构设计保证并发正确(线程本地、加锁、隔离),再在正确性的约束下做延迟优化(预检、分流、复用)。优化的每一步都必须回答"它破坏了什么正确性前提"。
17.2 并发正确性:共享状态的三种防护
并行路由(多输入并发 + 异常隔离)要求共享状态安全。三种防护手段:
- 线程本地存储:智能体用户上下文存于 threading.local,多线程并发调用同一实例不再互相覆盖身份——消除"身份串扰"这一类最隐蔽的并发错误;
- 加锁:单例与缓存 double-checked locking,审批推进串行化,实例推进改实例粒度锁(避免全局锁把并行变串行);
- contextvars 隔离:追踪上下文按任务隔离,互不串扰。
原理: 三者的分工对应三类并发风险的来源:可变实例状态(线程本地)、共享单例(加锁)、全局上下文(contextvars 隔离)。并发正确性的本质是"状态的可见域"——每个状态都要明确"谁能看到、何时看到",并发错误几乎都是可见域失控。
17.3 延迟优化:把模型调用从热路径上拿下来
延迟优化的主线是"减少模型调用、复用已有结果":
- 规则预检零延迟:高频句式在规则层命中,LLM 调用 0 次(对比:强模型 thinking 25s+);
- 多轮补充信息零 LLM:补充信息仅规则匹配,沿用原意图,不走语义识别;
- 双模型分流:意图识别用强模型、对话回复用快模型,各取所需;预识别结果复用避免第二次强模型调用;
- 单查询优化:两处映射合并一次全表查询(跨公网 19.4MB 大字段两遍 ≈42s → 一遍 ≈21s),快路径保持零查询。
原理: 延迟优化的本质是把概率性、高成本的模型调用从高频确定路径上拿下来。规则预检、补充信息收敛、预识别复用,本质都是"确定性手段先行"(与第 9 章识别链路同一原则)。当确定手段能裁决时,模型调用就不该发生——这既是性能原则,也是成本原则。
17.4 并发模型选择与延迟预算方法
17.4.1 Python 并发模型:线程池 vs 协程
框架的并行路由基于 ThreadPoolExecutor。理解这一选择需要回到 Python 的并发约束:GIL 使纯计算型线程无法并行,但 IO 型任务(网络调用、数据库、模型请求)会释放 GIL,线程池在 IO 密集场景仍能并行提速。
| 维度 | 线程池(框架所选) | 协程 / asyncio |
|---|---|---|
| 模型 | 线程封装同步代码 | 事件循环 + 异步语法 |
| 侵入性 | 低(业务代码保持同步) | 高(全链路 async 化) |
| 调试 | 常规 | async/await 传播难排查 |
| 适用 | IO 密集、调用栈深、混合同步依赖 | 高并发短任务、无阻塞依赖 |
原理: 框架业务链路包含大量同步组件(规则引擎、审批流、数据库),全部协程化的改造面与调试成本极高。线程池以"低侵入"为代价接受"线程开销",换取业务代码保持同步可调试。并发正确性(线程本地、加锁、隔离,见 17.2 节)正是为这一选择补上的安全网。并发模型的选择取决于调用栈的形态,而非并发的上限。
17.4.2 延迟预算:从 SLO 倒推设计
性能优化的工程方法是延迟预算(latency budget):从用户可感知的端到端 SLO 出发,逐环节分解,使每个环节的优化目标可度量、可问责。
graph LR
SL["用户 SLO
对话回复 P95 ≤ 5s"] --> E1["入口与安全检测 ≤ 20ms
(规则匹配,无模型调用)"]
E1 --> E2["意图识别 ≤ 1s
(规则预检命中即 0ms;模型兜底 ≤ 1s)"]
E2 --> E3["路由与上下文 ≤ 10ms"]
E3 --> E4["业务处理(Agent) ≤ 3s
(规则裁决 + 模型生成)"]
E4 --> E5["返回与流式 ≤ 500ms
(SSE 分块)"]
原理: 预算的价值在"优化有目标、回归可发现"——每个环节的实测值对照预算,超预算即报警,而非等到端到端劣化才察觉。预算还揭示"瓶颈在哪里":若识别强模型兜底占去预算大头,优化优先级就应落在"提高规则预检命中率"上,而非盲目并发。
17.5 失败模式与权衡
| 失败形态 | 根因 | 设计应对 |
|---|---|---|
| 并发身份串扰 | 实例可变状态共享 | 线程本地存储 |
| 全局锁拖垮并行 | 类级锁互斥 | 实例粒度锁 |
| 缓存导致脏读 | 缓存无代次校验 | 进程级缓存 + 代次一致性 |
| 优化破坏正确性 | 缓存绕过校验 | 校验在优化之前 |
权衡: 预检优化以"规则层先命中"为代价——若规则误命中,会绕过模型的正确判断。因此预检守卫(动作类命中且句首为查询动词时跳过,保留主谓宾歧义给模型)是必要的:优化的前提是它不会把"该由模型裁决"的输入抢走。
17.6 本章小结
- 并发与性能的顺序不可颠倒:先正确,后快速;
- 并发正确的本质是状态可见域的控制(线程本地、加锁、隔离);
- 延迟优化的本质是把模型调用从高频确定路径上拿下来;
- 优化每一步都须回答"它破坏了什么正确性前提"。