第四章 推理 API 与部署¶
导读¶
工程师要"用 LLM",第一件事是选择推理方式:调云 API(OpenAI / Anthropic / 国内厂商)还是自部署开源模型(vLLM / Ollama)。这一章从工程师视角看"调用 LLM"的全景——三套 API 形态、流式输出、工具调用、计费限流、本地推理、成本与路由。读完本章你应该能决定"哪个场景用哪个 API"以及"什么时候该上本地推理",而不是默认都调 OpenAI。本章是后续 RAG / Agent 章节的工程前置——Agent 的循环依赖流式输出与工具调用,没有这两点就建不起反馈闭环。
4.1 三套 API 形态¶
工程师调 LLM 主要有三种 API:OpenAI Chat Completions、Anthropic Messages、开源 vLLM 的 OpenAI 兼容 server。三者接口格式、鉴权方式、特性各有差异,下面分别拆开看。
OpenAI Chat Completions¶
OpenAI 的 /v1/chat/completions 是行业事实标准——几乎所有 LLM 框架(LangChain、LlamaIndex、AutoGen)都默认支持 OpenAI 格式。请求体核心字段:model(模型名)、messages(消息数组)、tools(工具定义)、tool_choice、temperature、max_tokens、stream、response_format。
OpenAI 的鉴权用 Bearer token——Authorization: Bearer sk-xxx。流式输出用 SSE(Server-Sent Events),每个 chunk 形如 data: {...}\n\n。工具调用通过 tools 参数定义 schema,模型返回 tool_calls 字段触发调用。
OpenAI 格式有几个工程优势。第一,生态最大:所有框架都原生支持,文档最全,社区案例最多。第二,功能最完整:JSON mode、structured output、function calling、vision、batch API、fine-tuning API 都齐。第三,OpenAI 兼容:很多国产 API(DeepSeek、Moonshot、Zhipu)也提供 OpenAI 兼容接口,让用户用同一份代码切换后端。
OpenAI 兼容协议的工程价值¶
OpenAI 兼容协议是 2024 年开源界的"事实标准"——vLLM、TGI、SGLang、TensorRT-LLM 都提供 OpenAI 兼容 server,国产 API(DeepSeek、Moonshot Kimi、Zhipu、ByteDance)也都兼容。这让工程师可以"写一次 OpenAI SDK 代码,换 base_url 切换后端",极大降低迁移成本。工程上做 LLM 应用时要充分利用这一点:上层代码只调 OpenAI SDK 抽象层,下层实现可以是云 API 或本地 vLLM。LangChain、LiteLLM 等框架就是基于这个思路。
但 OpenAI 兼容不等于完全等价——有些特性只有原版 OpenAI 支持(如 structured output 的 json_schema 模式、batch API、fine-tuning),国产与开源兼容实现常常功能不全。生产代码要先测每个后端支持哪些特性,再决定能不能用兼容层。
Anthropic Messages¶
Anthropic 的 /v1/messages 是另一套主流 API,Claude 系列模型走这条。请求体与 OpenAI 类似但有差异。第一,system 单独:system 是顶层字段而不是 messages 数组里的一条。第二,content 是数组:每条消息的 content 必须是数组,每个元素有 type(text / image / tool_use / tool_result)。第三,tool 用 tool_use / tool_result:模型生成 tool_use 块,工具结果用 user role + tool_result 块返回。
Anthropic 的鉴权用 x-api-key header,再加 anthropic-version header。流式输出用 SSE 但格式与 OpenAI 不同——它有 event 字段(message_start / content_block_delta / message_stop 等),解析时要按 event 类型分发。
工程上 Anthropic 的几个特点。第一,长上下文是强项:Claude 3.5 / 4 系列窗口 200K,且有效记忆强于竞品。第二,prompt caching 内置:重复 prompt 部分自动缓存,只收 10% 费用,对 Agent 长循环友好。第三,XML 风格 prompt 推荐:官方文档建议用 <example> <instructions> 这种 XML 标签组织 prompt,模型输出更稳定。
Anthropic vs OpenAI 选型¶
工程师在 OpenAI 与 Anthropic 之间选型时几个考量。第一,长上下文任务走 Anthropic:Claude 200K 窗口 + 有效记忆强,处理长文档(法律、财报、代码库)比 GPT-4o 128K 略好。第二,代码任务两者都行:GPT-4o 与 Claude 3.5 在 HumanEval 上接近,差距在具体语言/框架——实际项目要 benchmark。第三,Agent 工具调用走 Anthropic:Claude 的 prompt caching 对 Agent 长循环友好(system + 工具描述重复利用),节省 70%+ input token 成本。第四,JSON 模式走 OpenAI:OpenAI 的 structured output(json_schema 模式)比 Anthropic 的工具 schema 强制更直接,纯数据抽取场景更稳。第五,多模态走 GPT-4o:GPT-4o 视觉能力强且便宜,Claude 3.5 vision 贵且效果略差。综合:Agent / 长文 / 代码 → Claude;视觉 / 结构化数据 / 多模态 → GPT-4o。
开源 vLLM / Ollama¶
vLLM 是当前最流行的开源 LLM 推理引擎,提供 OpenAI 兼容 server——意味着你用 OpenAI SDK 调本地 vLLM,只需要改 base_url 即可。vLLM 的核心优化是 PagedAttention(KV cache 分页管理),让吞吐量达到 HuggingFace transformers 的 10-50 倍。
vLLM 启动一行命令:python -m vllm.entrypoints.openai.api_server --model Qwen/Qwen2.5-7B-Instruct,启动后监听本地 8000 端口。调用方式与 OpenAI SDK 完全一样,只是 base_url 改成 http://localhost:8000/v1,API key 任意填。
vLLM 与 OpenAI / Anthropic 的关键差异在计费——本地部署是按"硬件占用"算而不是按 token。一台 8×H100 服务器月租约 2 万美元,跑 70B 模型理论上无限 token——只要你能把硬件利用率打满。这意味着:调用量小时本地比云贵(硬件闲置浪费),调用量大时本地比云便宜(云按 token 涨价,本地固定成本)。
Ollama 是另一个本地推理工具,定位是"本地开发友好"——一键拉模型、自动量化、REST API。比 vLLM 简单但吞吐量低一个量级,适合开发调试不适合生产。Ollama 的杀手锏是"开发者体验"——ollama run llama3 一行命令就跑起来,比 vLLM 配置简单得多。
vLLM 的几个工程实战要点¶
工程师部署 vLLM 时几个要点。第一,GPU 利用率:vLLM 默认配置已经很好,但要监控 GPU 利用率——MFU(Model FLOPs Utilization)能跑到 50-60% 算正常,低于 40% 要排查(batch size 太小?并发不够?)。第二,max_model_len 配置:默认按模型 max_len,但实际使用不到那么长可调小(如 32K)省显存。第三,tensor_parallel_size:多卡场景设 tp=N,让模型分 N 卡张量并行——7B 单卡够,70B 要 tp=2 或 4。第四,量化:vLLM 支持 AWQ / GPTQ 量化——70B AWQ 4bit 能在单张 80GB H100 跑(原 fp16 需要 2 张)。
第五,continuous batching:vLLM 默认开,让多个请求拼 batch——这是吞吐量高的核心。第六,speculative decoding:vLLM 0.6+ 支持推测解码,用小模型先生成候选 + 大模型并行验证,速度提升 2-3 倍——适合延迟敏感场景。第七,监控:vLLM 内置 metrics endpoint(/metrics),用 Prometheus + Grafana 监控 throughput / latency / queue length。第八,版本管理:vLLM 版本迭代快,每次升级要测试模型兼容性——某些模型在新版本可能不工作。
4.2 流式输出¶
LLM 生成是逐 token 的,单次完整生成可能几秒到几十秒。如果让用户等所有 token 生成完再显示,体验差到不能接受——这就是为什么所有 LLM 产品都用流式输出,让用户看到"打字机效果"。
SSE 协议¶
主流 LLM API 用 SSE(Server-Sent Events)做流式传输。SSE 本质是 HTTP 长连接,服务器持续推送 data: ... 块,每个块是一个 JSON。客户端解析每个 data 块得到一个 chunk,包含新生成的 token 增量。
OpenAI 流式 chunk 格式(简化):
data: {"choices":[{"delta":{"content":"你"}, "index":0}]}
data: {"choices":[{"delta":{"content":"好"}, "index":0}]}
data: [DONE]
Anthropic 流式 chunk 格式(简化):
event: message_start
data: {"type":"message_start","message":{...}}
event: content_block_delta
data: {"type":"content_block_delta","delta":{"text":"你"}}
event: message_stop
data: {"type":"message_stop"}
注意两边格式不同——OpenAI 用 delta.content 字段,Anthropic 用 event 字段区分阶段。工程师写跨平台流式代码时要分别处理。
TTFT 与 TPOT¶
工程师看流式输出有两个延迟指标。第一,TTFT(Time To First Token):从请求发出到第一个 token 到达的时间。TTFT 包含网络延迟 + prompt 处理时间,prompt 越长 TTFT 越大。GPT-4o 的 TTFT 大约 500ms,Claude 3.5 约 600ms,本地 vLLM 7B 约 200ms。TTFT 决定"用户多久看到第一个字"——这是用户感知"反应快慢"的关键指标。
第二,TPOT(Time Per Output Token):第一个 token 之后每个 token 的平均生成时间。TPOT 主要由模型规模 + 硬件决定——7B 在 A100 上 TPOT 约 20ms,70B 约 100ms。TPOT 决定"打字机速度"——TPOT 50ms 时用户看到约 20 token/秒(接近人阅读速度),TPOT 200ms 时只有 5 token/秒(用户感觉慢)。
流式为什么对 UX 关键¶
流式输出对用户体验有几个关键价值。第一,用户能立刻看到反馈,不需要等几秒才知道"模型在干活"。第二,用户能提前结束:看到模型输出方向不对,可以打断节省时间与 token。第三,长输出感知更短:3000 token 输出,非流式要等 30 秒,流式从 1 秒就开始显示,用户心理等待时间短几倍。第四,支持流式后处理:边生成边解析(如流式 JSON 解析、流式 markdown 渲染),让 UI 更顺滑。
工程上几乎所有 LLM 产品都默认开流式——不开流式用户体验差一档。但流式也有几个坑:客户端要做 chunk 累积、错误处理(连接断开要重连)、token 边界处理(一个汉字切两个 chunk 时要 buffer)。这些细节在生产代码里都要处理。
工程上的流式坑¶
工程师实现流式时容易踩的坑有几个。第一,汉字被切到两个 chunk:UTF-8 编码下中文一个字 3 字节,token 边界与字节边界不对齐,半个汉字字节流不能直接 print 否则乱码——要做 UTF-8 buffer 等完整汉字再输出。第二,网络抖动导致 chunk 延迟:流式时某个 chunk 500ms 不来,用户体验卡顿——要设置心跳机制(如每 2 秒发一个空 chunk 或 ping)。第三,连接断开重连:流式时连接断了要能续传——但 SSE 不支持原生续传,要做客户端 buffer + 重发请求,且模型输出可能不完全一致(温度 >0)。第四,chunk 错误处理:某个 chunk 是 error 而不是 token,要识别并处理——不要把 error 当 token 累积。
第五,多模态流式:图片输出时流式更复杂,每个 chunk 可能是 text 或 image delta,UI 要分别渲染。第六,agent loop 中的流式:Agent 多步循环时,每步的 LLM 调用都流式输出——但用户要看的不只是 LLM 输出,还要看工具执行结果——要把两者拼接展示,工程上要设计好"工具开始/工具执行/工具结果/下一步推理"的 UI 流。
4.3 工具调用 API¶
工具调用(Function Calling)是 Agent 的核心机制——通过 tools 参数定义可用工具,模型决定何时调用哪个工具传什么参数。这节展开 API 细节。
tools 参数¶
OpenAI 与 Anthropic 都用 tools 参数定义工具列表。每个 tool 有 name、description、parameters schema(JSON Schema)。例如:
{
"type": "function",
"function": {
"name": "get_weather",
"description": "查询指定城市天气",
"parameters": {
"type": "object",
"properties": {"city": {"type": "string"}},
"required": ["city"]
}
}
}
模型生成时如果决定调用工具,会在 response 里返回 tool_calls 字段(OpenAI)或 tool_use 块(Anthropic),包含工具名与参数。客户端拿到 tool_calls 后执行对应工具,把结果用 role="tool" 消息塞回 messages 数组,再调一次 API 让模型基于工具结果继续生成。
tool_choice 的三种模式¶
tool_choice 控制"模型是否一定调工具"。三种模式:
- auto(默认):模型自主决定是否调工具。简单问题直接答,复杂问题调工具。这是 99% 场景的默认。
- required:强制必须调工具。适合"明确要执行操作"的场景(如用户说"帮我订明天早上 9 点的会议")。required 模式模型不会直接答而是必调工具。
- none:禁止调工具。适合"就要自然语言回答"的场景,避免模型调一些不必要的工具浪费时间。
还有第四种特定工具模式:tool_choice={"type": "function", "function": {"name": "get_weather"}}——强制调用特定工具。适合"已知下一步要执行什么"的 workflow 场景。
并行工具调用¶
2024 年起 GPT-4o 与 Claude 3.5 都支持并行工具调用——模型在单次返回里给出多个 tool_calls,客户端并行执行这些工具。例如用户问"北京和上海今天的天气",模型返回两个 tool_calls(一个查北京一个查上海),客户端并行调两个天气 API,把两个结果都塞回去。
并行工具调用让 Agent 多步任务效率翻倍——以前是串行调 5 个工具要 5 次 LLM 调用,现在是 1 次 LLM 调用 + 5 个工具并行执行。但要注意工具间依赖关系——如果工具 B 依赖工具 A 的结果,不能并行。
流式工具调用¶
流式 + 工具调用是 2024 年的新能力。模型流式生成时如果决定调工具,会在 stream 里依次输出 tool_call 的 name、arguments 增量。客户端要累积这些增量拼出完整 tool_call,再执行。这比非流式工具调用复杂——增量解析易出错,要 buffer + JSON 部分解析。
工程上的几个坑。第一,JSON 增量解析:arguments 字段是 JSON,流式时半截 JSON 不能直接 parse,要用容错解析库(如 partial-json-parser)。第二,多 tool_call 流式:模型可能流式生成多个 tool_calls,每个都要分别累积。第三,判断何时 tool_call 结束:流式没有明确"tool_call 完整"信号,要看 finish_reason 或 event type 判断。
工具调用的几个工程实战细节¶
工程师做工具调用有几个细节要注意。第一,tool description 要详细:description 里要说清楚工具用途、何时该用、不该用——模型主要靠 description 决定何时调,描述不清会误调。第二,parameters schema 要严格:每个字段说清楚类型、单位、取值范围、示例——schema 越详细模型生成的参数越准。第三,工具数量要控制:tools 列表过长会让模型选择困难("select 几十个里挑一个"),且每个 tool 的 schema 都占 prompt token——建议单次调用工具数 < 20 个,超过要做"工具路由"或"动态工具加载"。
第四,工具执行的幂等性:被模型多次调用要保证结果一致——如"创建订单"这种有副作用的工具要用 idempotency key 避免重复创建。第五,工具超时处理:工具执行卡住要超时(如 30 秒),超时返回错误让模型重新决策,不要无限等。第六,工具错误信息要"模型友好":返回错误时要说清楚"为什么错、怎么修"——如"参数 city 必须是中文,你传的是拼音"——让模型能修正重试。第七,工具调用日志要保留:每个 tool_call 的入参、返回、耗时、是否成功都要进日志——这是 Agent 调试的核心信号。
4.4 Token 计费 / 限流 / 配额¶
LLM API 不便宜,工程师要懂计费规则、限流机制、配额管理,否则上线后账单会爆。
计费模型¶
主流 API 计费按 token,分 input / output / cached input 三档:
- input token:prompt 部分的 token,按 input 单价计。GPT-4o $5/1M,Claude 3.5 $3/1M。
- output token:模型生成的 token,按 output 单价计,通常贵 3-5 倍。GPT-4o $15/1M,Claude 3.5 $15/1M。
- cached input:prompt 中重复部分(如 system prompt、工具定义)命中缓存时只收 10-25%。OpenAI 与 Anthropic 都支持 prompt caching。
工程上的几个要点。第一,output token 贵于 input——优化方向是减少输出长度(用 max_tokens 限制、用 stop 提前截断、让模型答简短)。第二,长上下文溢价:超过阈值(32K 或 64K)的上下文加价 2-3 倍。Agent 跑长任务时要监控 context length。第三,cached input 要主动配置:OpenAI 要在 prompt 里加特殊标记(cache_control),Anthropic 在 system 或 messages 里加 cache_control 标记。不配置就没有缓存优惠。
RPM / TPM 限流¶
主流 API 有两种限流。第一,RPM(Requests Per Minute):每分钟请求数限制。Tier 1 用户 GPT-4o 500 RPM,Tier 5 用户 10000 RPM。第二,TPM(Tokens Per Minute):每分钟 token 数限制,通常按 input + output 总和算。Tier 1 用户 GPT-4o 150K TPM。
超限流时 API 返回 429 状态码,response header 里有 retry-after 字段提示等多久。工程师必须处理 429——不处理的系统上线必爆。
429 重试策略¶
工程上处理 429 的标准做法是指数退避(exponential backoff):
import time
import random
def call_with_retry(api_call, max_retries=5):
for attempt in range(max_retries):
try:
return api_call()
except RateLimitError:
sleep = (2 ** attempt) + random.random()
time.sleep(sleep)
raise Exception("Max retries exceeded")
工程上的几个细节。第一,加 jitter(随机抖动):所有客户端同时重试会让限流雪崩,加 1 秒以内的随机抖动让重试错开。第二,指数退避 + 上限:base * 2^attempt,但最大不超过 60 秒——避免重试间隔过大让任务卡死。第三,重试次数有上限:5 次重试仍失败就报错让人工处理,不要无限重试。第四,重试要幂等:重试同一请求要保证结果一样(如创建工单这种有副作用的操作不能简单重试,要用 idempotency key)。
配额管理¶
企业级 LLM 应用要做配额管理。第一,per-tenant 配额:每个客户/部门独立配额,避免一个客户烧光整个账号预算。第二,预算告警:日预算 / 月预算超 80% 时告警,超 100% 时熔断。第三,多账号轮询:单个 API key 超限流时切到备用账号——这是降低限流影响的事实标准做法。第四,模型路由降本:简单任务走便宜模型(Haiku / mini),复杂任务走贵模型(Sonnet / Opus),整体成本能降 50-80%。下一节展开。
多账号轮询的工程实现¶
多账号轮询是降低限流影响的事实标准,但实现起来有几个细节。第一,API key 池:维护多个 API key(不同账号或同账号多 key),按 round-robin 或负载均衡分配。第二,限流状态共享:当一个 key 收到 429,要让池里其他 key 知道"这个 key 暂时不能用"——通常用 Redis 共享限流状态。第三,key 健康检查:定期主动调一次最便宜的 API 检测 key 是否仍可用(避免某 key 已被禁用还轮询到)。第四,配额同步:多 key 共享同一份配额预算(如总预算 $1000),要做到原子扣费——否则并发请求会超扣。
第五,key 失效告警:某 key 突然失效(账号封禁、信用卡过期)要立刻告警,否则该 key 路径所有请求都失败。第六,key 池监控 dashboard:每个 key 的 RPM/TPM/错误率/成本都要实时监控,发现异常 key 主动剔除。第七,合规风险:用多个账号轮询绕过单账号限流要遵守厂商 TOS——OpenAI / Anthropic 对"规避限流"有不同容忍度,企业级用要咨询商务而非偷偷搞。
4.5 本地推理速览¶
不是所有场景都该调云 API——合规、成本、延迟三方面考虑时可能上本地推理。这节简述两个主流本地推理工具。
vLLM¶
vLLM 是当前开源 LLM 推理引擎事实标准。核心优化是 PagedAttention——把 KV cache 当虚拟内存管理,按页分配,避免显存碎片,让 batch size 提升几倍。配合 continuous batching(动态拼 batch),吞吐量达到 HuggingFace transformers 的 10-50 倍。
vLLM 适合:高吞吐生产场景(日均百万级调用)、私有数据合规场景(数据不出内网)、成本敏感大规模场景(70B 模型月调用量过亿 token 时本地比云便宜)。不适合:单机调试(Ollama 更轻量)、模型量化场景(要用 GPTQ / AWQ 配合)、特殊硬件(vLLM 对 NVIDIA 支持好,AMD / Apple Silicon 支持不完整)。
Ollama¶
Ollama 是本地开发友好的推理工具——一键拉模型、自动量化、REST API、模型注册表。ollama run llama3.2 一行命令就能在 MacBook 上跑 3B 模型,无需 GPU 也能跑。
Ollama 适合:本地开发调试、单用户桌面场景、原型验证。不适合:生产高并发(吞吐量低)、企业部署(运维功能弱)、精确性能调优(参数暴露少)。Ollama 是 vLLM 的"低配开发版",工程师的本机开发环境标配。
何时上本地推理¶
工程师选本地推理的判断标准有几个。第一,合规要求:私有数据不能出内网(金融、医疗、政务)→ 必上本地。第二,成本规模:月调用量 > 1 亿 token 时本地 70B 比云便宜 50%+,值得切换。第三,延迟敏感:实时场景(< 200ms TTFT)→ 本地 7B 是甜点。第四,模型定制:要做 SFT 或领域适配 → 本地可控。第五,规模太小不值得:月调用 < 1000 万 token 时云更便宜,本地硬件折旧 + 运维反而贵。
本地推理的隐藏成本¶
工程师评估本地推理时常忽略几个隐藏成本。第一,硬件折旧:GPU 不是一次性投入,每张 H100 折旧约 1.5-2 万美元/年(按 3 年寿命算)——8 卡服务器年折旧 12-16 万美元,还不算电费与机房。第二,运维人力:本地推理要 24×7 运维——GPU 故障、模型 OOM、版本升级、监控告警,至少 1 个全职 SRE。第三,模型更新成本:云 API 自动用最新模型(如 GPT-4o 升级到 GPT-5),本地要手动下载 + 测试 + 切换——人员投入大。第四,容灾备份:单机故障整个服务挂掉——要做主备、负载均衡、自动故障转移,工程复杂度高。
第五,性能调优:本地推理要持续优化——KV cache 配置、batch size 调优、量化策略——不优化好吞吐量低、成本反而高。第六,安全与权限:私有数据本地推理也要做权限隔离(多租户场景数据不能互看)——这一层云厂商已经做好,本地要自己实现。综合下来,本地推理的真实总成本 = 硬件折旧 + 运维人力 + 电力机房 + 调优时间 + 容灾备份——账面算下来常比云还贵,规模必须足够大才能摊薄这些固定成本。
4.6 成本估算与模型路由¶
工程师做 LLM 应用最大的成本不是模型本身,是"模型选错"——用 Opus 做简单分类,是 10 倍成本浪费。这节讲成本估算与模型路由策略。
决策树¶
按任务类型 + 难度 + 上下文长度分流:
- 简单分类 / 抽取 / 摘要(短上下文 <8K)→ Haiku / GPT-4o mini / 本地 8B。$0.15-0.25/1M,TTFT < 500ms。覆盖 60-70% 客服 / 数据处理场景。
- 中等推理 / 工具调用 / 多轮对话(中上下文 8-32K)→ Sonnet / GPT-4o / Flash。$3-5/1M,TTFT 500-1000ms。Agent 主用,性价比最高。
- 超长上下文 / 高难推理(长上下文 >64K)→ Opus / GPT-4o Pro / Gemini Ultra。$15-30/1M,TTFT 1-3s。复杂规划、长文档分析、超难推理才用。
模型路由的实现¶
工程上做模型路由有几种实现。第一,静态路由:按任务类型预先指定(如客服 → Haiku,代码 → Sonnet)。最简单,覆盖 80% 场景。第二,复杂度评估路由:先让便宜模型评估任务复杂度,再决定路由到哪——这是 cascade 思想,便宜模型当"分诊台"。第三,fallback 路由:先调便宜模型,置信度低时升到贵模型重试——这是 Sana、FrugalGPT 等论文的思路。第四,学习式路由:用历史数据训一个分类器,预测"这个 prompt 该走哪个模型"——准确率最高但要训练数据。
工程上推荐从静态路由起步——80% 场景能用,简单稳定。规模上来后再升级到复杂度评估或学习式路由。一上来就上学习式路由是过度工程。
cascade 路由的工程实践¶
cascade 路由是 2024 年成本优化的热门方案。核心思路是"先调便宜模型,置信度低再升贵"——但实现上有几个细节。第一,置信度信号:怎么判断便宜模型答得对不对?要看 logprob(最高 logit 概率)或自评分数(让模型自己打分),但两者都不准——这是 cascade 的最大不确定性。第二,升档阈值:置信度低于多少升档?阈值太低升级率不够省钱效果差,阈值太高升级率太高反而更贵——要在评测集上 sweep 找甜点。第三,升档时复用前一次的输出:升级后让贵模型"基于便宜模型的答案改"而不是从头答,能省不少 token——这是 cascade 的关键技巧。
第四,cascade 的延迟问题:cascade 是两次 LLM 调用,TTFT 翻倍——对延迟敏感场景不友好。要权衡"成本节省" vs "延迟增加"。第五,cascade 的失败模式:便宜模型答得不对但置信度高("自信地错"),cascade 不会升级——这种错最危险,要靠其他信号(如规则校验)兜底。第六,Agent 场景的 cascade:Agent 多步循环里每步都可以 cascade,但要在多步中保持一致性——简单步骤用便宜模型,复杂步骤用贵模型,整体成本能降 50%。
成本审计¶
LLM 应用上线后必须做成本审计,否则账单会失控。第一,per-tenant 计费:记录每个客户/部门的 token 用量与成本,做到"谁用谁付费"。第二,per-feature 计费:记录每个功能(聊天/RAG/Agent)的 token 用量,找出成本大头。第三,异常告警:单个用户 token 用量突增(如某用户一天烧 10M token)告警,避免恶意调用或 bug 烧预算。第四,月度成本复盘:每月对比预算 vs 实际,找出超支原因——是用户增长?是 prompt 变长?是模型选错?第五,cost dashboard:做一个内部 dashboard,让 PM / 运营能看到实时成本,发现异常主动优化。
成本审计的技术实现¶
成本审计的工程实现有几个要点。第一,token 计数:每次 API 调用后从 response 里读 usage 字段(input_tokens / output_tokens / cached_tokens),不要自己估算——估算与实际差 10-30%。第二,成本计算:按当时单价 × token 数算成本,单价变化时要记录历史单价表——避免"用今天的单价算昨天的成本"出错。第三,异步写入:成本日志写入要异步(不阻塞主请求路径),否则会拖慢响应。第四,聚合维度:按 tenant / feature / model / 时间维度都能聚合——用 OLAP 数据库(如 ClickHouse)比 SQL 数据库更适合。
第五,实时 vs 批处理:实时成本(每秒更新)适合告警,批处理成本(每日聚合)适合复盘——两个 pipeline 都要。第六,对账机制:定期与厂商账单对账——本地累计的 token 数与厂商账单可能有差异(如厂商按 server-side 重新切 token),差异超 5% 要排查。第七,预算告警自动化:超 80% 自动邮件、超 100% 自动钉钉/Slack、超 120% 自动熔断(停止接受新请求)——三层告警避免失控。
代码片段:三套 API 的流式 + 工具调用¶
下面三段代码分别用 OpenAI / Anthropic / vLLM 本地调用,每段约 10 行,演示流式输出。流式工具调用需额外处理 delta.tool_calls 累积,本节不展开。
# 1. OpenAI 流式 + 工具调用
from openai import OpenAI
client = OpenAI()
tools = [{"type": "function", "function": {"name": "get_weather",
"parameters": {"type": "object", "properties": {"city": {"type": "string"}}}}}]
stream = client.chat.completions.create(model="gpt-4o-mini",
messages=[{"role": "user", "content": "北京今天天气?"}],
tools=tools, stream=True)
for chunk in stream:
if chunk.choices[0].delta.content:
print(chunk.choices[0].delta.content, end="", flush=True)
# 2. Anthropic 流式 + 工具调用
from anthropic import Anthropic
c = Anthropic()
with c.messages.stream(model="claude-3-5-sonnet-20241022",
max_tokens=512, system="你是天气助手",
tools=[{"name": "get_weather", "input_schema":
{"type": "object", "properties": {"city": {"type": "string"}}}}],
messages=[{"role": "user", "content": "北京今天天气?"}]) as stream:
for text in stream.text_stream:
print(text, end="", flush=True)
# 3. vLLM 本地(OpenAI 兼容)
from openai import OpenAI
client = OpenAI(base_url="http://localhost:8000/v1", api_key="any")
stream = client.chat.completions.create(model="Qwen/Qwen2.5-7B-Instruct",
messages=[{"role": "user", "content": "北京今天天气?"}],
stream=True, temperature=0.7)
for chunk in stream:
if chunk.choices[0].delta.content:
print(chunk.choices[0].delta.content, end="", flush=True)
三段代码结构几乎一样——这就是 OpenAI 兼容协议的威力,换 base_url 与 model 名就能切换后端。生产代码建议封装一个 LLM client 抽象层,上层只调 llm.chat(),下层实现 OpenAI / Anthropic / vLLM 三种后端。
工程实战要点¶
- 云 API 优先,规模上来再上本地:云 API 启动快、无运维、按 token 付费;规模 > 1 亿 token/月 再考虑本地,否则硬件折旧 + 运维反而贵。
- 流式输出对 UX 关键:所有面向用户的 LLM 产品必开流式,TTFT > 1s 用户感知卡顿;后端纯计算场景可以非流式。
- 工具调用 latency >> 模型 latency(瓶颈在工具):Agent 跑慢的 80% 原因是工具执行慢(API 调用、DB 查询、文件 IO),不是 LLM 慢——优化工具并发执行是关键。
- 重试 + 限流 + 配额缺一不可:429 必处理(指数退避 + jitter);RPM/TPM 监控;per-tenant 配额隔离;超预算告警熔断。
- 成本审计要 per-tenant:上线后必做 cost dashboard,单客户 token 突增、单功能成本占比都要能定位——否则账单会失控。
小结¶
- 三套 API 形态:OpenAI Chat Completions(事实标准,生态最大)/ Anthropic Messages(长上下文 + 缓存强)/ vLLM OpenAI 兼容(高吞吐本地推理)。
- 流式输出用 SSE,TTFT 与 TPOT 是两个关键延迟指标;流式对 UX 必要但客户端要处理 chunk 累积与边界。
- 工具调用三种 tool_choice 模式(auto / required / none);并行 + 流式工具调用是 2024 年新能力。
- 计费分 input / output / cached input 三档,output 贵 3-5 倍;RPM/TPM 限流 + 429 指数退避 + 配额管理是工程标配。
- 本地推理:vLLM 高吞吐生产,Ollama 本地开发;月调用 > 1 亿 token 或合规要求时上本地。
- 模型路由按任务难度 + 上下文 + 延迟分流到 Haiku / Sonnet / Opus,整体成本降 50-80%;成本审计 per-tenant 必做。
练习题¶
- OpenAI 与 Anthropic API 在消息格式上的主要差异?
- 流式输出对首 token 延迟与每 token 延迟分别什么影响?
- tool_choice 的三种模式?
- 429 限流响应如何处理?
- vLLM 与 Ollama 各自适用场景?
- 模型路由策略:什么任务适合 Haiku,什么适合 Opus?
参考答案¶
第 1 题答案
OpenAI 与 Anthropic API 的消息格式差异主要有三点。第一,**system 位置**:OpenAI 把 system 作为 messages 数组的第一条(role: "system");Anthropic 把 system 单独作为顶层字段,不在 messages 数组里。第二,**content 类型**:OpenAI content 是字符串或多模态数组(图文混合);Anthropic content 必须是数组,每个元素有 type(text/image/tool_use/tool_result)。第三,**tool 调用返回**:OpenAI 用 role: "tool" + tool_call_id 关联;Anthropic 用 role: "user" + content type: "tool_result" + tool_use_id 关联。跨平台代码要用适配层抽象。第 2 题答案
流式输出对 TTFT(首 token 延迟)几乎无影响——TTFT 主要由网络延迟 + prompt 处理时间决定,流式只是让第一个 token 一到就发送,不会延迟。但对"用户感知的延迟"改善巨大——用户看到第一个字就感知"系统在工作",不必等全部生成完。对 TPOT(每 token 延迟)也无影响——TPOT 由模型规模 + 硬件决定。流式的真正价值是 UX:让用户立刻看到反馈、能提前打断、心理等待时间短几倍。生产 LLM 产品几乎必开流式。第 3 题答案
tool_choice 三种模式:1. **auto**(默认)——模型自主决定是否调工具,简单问题直接答复杂问题调工具,99% 场景默认。2. **required**——强制必须调工具,适合"明确要执行操作"的场景(如用户说"订明天 9 点会议")。3. **none**——禁止调工具,适合"就要自然语言回答"避免不必要的工具调用。还有第四种特定工具模式:tool_choice={"type":"function","function":{"name":"..."}},强制调用特定工具,适合"已知下一步要执行什么"的 workflow。第 4 题答案
429 限流响应处理标准做法是指数退避(exponential backoff)+ jitter:第一次重试等 base * 2^0 + random 秒,第二次 base * 2^1 + random 秒,依此类推。加 jitter(随机抖动)让所有客户端重试错开避免限流雪崩;指数退避要有上限(如 60 秒)避免任务卡死;重试次数有上限(5 次)仍失败就报错人工处理。重试要幂等——有副作用的操作要用 idempotency key 避免重复执行。生产代码建议封装重试中间件,所有 LLM 调用都过这层。第 5 题答案
vLLM 与 Ollama 的适用场景不同。vLLM 是高吞吐推理引擎,适合生产高并发场景(日均百万级调用)、私有数据合规场景、企业部署;PagedAttention + continuous batching 让吞吐量达到 transformers 的 10-50 倍。Ollama 是本地开发友好的工具,适合单机调试、原型验证、桌面单用户场景;一键拉模型、自动量化、REST API,但吞吐量低一个量级。工程师的本机开发环境标配 Ollama,生产部署用 vLLM。规模太小(月调用 < 1000 万 token)两者都不必,直接调云 API 更便宜。第 6 题答案
模型路由策略:**简单任务走 Haiku/Mini**——分类、抽取、摘要、改写、客服 FAQ 这种短上下文 + 低难度任务,$0.15-0.25/1M token 性价比最高,覆盖 60-70% 业务场景。**中等推理走 Sonnet/Flash**——Agent 多步推理、Function Calling、代码生成、多轮对话,$3-5/1M token,是 Agent 主用模型,性价比平衡点。**超难/长上下文走 Opus/Pro**——长文档分析(>64K 上下文)、复杂规划、超难推理(奥赛级数学、深度法律分析),$15-30/1M token,只在必要时才用,否则烧预算。整体策略:先用便宜模型跑通,发现准确率不够再升级。下一章:第五章 RAG 基础