前言
"提示词的好坏决定着模型的输出效果,但是模型是否能正常输出,以及输出的效果好不好,还有一些其他的参数也会有所影响。"
这是很多刚入门 AI 应用开发者的困惑。大家都知道要写好 Prompt,但对于 API 调用中的各种参数却一知半解。
今天这篇文章,我会用最详细的方式,带你彻底搞懂 OpenAI API 协议——这个几乎成为所有大模型应用开发底层基础的标准接口。无论你使用 Spring AI、LangChain 还是其他智能体框架,理解这个协议都是你进阶 AI 开发的必经之路。
一、什么是 OpenAI API 协议
OpenAI API 协议是 OpenAI 公司定义的一套与大语言模型交互的 HTTP API 接口规范。由于其先发优势和广泛的应用场景,这套协议已经成为事实上的行业标准。
为什么它如此重要?
1. 行业标准地位
目前主流的大模型几乎都已经完全兼容 OpenAI 的接口规范。这意味着:
- 学会一套 API,就能调用市面上几乎所有大模型
- 代码迁移成本极低,只需更换
base_url和api_key - 各种智能体框架都基于此协议进行封装
2. 框架生态支持
主流智能体框架都对此协议进行了深度封装:
- Spring AI / Spring AI Alibaba:Java 生态首选
- LangChain:Python/JavaScript 生态最流行
- AutoGen:微软开源的多智能体框架
- LlamaIndex:专注于 RAG 应用开发
但无论框架如何封装设计,最终都会归结为向大模型的 API 接口发送一个格式化的 HTTP 请求。甚至,我们可以只用一个 HttpClient 就能开展智能体应用开发。
3. 中转站生态
目前主流的 API 中转站都兼容 OpenAI 协议:
- New API:开源的一站式 API 管理/分发系统
- One API:New API 的前身
- 各种商业中转服务
二、协议基础:API 端点与认证
完整的 API 路径格式
https://域名地址/v1/chat/completions
但在不同框架中,配置方式略有不同:
| 框架/平台 | 配置方式 | 拼接规则 |
|---|---|---|
| LangChain、AutoGen 等 | 配置到 /v1/ | 框架自动拼接 chat/completions |
| Spring AI | 配置到 /v1 之前 | 框架自动拼接 /v1/chat/completions |
| 直接 HTTP 调用 | 完整路径 | 无自动拼接 |
常见模型的 base_url
| 模型服务商 | base_url |
|---|---|
| OpenAI ChatGPT | api.openai.com/v1 |
| 阿里云通义千问 | dashscope.aliyuncs.com/compatible-… |
| DeepSeek | api.deepseek.com/v1 |
| 智谱 AI (GLM) | open.bigmodel.cn/api/paas/v4 |
| Moonshot (Kimi) | api.moonshot.cn/v1 |
| Ollama 本地模型 | http://localhost:11434/v1 |
| vLLM 本地部署 | http://localhost:8000/v1 |
认证方式
所有请求都需要在 HTTP 请求头中携带 API Key:
Authorization: Bearer <your-api-key>
API Key 用于:
- 验证身份信息
- 授权访问
- 计费统计
三、输入参数详解
完整参数表格
| 参数名称 | 是否必填 | 数据类型 | 默认值 | 取值范围 | 作用 | 参数说明 |
|---|---|---|---|---|---|---|
| model | ✅ 必填 | String | - | - | 指定模型 | 确定使用哪一个具体的大模型,如 gpt-4、qwen-plus、deepseek-chat |
| messages | ✅ 必填 | Array | - | - | 消息列表 | 传递给模型的对话历史或指令,包含 role 和 content |
| temperature | ❌ 选填 | Float | 1.0 | 0.0 - 2.0 | 温度系数 | 控制输出随机性,低值稳定,高值创意 |
| top_p | ❌ 选填 | Float | 1.0 | 0.0 - 1.0 | 核采样 | 与 temperature 类似但算法不同,建议只调其一 |
| max_tokens | ❌ 选填 | Integer | 模型最大 | - | 最大输出长度 | 限定单次响应最大 Token 数 |
| n | ❌ 选填 | Integer | 1 | 1-N | 生成次数 | 为同一 Prompt 生成 n 个不同回应 |
| stream | ❌ 选填 | Boolean | false | true/false | 流式输出 | 是否流式返回 Token |
| stop | ❌ 选填 | String/Array | null | - | 停止标记 | 遇到指定字符串立即停止生成 |
| presence_penalty | ❌ 选填 | Float | 0 | -2.0 - 2.0 | 出现惩罚 | 正值鼓励新话题,负值减少新话题 |
| frequency_penalty | ❌ 选填 | Float | 0 | -2.0 - 2.0 | 频率惩罚 | 正值减少重复,负值增加连贯 |
| seed | ❌ 选填 | Integer | null | - | 复现种子 | 提高结果可复现性(非 100% 保证) |
| tools | ❌ 选填 | Array | null | - | 工具定义 | 定义可调用的外部工具/函数 |
| tool_choice | ❌ 选填 | String/Object | auto | - | 工具选择 | 控制工具调用行为 |
| response_format | ❌ 选填 | Object | null | - | 响应格式 | 指定输出格式如 JSON |
| user | ❌ 选填 | String | null | - | 用户标识 | 用于追踪和识别终端用户 |
核心参数详解
1. messages(消息列表)
这是最重要的参数,定义了与模型的完整对话上下文。messages 是一个数组,每个元素都是一个消息对象,包含 role(角色)和 content(内容)两个核心字段。
role 字段:用来标识一段消息是谁说的、以及它在对话中的作用。最常见的三个角色是 system、user、assistant。
{
"messages": [
{"role": "system", "content": "你是一个有帮助的助手。"},
{"role": "user", "content": "你好!"},
{"role": "assistant", "content": "你好!有什么可以帮助你的吗?"},
{"role": "user", "content": "请介绍一下你自己。"}
]
}
角色类型详细说明:
| 角色 | 作用 | 使用场景 | 示例 |
|---|---|---|---|
| system | 设定模型的行为、角色和约束 | 放在 messages 第一位,定义 AI 的"人设"和回答风格 | "你是一个专业的Python编程助手,回答时请给出代码示例。" |
| user | 用户的输入或问题 | 每次用户提问时添加到 messages 末尾 | "请帮我写一个快速排序算法。" |
| assistant | 模型的历史回复 | 多轮对话时保存之前的 AI 回复,保持上下文连贯 | "好的,这是快速排序的Python实现..." |
| tool | 工具调用的返回结果 | ⚠️ 仅用于 Function Calling,必须紧跟在包含 tool_calls 的 assistant 消息之后 | {"role": "tool", "tool_call_id": "xxx", "content": "北京今天晴,25℃"} |
⚠️ 重要提示:
tool角色不能单独使用,它必须是对之前 assistant 消息中tool_calls的响应。如果 assistant 消息没有tool_calls,则后面不能跟tool角色的消息,否则会报错。
三大核心角色详解:
🔹 system(系统角色)
system 消息用于在对话开始前设定模型的"人设"和行为规范。它就像给 AI 的一份"工作说明书"。
{"role": "system", "content": "你是一位资深的前端工程师,精通 React、Vue、TypeScript。\n回答问题时:\n1. 优先给出代码示例\n2. 解释关键概念\n3. 提供最佳实践建议"}
使用技巧:
- 通常只放一条 system 消息,位于 messages 数组开头
- 可以定义角色、语气、输出格式、禁止事项等
- 越具体的 system prompt,模型输出越符合预期
- 部分模型对 system 消息的遵循程度不同,可测试调整
常见 system prompt 模板:
| 场景 | System Prompt 示例 |
|---|---|
| 编程助手 | 你是一个{语言}编程专家,回答时请给出代码示例和注释。 |
| 翻译助手 | 你是一个专业翻译,请将用户输入翻译成{目标语言},保持原文语气和风格。 |
| 文案写作 | 你是一个营销文案专家,请用{风格}的风格撰写文案,字数控制在{字数}字以内。 |
| 数据分析 | 你是一个数据分析师,请用清晰的表格和图表描述分析数据,给出专业见解。 |
| AI Agent | 你是一个智能助手,可以根据用户需求调用工具完成任务。可用工具:{工具列表} |
🔹 user(用户角色)
user 消息代表用户的输入,是模型需要响应的内容。每次用户提问时,都要将新消息添加到 messages 数组末尾。
{"role": "user", "content": "请解释一下 Java 中的多态是什么?"}
使用技巧:
- 清晰、具体的问题会得到更好的回答
- 可以在 user 消息中提供上下文或示例
- 复杂任务可以拆分成多个 user 消息逐步引导
🔹 assistant(助手角色)
assistant 消息代表模型的历史回复。在多轮对话中,需要将之前的 assistant 回复加入 messages,让模型"记住"对话上下文。
{"role": "assistant", "content": "多态是面向对象编程的核心概念之一..."}
使用场景:
- 多轮对话:保持对话连贯性
[
{"role": "user", "content": "什么是 Java 的多态?"},
{"role": "assistant", "content": "多态是指同一操作作用于不同对象..."},
{"role": "user", "content": "能给我一个代码示例吗?"} // 模型知道"它"指的是多态
]
- Few-shot 提示:通过示例引导模型输出格式
[
{"role": "system", "content": "你是情感分析助手,判断用户输入的情感倾向。"},
{"role": "user", "content": "今天天气真好!"},
{"role": "assistant", "content": "正面情感"},
{"role": "user", "content": "这电影太无聊了。"},
{"role": "assistant", "content": "负面情感"},
{"role": "user", "content": "我刚完成了一个项目。"} // 模型会学习输出"中性情感"或类似格式
]
- AI Agent 工具调用流程:assistant 返回 tool_calls,tool 返回结果
[
{"role": "user", "content": "北京今天天气怎么样?"},
{"role": "assistant", "content": null, "tool_calls": [{"id": "call_1", "function": {"name": "get_weather", "arguments": "{\"city\":\"北京\"}"}}]},
{"role": "tool", "tool_call_id": "call_1", "content": "北京今天晴,气温25℃"},
{"role": "assistant", "content": "北京今天天气晴朗,气温25摄氏度,适合外出活动。"}
]
⚠️ tool 角色使用规范:
tool 角色只能在 Function Calling 场景中使用,必须满足以下条件:
| 要求 | 说明 |
|---|---|
| 前置条件 | 前一条消息必须是 assistant 且包含 tool_calls 字段 |
| 必填字段 | role: "tool"、tool_call_id(对应 tool_calls 中的 id)、content(工具返回结果) |
| 位置要求 | 必须紧跟在包含 tool_calls 的 assistant 消息之后 |
错误示例(会报错):
// ❌ 错误:tool 消息前面没有 tool_calls
[
{"role": "user", "content": "查询天气"},
{"role": "tool", "content": "北京晴,25℃"} // 错误!前面没有 assistant 的 tool_calls
]
正确示例:
// ✅ 正确:tool 消息紧跟 tool_calls
[
{"role": "user", "content": "查询北京天气"},
{"role": "assistant", "content": null, "tool_calls": [{"id": "call_123", ...}]},
{"role": "tool", "tool_call_id": "call_123", "content": "北京晴,25℃"} // 正确!
]
2. temperature(温度系数)
这是控制输出多样性的关键参数:
| 温度值 | 适用场景 | 特点 |
|---|---|---|
| 0.1 - 0.3 | 代码生成、事实问答、法律文本 | 高确定性、可预测 |
| 0.5 - 0.7 | 通用对话、翻译、摘要 | 平衡创意与准确 |
| 0.8 - 1.0 | 创意写作、头脑风暴、营销文案 | 高创意、多样性 |
| > 1.0 | 极端创意场景 | 输出可能不可预测 |
建议:通常默认 0.7 即可,需要更稳定输出可设 0.3。
3. stream(流式输出)
这个参数对用户体验至关重要:
| 值 | 行为 | 适用场景 |
|---|---|---|
| false | 等待完整响应后一次性返回 | 批量处理、后台任务 |
| true | 逐 Token 流式返回 | 实时对话、提升用户体验 |
流式输出的优势:
- 用户能实时看到生成内容,减少等待焦虑
- 可以提前中断不满意的内容
- 更接近人类对话的自然感
4. tools / tool_choice(工具调用)
这是 AI Agent 开发的核心参数,用于 Function Calling:
{
"tools": [
{
"type": "function",
"function": {
"name": "get_weather",
"description": "获取指定城市的天气信息",
"parameters": {
"type": "object",
"properties": {
"city": {
"type": "string",
"description": "城市名称"
}
},
"required": ["city"]
}
}
}
],
"tool_choice": "auto"
}
tool_choice 选项:
auto:模型自动决定是否调用工具(默认)none:不调用任何工具required:必须调用工具{"type": "function", "function": {"name": "xxx"}}:强制调用指定工具
参数使用建议
日常开发最常用的参数组合:
{
"model": "qwen-plus",
"messages": [...],
"stream": true,
"temperature": 0.7
}
需要稳定输出的场景:
{
"model": "qwen-plus",
"messages": [...],
"stream": false,
"temperature": 0.3,
"seed": 42
}
AI Agent 开发场景:
{
"model": "qwen-plus",
"messages": [...],
"tools": [...],
"tool_choice": "auto",
"stream": false
}
四、输出参数详解
非流式输出结构
当 stream: false 时,API 会返回完整的 JSON 响应:
{
"id": "chatcmpl-abc123",
"object": "chat.completion",
"created": 1699000000,
"model": "qwen-plus",
"choices": [
{
"index": 0,
"message": {
"role": "assistant",
"content": "你好!我是一个AI助手...",
"tool_calls": null
},
"finish_reason": "stop"
}
],
"usage": {
"prompt_tokens": 20,
"completion_tokens": 50,
"total_tokens": 70
}
}
非流式输出参数表格
| 参数名称 | 数据类型 | 作用 | 参数说明 |
|---|---|---|---|
| id | String | 唯一标识符 | 本次 API 请求的唯一 ID |
| object | String | 响应对象类型 | 固定为 chat.completion |
| model | String | 模型名称 | 实际使用的模型名称 |
| created | Integer | 时间戳 | API 响应创建的 Unix 时间戳 |
| choices | Array | 输出列表 | 包含模型生成的所有回复选项 |
| choices[i].index | Integer | 选项索引 | 从 0 开始的索引 |
| choices[i].message | Object | 消息内容 | 包含完整的回复信息 |
| choices[i].message.role | String | 角色 | 固定为 assistant |
| choices[i].message.content | String | 生成文本 | 模型生成的全部文本内容 |
| choices[i].message.tool_calls | Array | 工具调用 | AI Agent 核心:包含要调用的函数名和参数 |
| choices[i].finish_reason | String | 停止原因 | stop(正常结束)、length(达到 max_tokens)、tool_calls(调用工具) |
| usage | Object | Token 统计 | 计费的核心依据 |
| usage.prompt_tokens | Integer | 输入 Token | 输入消息消耗的 Token 数 |
| usage.completion_tokens | Integer | 输出 Token | 模型输出消耗的 Token 数 |
| usage.total_tokens | Integer | 总 Token | 总消耗 Token 数 |
流式输出结构
当 stream: true 时,API 会通过 SSE (Server-Sent Events) 流式返回数据块:
data: {"id":"chatcmpl-abc123","object":"chat.completion.chunk","created":1699000000,"model":"qwen-plus","choices":[{"index":0,"delta":{"role":"assistant"},"finish_reason":null}]}
data: {"id":"chatcmpl-abc123","object":"chat.completion.chunk","created":1699000000,"model":"qwen-plus","choices":[{"index":0,"delta":{"content":"你"},"finish_reason":null}]}
data: {"id":"chatcmpl-abc123","object":"chat.completion.chunk","created":1699000000,"model":"qwen-plus","choices":[{"index":0,"delta":{"content":"好"},"finish_reason":null}]}
data: {"id":"chatcmpl-abc123","object":"chat.completion.chunk","created":1699000000,"model":"qwen-plus","choices":[{"index":0,"delta":{"content":"!"},"finish_reason":null}]}
data: {"id":"chatcmpl-abc123","object":"chat.completion.chunk","created":1699000000,"model":"qwen-plus","choices":[{"index":0,"delta":{},"finish_reason":"stop"}],"usage":{"prompt_tokens":20,"completion_tokens":3,"total_tokens":23}}
data: [DONE]
流式输出参数表格
| 参数名称 | 数据类型 | 作用 | 参数说明 |
|---|---|---|---|
| id | String | 唯一标识符 | 每个 chunk 中相同,标识同一次请求 |
| object | String | 响应对象类型 | 固定为 chat.completion.chunk |
| model | String | 模型名称 | 实际使用的模型名称 |
| created | Integer | 时间戳 | API 响应创建的 Unix 时间戳 |
| choices | Array | 分块输出列表 | 包含本次增量内容 |
| choices[i].index | Integer | 选项索引 | 从 0 开始的索引 |
| choices[i].delta | Object | 增量内容 | 本次数据块新增的内容 |
| choices[i].delta.role | String | 角色 | 仅在第一个 chunk 出现 |
| choices[i].delta.content | String | 增量文本 | 本次新增的极小部分文本,需拼接 |
| choices[i].delta.tool_calls | Array | 工具调用增量 | 流式工具调用,增量式返回 |
| choices[i].finish_reason | String | 停止原因 | 仅在最后一个 chunk 返回 |
| usage | Object | Token 统计 | 通常在最后一个 chunk 返回 |
finish_reason 完整说明
| 值 | 含义 | 处理建议 |
|---|---|---|
| stop | 正常完成 | 输出完整,可直接使用 |
| length | 达到 max_tokens 限制 | 输出被截断,可能需要继续 |
| tool_calls | 模型请求调用工具 | AI Agent 核心,需要执行工具并返回结果 |
| content_filter | 内容被安全过滤 | 输入或输出触发了安全策略 |
| function_call | 旧版函数调用(已废弃) | 使用 tool_calls 替代 |
五、流式 vs 非流式对比
| 对比维度 | 流式输出 (stream: true) | 非流式输出 (stream: false) |
|---|---|---|
| 响应方式 | 逐 Token 返回 | 一次性返回完整结果 |
| 首字延迟 | 低,快速显示 | 高,需等待完整生成 |
| 用户体验 | 好,实时反馈 | 差,需等待 |
| 实现复杂度 | 较高,需处理 SSE | 简单,普通 HTTP 请求 |
| Token 统计 | 最后一个 chunk 返回 | 随响应返回 |
| 错误处理 | 复杂,可能中途失败 | 简单,统一处理 |
| 适用场景 | 实时对话、聊天应用 | 批量处理、后台任务 |
| 工具调用 | 增量式返回 | 完整返回 |
六、AI Agent 开发中的核心参数
在 AI Agent(智能体)开发中,以下参数最为关键:
1. messages - 对话上下文
Agent 需要维护完整的对话历史,包括:
- 系统提示词(定义 Agent 行为)
- 用户消息
- 助手回复
- 工具调用记录
- 工具返回结果
2. tools / tool_calls - 工具调用
这是 Agent 与外部世界交互的核心:
// 模型请求调用工具
{
"choices": [{
"message": {
"role": "assistant",
"content": null,
"tool_calls": [{
"id": "call_abc123",
"type": "function",
"function": {
"name": "get_weather",
"arguments": "{\"city\": \"北京\"}"
}
}]
},
"finish_reason": "tool_calls"
}]
}
3. 多轮对话流程
用户输入 → 模型判断 → 返回 tool_calls → 执行工具 →
将结果加入 messages → 再次调用模型 → 返回最终答案
七、Spring AI 与 Spring AI Alibaba 封装
Spring AI 基本配置
# application.yml
spring:
ai:
openai:
api-key: ${OPENAI_API_KEY}
base-url: https://api.openai.com
chat:
options:
model: gpt-4
temperature: 0.7
Spring AI Alibaba 配置
# application.yml
spring:
ai:
dashscope:
api-key: ${DASHSCOPE_API_KEY}
chat:
options:
model: qwen-plus
temperature: 0.7
代码示例对比
Spring AI 调用示例:
@RestController
public class ChatController {
private final ChatClient chatClient;
public ChatController(ChatClient.Builder chatClientBuilder) {
this.chatClient = chatClientBuilder.build();
}
// 非流式调用
@GetMapping("/chat")
public String chat(@RequestParam String message) {
return chatClient.prompt()
.user(message)
.call()
.content();
}
// 流式调用
@GetMapping("/chat/stream")
public Flux<String> chatStream(@RequestParam String message) {
return chatClient.prompt()
.user(message)
.stream()
.content();
}
}
Spring AI 完整配置示例:
@Configuration
public class AIConfig {
@Bean
public ChatClient chatClient(ChatClient.Builder builder) {
return builder
.defaultSystem("你是一个有帮助的AI助手。")
.defaultOptions(ChatOptions.builder()
.model("qwen-plus")
.temperature(0.7)
.maxTokens(2000)
.build())
.build();
}
}
八、原生 Java HTTP 调用示例
非流式调用示例
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.time.Duration;
public class OpenAIClient {
private static final String API_URL = "https://dashscope.aliyuncs.com/compatible-mode/v1/chat/completions";
private static final String API_KEY = "your-api-key";
private final HttpClient httpClient;
public OpenAIClient() {
this.httpClient = HttpClient.newBuilder()
.connectTimeout(Duration.ofSeconds(30))
.build();
}
/**
* 非流式调用
*/
public String chat(String userMessage) throws Exception {
// 构建请求体
String requestBody = """
{
"model": "qwen-plus",
"messages": [
{"role": "system", "content": "你是一个有帮助的AI助手。"},
{"role": "user", "content": "%s"}
],
"temperature": 0.7,
"stream": false
}
""".formatted(userMessage);
// 构建请求
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create(API_URL))
.header("Content-Type", "application/json")
.header("Authorization", "Bearer " + API_KEY)
.POST(HttpRequest.BodyPublishers.ofString(requestBody))
.build();
// 发送请求
HttpResponse<String> response = httpClient.send(
request,
HttpResponse.BodyHandlers.ofString()
);
// 解析响应
if (response.statusCode() == 200) {
return parseContent(response.body());
} else {
throw new RuntimeException("API调用失败: " + response.body());
}
}
/**
* 解析非流式响应
*/
private String parseContent(String responseBody) {
// 简单解析(实际项目中建议使用 Jackson/Gson)
int contentStart = responseBody.indexOf("\"content\":\"") + 11;
int contentEnd = responseBody.indexOf("\"", contentStart);
// 处理转义字符等...实际应使用 JSON 库
return responseBody.substring(contentStart, contentEnd);
}
public static void main(String[] args) throws Exception {
OpenAIClient client = new OpenAIClient();
String response = client.chat("你好,请介绍一下你自己。");
System.out.println(response);
}
}
流式调用示例
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.time.Duration;
import java.util.function.Consumer;
public class OpenAIStreamClient {
private static final String API_URL = "https://dashscope.aliyuncs.com/compatible-mode/v1/chat/completions";
private static final String API_KEY = "your-api-key";
private final HttpClient httpClient;
public OpenAIStreamClient() {
this.httpClient = HttpClient.newBuilder()
.connectTimeout(Duration.ofSeconds(30))
.build();
}
/**
* 流式调用
* @param userMessage 用户消息
* @param onContent 每次收到内容时的回调
*/
public void chatStream(String userMessage, Consumer<String> onContent) throws Exception {
// 构建请求体
String requestBody = """
{
"model": "qwen-plus",
"messages": [
{"role": "system", "content": "你是一个有帮助的AI助手。"},
{"role": "user", "content": "%s"}
],
"temperature": 0.7,
"stream": true
}
""".formatted(userMessage);
// 构建请求
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create(API_URL))
.header("Content-Type", "application/json")
.header("Authorization", "Bearer " + API_KEY)
.header("Accept", "text/event-stream")
.POST(HttpRequest.BodyPublishers.ofString(requestBody))
.build();
// 发送流式请求
HttpResponse<java.util.stream.Stream<String>> response = httpClient.send(
request,
HttpResponse.BodyHandlers.ofLines()
);
// 处理流式响应
response.body().forEach(line -> {
if (line.startsWith("data: ") && !line.equals("data: [DONE]")) {
String jsonData = line.substring(6);
String content = extractDeltaContent(jsonData);
if (content != null && !content.isEmpty()) {
onContent.accept(content);
}
}
});
}
/**
* 从 SSE 数据中提取 delta content
*/
private String extractDeltaContent(String jsonData) {
// 简单解析(实际项目建议使用 Jackson/Gson)
try {
int deltaStart = jsonData.indexOf("\"delta\":");
if (deltaStart == -1) return null;
int contentStart = jsonData.indexOf("\"content\":\"", deltaStart);
if (contentStart == -1) return null;
contentStart += 11;
int contentEnd = jsonData.indexOf("\"", contentStart);
return jsonData.substring(contentStart, contentEnd)
.replace("\\n", "\n")
.replace("\\\"", "\"")
.replace("\\\\", "\\");
} catch (Exception e) {
return null;
}
}
public static void main(String[] args) throws Exception {
OpenAIStreamClient client = new OpenAIStreamClient();
System.out.println("AI回复:");
client.chatStream("请写一首关于春天的短诗。", content -> {
System.out.print(content); // 实时打印
});
System.out.println("\n--- 完成 ---");
}
}
使用 Jackson 的完整示例
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.databind.ObjectMapper;
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.util.List;
public class OpenAIJsonClient {
private static final String API_URL = "https://dashscope.aliyuncs.com/compatible-mode/v1/chat/completions";
private static final String API_KEY = "your-api-key";
private final HttpClient httpClient;
private final ObjectMapper objectMapper;
public OpenAIJsonClient() {
this.httpClient = HttpClient.newHttpClient();
this.objectMapper = new ObjectMapper();
}
// 请求对象
public static class ChatRequest {
public String model;
public List<Message> messages;
public Double temperature;
public Boolean stream;
public static class Message {
public String role;
public String content;
public Message(String role, String content) {
this.role = role;
this.content = content;
}
}
}
// 响应对象
public static class ChatResponse {
public String id;
public String object;
public Long created;
public String model;
public List<Choice> choices;
public Usage usage;
public static class Choice {
public Integer index;
public Message message;
public String finish_reason;
public static class Message {
public String role;
public String content;
@JsonProperty("tool_calls")
public List<ToolCall> toolCalls;
}
}
public static class Usage {
@JsonProperty("prompt_tokens")
public Integer promptTokens;
@JsonProperty("completion_tokens")
public Integer completionTokens;
@JsonProperty("total_tokens")
public Integer totalTokens;
}
public static class ToolCall {
public String id;
public String type;
public Function function;
public static class Function {
public String name;
public String arguments;
}
}
}
/**
* 非流式调用(使用强类型对象)
*/
public ChatResponse chat(String userMessage) throws Exception {
// 构建请求
ChatRequest request = new ChatRequest();
request.model = "qwen-plus";
request.temperature = 0.7;
request.stream = false;
request.messages = List.of(
new ChatRequest.Message("system", "你是一个有帮助的AI助手。"),
new ChatRequest.Message("user", userMessage)
);
String requestBody = objectMapper.writeValueAsString(request);
HttpRequest httpRequest = HttpRequest.newBuilder()
.uri(URI.create(API_URL))
.header("Content-Type", "application/json")
.header("Authorization", "Bearer " + API_KEY)
.POST(HttpRequest.BodyPublishers.ofString(requestBody))
.build();
HttpResponse<String> response = httpClient.send(
httpRequest,
HttpResponse.BodyHandlers.ofString()
);
if (response.statusCode() == 200) {
return objectMapper.readValue(response.body(), ChatResponse.class);
} else {
throw new RuntimeException("API调用失败: " + response.body());
}
}
public static void main(String[] args) throws Exception {
OpenAIJsonClient client = new OpenAIJsonClient();
ChatResponse response = client.chat("什么是 Java 的多态?");
System.out.println("模型:" + response.model);
System.out.println("回复:" + response.choices.get(0).message.content);
System.out.println("Token消耗:" + response.usage.totalTokens);
}
}
九、New API 中转站与 Anthropic Claude
New API 中转站
New API 是目前最流行的开源 API 管理/分发系统,它完全兼容 OpenAI API 协议:
核心特性:
- 支持多种大模型(OpenAI、Claude、Gemini、国产模型等)
- 统一的 OpenAI 协议接口
- API Key 管理和计费
- 渠道管理和负载均衡
使用方式:
只需将 base_url 指向 New API 服务地址:
String baseUrl = "https://your-new-api-server/v1";
// 或者本地部署
String baseUrl = "http://localhost:3000/v1";
Anthropic Claude 的独立体系
值得注意的是,Anthropic 的 Claude 系列模型采用了自成一体的 API 协议,与 OpenAI 协议存在差异:
| 对比项 | OpenAI API | Anthropic API |
|---|---|---|
| 端点 | /v1/chat/completions | /v1/messages |
| 消息格式 | messages 数组 | messages + system 分离 |
| 系统提示 | 放在 messages 中 | 单独的 system 参数 |
| 流式字段 | choices[0].delta.content | delta.text |
| 工具调用 | tool_calls | content 中的 tool_use 块 |
Anthropic API 示例:
{
"model": "claude-sonnet-4-20250514",
"max_tokens": 1024,
"system": "你是一个有帮助的AI助手。",
"messages": [
{"role": "user", "content": "你好!"}
]
}
兼容方案:
大多数中转站(如 New API)会自动转换协议,让你可以用 OpenAI 协议调用 Claude。但如果你直接调用 Anthropic 官方 API,需要遵循其原生协议。
十、最佳实践总结
参数配置建议
| 场景 | temperature | stream | 其他建议 |
|---|---|---|---|
| 日常对话 | 0.7 | true | 默认即可 |
| 代码生成 | 0.3 | false | 设置 seed 提高稳定性 |
| 创意写作 | 0.9 | true | 可配合 top_p |
| JSON 输出 | 0.3 | false | 使用 response_format |
| AI Agent | 0.7 | false | 配置 tools |
开发建议
- 优先使用流式输出:显著提升用户体验
- 合理设置 temperature:根据场景选择,不要盲目使用默认值
- 关注 token 消耗:usage 字段是计费依据,做好监控
- 善用 seed 参数:需要稳定输出时使用
- 框架封装优先:Spring AI 等框架简化了开发,但理解底层协议很重要
错误处理
try {
ChatResponse response = client.chat(message);
// 处理响应
} catch (Exception e) {
// 常见错误处理
if (e.getMessage().contains("401")) {
System.err.println("API Key 无效");
} else if (e.getMessage().contains("429")) {
System.err.println("请求频率超限,稍后重试");
} else if (e.getMessage().contains("500")) {
System.err.println("模型服务异常");
}
}
总结
OpenAI API 协议已经成为大模型应用开发的事实标准。掌握这套协议的入参、出参、流式/非流式输出,是理解 AI Agent 开发底层原理的关键。
核心要点回顾:
- messages 是最重要的输入参数,定义对话上下文
- stream 决定流式或非流式输出,影响用户体验
- tools/tool_calls 是 AI Agent 开发的核心
- choices[].message.content 和 usage 是最重要的输出字段
- Spring AI、LangChain 等框架都对此协议进行了封装
- New API 等中转站实现了多模型的统一接入
欢迎关注公众号 FishTech Notes,一起交流使用心得!