LLM 应用(04):LLM 应用工程——API 调用、流式、成本与延迟优化、限流重试、可观测与评测
更新时间:2026-09-02。本文是
data-ai/llm-app/第 04 篇,在LLM 应用索引下。前三篇讲了 Prompt、RAG、Agent"怎么做对",这篇讲"怎么跑稳、跑便宜、跑得可维护"——这是 demo 和生产系统的真正分野。
本文要回答的问题
- 同步和流式(streaming)调用怎么选?首 token 延迟是什么?
- 网络抖动、429 限流、超长上下文这些工程问题怎么处理?
- token 怎么算?成本和延迟从哪里省?
- 线上 LLM 出问题怎么定位?要埋哪些点?
- 模型怎么选型、怎么做离线+在线评测?
一、调用基础
# 同步(OpenAI 兼容接口风格,伪代码)
resp = client.chat.completions.create(
model="gpt-4o-mini",
messages=[{"role": "user", "content": q}],
temperature=0.2,
timeout=30,
)
answer = resp.choices[0].message.content
usage = resp.usage # prompt_tokens / completion_tokens,记账用生产环境的基本封装要点:
- 统一超时:连接超时 + 读取超时分开设;LLM 响应慢,读取超时要给足(如 60~120s)。
- 重试与退避:对 429(限流)、5xx、网络超时做指数退避重试(如等 1s、2s、4s + 抖动),最多 3 次;对 400(请求本身错)不要重试。
- 熔断与降级:主模型连续失败时切备用模型或返回兜底,别让整个请求挂死。
- 记 usage:每次调用记录 prompt/completion token、模型、延迟,用于成本核算和容量规划。
二、流式输出(Streaming)
LLM 生成是逐 token 的,非流式要等全部生成完才返回(可能十几秒白屏)。流式用 SSE 边生成边推:
非流式: |-------- 等待全部生成 --------| 一次性显示 总延迟高、体验差
流式: |--首token--|token|token|...| 逐字蹦出 首 token 延迟(TTFT)低- 用
stream=True,逐 chunk 读取增量内容推给前端。 - 关键体验指标是 TTFT(Time To First Token,首 token 延迟) 和吞吐(tokens/s)。
- 流式下做 JSON 解析需要增量拼接后再解析,或用支持流式的结构化输出;错误处理要覆盖"流中途断开"。
三、限流、配额与并发
- 429 / rate limit:按 RPM(请求/分)、TPM(token/分)限流。客户端要排队/削峰,不要无脑并发打爆配额。
- 并发控制:用信号量/连接池限制同时在飞的请求数;批量任务用有界并发。
- 批处理(Batch API):离线、不要求实时的大批量任务(如批量 embedding、批量打标)用批量接口,价格通常低 50%,次日返回。
- embedding 也限流:建库时批量 embedding 要控制并发、断点续传。
四、成本与延迟优化
token 是 LLM 应用的"计费单位"和"延迟单位",优化都围绕减少/复用 token:
成本 ≈ (输入 token × 输入单价 + 输出 token × 输出单价) × 调用次数
注意:输出 token 单价通常是输入的 3~5 倍,且生成比读取慢得多| 手段 | 省什么 | 说明 |
|---|---|---|
| 模型分级(路由) | 成本+延迟 | 简单任务用小/便宜模型,难任务才上大模型;先让小模型判断难度 |
| Prompt 精简 | 输入 token | 去掉冗余示例/指令;RAG 只塞最相关的几条 |
| 缓存 | 两者 | 相同/相似问题缓存答案;embedding 结果按内容 hash 缓存 |
| 上下文压缩 | 输入 token | 长历史做摘要、RAG 检索后压缩、只留相关片段 |
| 限制 max_tokens | 输出 token | 防止跑飞、控上限 |
| Batch API | 成本 | 离线任务半价 |
| 流式 + 早停 | 延迟/成本 | 用户看到所需即可,没必要的长生成及时截断 |
| 降维/短 embedding | 成本 | 选更小维度的 embedding 模型,存储和调用都省 |
成本护栏:给每用户/每请求设 token 预算上限,超限告警或拒绝,防止 Agent 死循环烧钱。
五、结构化输出的工程容错
即使有 JSON mode/function calling,线上仍要防御:
调用 -> 拿到文本
-> 尝试 json.loads / 按 schema 解析
-> 成功:用 Pydantic 校验字段/类型/枚举
-> 失败:把"解析错误信息 + 原始输出"回喂模型,要求修正(重试 1~2 次)
-> 仍失败:走兜底(默认值 / 转人工 / 明确报错)永远不要假设模型一定合法;把 LLM 输出当成"不可信的外部输入"对待,和对待用户输入一样做校验。
六、可观测性(Observability)
LLM 应用是"非确定性系统",出问题比传统服务难查,trace 是生命线。每次调用应记录:
一次请求的 trace:
request_id / 用户 / 时间
-> prompt 全文(含 system/检索到的资料)
-> 模型、参数(temperature 等)
-> 工具调用:名称、入参、返回、耗时(Agent/ RAG)
-> 检索:召回了哪些 chunk、分数、rerank 后顺序(RAG)
-> 输出全文、token usage、端到端延迟
-> 是否重试/降级、错误信息
-> 用户反馈(点赞/点踩)- 用 OpenTelemetry / LangSmith / Langfuse / Phoenix 等做链路追踪与评测平台。
- 监控指标:延迟(P50/P95)、TTFT、token 成本/请求、错误率、重试率、缓存命中率、空结果率、用户点踩率。
- 保留失败样本,它们是改进 prompt/检索的金矿。
七、评测与持续迭代
离线评测(上线前/每次改动):
维护评测集(输入+期望) -> 跑全链路 -> 算指标:
任务正确率、JSON 合法率、检索召回、答案忠实度、平均成本/延迟
-> 与基线对比,防"改好 A 改坏 B"(回归)
在线监控(上线后):
用户反馈、点踩、人工抽检、LLM-as-judge 自动打分
-> 发现 bad case -> 加入评测集 -> 改进 -> 回归模型选型:用你自己的评测集横评几个候选模型(大模型 vs 小模型、开源 vs 闭源),在"质量 / 成本 / 延迟 / 数据合规(能否私有化部署)"间权衡。不要只看榜单——榜单和你的真实任务分布可能完全不同。开源模型(Llama/Qwen/DeepSeek 等)+ 私有部署适合数据不能出域的企业场景。
小结
LLM 应用工程化的关键词是:流式降 TTFT、超时+指数退避重试+熔断降级、信号量控并发、按 token 记账并用模型分级/缓存/压缩/Batch 降本、结构化输出必校验重试、全链路 trace 可观测、离线评测集防回归 + 在线反馈闭环。把 LLM 当"不可靠、有延迟、按 token 计费的外部依赖"来设计,而不是普通函数——这是从 demo 走向生产的核心心态。回到 LLM 应用索引。