优化的最高境界不是换更强的模型,而是让该做的事情不需要模型来做。
延迟账单:477ms 到底花在哪了?
全量 LLM function calling:每次把所有 tool schema(5 个工具 × ~150 字描述)传给 Qwen,LLM 读完所有描述再抉择。我们用 qwen-turbo 实测了 500 次(50 条 query × 10 repeats):p50=477ms、p95=672ms、p99=1200ms。延迟分解:input token 编码(~730 tokens) + LLM 推理 + output token 解码(~24 tokens)≈ 477ms p50。
为什么准确率 92% 还不够? 因为按意图拆开,query-order 只有 80%------"我的订单到哪了"被系统性地误判为 check-shipping("到哪了"→语义上天然偏向物流)。一个错误的路由不只是延迟,是后续整个对话链路的崩塌------用户会收到错误的查询结果,然后反复纠错。qwen-max 可能更准,但会更慢(参数量翻倍),且同样存在"语义相近"的混淆问题。92% 准确率值不值 477ms?答案是不值------因为后面我们会看到,P0+P1 已经在 25ms 内达到了更高的命中率。
每天 1 万次请求,仅意图识别就花掉约 1.33 小时(477ms × 10000),年成本约 ¥2,400(qwen-turbo)。
注意 :这个 477ms 只是"意图识别→工具选择"这道工序的延迟。后面 RAG 流水线中 LLM 生成最终答案的延迟是 ~9.1s(qwen3.6-plus 最新实测),完全不同量级。这里优化的对象仅限意图识别------别搞混了。
目标是 50ms,但不想换模型、不想加 GPU、不想降准确率。
分层拦截的设计逻辑
核心思路:大部分请求在到达 LLM 之前就应该完成意图识别。不是"每一步都快",而是"只让必要的步骤发生"。
P0 规则过滤 ~5ms
用户输入包含明确关键词。"我想退掉昨天买的洗衣机"→ 关键词"退"直接识别为退款意图,映射到 request-return 工具。关键设计:关键词不是人工维护的,从 Skill YAML 的 trigger_keywords 字段自动抽取 + TF-IDF Top-3。关键词覆盖 53%,但触发后准确率仅 81%------53 条命中里 10 条判错。有效解决率 43%。剩下(如"我的东西到哪了"这种不含明确关键词的)不能指望关键词。
P1 FAISS 语义重排 ~20ms
当关键词失效时,用语义来补。整句 BGE-small-zh-v1.5 embedding + HNSW 索引,但三个工程细节让精度大幅提升:
- 指令前缀(instruction-aware encoding):零延迟增加,精度提升 5-10%
- 意图描述内建反向锚点:在退款意图描述末尾写"区分:纯物流咨询请走 shipping 通道",互为对比的描述在向量空间中相互推开
- 意图词加权:关键词匹配结果 ×1.5 给语义匹配注入方向偏好
指令前缀怎么用? BGE-small-zh-v1.5 官方明确给出了指令:
为这个句子生成表示以用于检索相关文章:。标准用法:文档入库时不加 前缀,查询时加前缀------模型训练时就是这样对齐的。如果查询不加前缀,或者文档误加了前缀,向量空间错位,召回会烂掉。我们的实际选择:考虑到全链路有多个 embedding 消费方(P1 意图匹配 + RAG 文档检索 + 语义缓存),统一不加指令前缀,牺牲约 5-10% 精度换零运维心智负担。
结果:P0+P1 Top-2 命中率达到 85% ,Top-1 达到 75%。P1 把 P0 未覆盖的 57% 中的绝大多数兜了回来------从 43% 到 85%,增量贡献 +42%。仅剩 15% 的极端歧义 case 需要 P2 介入。
P2 本地小模型确认 ~157ms
从 P1 Top-3 意图候选里最终确认一个。不在云端跑------p99 延迟抖动(500ms+)不可接受。
本地模型明确说明 :P2 意图确认运行 Qwen2.5-1.5B-Instruct ,部署在 RTX 4070 ,p50 推理延迟 94ms(benchmark 实测),显存占用 2.88GB。为什么选 1.5B 而非 1.7B 或 3B?Qwen2.5-1.5B 在简化后的二选一确认任务上达到 92% Top-1 / 94ms p50------精度-速度-显存的甜点。完整 4 模型 × 2 设备全量对比见第六篇。
所有 benchmark 均在 **RTX 4070 GPU(8GB 显存)**实测完成。2.88GB 显存门槛意味着任何主流独显都能跑。关键不是 GPU 算力------是 P0+P1 已经把 5-10 个意图筛选为 Top-2 候选,P2 只做二选一确认,问题复杂度降了一个数量级。
P2 之后的工具选择:一张映射表 ~0ms
意图一旦确认,工具选择就是 O(1) 查表。不需要 LLM,不需要 embedding。这是工具选择只需二层(P0+P1)的根本原因------它依赖的是意图识别的输出,不是独立的复杂决策。
P2 交叉校验逻辑:当 P2 的 Top-1 与 P1 的 Top-1 不一致时,同时纳入 Top-2,给后续执行器 agent 更多选择空间。
最终:P0+P1+P2 → ~99% 意图识别命中率(P1 Top-2 85% + P2 对剩余 15% 的兜底确认)。p50 延迟仅 ~25ms------85% 的请求在 P0 或 P1 阶段即命中,无需走到 P2(P0+P1 本身约 25ms,P2 的 ~157ms 仅在 p85+ 尾部触发,不影响 p50 统计量);p95 约 ~182ms(含 P2 完整路径)。
为什么是 P0→P1→P2 这个顺序?
设计逻辑:零成本先跑(P0,解决 43%)→ 语义补缺(P1,Top-2 到 85%)→ LLM 兜底(P2,仅 15% 触发,仅做候选确认)。这才是真正的"经济性设计"。
总延迟对比
| 方案 | p50 | p95 | p99 | 年成本* |
|---|---|---|---|---|
| 纯 LLM function calling (qwen-turbo) | 477ms | 672ms | 1200ms | ~¥2,400 |
| 纯 LLM function calling (qwen-max 估) | ~700ms | ~900ms | ~1500ms | ~¥21,000 |
| P0+P1+P2(本项目,共享 GPU) | ~25ms | ~182ms | ~312ms | ~¥2,500 |
*按 qwen-turbo 实测 token 消耗 ~730 input + ~24 output 计算(500 次请求的平均值)。P0+P1+P2 方案中 P2 本地环节运行 Qwen2.5-1.5B-Instruct on GPU,详见下文 benchmark 数据。qwen-max 行标注为"估"------未实测。完整成本拆解见第十一篇。
意图识别 P0→P1→P2 三级流水线 + 工具选择一层映射
#mermaid-svg-2K0iu8zXvM79u50i{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-2K0iu8zXvM79u50i .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-2K0iu8zXvM79u50i .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-2K0iu8zXvM79u50i .error-icon{fill:#552222;}#mermaid-svg-2K0iu8zXvM79u50i .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-2K0iu8zXvM79u50i .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-2K0iu8zXvM79u50i .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-2K0iu8zXvM79u50i .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-2K0iu8zXvM79u50i .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-2K0iu8zXvM79u50i .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-2K0iu8zXvM79u50i .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-2K0iu8zXvM79u50i .marker{fill:#333333;stroke:#333333;}#mermaid-svg-2K0iu8zXvM79u50i .marker.cross{stroke:#333333;}#mermaid-svg-2K0iu8zXvM79u50i svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-2K0iu8zXvM79u50i p{margin:0;}#mermaid-svg-2K0iu8zXvM79u50i .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-2K0iu8zXvM79u50i .cluster-label text{fill:#333;}#mermaid-svg-2K0iu8zXvM79u50i .cluster-label span{color:#333;}#mermaid-svg-2K0iu8zXvM79u50i .cluster-label span p{background-color:transparent;}#mermaid-svg-2K0iu8zXvM79u50i .label text,#mermaid-svg-2K0iu8zXvM79u50i span{fill:#333;color:#333;}#mermaid-svg-2K0iu8zXvM79u50i .node rect,#mermaid-svg-2K0iu8zXvM79u50i .node circle,#mermaid-svg-2K0iu8zXvM79u50i .node ellipse,#mermaid-svg-2K0iu8zXvM79u50i .node polygon,#mermaid-svg-2K0iu8zXvM79u50i .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-2K0iu8zXvM79u50i .rough-node .label text,#mermaid-svg-2K0iu8zXvM79u50i .node .label text,#mermaid-svg-2K0iu8zXvM79u50i .image-shape .label,#mermaid-svg-2K0iu8zXvM79u50i .icon-shape .label{text-anchor:middle;}#mermaid-svg-2K0iu8zXvM79u50i .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-2K0iu8zXvM79u50i .rough-node .label,#mermaid-svg-2K0iu8zXvM79u50i .node .label,#mermaid-svg-2K0iu8zXvM79u50i .image-shape .label,#mermaid-svg-2K0iu8zXvM79u50i .icon-shape .label{text-align:center;}#mermaid-svg-2K0iu8zXvM79u50i .node.clickable{cursor:pointer;}#mermaid-svg-2K0iu8zXvM79u50i .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-2K0iu8zXvM79u50i .arrowheadPath{fill:#333333;}#mermaid-svg-2K0iu8zXvM79u50i .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-2K0iu8zXvM79u50i .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-2K0iu8zXvM79u50i .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-2K0iu8zXvM79u50i .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-2K0iu8zXvM79u50i .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-2K0iu8zXvM79u50i .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-2K0iu8zXvM79u50i .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-2K0iu8zXvM79u50i .cluster text{fill:#333;}#mermaid-svg-2K0iu8zXvM79u50i .cluster span{color:#333;}#mermaid-svg-2K0iu8zXvM79u50i div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-2K0iu8zXvM79u50i .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-2K0iu8zXvM79u50i rect.text{fill:none;stroke-width:0;}#mermaid-svg-2K0iu8zXvM79u50i .icon-shape,#mermaid-svg-2K0iu8zXvM79u50i .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-2K0iu8zXvM79u50i .icon-shape p,#mermaid-svg-2K0iu8zXvM79u50i .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-2K0iu8zXvM79u50i .icon-shape .label rect,#mermaid-svg-2K0iu8zXvM79u50i .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-2K0iu8zXvM79u50i .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-2K0iu8zXvM79u50i .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-2K0iu8zXvM79u50i :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} P2 本地小模型确认 ~157ms
P1 FAISS 语义匹配 ~20ms
P0 规则过滤 ~5ms
命中
未命中 57%
'我的东西到哪了'
命中
剩余 15% 模糊
用户输入
'我想退掉昨天买的洗衣机'
关键词匹配
trigger_keywords
来自 Skill YAML
✅ 命中 43%
意图=退款
BGE-small Embedding
-
HNSW 检索
-
意图词加权 ×1.5
✅ 累计命中 85%
Top-2 意图候选
Qwen2.5-1.5B-Instruct
RTX 4070
交叉校验 P1 Top-1
不一致时纳入 Top-2
✅ 累计命中 ~99%
意图→工具映射 ~0ms
退款 → request-return
路由到执行器
三方案延迟对比
#mermaid-svg-XK1psU5tDyi903WL{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-XK1psU5tDyi903WL .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-XK1psU5tDyi903WL .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-XK1psU5tDyi903WL .error-icon{fill:#552222;}#mermaid-svg-XK1psU5tDyi903WL .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-XK1psU5tDyi903WL .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-XK1psU5tDyi903WL .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-XK1psU5tDyi903WL .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-XK1psU5tDyi903WL .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-XK1psU5tDyi903WL .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-XK1psU5tDyi903WL .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-XK1psU5tDyi903WL .marker{fill:#333333;stroke:#333333;}#mermaid-svg-XK1psU5tDyi903WL .marker.cross{stroke:#333333;}#mermaid-svg-XK1psU5tDyi903WL svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-XK1psU5tDyi903WL p{margin:0;}#mermaid-svg-XK1psU5tDyi903WL :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 意图识别延迟对比 (p50 / p95) 纯 LLM function callingP0 + P2P0 + P1 + P2 (本项目) 10009008007006005004003002001000 延迟 (ms)
这套方法论不止适用于意图识别:RAG 流水线审计
在进入 RAG 优化之前,用同一套"砍 LLM"的眼光审视一下 RAG 流水线。RAG 的延迟里,有多少是检索造成的,有多少是不必要的 LLM 调用?
我们把 4-Step RAG 流水线逐步骤审计了一遍:
| 步骤 | 是否需要 LLM? | 审计结论 |
|---|---|---|
| Step 1 问题改写 | ✗ | 大部分 query 不需要改写,可用 Embedding 替代 |
| Step 2 安全检查 | 部分 | 本地小模型优先,云端仅兜底 |
| Step 3 检索+精排 | ✗ | 零 LLM,纯工程手段(具体优化见第三篇) |
| Step 4 答案生成 | ✓ | 唯一无法替代的 LLM 调用 |
RAG 流水线的延迟大头不是检索,而是那些可以砍掉的 LLM 调用。 真正不可替代的只有 Step 4 的答案生成(~9.1s),而 Step 1 的问题改写每一次都在调云端 LLM------对于"洗衣机有什么功能"这种完全不需要改写的问题,纯属浪费。
审计告诉我们哪些步骤不需要 LLM ;下一篇我们聚焦唯一需要 LLM 的那一步------它的输入质量如何确保。也就是 Step 3 检索优化:混合检索、图增强、以及藏在它们背后的一堆工程细节。