如何调用大模型
作者:程序员马丁
热门项目实战社群,收获国内众多知名公司面试青睐,近千名同学面试成功!助力你在校招或社招上拿个offer。
上一篇咱们搞清楚了大模型是什么——参数量、Token、上下文窗口、Temperature 这些核心概念,以及 Chat 模型和基座模型的区别。最后留了一个悬念:开发者需要通过 API 调用大模型,才能把它集成到自己的系统里。
这一篇,咱们动手。
这篇结束后,你会用自己写的 Java 程序和大模型对话——发一个问题过去,拿到一个回答回来,就像调用一个普通的 HTTP 接口一样。而且你会掌握两种调用方式:非流式(一次性拿到完整回答)和流式(像 ChatGPT 那样逐字输出)。
在写代码之前,有一件事必须先搞清楚:你要发什么格式的请求,会收到什么格式的响应。这就是 OpenAI 接口协议。
OpenAI 接口协议:大模型 API 的“普通话”
1. 为什么要先讲 OpenAI 协议
你可能会问:我们后面用的是 SiliconFlow 平台上的 Qwen 模型,为什么要讲 OpenAI 的协议?
原因很简单:OpenAI 的 Chat Completions API 已经成了大模型 API 的事实标准。就像 HTTP 是 Web 世界的通用协议一样,OpenAI 的接口格式是大模型 API 世界的“普通话”。
国内的 DeepSeek、通义千问、智谱 GLM、SiliconFlow,国外的 Anthropic(Claude)、Google(Gemini)——几乎所有主流厂商都提供了兼容 OpenAI 协议的 API 接口(截止 2025 年 2 月,Anthropic 和 Google 都已经有了自己的原生 API 协议,但同时也提供了 OpenAI 兼容层)。这意味着什么?你只需要学一套协议,就能调用市面上几乎所有的大模型。换模型的时候,改一下 baseURL 和 API Key 就行,代码逻辑一行不用动。
所以这套协议值得你花时间搞清楚,后续系列中所有的 API 调用(Chat API、Embedding API、Reranker API)都建立在这个基础上。
2. 请求格式详解
调用大模型 API,本质上就是发一个 HTTP POST 请求。请求体是一个 JSON,长这样:
{
"model": "Qwen/Qwen3-32B",
"messages": [
{
"role": "system",
"content": "你是一个专业的电商客服助手,只回答和退货、换货、物流相关的问题。"
},
{
"role": "user",
"content": "买了一周的东西还能退吗?"
}
],
"temperature": 0.1,
"max_tokens": 512,
"stream": false
}
下面逐个字段讲清楚。
2.1 model:指定要调用的模型
model 字段告诉平台你要用哪个模型来处理这次请求。不同平台的模型 ID 格式可能不同:
| 平台 | 模型 ID 示例 | 说明 |
|---|---|---|
| SiliconFlow | Qwen/Qwen3-32B | 厂商/模型名 格式 |
| OpenAI | gpt-4o | 直接用模型名 |
| DeepSeek | deepseek-chat | 直接用模型名 |
本系列统一使用 SiliconFlow 平台上的 Qwen/Qwen3-32B,中文效果好,适合学习和实验。该模型为付费模型,但价格很低,充值几块钱就够实验很久。
2.2 messages:对话消息数组
messages 是整个请求中最核心的字段。它是一个数组,里面的每条消息都有两个属性:role(角色)和 content(内容)。
模型不是只看你当前这一句话,而是看整个 messages 数组。你可以把它理解为一段对话记录——模型会根据这段完整的对话记录来生成回答。
2.3 messages 中的角色机制
messages 数组中的每条消息都有一个 role,一共有三种角色:
system(系统角色)
系统消息用来定义模型的行为规则,相当于给模型一份工作手册。模型会始终遵守系统消息中的指令。
比如你设置了 "role": "system", "content": "你是一个专业的电商客服助手,只回答和退货、换货、物流相关的问题",那么当用户问“今天天气怎么样”时,模型会拒绝回答,因为这不在它的“工作范围”内。
system 消息在 RAG 系统中非常重要——后续我们会通过 system 消息告诉模型"根据以下参考资料回答用户的问题,如果资料中没有相关信息,请如实告知"。
user(用户角色)
用户消息就是用户的输入,也就是用户问的问题。
assistant(助手角色)
助手消息是模型之前的回答。它的作用是构建多轮对话的上下文。
举个例子,一段多轮对话的 messages 数组长这样:
{
"messages": [
{"role": "system", "content": "你是一个电商客服助手"},
{"role": "user", "content": "你们支持七天无理由退货吗?"},
{"role": "assistant", "content": "支持的。自签收之日起7天内,商品未使用且不影响二次销售的,可以申请七天无理由退货。"},
{"role": "user", "content": "那运费谁出?"}
]
}
模型看到这个数组,就知道之前聊过退货的话题,所以能理解“那运费谁出”问的是退货运费,而不是发货运费。如果你只发最后一句“那运费谁出”,模型根本不知道你在说什么。
大模型本身是没有记忆的。每次 API 调用都是独立的,模型不会记住上一次调用的内容。所谓的多轮对话,其实是你在每次请求中把之前的对话历史都带上,让模型看到完整的上下文。这也是为什 么对话越长,消耗的 Token 越多——因为每次请求都要把历史消息重新发一遍。
来看一个更直观的例子——同一个问题,不同的 system 消息会导致完全不同的回答风格:
| system 消息 | 用户问题 | 模型回答风格 |
|---|---|---|
| 你是一个专业的技术顾问 | Java 和 Python 哪个好? | 客观分析两种语言的优劣势和适用场景 |
| 你是一个 Java 狂热粉丝 | Java 和 Python 哪个好? | 疯狂吹 Java,贬低 Python |
| 你是一个五岁小孩 | Java 和 Python 哪个好? | 用幼稚的语气回答,可能说"我不知道这是什么" |
这就是 system 消息的威力——它能从根本上改变模型的行为模式。
关于 OpenAI 的 developer 角色:OpenAI 在新版 API 中引入了
developer角色来替代system角色(参考 OpenAI 官方文档)。两者的功能类似,都是用来定义模型的行为规则。不过目前大多数兼容 OpenAI 协议的平台(包括 SiliconFlow、DeepSeek 等)仍然使用system角色,所以本系列统一使用system。如果你直接调用 OpenAI 官方 API,可以用developer替换system,效果是一样的。
2.4 temperature、max_tokens、top_p 等参数
除了 model 和 messages,还有几个常用的可选参数:
| 参数 | 类型 | 说明 | RAG 场景推荐值 |
|---|---|---|---|
temperature | float | 控制回答的随机性,0~2 之间。上一篇详细讲过 | 0~0.3 |
max_tokens | int | 模型最多生成多少个 Token。超过这个数就会被截断 | 512~2048(根据预期回答长度设置) |
top_p | float | 另一种控制随机性的方式,0~1 之间。和 temperature 二选一即可 | 0.7~0.9 |
stream | boolean | 是否启用流式返回。true 为流式,false 为非流式 | 根据场景选择 |
temperature和top_p都是控制随机性的参数,一般只调其中一个就行。本系列统一用temperature,更直观。
2.5 stream:流式开关
stream 参数决定了模型的回答是一次性返回还是逐块推送:
stream: false(默认):模型生成完所有内容后,一次性返回完整的 JSON 响应。适合后台处理场景stream: true:模型每生成一小段内容就立刻推送给客户端,客户端可以实时展示。适合面向用户的对话场景
这两种方式的区别和实现,后面会用完整的代码示例来演示。
3. 响应格式详解
发出请求后,模型会返回一个 JSON 响应(这里先看非流式的响应格式):
{
"id": "chatcmpl-abc123",
"object": "chat.completion",
"created": 1700000000,
"model": "Qwen/Qwen3-32B",
"choices": [
{
"index": 0,
"message": {
"role": "assistant",
"content": "支持的。根据我们的退货政策,自签收之日起7天内,商品未经使用且不影响二次销售的,您可以申请七天无理由退货。请在订单详情页点击\"申请退货\"按钮,按照提示操作即可。"
},
"finish_reason": "stop"
}
],
"usage": {
"prompt_tokens": 42,
"completion_tokens": 68,
"total_tokens": 110
}
}
关键字段说明:
id:这次请求的唯一标识,用于日志追踪choices:模型的回答数组。通常只有一个元素(除非你设置了n参数要求生成多个回答)choices[0].message:模型的回答,格式和请求中的 message 一样,role是assistant,content是回答内容choices[0].finish_reason:模型停止生成的原因usage:Token 用量统计
3.1 usage:Token 用量统计
usage 字段告诉你这次调用消耗了多少 Token:
| 字段 | 含义 |
|---|---|
prompt_tokens | 你发送的内容(system + user + assistant 历史消息)消耗的 Token 数 |
completion_tokens | 模型生成的回答消耗的 Token 数 |
total_tokens | 总 Token 数 = prompt_tokens + completion_tokens |
API 按 Token 计费,所以这个字段帮你监控成本。上面的例子中,输入 42 个 Token,输出 68 个 Token,总共 110 个 Token。
3.2 finish_reason:模型为什么停下来了
finish_reason 有两个常见的值:
| 值 | 含义 | 说明 |
|---|---|---|
stop | 正常结束 | 模型认为回答已经完整,主动停止 |
length | 达到长度上限 | 回答被 max_tokens 截断了,内容可能不完整 |
如果你经常看到 finish_reason: "length",说明你的 max_tokens 设小了,模型的回答被截断了。把 max_tokens 调大一些就行 。
4. 为什么国内厂商都兼容这个协议
OpenAI 是最早定义这套 Chat Completions API 协议的,围绕它已经形成了庞大的生态:
- 各种 SDK 和框架(LangChain、Spring AI、LlamaIndex)都默认支持 OpenAI 协议
- 开发者社区的教程、示例代码大多基于这个协议
- 各种工具(Postman 模板、VS Code 插件、命令行工具)都兼容这个格式
国内厂商兼容这个协议,开发者就能零成本迁移。你用 SiliconFlow 写的代码,想切换到 DeepSeek 官方 API,只需要改两个东西:
// SiliconFlow
baseURL = "https://api.siliconflow.cn/v1/chat/completions"
apiKey = "你的 SiliconFlow API Key"
// 切换到 DeepSeek
baseURL = "https://api.deepseek.com/v1/chat/completions"
apiKey = "你的 DeepSeek API Key"
代码逻辑一行不用改。这就是兼容协议的好处。
SiliconFlow 平台:注册与获取 API Key
1. 为什么选择 SiliconFlow
上一篇已经提过技术选型的理由,这里再简单汇总一下:
- 国内平台,网络访问没有障碍,不需要特殊网络环境
- 注册简单,手机号即可注册,新用户有免费额度
- 支持多种主流开源模型(Qwen、DeepSeek、GLM、Llama 等),一个平台就能用多种模型
- API 完全兼容 OpenAI 协议,学会了这一套,切换到其他平台零成本
- 部分模型可免费调用,足够学习和实验使用
2. 注册步骤
- 访问 SiliconFlow 官网:
https://siliconflow.cn - 点击右上角的“注册”按钮,使用手机号注册账号
- 注册完成后登录,进入控制台
- 在左侧菜单中找到“API 密钥”,点击进入
- 点击“新建 API 密钥”按钮,系统会生成一个以
sk-开头的密钥字符串 - 把这个密 钥复制下来,保存到安全的地方
API Key 是你调用 API 的身份凭证,相当于密码。不要把它提交到 Git 仓库、发到群聊里、或者写在公开的代码中。后面的代码示例中,我们会用
YOUR_API_KEY作为占位符,你需要替换成自己的真实 Key。
3. 本系列用到的模型
下面列出本系列会用到的模型:
| 模型 ID | 类型 | 用途 | 本系列使用场景 |
|---|---|---|---|
Qwen/Qwen3-32B | Chat 模型 | 对话、问答、文本生成 | 本篇的 API 调用实战,后续 RAG 的生成环节 |
BAAI/bge-m3 | Embedding 模型 | 文本向量化 | 后续 RAG 的向量化环节 |
BAAI/bge-reranker-v2-m3 | Reranker 模型 | 检索结果重排序 | 后续 RAG 的检索环节 |
模型的可用性和价格可能会变化,以 SiliconFlow 官网的实际展示为准。充值少量金额(几块钱就够实验很久)。
拿到 API Key 之后,咱们就可以开始写代码了。
非流式调用:发一个问题,拿一个完整回答
非流式调用是最简单的调用方式:你发一个请求,等模型生成完所有内容,一次性拿到完 整的回答。就像调用一个普通的 REST API 一样。
完整示例可以查看 TinyRAG 项目 com.nageoffer.ai.tinyrag.openai 目录下代码。
1. Maven 依赖
在你的 pom.xml 中添加以下依赖:
<dependencies>
<!-- OkHttp:HTTP 客户端 -->
<dependency>
<groupId>com.squareup.okhttp3</groupId>
<artifactId>okhttp</artifactId>
<version>4.12.0</version>
</dependency>
<!-- Gson:JSON 处理 -->
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
<version>2.13.1</version>
</dependency>
</dependencies>
为什么用 OkHttp 而不是 Spring 的 RestTemplate 或 WebClient?因为 OkHttp 是纯 HTTP 客户端,不依赖 Spring 框架,代码更简洁,也方便你在任何 Java 项目中使用。后续系列中所有的 API 调用都用 OkHttp + Gson 这套组合。
2. 完整代码实现
public class NonStreamingChat {
// SiliconFlow API 地址
private static final String API_URL = "https://api.siliconflow.cn/v1/chat/completions";
// 替换成你自己的 API Key
private static final String API_KEY = "YOUR_API_KEY";
public static void main(String[] args) throws IOException {
// 1. 构建请求体 JSON
JsonObject requestBody = new JsonObject();
requestBody.addProperty("model", "Qwen/Qwen3-32B");
requestBody.addProperty("temperature", 0);
requestBody.addProperty("max_tokens", 1024);
requestBody.addProperty("stream", false);
// 构建 messages 数组
JsonArray messages = new JsonArray();
// system 消息:定义模型的行为规则
JsonObject systemMsg = new JsonObject();
systemMsg.addProperty("role", "system");
systemMsg.addProperty("content", "你是一个专业的电商客服助手,回答要简洁明了。");
messages.add(systemMsg);
// user 消息:用户的问题
JsonObject userMsg = new JsonObject();
userMsg.addProperty("role", "user");
userMsg.addProperty("content", "买了一周的东西还能退吗?");
messages.add(userMsg);
requestBody.add("messages", messages);
// 2. 创建 OkHttp 客户端(设置超时时间,大模型响应可能较慢)
OkHttpClient client = new OkHttpClient.Builder()
.connectTimeout(30, TimeUnit.SECONDS)
.readTimeout(60, TimeUnit.SECONDS)
.build();
// 3. 构建 HTTP 请求
Request request = new Request.Builder()
.url(API_URL)
.addHeader("Authorization", "Bearer " + API_KEY)
.addHeader("Content-Type", "application/json")
.post(RequestBody.create(
requestBody.toString(),
MediaType.parse("application/json")
))
.build();
// 4. 发送请求并处理响应
try (Response response = client.newCall(request).execute()) {
if (!response.isSuccessful()) {
System.out.println("请求失败,状态码:" + response.code());
System.out.println("错误信息:" + response.body().string());
return;
}
// 5. 解析 JSON 响应
String responseBody = response.body().string();
Gson gson = new Gson();
JsonObject jsonResponse = gson.fromJson(responseBody, JsonObject.class);
// 提取模型的回答
String answer = jsonResponse
.getAsJsonArray("choices")
.get(0).getAsJsonObject()
.getAsJsonObject("message")
.get("content").getAsString();
// 提取 finish_reason
String finishReason = jsonResponse
.getAsJsonArray("choices")
.get(0).getAsJsonObject()
.get("finish_reason").getAsString();
// 提取 Token 用量
JsonObject usage = jsonResponse.getAsJsonObject("usage");
int promptTokens = usage.get("prompt_tokens").getAsInt();
int completionTokens = usage.get("completion_tokens").getAsInt();
int totalTokens = usage.get("total_tokens").getAsInt();
// 6. 打印结果
System.out.println("=== 模型回答 ===");
System.out.println(answer);
System.out.println();
System.out.println("=== 调用信息 ===");
System.out.println("结束原因:" + finishReason);
System.out.println("输入 Token:" + promptTokens);
System.out.println("输出 Token:" + completionTokens);
System.out.println("总 Token:" + totalTokens);
}
}
}
3. 运行效果
把 YOUR_API_KEY 替换成你自己的 API Key,运行这段代码,控制台输出大概长这样:
=== 模型回答 ===
根据中国《消费者权益保护法》规定,**普通商品**(非定制、非易腐等)通常享有**七天无理由退货**的权利,但需满足以下条件:
1. **时间计算**:自签收商品之日起7日内(含第7天);
2. **商品状态**:保持完好,未拆封或未使用(部分商品 如食品、化妆品等需未拆封);
3. **凭证保留**:保留发票、包装及配件;
4. **运费承担**:无理由退货需自行承担退货运费(特殊政策除外)。
**建议**:
- 立即联系卖家客服,说明情况并确认是否符合退货条件;
- 若卖家拒绝,可向平台投诉或通过12315平台维权。
不同平台/商家可能有细微差异,请以实际购买页面的退货政策为准。
=== 调用信息 ===
结束原因:stop
输入 Token:34
输出 Token:527
总 Token:561
每次运行的回答内容可能略有不同(因为 temperature 不是 0),但大 意是一样的。如果你把 temperature 设成 0,每次运行的结果几乎相同(由于 GPU 浮点运算的不确定性,极少数情况下仍可能有细微差别)。
4. 代码逐行解读
整段代码做了六件事,对应注释中的 1~6:
- 构建请求体:用 Gson 的
JsonObject手动拼装 JSON。messages数组里放了一条 system 消息和一条 user 消息。这就是前面讲的 OpenAI 协议的请求格式 - 创建 HTTP 客户端:OkHttp 的标准用法。注意设置了超时时间——大模型生成回答可能需要几秒到十几秒,默认的超时时间可能不够
- 构建 HTTP 请求:POST 请求,URL 是 SiliconFlow 的 Chat API 地址。
Authorization头用Bearer方式传递 API Key,这是 OAuth 2.0 的标准做法 - 发送请求:
client.newCall(request).execute()是同步调用,会阻塞当前线程直到收到响应。用 try-with-resources 确保响应体被正确关闭 - 解析响应:从 JSON 响应中提取
choices[0].message.content(模型的回答)和usage(Token 用量)。这就是前面讲的响应格式 - 打印结果:把回答和调用信息输出到控制台
整个流程和你平时调用第三方 REST API 没有本质区别——构建请求、发送请求、解析响应。大模型 API 并没有什么神秘的。