很多团队把 LLM 当成一个「更聪明的字符串函数」直接塞进生产代码:拼个 prompt、调一次接口、把返回值当真值用。Demo 跑得很爽,上线后开始被各种诡异问题反噬——超时、幻觉、成本失控、偶发格式崩坏。本文从工程视角拆解「把 LLM 用对」的几个核心范式,以及与之对应的反模式。
直觉:LLM 是一个高方差、不可靠的远程子系统
先校正心智模型。传统函数 f(x) 是确定的、低延迟的、几乎零成本的。LLM 调用恰好相反:
- 非确定:相同输入、相同 temperature 也可能输出不同(采样 + 后端 batch 调度抖动)。
- 高延迟:首 token(TTFT)通常几百毫秒,长输出按 token 串行生成,整体可达秒级。
- 有成本:按 token 计费,输入输出分别计价,长上下文会线性甚至更陡地放大开销。
- 会自信地错:幻觉不是 bug,是采样分布的固有产物。
正确的工程定位:把它当成一个有限可靠的外部服务,像对待一个会偶发 500、偶发返回脏数据的第三方 API 那样去做容错、限流、降级、校验。
机制:数据流与三个必须卡住的关口
一条生产级 LLM 调用的数据流大致是:
1 | 用户输入 → 输入净化/注入防护 → 上下文组装(检索/裁剪) → prompt 模板 |
三个最容易被忽略、却决定系统稳定性的关口:
1. 输出结构化与校验
不要用正则去抠自由文本。让模型输出 JSON,并用 schema 严格校验。Anthropic 的 Claude、以及多数主流模型都支持 tool use / 工具调用,本质是让模型把输出约束进你定义的 JSON Schema 里——这比「请你一定输出 JSON」这种 prompt 祈祷靠谱得多。
1 | import json |
校验失败时的处理策略,按代价从低到高:直接重试 → 把错误信息回灌让模型自修复 → 降级到规则兜底。关键是:校验失败必须是一条正常代码路径,而不是异常崩溃。
2. 重试、超时与幂等
LLM 网关偶发限流(429)、超时是常态。需要带指数退避的重试,但要注意:生成类调用不是天然幂等的,重试会产生不同结果,也会重复计费。对「写库」「发消息」这类副作用操作,要在业务层加幂等键,而不是依赖模型层去重。
3. 上下文预算管理
上下文窗口是有限资源。设窗口为 ,则约束是:
其中 (检索/历史拼进去的内容)往往是最大变量。一个常见反模式是「把所有历史和所有检索结果一股脑塞进去」,结果是:成本线性上涨、延迟变长,而且容易触发「lost in the middle」——模型对长上下文中间段落的利用率显著下降。正解是做预算分配 + 相关性裁剪:检索 top-k 后再 rerank,只保留真正相关的片段。
公式视角:成本与延迟怎么估
单次调用成本可粗略建模为:
由于 通常是 的数倍,且输出是串行生成的,控制输出长度对成本和延迟的杠杆远大于压缩输入。让模型「只回 JSON、不要解释」既省钱又快。
延迟则近似:
这意味着流式输出(streaming)对体感延迟极有价值——用户在 TTFT 之后就能看到内容,而不是干等整段生成完。
工程权衡与反模式清单
- 反模式:把 prompt 散落在代码各处。 prompt 是会迭代的「配置/资产」,应集中管理、版本化,最好能 A/B 和回归测试。
- 反模式:没有评测就改 prompt。 改一句话可能让整体效果回退却没人发现。建一个哪怕只有几十条样例的离线评测集(含 LLM-as-judge 或规则断言),每次改动跑一遍。
- 反模式:用昂贵大模型干所有活。 做模型分级(routing):简单分类/抽取用小模型,复杂推理才上大模型。可显著降本。
- 反模式:把用户输入直接拼进 prompt。 这是 prompt injection 的温床。把不可信内容放进明确隔离的区块,并对工具调用结果做权限校验——尤其当模型能触发真实副作用(下单、删数据)时。
- 权衡:缓存。 对稳定的长系统提示,启用 prompt caching 可大幅降低重复输入的成本与 TTFT;代价是需要把可变内容放在 prompt 末尾以保持前缀稳定。
- 权衡:把链路拆细 vs. 一次搞定。 多步 agent 链路更可控、可观测,但每一步都有失败概率,端到端成功率是各步的乘积 ,链路越长越脆。能用一次结构化调用解决的,就别硬拆成五步。
小结
把 LLM 用进生产,难点几乎不在「写出聪明的 prompt」,而在把一个非确定、有成本、会出错的远程子系统用工程手段约束成可依赖的组件:结构化输出 + schema 校验、带退避与幂等的调用、上下文预算管理、模型分级、注入防护,以及最容易被跳过却最值钱的——离线评测集。先把这套「脚手架」搭好,再去打磨 prompt,顺序别反了。