转载&存档丨AI 标准协议及调用(1)
AI 标准协议及调用
前言
AI近期来发展迅速,这段时间刷视频和朋友圈总是看到大家又在说什么前端已死、后端已死,还有什么你们搞大模型的都是码奸之类的话,这些也都是AI迅速发展的表现。在这种环境下,学习开发的人很难不焦虑,有些时候甚至会想还有必要学基础的编程吗,直接全部vibe coding不就好了吗,但其实不是这样的,编程能力从来不是跳跃式获得的,所有的学习都是一条平滑上升的曲线,要学好计算机,先从最基本的coding学起,学习前端、后端,再到全栈、agent,逐渐再转向研发大模型,这样才算是健全的学习道路,而非是从一开始就跑去学习大模型。本质上,AI 不是起点,而是建立在扎实工程能力之上的。接下来就会讲解在学习AI的过程中最基础的AI标准协议及调用。
现在的 API 协议已经相当标准化和通用化
参考 HTTP 协议
在讲 AI API 协议之前,我们先复习一下 HTTP 协议,这在之前也有讲过。HTTP(超文本传输协议)是互联网通信的基础,也是现在AI应用中最常用的传输层协议,它定义了客户端和服务器之间如何交换数据的规则:
- 统一的请求格式:包含 GET、POST、PUT、DELETE 等方法
- 标准化的状态码:200 成功、404 未找到(这个大家应该都见过)、500 服务器错误等
- 通用的头部字段:Content-Type、Authorization 等等,这些就不赘述了
AI API 协议
同样的道理,AI 模型的调用也需要标准化的协议,:
- 统一接口:开发者可以用相似的方式调用不同的模型,只需要改变某些参数就可以,之后会接触到的mcp其实也是一种统一接口
- 降低学习成本:掌握一种协议后,可以快速迁移到其他兼容服务,多数协议其实差别不大,只需要改一些字段
- 生态系统建设:标准化促进了工具库、框架的发展,这对开发者来说十分友好
- 互操作性:应用可以轻松切换不同的 AI 服务提供商,包括国内的和国外的
目前主流的 AI API 协议和工具主要有以下几种:
主流协议
- OpenAI API 协议:由 OpenAI 制定,已成为事实上的行业标准,被广泛采用
- Anthropic (Claude) API 协议:由 Anthropic 为 Claude 系列模型设计,强调安全性
其他协议和工具
- Google Gemini API:Google 的多模态 AI 协议,支持文本、图像、视频等多种输入
- LiteLLM:统一的 API 接口,支持 100+ 种 LLM 模型,一套代码调用所有模型
- OpenRouter:AI 模型聚合平台,提供统一的 OpenAI 兼容接口访问多家模型
- Ollama:本地运行开源模型的工具,提供 OpenAI 兼容的 API 接口
本文将详细讲解 OpenAI 和 Claude 两种主流协议,其他工具会简要介绍使用方法。
OpenAI API 协议详解
协议概述
OpenAI API 协议是目前最广泛使用的 AI API 标准,许多国内外厂商都提供了兼容接口,包括:
- 阿里云通义千问(Qwen)
- DeepSeek
- 智谱 AI(GLM)
- 月之暗面(Kimi)
- 百度文心一言(这个和似了其实区别不大)
核心端点(Endpoint)
主要的 API 端点是 /v1/chat/completions,用于对话式交互。
请求参数详解
首先,让我们看一个完整的 API 请求示例,了解整体结构:
1 | { |
接下来,我们逐个解释这些参数的含义和用法。
必需参数
model (string)
- 含义:指定要使用的模型名称
- 示例:
"gpt-3.5-turbo"、"deepseek-chat"、"qwen-turbo" - 说明:不同服务商的模型名称不同,需查阅对应文档
messages (array)
- 含义:对话历史记录,包含用户和助手的消息
- 结构:每条消息是一个对象,包含
role和content字段 - 示例:
1 | [ |
role 的类型:
system:系统提示词,定义 AI 的行为和角色user:用户的输入assistant:AI 的回复(可能包含tool_calls字段,表示需要调用工具)tool:工具调用的返回结果(用于 Function Calling),必须包含:tool_call_id:对应 assistant 消息中的工具调用 IDcontent:工具执行的结果(通常是 JSON 字符串)
常用可选参数
temperature (number, 0-2)
- 含义:控制输出的随机性
- 默认值:有些模型通常为 1.0(但在实际生产中一般不需要自己调整这个)
- 说明:
- 接近 0:输出更确定、保守
- 接近 2:输出更随机、创造性
- 使用建议:代码生成用 0.2-0.5,创意写作用 0.7-1.2
max_tokens (integer)
- 含义:生成的最大 token 数量
- 说明:1 个 token 约等于 0.75 个英文单词,或 0.5 个中文字符(好像各家的说法都不太一样,这个简单了解即可)
- 注意:设置过小可能导致回复被截断
stream (boolean)
- 含义:是否启用流式输出
- 默认值:false
- 说明:
- true:逐字返回,适合实时显示
- false:等待完整响应后返回
stop (string or array)
- 含义:停止序列,遇到时停止生成
- 示例:
["###", "END"] - 用途:控制输出格式,防止生成过多内容
tools (array)
- 含义:定义模型可以调用的工具(Function Calling)
- 用途:让模型能够调用外部函数或 API
- 详见后文 Tool Call 章节
响应格式
成功响应示例:
1 | { |
响应字段说明:
id:本次请求的唯一标识符object:对象类型,通常为chat.completioncreated:创建时间戳model:实际使用的模型choices:生成的回复列表(通常只有一个)index:回复的索引message:消息对象role:角色(assistant)content:生成的文本内容
finish_reason:结束原因stop:自然结束length:达到 max_tokens 限制content_filter:内容被过滤tool_calls:需要调用工具
usage:token 使用情况prompt_tokens:输入消耗的 tokencompletion_tokens:输出消耗的 tokentotal_tokens:总计
Anthropic (Claude) API 协议详解
协议特点
Claude API 协议与 OpenAI 有相似之处,但也有独特设计:
- 更强调安全性和可控性
- 支持更长的上下文窗口
- 提供了更细粒度的控制选项
核心端点
主要端点是 /v1/messages。
请求参数详解
我们先看一个完整的 Claude API 请求示例:
1 | { |
这里要注意一下 Claude API 与 OpenAI 的主要区别:
system提示词是独立参数,不在messages数组中max_tokens是必需参数messages中不包含system角色的消息
接下来详细解释各个参数:
必需参数
model (string)
- 含义:指定要使用的 Claude 模型
- 示例:
"claude-3-5-sonnet-20241022"、"claude-3-opus-20240229"、"claude-3-haiku-20240307" - 说明:不同模型在性能、速度和成本上有差异
messages (array)
- 含义:对话消息列表
- 结构与 OpenAI 类似,但有细微差异
- 注意:Claude 的 system 提示词是单独的参数,不在 messages 中
- 示例:
1 | [ |
max_tokens (integer)
- 含义:生成的最大 token 数量
- 必需参数(与 OpenAI 不同,OpenAI 中是可选的)
- 必须明确指定最大生成长度
- 建议:根据实际需求设置,避免设置过大浪费成本
可选参数
system (string)
- 含义:系统提示词,定义 AI 的行为和角色
- 独立参数,不在 messages 数组中
- 示例:
"你是一个专业的 Python 编程助手,代码要简洁高效。"
temperature (number, 0-1)
- 范围:0 到 1(注意:Claude 的范围是 0-1,而 OpenAI 是 0-2)
- 默认值:1.0
响应格式
1 | { |
其他 AI 协议和工具简介
Google Gemini API 协议详解
Google Gemini 是一个强大的多模态 AI 模型,支持文本、图像、音频、视频等多种输入。
协议特点
- 原生多模态:无需额外配置即可处理文本、图像、音频、视频
- 独特的消息结构:使用
contents和parts而非messages - Gemini 3 新特性:
- 支持
thinking_level控制推理深度 - 引入
thought_signature维持多轮对话逻辑连贯 - 函数调用支持唯一
id,实现并行调用 - 函数结果使用专门的
tool角色
核心端点
主要端点是 /v1beta/models/{model}:generateContent,用于生成内容。
请求参数详解
首先看一个完整的 Gemini API 请求示例(使用 REST API):
1 | { |
必需参数
contents (array)
- 含义:对话内容列表,包含用户和模型的消息
- 结构:每个 content 包含
role和parts字段 - 角色类型:
user:用户输入model:模型回复(注意:不是assistant)tool:工具/函数调用结果(Gemini 3 新增)- 示例:
1 | { |
parts (array)
- 含义:消息的组成部分,支持多种类型
- 类型:
text:文本内容inline_data:内联数据(如图像的 base64)file_data:文件引用function_call:函数调用请求(模型生成)function_response:函数调用结果(用户返回)
可选参数
generationConfig (object)
生成配置对象,控制输出行为:
temperature(number, 0-2):控制随机性topP(number, 0-1):核采样参数,默认 0.95topK(integer):Top-K 采样,默认 40maxOutputTokens(integer):最大输出 token 数,默认 2048stopSequences(array):停止序列列表candidateCount(integer):生成候选数量,默认 1thinking_level(string):推理深度(Gemini 3 新增)MINIMAL:快速响应,适合简单问题MEDIUM:平衡速度和质量HIGH:深度推理,适合复杂问题
safetySettings (array)
安全设置,控制内容过滤:
- 类别:
HARM_CATEGORY_HARASSMENT:骚扰HARM_CATEGORY_HATE_SPEECH:仇恨言论HARM_CATEGORY_SEXUALLY_EXPLICIT:色情内容HARM_CATEGORY_DANGEROUS_CONTENT:危险内容- 阈值:
BLOCK_NONE:不阻止BLOCK_ONLY_HIGH:仅阻止高风险BLOCK_MEDIUM_AND_ABOVE:阻止中等及以上风险BLOCK_LOW_AND_ABOVE:阻止低等及以上风险
systemInstruction (object)
系统指令,类似 OpenAI 的 system message:
1 | { |
响应格式
2026 最新 Gemini 3 响应示例 (JSON):
1 | { |
Gemini 3 核心新增字段说明:
parts[].thought:推理思考过程(需开启thinking_level)thought_signature:思维签名,用于多轮对话维持逻辑连贯性- 重要:在函数调用的多轮交互中,必须将
thought_signature包含在历史记录中,否则模型会丢失”为什么要调用这个函数”的逻辑上下文 avg_logprobs:平均对数概率,评估回答置信度(越接近 0 越自信)groundingMetadata:联网搜索溯源信息searchEntryPoint:搜索入口groundingChunks:引用的网页来源usageMetadata新增字段:cachedContentTokenCount:缓存内容 Token 数reasoningTokenCount:推理过程消耗的 TokenmediaTokenCount:多模态内容消耗的 Token
通用响应字段说明:
finishReason:结束原因STOP:自然结束MAX_TOKENS:达到最大 token 限制SAFETY:触发安全过滤RECITATION:检测到重复内容safetyRatings:安全评级category:安全类别probability:风险概率(NEGLIGIBLE/LOW/MEDIUM/HIGH)blocked:是否被阻止
函数调用(Function Calling)
Gemini 3 函数调用的关键变化:
强制要求 ID 匹配:为了支持并行函数调用(Parallel Function Calling)和复杂的长程推理,Gemini 3 现在生成的
function_call对象中包含一个唯一的id。当你返回function_response时,必须带上对应的id,否则在多轮对话或并行调用时,模型会因为无法对齐”哪个结果对应哪个请求”而报错(400 Error)。思维签名(thought_signature):在函数调用的多轮交互中,你必须确保模型返回的
thought_signature被包含在历史记录中,否则模型会丢失”为什么要调用这个函数”的逻辑上下文。专门的 tool 角色:虽然在某些早期实现中会将函数结果标记为
user角色,但 Gemini 3 的标准做法是引入了专门的tool角色(或在某些 SDK 中称为function角色)。将函数结果发回给模型时,消息的role应当设为tool或function,而不是user。这有助于模型区分”用户说的话”和”工具返回的客观事实”。
与 OpenAI 协议的主要区别
| 特性 | OpenAI | Gemini |
|---|---|---|
| 消息结构 | messages |
contents |
| 消息组成 | content (string) |
parts (array) |
| 助手角色名 | assistant |
model |
| 工具结果角色 | tool |
tool (Gemini 3) |
| 系统提示 | role: "system" |
systemInstruction |
| 生成配置 | 顶层参数 | generationConfig 对象 |
| 安全设置 | 无(依赖内容审核) | safetySettings 数组 |
| 多模态支持 | 需要特殊格式 | 原生支持,使用 parts 数组 |
| 推理深度控制 | 无 | thinking_level |
| 思维签名 | 无 | thought_signature |
| 函数调用 ID | tool_calls[].id |
function_call.id |
| 函数结果 ID | tool_call_id |
function_response.id |
| 函数参数格式 | JSON 字符串 | 对象 |
| 联网搜索溯源 | 无 | groundingMetadata |
| Token 分项统计 | 基础统计 | 详细分项(推理、媒体等) |
获取 API Key
- 访问 Google AI Studio
- 登录 Google 账号
- 点击 “Get API Key” 创建新的 API Key
- 复制 API Key 并保存到环境变量
最佳实践
- 多模态优势:充分利用原生多模态能力,无需额外配置
- 安全设置:根据应用场景调整安全阈值
- 流式输出:对于长回复,使用流式输出提升用户体验
- 函数调用:利用 Function Calling 实现复杂的工具集成
流式输出(Streaming)详解
流式输出是什么东西?
流式输出是指 AI 模型逐步生成并返回响应内容,而不是等待全部内容生成完毕后一次性返回。
非流式 vs 流式
非流式输出:
1 | 用户提问 → AI 思考 → 等待... → 完整回复一次性显示 |
用户体验:需要等待较长时间,看到的是突然出现的完整文本。
流式输出:
1 | 用户提问 → AI 思考 → 逐字显示 → 逐字显示 → 逐字显示 → 完成 |
用户体验:立即看到响应开始,文字逐渐出现,类似打字效果。
流式输出有什么用?
在大语言模型(LLM)爆发之前,普通的网页请求(比如看新闻、查天气)基本都是“全量返回”的:服务器把所有内容一次性计算好、打包,再传给浏览器,由浏览器统一渲染。但在大模型时代,这种方式就不太合适了。因为模型本质上是一个“概率预测器”,它不是一次性生成整段内容,而是按 Token(字/词)逐步向外预测、逐步生成的。如果仍然等到 LLM 把所有内容都生成完再“全量返回”,用户往往需要经历较长的等待时间,体验会明显变差。在这种情况下,流式输出就显得很有必要:
- 改善用户体验:用户不需要长时间等待,立即看到响应开始
- 降低感知延迟:即使总时间相同,流式输出让用户感觉更快
- 实时反馈:用户可以提前看到部分内容,决定是否继续等待
- 处理长文本:对于长回复,流式输出避免了长时间的空白等待
来讲讲 SSE(Server-Sent Events)
SSE(Server-Sent Events)是一种服务器向客户端推送数据的技术,AI API 的流式输出就是基于 SSE 实现的。
SSE 的特点
- 单向通信:服务器 → 客户端(客户端不能通过 SSE 发送数据)
- 基于 HTTP:使用标准 HTTP 协议,无需特殊协议
- 自动重连:连接断开后会自动重连
- 文本格式:传输的是文本数据,通常是 JSON
SSE 的数据格式
SSE 使用特定的文本格式传输数据:
1 | data: {"content": "你"} |
每条消息以 data: 开头,以两个换行符 \n\n 结束。
SSE vs WebSocket
| 特性 | SSE | WebSocket |
|---|---|---|
| 通信方向 | 单向(服务器 → 客户端) | 双向(客户端 ↔ 服务器) |
| 协议 | HTTP | WebSocket 协议(ws://) |
| 连接建立 | 简单,标准 HTTP 请求 | 需要握手升级 |
| 浏览器支持 | 原生支持(EventSource API) | 原生支持(WebSocket API) |
| 自动重连 | 内置自动重连 | 需要手动实现 |
| 数据格式 | 文本(通常 JSON) | 文本或二进制 |
| 防火墙友好 | 是(使用标准 HTTP) | 可能被阻止 |
| 适用场景 | 服务器推送、实时通知、AI 流式 | 聊天、游戏、实时协作 |
为什么 AI API 使用 SSE 而不是 WebSocket?
- 单向通信足够:AI 生成响应是单向的,不需要双向通信
- 更简单:SSE 基于 HTTP,无需额外的协议升级
- 更好的兼容性:HTTP 更容易通过代理、负载均衡器
- 自动重连:SSE 内置重连机制,更可靠
- 标准化:OpenAI 等厂商都采用 SSE,已成为事实标准
如何实现流式输出
在实现流式输出之前,我们需要理解其背后的工作原理和处理逻辑。
流式输出的工作原理
1. 服务器端的生成过程
AI 模型生成文本的过程本质上是逐个 token 预测的:
1 | 输入: "什么是机器学习?" |
在非流式模式下,服务器会等待所有 token 生成完毕后,一次性返回完整结果。而在流式模式下,服务器每生成一个或几个 token,就立即通过 SSE 推送给客户端。
2. SSE 数据传输格式
服务器通过 SSE 发送的数据格式如下:
1 | data: {"id":"chatcmpl-123","object":"chat.completion.chunk","choices":[{"index":0,"delta":{"content":"机"},"finish_reason":null}]} |
每条消息:
- 以
data:开头 - 包含一个 JSON 对象(称为 chunk)
- 以两个换行符
\n\n结束 - 最后一条消息是
data: [DONE]表示流结束
3. 客户端的处理流程
客户端需要按照以下步骤处理流式响应:
1 | 1. 建立 HTTP 连接(设置 stream=True) |
关于 SSE 的”粘包”处理(重要)
由于网络传输的特性,客户端收到的一个数据块并不一定正好是一个完整的 data: {...}\n\n。
常见现象:
- 有时一次读到两个完整的 chunk
- 有时只读到半个 chunk(JSON 被截断)
- 有时一个 chunk 被拆分到多次读取中
解决方案:
1 | # 客户端需要维护一个缓冲区 |
关键点:
- 使用缓冲区累积接收到的数据
- 只有找到
\n\n分隔符时才解析 - 未完成的部分保留在缓冲区中,等待下次数据到达
4. 关键技术点
- 增量更新(Delta):每个 chunk 只包含新增的内容片段,不是完整内容
- 实时显示:使用
print(..., end="", flush=True)或类似机制立即输出,不等待换行 - 内容累积:客户端需要自己拼接所有 chunk 的内容,得到完整文本
- 结束检测:通过
finish_reason字段判断是否结束(stop、length等)
性能优化:Flush 机制
在实际部署中,如果后端服务器(如 Nginx)开启了缓存(Buffering),流式效果会失效——文字会一坨一坨地蹦出来,而不是平滑流出。
问题原因:
- Nginx 等反向代理默认会缓冲响应内容
- 只有缓冲区满了或响应结束时才会发送给客户端
- 这导致流式传输的实时性丧失
解决方案:
1 | # 后端代码中必须设置响应头 |
Nginx 配置(可选):
1 | location /api/chat { |
确保每一个 chunk 都能实时流出,不被中间层缓存。
5. 流式 vs 非流式的数据对比
非流式响应(一次性返回):
1 | { |
流式响应(多次返回):
1 | // 第1次 |
注意:
- 流式使用
delta字段(增量),非流式使用message字段(完整) - 流式的
finish_reason在最后一个 chunk 才不为 null - 流式需要客户端自己拼接所有
delta.content
6. Delta 模式的例外:工具调用(Function Calling)
当涉及到 Function Calling 时,流式的 delta 结构会发生变化。函数参数是以字符串形式逐段传输的:
1 | // 第1个 chunk:开始传输函数调用 |
处理要点:
- 函数参数(
arguments)是 JSON 字符串,会被拆分成多个片段 - 客户端需要拼接所有
arguments片段,最后再解析为 JSON 对象 - 完整的参数示例:
{"location": "Shanghai"} finish_reason为"tool_calls"表示需要执行工具调用
1 | # 客户端处理示例 |
JavaScript 实现
使用 OpenAI SDK:
1 | import OpenAI from "openai"; |
使用原生 Fetch API:
1 | async function streamChatFetch(userMessage) { |
前端实现(浏览器)
使用 EventSource API:
1 | // 注意:OpenAI API 不支持直接使用 EventSource(需要 POST 请求) |
使用 Fetch API(推荐):
1 | async function streamChatBrowser(userMessage) { |
流式输出的最佳实践
- 错误处理:流式输出中途可能中断,需要妥善处理错误
- 缓冲处理:可能一次收到多个 chunk,需要正确解析
- 用户体验:添加打字动画效果,提升视觉体验
- 取消机制:允许用户中途取消生成
- 内容累积:保存完整内容,方便后续使用
