OpenAI API 协议完全指南 - 大模型应用开发的通用标准

0 阅读19分钟

前言

"提示词的好坏决定着模型的输出效果,但是模型是否能正常输出,以及输出的效果好不好,还有一些其他的参数也会有所影响。"

这是很多刚入门 AI 应用开发者的困惑。大家都知道要写好 Prompt,但对于 API 调用中的各种参数却一知半解。

今天这篇文章,我会用最详细的方式,带你彻底搞懂 OpenAI API 协议——这个几乎成为所有大模型应用开发底层基础的标准接口。无论你使用 Spring AI、LangChain 还是其他智能体框架,理解这个协议都是你进阶 AI 开发的必经之路。


一、什么是 OpenAI API 协议

OpenAI API 协议是 OpenAI 公司定义的一套与大语言模型交互的 HTTP API 接口规范。由于其先发优势和广泛的应用场景,这套协议已经成为事实上的行业标准

为什么它如此重要?

1. 行业标准地位

目前主流的大模型几乎都已经完全兼容 OpenAI 的接口规范。这意味着:

  • 学会一套 API,就能调用市面上几乎所有大模型
  • 代码迁移成本极低,只需更换 base_urlapi_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 ChatGPTapi.openai.com/v1
阿里云通义千问dashscope.aliyuncs.com/compatible-…
DeepSeekapi.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-4qwen-plusdeepseek-chat
messages✅ 必填Array--消息列表传递给模型的对话历史或指令,包含 rolecontent
temperature❌ 选填Float1.00.0 - 2.0温度系数控制输出随机性,低值稳定,高值创意
top_p❌ 选填Float1.00.0 - 1.0核采样与 temperature 类似但算法不同,建议只调其一
max_tokens❌ 选填Integer模型最大-最大输出长度限定单次响应最大 Token 数
n❌ 选填Integer11-N生成次数为同一 Prompt 生成 n 个不同回应
stream❌ 选填Booleanfalsetrue/false流式输出是否流式返回 Token
stop❌ 选填String/Arraynull-停止标记遇到指定字符串立即停止生成
presence_penalty❌ 选填Float0-2.0 - 2.0出现惩罚正值鼓励新话题,负值减少新话题
frequency_penalty❌ 选填Float0-2.0 - 2.0频率惩罚正值减少重复,负值增加连贯
seed❌ 选填Integernull-复现种子提高结果可复现性(非 100% 保证)
tools❌ 选填Arraynull-工具定义定义可调用的外部工具/函数
tool_choice❌ 选填String/Objectauto-工具选择控制工具调用行为
response_format❌ 选填Objectnull-响应格式指定输出格式如 JSON
user❌ 选填Stringnull-用户标识用于追踪和识别终端用户

核心参数详解

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": "多态是面向对象编程的核心概念之一..."}

使用场景:

  1. 多轮对话:保持对话连贯性
[
  {"role": "user", "content": "什么是 Java 的多态?"},
  {"role": "assistant", "content": "多态是指同一操作作用于不同对象..."},
  {"role": "user", "content": "能给我一个代码示例吗?"}  // 模型知道"它"指的是多态
]
  1. Few-shot 提示:通过示例引导模型输出格式
[
  {"role": "system", "content": "你是情感分析助手,判断用户输入的情感倾向。"},
  {"role": "user", "content": "今天天气真好!"},
  {"role": "assistant", "content": "正面情感"},
  {"role": "user", "content": "这电影太无聊了。"},
  {"role": "assistant", "content": "负面情感"},
  {"role": "user", "content": "我刚完成了一个项目。"}  // 模型会学习输出"中性情感"或类似格式
]
  1. 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
  }
}

非流式输出参数表格

参数名称数据类型作用参数说明
idString唯一标识符本次 API 请求的唯一 ID
objectString响应对象类型固定为 chat.completion
modelString模型名称实际使用的模型名称
createdInteger时间戳API 响应创建的 Unix 时间戳
choicesArray输出列表包含模型生成的所有回复选项
choices[i].indexInteger选项索引从 0 开始的索引
choices[i].messageObject消息内容包含完整的回复信息
choices[i].message.roleString角色固定为 assistant
choices[i].message.contentString生成文本模型生成的全部文本内容
choices[i].message.tool_callsArray工具调用AI Agent 核心:包含要调用的函数名和参数
choices[i].finish_reasonString停止原因stop(正常结束)、length(达到 max_tokens)、tool_calls(调用工具)
usageObjectToken 统计计费的核心依据
usage.prompt_tokensInteger输入 Token输入消息消耗的 Token 数
usage.completion_tokensInteger输出 Token模型输出消耗的 Token 数
usage.total_tokensInteger总 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]

流式输出参数表格

参数名称数据类型作用参数说明
idString唯一标识符每个 chunk 中相同,标识同一次请求
objectString响应对象类型固定为 chat.completion.chunk
modelString模型名称实际使用的模型名称
createdInteger时间戳API 响应创建的 Unix 时间戳
choicesArray分块输出列表包含本次增量内容
choices[i].indexInteger选项索引从 0 开始的索引
choices[i].deltaObject增量内容本次数据块新增的内容
choices[i].delta.roleString角色仅在第一个 chunk 出现
choices[i].delta.contentString增量文本本次新增的极小部分文本,需拼接
choices[i].delta.tool_callsArray工具调用增量流式工具调用,增量式返回
choices[i].finish_reasonString停止原因仅在最后一个 chunk 返回
usageObjectToken 统计通常在最后一个 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 APIAnthropic API
端点/v1/chat/completions/v1/messages
消息格式messages 数组messages + system 分离
系统提示放在 messages 中单独的 system 参数
流式字段choices[0].delta.contentdelta.text
工具调用tool_callscontent 中的 tool_use

Anthropic API 示例:

{
  "model": "claude-sonnet-4-20250514",
  "max_tokens": 1024,
  "system": "你是一个有帮助的AI助手。",
  "messages": [
    {"role": "user", "content": "你好!"}
  ]
}

兼容方案:

大多数中转站(如 New API)会自动转换协议,让你可以用 OpenAI 协议调用 Claude。但如果你直接调用 Anthropic 官方 API,需要遵循其原生协议。


十、最佳实践总结

参数配置建议

场景temperaturestream其他建议
日常对话0.7true默认即可
代码生成0.3false设置 seed 提高稳定性
创意写作0.9true可配合 top_p
JSON 输出0.3false使用 response_format
AI Agent0.7false配置 tools

开发建议

  1. 优先使用流式输出:显著提升用户体验
  2. 合理设置 temperature:根据场景选择,不要盲目使用默认值
  3. 关注 token 消耗:usage 字段是计费依据,做好监控
  4. 善用 seed 参数:需要稳定输出时使用
  5. 框架封装优先: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.contentusage 是最重要的输出字段
  • Spring AI、LangChain 等框架都对此协议进行了封装
  • New API 等中转站实现了多模型的统一接入

欢迎关注公众号 FishTech Notes,一起交流使用心得!