大模型 API 快速上手

7 阅读17分钟

第 4 章 大模型 API 快速上手

"与其把大模型当成一个黑盒子,不如把它当成一个特殊的 RPC 服务——你发一段上下文,它根据概率返回最合理的续写。"

前三章建立了认知基础。这一章正式动手,但我们不会在代码细节上浪费时间——10 年+的后端经验意味着你第一次看到 SDK 调用就能读懂。本章的重点在于理解大模型 API 与传统 HTTP 服务的本质差异,以及几个关键概念背后的设计逻辑


4.1 大模型 API 的本质:一次特殊的 RPC

本质上,大模型 API 是一个无状态的远程过程调用:

输入:messages 数组(一段对话历史)
输出:下一条消息(模型预测的"最合理续写"

与传统 HTTP 服务的最大区别有三点:

4.1.1 天然无状态,上下文由调用方维护

传统服务可以在服务端维护 Session,但大模型 API 是严格无状态的——每次请求都要把完整的对话历史发过去。这意味着"会话管理"是你的应用层责任,不是模型的责任。

这跟 JWT 认证的设计哲学一致:服务端不存状态,所有信息都在 Token 里。

工程影响

  • 多轮对话的成本随轮数线性增长(每次都要发完整历史)
  • 需要在应用层设计会话存储策略(Redis、数据库等)
  • 跨服务调用时需要显式传递上下文,不能依赖隐式 Session

4.1.2 响应时间不可控

传统 API P99 延迟通常在百毫秒级,但大模型 API 的延迟取决于输出长度——生成 2000 个 Token 可能需要 10-20 秒。这从根本上改变了你的 API 设计策略。

为什么延迟不可控?

大模型生成是串行的:生成第 N 个 Token 时,必须等第 N-1 个 Token 确定后才能计算注意力权重。这意味着无论你的并发度多高,单个请求的延迟下限由输出长度决定。

工程应对

  • 流式输出(Streaming)改善感知延迟,但无法改善总耗时
  • 耗时任务必须异步化(消息队列 + 回调机制)
  • 超时设置要区分"首 Token 延迟"和"总生成时间"

4.1.3 输出具有概率性

相同输入不一定返回相同输出(temperature 参数控制随机程度)。这意味着你不能用传统的"幂等性"假设来设计调用逻辑。

工程影响

  • 重试机制不能简单重复调用,需要调整参数或增加随机性
  • 缓存策略要考虑"近似匹配"而非"精确匹配"
  • 测试用例需要多次运行取统计结果,不能依赖单次输出

4.2 messages 数组:理解对话上下文的设计

所有主流大模型 API 都采用 messages 数组格式,每条消息有三种 role:

Role定位后端类比
system全局行为约束,设定角色、规则、输出格式Spring 的 @Configuration,影响整个应用
user用户的当前输入HTTP 请求的 body
assistant模型上一轮的回复(多轮对话时追加)服务端之前的响应,作为下轮的上下文

4.2.1 System Prompt 的工程价值

System Prompt 是"一次性"的,放在开头,影响整个对话。它的作用类似于应用的全局配置:

  • 角色设定:"你是一位资深 Java 架构师"
  • 行为约束:"只回答技术问题,拒绝非技术话题"
  • 输出格式:"所有回复使用 Markdown 格式,代码块标注语言"
  • 安全边界:"不要生成有害内容,遇到敏感话题礼貌拒绝"

关键洞察:System Prompt 的质量直接影响模型表现。一个精心设计的 System Prompt 可以让 GPT-4o-mini 达到接近 GPT-4o 的效果;反之,一个糟糕的 System Prompt 会让最强模型也表现失常。

4.2.2 上下文窗口的工程约束

多轮对话时,每次都要把完整的历史 messages 发送给模型。这带来两个工程问题:

问题一:长度限制(Context Window)

每个模型有固定的上下文窗口(8K、32K、128K、200K Token 不等)。超过限制的输入会被截断或报错。

应对策略

  • 滑动窗口:只保留最近 N 轮对话
  • 摘要压缩:早期对话用 LLM 生成摘要替代原文
  • 关键信息提取:把重要事实(用户偏好、已确认信息)提取到独立字段,不依赖完整历史

问题二:成本累积

Token 是计费单位,历史越长,每次调用成本越高。一个 20 轮的对话,第 20 轮的成本可能是第 1 轮的 10 倍以上。

成本优化方向

  • 对话历史的智能截断(不是简单保留最近 N 轮)
  • 关键信息的显式存储(避免重复携带大量历史)
  • 小模型做意图分类,大模型只做生成(详见第 14 章)

4.3 流式输出:必须做,但要做对

4.3.1 为什么必须用流式输出

如果生成一个完整回复需要 15 秒,用户盯着转圈圈等 15 秒,体验极差。流式输出让第一个 Token 在 300-500ms 内就推送给用户,"感知延迟"降为原来的 1/30。

这不是可选项,是生产级大模型应用的标配。

4.3.2 技术实现:Server-Sent Events

客户端建立一个持久的 HTTP 连接,服务端持续推送数据片段(chunk)。前端逐字追加到界面,形成"打字机"效果。

SSE vs WebSocket 的选型

维度SSEWebSocket
通信方向服务端→客户端单向双向
协议复杂度简单(HTTP 之上)需要握手和帧管理
重连机制浏览器自动处理需手动实现
适用场景单向推送(如流式生成)双向交互(如实时协作)

对于大模型流式输出,SSE 足够。只有在需要用户"打断"模型生成时才需要 WebSocket。

4.3.3 流式输出带来的工程挑战

挑战一:错误发生在流的中途

模型生成到一半,网络断开或模型报错。已推送的内容无法撤回,用户看到的是残缺的回复。

应对策略

  • 在流结束时发送一个"完成标记",前端只有收到标记才认为生成完成
  • 中途报错时,前端显示"生成中断"并提供"重新生成"按钮
  • 关键场景(如代码生成)在服务端缓存完整生成结果,流只是推送手段

挑战二:内容完整性验证

流式推送过程中,如何确保最终内容没有被截断或篡改?

应对策略

  • 服务端生成完整内容后计算哈希,随最后一个 chunk 推送
  • 前端校验哈希,不匹配时提示用户"内容可能不完整"
  • 金融、医疗等敏感场景,要求用户确认后再执行

挑战三:流量控制与背压

如果前端渲染速度跟不上后端推送速度,会导致内存堆积或丢帧。

应对策略

  • 使用背压机制(Backpressure),前端处理完一个 chunk 再请求下一个
  • 或在前端设置缓冲区,平滑渲染节奏

4.4 Function Calling:大模型与业务系统的集成枢纽

Function Calling(工具调用)是大模型 API 最重要的能力,没有之一。理解它,是理解 Agent 架构的前提。

4.4.1 解决了什么问题

大模型的训练数据有截止日期,它不知道你的私有数据(订单、库存、用户信息)。Function Calling 允许模型在需要时"挂起"生成,通知你调用某个业务接口,拿到结果后再继续生成回复。

4.4.2 工作流程详解

① 用户:"帮我查一下订单 12345 的物流状态"
② 你把 [query_order, create_refund, ...] 工具定义发给模型
③ 模型返回:{ "tool": "query_order", "args": {"order_id": "12345"} }
   (此时模型暂停生成,等待你执行)
④ 你的后端调用真实的订单服务
⑤ 把查询结果发回给模型
⑥ 模型基于真实数据生成最终回复给用户

关键洞察:模型只做两件事——决策(调不调、调哪个、传什么参数)和生成(基于结果写回复)。真正的"执行"永远在你的业务代码里,模型不能直接访问你的数据库。

后端类比:Function Calling 就像一个智能的路由层。用户发来的意图(自然语言),模型将其转化为结构化的函数调用,相当于做了 NLU(自然语言理解)+ 参数提取,然后把请求路由到你的 Service 层执行。

4.4.3 工具定义:本质是 API 文档

你需要用 JSON Schema 格式描述每个工具的名称、用途、参数。这份描述就是给模型看的"接口文档"——描述越清晰,模型调用越准确。

工具描述的质量直接影响调用准确率

差的描述:"查询订单"
好的描述:"根据订单 ID 查询订单的当前状态、物流信息、支付金额。订单 ID 是 10-20 位的数字字符串,可在订单确认邮件中找到。"

写工具描述要像写 API 文档一样认真。模型对模糊描述的理解能力有限,明确的约束和示例能显著提升准确率。

4.4.4 并行工具调用

新版模型支持在一个响应中同时请求调用多个工具(Parallel Tool Calling),适合需要聚合多个数据源的场景:

  • 同时查询订单状态 + 物流信息 + 用户评价
  • 同时查询多个城市的天气
  • 同时查询多个商品的库存

工程价值

  • 减少往返次数,降低总延迟
  • 减少 LLM 调用次数,降低成本
  • 但增加了你的后端并发处理复杂度

4.4.5 Function Calling 的常见陷阱

陷阱一:工具过多导致选择困难

给模型注册 50 个工具,它会频繁选错或犹豫不决。建议:

  • 按场景动态加载工具(客服场景只加载客服相关工具)
  • 使用工具分类,先让模型选类别,再在该类别内选具体工具
  • 核心工具控制在 10 个以内

陷阱二:参数提取不准确

用户说"帮我查一下上周三下的那个订单",模型需要提取订单 ID,但用户没给。这时模型可能会:

  • 瞎编一个订单 ID(幻觉)
  • 返回错误提示(体验差)
  • 调用一个"搜索订单"工具(如果提供了的话)

应对策略

  • 提供"信息收集"类工具,让模型主动追问缺失信息
  • 在工具描述中明确哪些参数是必需的,哪些是可选的
  • 对关键参数在后端做校验,发现缺失时返回明确的错误信息

陷阱三:循环调用

模型 A 调用工具 B,工具 B 的结果触发模型再次调用工具 A,形成循环。

应对策略

  • 设置最大调用次数限制(如最多 10 轮)
  • 记录调用链,检测到循环时强制终止
  • 在工具描述中明确"不要重复调用已调用过的工具"

4.5 结构化输出:从"模型返回文本"到"模型返回数据"

默认情况下,模型输出是自然语言文本。但在很多工程场景里,你需要的是结构化数据:分类标签、提取的字段、布尔判断等。

4.5.1 三种获取结构化输出的方式

方式可靠性适用场景
Prompt 约束原型验证、内部工具
JSON Mode需要 JSON 但 Schema 灵活的场景
Structured Outputs生产环境,Schema 严格固定

Prompt 约束:在 system prompt 里要求"只返回 JSON"。简单但不可靠,模型偶尔会"忘记"或包裹在 Markdown 代码块里。

JSON Mode:API 参数强制模型输出合法 JSON。可靠,但 Schema 不固定,字段可能缺失或类型不对。

Structured Outputs(OpenAI 最新功能):直接传入 JSON Schema,模型保证完全符合 Schema 输出。如果无法生成符合 Schema 的内容,会返回错误而非勉强输出。生产环境推荐。

4.5.2 工程注意点

即使用了 Structured Outputs,生产代码也必须加 try-catch 和 fallback——大模型的行为永远不是 100% 确定的,这是它与确定性系统最本质的区别。

推荐的安全模式

① 调用模型,要求结构化输出
② 解析输出
③ 如果解析失败或字段缺失:
   - 记录错误日志
   - 返回默认值或降级到备用逻辑
   - 不要直接抛异常导致服务中断

4.6 多厂商接入:接口统一性与差异化

目前主流大模型 API(OpenAI、Anthropic Claude、阿里通义、DeepSeek、字节豆包等)在接口设计上高度趋同——大多兼容 OpenAI Chat Completion 格式,只需修改 base_urlapi_key

4.6.1 架构建议:在业务代码与具体 SDK 之间加一层抽象

为什么需要抽象层

  • 今天用 GPT-4o,明天可能换 DeepSeek 或本地部署的 Qwen——不能让业务代码耦合到具体厂商
  • 不同任务用不同模型(意图分类用小模型,复杂生成用大模型)
  • 主备降级:主力模型不可用时自动切换到备用模型

这层抽象本质上就是一个简单的策略模式——上层只知道"调用 LLM",不关心背后是哪个厂商哪个模型。

4.6.2 厂商差异:抽象层需要屏蔽的细节

虽然接口格式趋同,但不同厂商在以下方面存在差异,抽象层需要处理:

差异点说明处理策略
System Prompt 处理部分厂商不支持 system role,或处理方式不同统一转换为 user role 的第一条消息
Function Calling 格式参数名、返回格式略有差异统一转换为标准格式
Token 计算不同厂商的 Tokenizer 不同,计费单位可能不同统一使用 OpenAI 的 tiktoken 估算
错误码限速、额度不足、内容审核的错误码不同统一映射为内部错误码
流式格式SSE 的数据格式略有差异统一解析为标准 chunk 格式

4.6.3 多模型路由策略

抽象层之上,可以进一步实现智能路由:

用户请求
    │
    ▼
┌─────────────────┐
│   意图识别       │ ← 用小模型(GPT-4o-mini)
│  (分类任务)    │
└────────┬────────┘
         │
    ┌────┴────┐
    ▼         ▼
简单任务    复杂任务
    │         │
    ▼         ▼
小模型      大模型
(低成本)    (高质量)

路由策略示例

  • 意图分类、情感分析 → GPT-4o-mini
  • 代码生成、复杂推理 → GPT-4o
  • 超长上下文(>100K)→ Claude 3.5 Sonnet
  • 中文内容生成 → 通义千问(中文语料更多)

4.7 生产环境的关键配置项

即使是简单的 API 调用,在生产环境也有几个参数需要认真对待:

参数含义生产建议
temperature输出随机性(0=确定,1=最大随机)分类/提取任务用 0;创意写作用 0.7-1.0
max_tokens最大输出 Token 数必须设置,防止无止境生成消耗预算
timeout请求超时流式建议 60s,非流式建议 30s
seed固定随机种子调试和测试时设置,方便复现结果
presence_penalty抑制重复话题长文本生成时适当设置,避免车轱辘话
frequency_penalty抑制重复用词同上

4.7.1 Temperature 的实用原则

很多人用默认值(1.0)做分类任务,导致结果不稳定。实用原则:

  • temperature = 0:分类、提取、代码生成等需要确定性答案的任务
  • temperature = 0.3-0.5:需要一定灵活性但大体确定的任务(如问答)
  • temperature = 0.7-1.0:需要创意和多样性的任务(如营销文案、头脑风暴)

4.7.2 Max Tokens 的安全设置

不设置 max_tokens 的风险:

  • 模型进入某种循环,无限生成(虽然概率低,但一旦发生成本爆炸)
  • 用户恶意构造输入,诱导模型生成超长内容

建议:

  • 根据场景设置合理的上限(如客服回复最多 500 Token)
  • 在 System Prompt 中也明确要求"回复简洁,不超过 X 字"
  • 监控实际输出的 Token 分布,调整上限

4.8 错误处理与重试策略

大模型 API 的错误类型与传统 API 有显著差异,需要专门设计处理策略。

4.8.1 常见错误类型

错误类型HTTP 状态码说明处理策略
Rate Limit429请求频率超过限制指数退避重试
Quota Exceeded429账户额度用尽立即切换备用模型
Context Length400输入超过上下文限制截断或压缩输入
Content Filter400内容触发安全过滤返回友好的拒绝提示
Server Error500/503服务端临时故障指数退避重试
Timeout-请求超时根据场景决定是否重试

4.8.2 指数退避重试

对于 Rate Limit 和 Server Error,使用指数退避 + 随机抖动:

1 次重试:等待 1s + random(0, 1s)
第 2 次重试:等待 2s + random(0, 1s)
第 3 次重试:等待 4s + random(0, 1s)
...
最大重试次数:3-5

随机抖动(Jitter)避免多个客户端同时重试造成的"惊群效应"。

4.8.3 熔断与降级

当主力模型持续报错时,需要熔断机制:

连续失败 5 次 → 熔断 30 秒 → 期间所有请求走备用模型
30 秒后 → 放少量请求试探 → 成功则恢复,失败则继续熔断

备用模型的选择:

  • 同厂商的低配模型(GPT-4o → GPT-4o-mini)
  • 不同厂商的等价模型(OpenAI → DeepSeek)
  • 本地部署的开源模型(延迟高但稳定)

本章小结

大模型 API 与传统 HTTP 服务的本质差异:

  1. 无状态 + 上下文自维护:对话历史是调用方的责任,需要在应用层设计会话存储和压缩策略

  2. 延迟不可控:必须用流式输出改善体验,必须用异步架构处理耗时请求,超时设置要区分首 Token 和总时间

  3. 概率性输出:所有输出都需要校验层,重试策略不能简单重复,测试需要统计思维

  4. Function Calling 是集成枢纽:模型负责决策,业务代码负责执行;工具定义质量直接影响准确率;注意循环调用和工具过多的陷阱

  5. 多厂商抽象层:让业务代码与具体模型解耦,处理厂商差异,实现智能路由和故障降级

  6. 错误处理策略:区分可重试错误和不可重试错误,实现指数退避、熔断、降级机制


思考题

  1. 公司有多个业务系统需要调用大模型,分别有不同的延迟要求(客服实时对话 < 3s,报告生成可接受 30s)。设计一个统一的 LLM 调用网关,如何处理这两类需求的差异?需要考虑队列、超时、降级哪些因素?

  2. Function Calling 的工具定义写得越详细越好吗?如果给模型注册 50 个工具,会有什么问题?如何设计工具的组织结构?

  3. temperature=0 能保证每次输出完全相同吗?如果不能,工程上如何处理结果的不确定性?在哪些场景下这种不确定性是可以接受的,哪些是绝对不可接受的?

  4. 设计一个多模型路由系统,能够根据请求的复杂度、上下文长度、任务类型自动选择最优模型,并在主力模型故障时自动降级。画出架构图并说明关键决策逻辑。


下一章预告:API 调通了,但如何写出效果好的 Prompt?第 5 章,Prompt Engineering——从经验法则到系统方法论。