LLM延迟预算:让AI功能感觉快又不烧钱的工程方法
AI SaaS上线后用户增多导致延迟飙升,核心解法是制定LLM延迟预算:规定每个工作流的响应速度、token上限、流式启动时机、缓存策略和多模型路由规则。
完整中文译文
为什么延迟预算在当下尤为重要
AI 平台新闻指向同一个方向:开发者们正从聊天演示转向生产级工作流。Agent 工具、Web 上下文 API、语音 Agent、编码助手和 RAG 平台的能力都在不断提升。与此同时,推理成本和可靠性正面临压力。
延迟如今已是一种产品指标。推理效率正在成为一种业务指标。然而许多文章止步于 TTFT、TPOT、量化、批处理或模型服务层面。很少有人展示 SaaS 开发者如何将这些理念转化为包含代码、仪表盘、兜底策略和客户安全限制的产品级预算。
简化模型:TTFT、TPOT 和总时间
你不需要攻读服务系统的博士才能上手。只需追踪三个数字。
首 Token 延迟(Time to First Token,TTFT) 是用户操作到首个流式 Token 之间的延迟。它包含网络时间、排队时间、提供商开销、工具设置、检索时间,以及模型的预填充阶段。
高 TTFT 是聊天框让人感觉死气沉沉的原因。
每输出 Token 耗时(Time Per Output Token,TPOT) 是首个 Token 出现后,生成各 Token 之间的平均时间。
高 TPOT 是流式输出感觉像滴水龙头的原因。
端到端延迟 是从请求到最终答案的完整时间。
end_to_end_latency = TTFT + (output_tokens - 1) * TPOT
这个公式并非对每个提供商都精确,但足以用来分析用户体验。
按工作流而非按模型来制定预算
一个常见错误是设置一个全局目标,比如"AI 响应必须在 5 秒内完成"。听起来干净利落,但很快就会失效。
不同的工作流需要不同的预算。
关键是为体验制定预算,而不是为原始模型调用制定预算。
用户可以原谅一个 40 秒的后台报告生成——只要 UI 告知了正在发生什么。同一个用户可能会放弃一个 6 秒的内联写作助手——如果什么都看不到的话。
实用的 LLM 延迟预算模板
为每个 AI 工作流创建一个预算对象。
{
"workflow": "support_rag_answer",
"max_ttft_ms": 2500,
"max_total_ms": 15000,
"max_input_tokens": 12000,
"max_output_tokens": 900,
"stream": true,
"cache_policy": "semantic_and_exact",
"fallback_model": "fast_general_model",
"requires_citations": true,
"async_after_ms": 12000
}
这将"让它更快"转化为了工程约束。你的应用现在可以决定是否裁剪上下文、启用流式、路由到更快的模型、切换为异步、拒绝超大型请求,或使用缓存答案。
为每个请求埋点
先为每个 AI 请求记录延迟和 Token 数据。在购买新工具或更换提供商之前就这样做。
以下是一个 TypeScript 风格的简单示例。
type LlmTrace = {
requestId: string;
tenantId: string;
workflow: string;
model: string;
inputTokens: number;
outputTokens: number;
ttftMs: number | null;
totalMs: number;
costUsd: number;
cacheHit: boolean;
status: "success" | "timeout" | "error";
};
async function runWithTrace(input: {
tenantId: string;
workflow: string;
prompt: string;
}) {
const started = Date.now();
let firstTokenAt: number | null = null;
let output = "";
const stream = await llm.stream({
model: "fast-general",
prompt: input.prompt,
max_tokens: 700
});
for await (const chunk of stream) {
if (!firstTokenAt) firstTokenAt = Date.now();
output += chunk.text;
sendToClient(chunk.text);
}
const finished = Date.now();
const trace: LlmTrace = {
requestId: crypto.randomUUID(),
tenantId: input.tenantId,
workflow: input.workflow,
model: "fast-general",
inputTokens: estimateTokens(input.prompt),
outputTokens: estimateTokens(output),
ttftMs: firstTokenAt ? firstTokenAt - started : null,
totalMs: finished - started,
costUsd: estimateCost(input.prompt, output),
cacheHit: false,
status: "success"
};
await saveTrace(trace);
return output;
}
保持埋点简单。如果你捕获了请求 ID、租户 ID、工作流、模型、Token 数、TTFT、总时间、成本、缓存命中和状态,就能回答大多数早期性能问题。
在触碰基础设施之前先控制输入 Token
长 Prompt 损害 TTFT。长上下文意味着在首个 Token 出现之前要做更多工作。
对于 AI SaaS 产品,输入膨胀通常来自:完整聊天历史、太多 RAG Chunk、原始 HTML、未使用的工具描述、重复的系统指令,或者传入整个客户记录而实际上只有几个字段有用。在优化 GPU 或更换供应商之前,先删掉无用的上下文。
使用上下文打包器。
type ContextItem = {
id: string;
text: string;
priority: number;
tokenEstimate: number;
};
function packContext(items: ContextItem[], maxTokens: number) {
const sorted = [...items].sort((a, b) => b.priority - a.priority);
const selected: ContextItem[] = [];
let used = 0;
for (const item of sorted) {
if (used + item.tokenEstimate > maxTokens) continue;
selected.push(item);
used += item.tokenEstimate;
}
return selected;
}
这并不花哨。这正是它的要点。基于优先级的简单打包器往往优于"发送所有内容然后祈祷"。
对于 RAG,使用更少但更好的 Chunk。对于 Agent,每步暴露更少的工具。对于浏览器自动化,在将页面内容放入 Prompt 之前先清洗一遍。
按任务类型限制输出 Token
输出 Token 决定总延迟和成本。许多 AI 功能并不需要长答案。
按工作流设置输出上限:
- 改写建议:120 Token
- 错误解释:250 Token
- 支持回答:700 Token
- 技术方案:1,200 Token
- 后台报告:异步任务,限额更大
同时给模型一个阻止废话的结构。
Answer in this format:
1. Direct answer: 2 sentences max
2. Steps: up to 5 bullets
3. Caveat: 1 short note if needed
这提高了可扫描性并减少了 Token 漂移。
使用流式输出改善感知,而非作为权宜之计
流式输出可以让 AI 功能感觉更快,但它不能修复一切。
适用流式的场景:
- 用户在阅读生成的文本
- 答案可能超过 2 秒
- 部分输出也有用
- 你可以在草稿开始后显示引用或工具结果
不适用的场景:
- 工作流必须返回有效 JSON
- 用户需要单一确定性结果
- 模型必须完成工具调用后才能说任何内容
- 你在隐藏模型启动之前的慢速检索或数据库步骤
对于 Agent 工作流,流式输出状态事件,而不仅仅是文本。
{ "type": "status", "message": "Searching relevant docs" }
{ "type": "status", "message": "Checking account permissions" }
{ "type": "status", "message": "Drafting answer with citations" }
这让用户在系统执行实际工作时保持知情。
按延迟等级路由模型
不是每个请求都值得使用你最强的模型。
创建延迟等级:
简单的路由器可以从规则开始。
function chooseModel(workflow: string, risk: "low" | "medium" | "high") {
if (workflow === "autocomplete") return "small-fast";
if (workflow === "bulk_report") return "batch-careful";
if (risk === "high") return "careful-reasoning";
return "fast-general";
}
之后,你可以根据测量到的性能、租户套餐、队列深度或失败率来路由。先从开发者能够理解和调试的规则开始。
缓存无聊的部分
缓存是改善延迟和成本最简单的方法之一,但要缓存正确的内容。
好的缓存候选:
- 未修改文档的 Embedding
- 常见查询的 RAG 检索结果
- 系统 Prompt 模板
- 分类输出
- 确定性转换
- 低风险重复问题的答案
不好的缓存候选:
- 没有租户范围的权限敏感答案
- 没有用户范围的个性化答案
- 基于快速变化数据的答案
- 可能包含过时价格、政策或账户状态的输出
始终在缓存键中包含租户和权限上下文。
function cacheKey(input: {
tenantId: string;
userRole: string;
workflow: string;
normalizedQuery: string;
sourceVersion: string;
}) {
return [
input.tenantId,
input.userRole,
input.workflow,
input.sourceVersion,
hash(input.normalizedQuery)
].join(":");
}
一次泄露数据的缓存命中比没有缓存更糟糕。
添加优雅降级
你的应用需要一个应对坏日子的计划:提供商变慢、队列峰值、长文档,或者租户运行大型作业。
有用的降级模式:
- 对于低风险请求,从谨慎模型切换到快速模型
- 当 TTFT 面临风险时减少检索的 Chunk
- 在负载高峰期间缩短输出长度
- 将长任务移至异步作业
- 显示部分结果并提示"继续生成"
- 在消耗 Token 之前请用户缩小请求范围
if (queueDepth > 100 && workflow === "support_rag_answer") {
budget.max_input_tokens = 6000;
budget.max_output_tokens = 500;
budget.fallback_model = "fast-general";
}
这不是在任何地方都降低质量,而是在压力下保护体验。
关注 p95,而非平均值
平均延迟会说谎。你的快乐路径可能看起来很好,而真实用户却在受苦。
按工作流和租户层级追踪这些指标:
- 每次请求的输入 Token 数
- 每次请求的输出 Token 数
- 每个成功任务的成本
起初一个简单的告警规则就足够了。
Alert when support_rag_answer p95 TTFT > 3000ms for 10 minutes.
Alert when cost per successful task rises 30% above 7-day baseline.
Alert when timeout rate > 2% for any paid tenant tier.
将延迟与成本挂钩。如果 p95 延迟和成本同时上升,你可能有上下文膨胀、重试循环、路由不当,或者某个工作流应该改为异步。
将重试视为预算风险
重试在代码里感觉无害,在生产环境中却很昂贵。
重试可以加倍成本、增加延迟,并产生重复的工具操作。对于 Agent,重试循环风险更大,因为模型可能再次调用工具。
- 带抖动的网络错误重试
- 不要盲目重试验证失败
- 在没有幂等键的情况下绝不重试写操作
- 少量尝试后停止
- 记录重试原因和增加的成本
const retryPolicy = {
maxAttempts: 2,
retryOn: ["rate_limit", "network_timeout"],
neverRetryOn: ["invalid_json", "permission_denied", "policy_blocked"]
};
如果一个工作流需要三次重试才能感觉可靠,它可能需要更好的设计,而不是更大的重试循环。
何时使用异步而非聊天
有些 AI 工作不应该假装是即时的。
适合异步的场景:
- 大型文档分析
- 多来源研究
- 有外部 API 速率限制的任务
好的异步 UX 包括:
- 立即确认任务收到
- 预计完成时间窗口
- 解释发生了什么的错误状态
这保护了你的聊天界面不会变成候诊室。
实施清单
在新 AI 功能上线前使用:
- [ ] 为每个工作流定义最大 TTFT 和总延迟
- [ ] 设置输入和输出 Token 上限
- [ ] 记录租户、工作流、模型、Token、成本、TTFT、总时间、状态
- [ ] 追踪 p95 延迟,而不仅仅是平均值
- [ ] 在有用时流式输出文本或状态事件
- [ ] 按工作流和风险等级路由模型
- [ ] 用租户感知的键缓存安全的重复工作
- [ ] 在改变基础设施之前先裁剪上下文
- [ ] 将长任务移至异步作业
- [ ] 对延迟、超时率、重试率和每个成功任务的成本告警
LLM 延迟预算不是官僚主义。它是产品质量的护栏。
当预算缺失时,每个 Prompt 都可以膨胀,每个 Agent 都可以游荡,每次重试都可以加倍支出,每个慢请求都可能变成一个谜。当预算存在时,你的团队可以做出明确的权衡:更快的首 Token、更短的输出、更好的上下文、更安全的缓存、异步工作流,或者只在需要的地方使用更强的模型。
快速的 AI 不仅仅关于速度。它关乎尊重用户时间的同时保护你的利润。
常见问题
什么是 LLM 延迟预算?
LLM 延迟预算是 AI 工作流的一套限制:首 Token 最大时间、最大总响应时间、输入 Token 上限、输出 Token 上限、模型路由、缓存规则和兜底行为。
AI 功能的良好 TTFT 是多少?
取决于工作流。内联建议应该几乎即时有感。聊天回答通常应该在一到两秒内开始流式输出。如果 UI 显示有意义的进度,RAG 或 Agent 工作流可以花费更长时间。
如何快速降低 LLM 延迟?
首先从裁剪输入 Token、限制输出长度、流式响应、缓存重复工作和将简单任务路由到更快的模型开始。这些改变通常比改变基础设施更容易。
每个 AI 工作流都应该流式输出吗?
不。流式输出对可读文本和进度更新效果良好。对于严格的 JSON、隐藏的工具调用工作流,或部分输出可能使用户困惑的任务,它不太有用。
延迟与 AI 成本如何关联?
长 Prompt、长输出、重试和工具循环通常同时增加延迟和成本。这就是为什么生产团队应该一起追踪 Token 数、延迟、缓存命中率和每个成功任务的成本。
自托管比使用 API 更快吗?
不会自动更快。自托管可以减少控制平面的不确定性,但良好的模型服务需要批处理、内存管理、扩展、监控和硬件调优。在假设自托管更好之前,先测量 TTFT、TPOT 和总成本。