接入大模型:从第一个 Hello World 到多模态对话
手把手教你接入大模型 API,从最基础的 curl 调用开始,实现单轮对话、持续对话,再到多模态能力。以 OpenAI Chat 协议为例,掌握 LLM 对接的核心技能。
接入大模型:从第一个 Hello World 到多模态对话
在上一篇文章Agent开发入门——从理解到实践中,我们一起写了第一个简单的 Agent。你可能注意到了一个关键细节:在 decide 方法里,我留了一行注释——"这里可以集成大模型进行决策"。
今天,我们就来真正实现这一步。
为什么要接入大模型?
还记得在快速补全你的认知差中讲的吗?LLM 就像一个极其聪明的大脑,但它被锁在了一个盒子里——这个盒子只有输入和输出两个端口,而且只接受文本。
我们写好的 SimpleAgent 现在就像一个"假装聪明"的机器人——它能接收输入、能执行动作,但"思考"的部分只是简单地拼接字符串。它不理解你的问题,也不会推理。
接入大模型,就是给这个机器人换上一个真正的大脑。
LLM 的主要对接接口协议
在正式开始之前,我们先了解一下主流的 LLM 对接方式。目前市面上的大模型,主要有以下三种对接协议:
| 协议类型 | 代表厂商 | 特点 |
|---|---|---|
| Chat 协议 | OpenAI、智谱、月之暗面、百川、DeepSeek 等 | 最早标准化,消息列表格式,几乎所有 LLM 都兼容 |
| Response 协议 | OpenAI(新版)、部分国产模型 | 单条消息格式,更简洁,适合简单场景 |
| Claude 协议 | Anthropic Claude | 独立的消息格式,system 提示单独传参,支持更长上下文 |
为什么我们选择 Chat 协议?
因为它就像 HTTP 之于 Web 开发一样——它是事实上的行业标准。
OpenAI 在 2022 年底推出 ChatGPT 时,同时开放了 Chat Completions API。由于它设计得足够简洁、足够好用,几乎所有后来的大模型厂商都选择了兼容这个协议。这意味着:
- 你学会了一次,就能对接几十个不同的大模型
- 切换模型时,只需要改 API 地址和 Key,代码基本不用动
- 社区资源最丰富,遇到问题最容易找到解决方案
这就像你学 Java 时先学 Spring Boot 一样——掌握了它,你就掌握了整个生态。
补充说明: Claude 协议与 Chat 协议的主要区别在于,Claude 把
system提示放在顶层参数中,而不是messages数组里。如果你以后需要对接 Claude,只需要调整一下请求结构即可,核心思路是一样的。
准备工作
在开始之前,你需要准备:
- 一个大模型 API Key:可以是 OpenAI 的,也可以是国内兼容 OpenAI 协议的服务商(如智谱、月之暗面、DeepSeek 等)
- curl 命令行工具:macOS 和 Linux 自带,Windows 可以使用 Git Bash 或 WSL
- 一颗好奇的心:这比你想象的简单得多
为什么先用 curl 而不是直接写 Java?
因为我想让你先看到最原始的请求和响应。就像学 JDBC 之前先理解 SQL 一样——当你亲眼看到 HTTP 请求长什么样,后面写代码时就知道每一行在做什么。
第一步:最基础的 Hello World
让我们从最简单的开始——给大模型发一句话,让它回复我们。
单轮对话(curl 版本)
curl https://api.openai.com/v1/chat/completions \
-H "Content-Type: application/json" \
-H "Authorization: Bearer YOUR_API_KEY" \
-d '{
"model": "gpt-3.5-turbo",
"messages": [
{
"role": "user",
"content": "你好,请用一句话介绍你自己"
}
]
}'
把上面的 YOUR_API_KEY 替换成你的真实 Key,然后在终端里运行它。
如果你用的是国内的兼容服务(比如智谱、月之暗面、DeepSeek),只需要把 URL 换成它们的地址即可。比如:
# 智谱 GLM
curl https://open.bigmodel.cn/api/paas/v4/chat/completions \
# ... 其他部分完全一样
# DeepSeek
curl https://api.deepseek.com/v1/chat/completions \
# ... 其他部分完全一样
# 月之暗面 Kimi
curl https://api.moonshot.cn/v1/chat/completions \
# ... 其他部分完全一样
看到了吗?除了 URL 不同,其他部分一模一样。这就是标准化协议的好处。
理解请求格式
让我们仔细看看这个请求的结构:
{
"model": "gpt-3.5-turbo", // 使用哪个模型
"messages": [ // 消息列表
{
"role": "user", // 角色:user(用户)
"content": "你好,请用一句话介绍你自己" // 消息内容
}
]
}
messages 是一个数组,每个元素都有两个字段:
role:消息的角色,可以是system(系统提示)、user(用户)、assistant(助手)content:消息的内容
为什么是数组而不是单条消息? 因为要支持多轮对话。我们马上就会讲到。
理解响应格式
运行上面的 curl 命令后,你会收到类似这样的响应:
{
"id": "chatcmpl-xxx",
"object": "chat.completion",
"created": 1234567890,
"model": "gpt-3.5-turbo",
"choices": [
{
"index": 0,
"message": {
"role": "assistant",
"content": "你好!我是一个AI助手,很高兴为你服务。"
},
"finish_reason": "stop"
}
],
"usage": {
"prompt_tokens": 20,
"completion_tokens": 15,
"total_tokens": 35
}
}
关键字段解读:
choices[0].message.content:大模型的回复内容choices[0].finish_reason:结束原因,stop表示正常结束usage:Token 使用情况,用于计费
这就像调用一个 REST API 返回的 JSON 响应一样简单,对吧?
第二步:加入系统提示
在实际应用中,我们通常需要告诉大模型"你是谁"、"你应该怎么做"。这就需要用到 system 角色。
curl https://api.openai.com/v1/chat/completions \
-H "Content-Type: application/json" \
-H "Authorization: Bearer YOUR_API_KEY" \
-d '{
"model": "gpt-3.5-turbo",
"messages": [
{
"role": "system",
"content": "你是一个专业的Java技术顾问,擅长Spring Boot和微服务架构。回答问题时请简洁明了,并给出代码示例。"
},
{
"role": "user",
"content": "如何在Spring Boot中配置数据源?"
}
]
}'
注意到区别了吗? 我们在 messages 数组的第一项添加了一个 system 角色的消息。这条消息不会显示给用户,但会影响大模型的行为方式。
这就像你在写 Java 代码时,先定义好接口规范,然后再实现具体逻辑一样。
第三步:持续对话
还记得前面说的吗?"所谓的多轮对话,其实是客户端把历史记录拼成了一段长长的文本,每次一起发过去。"
让我们来体验一下:
curl https://api.openai.com/v1/chat/completions \
-H "Content-Type: application/json" \
-H "Authorization: Bearer YOUR_API_KEY" \
-d '{
"model": "gpt-3.5-turbo",
"messages": [
{
"role": "system",
"content": "你是一个友好的助手"
},
{
"role": "user",
"content": "我叫小明,我是一个Java后端开发"
},
{
"role": "assistant",
"content": "你好小明!很高兴认识你。作为一名Java后端开发,有什么我可以帮你的吗?"
},
{
"role": "user",
"content": "你还记得我叫什么吗?"
}
]
}'
大模型会回答:"你叫小明"。 为什么?因为它"看到"了前面所有的对话历史。
这就像你在 Controller 里维护了一个 session,把用户的信息保存下来一样。只不过这里的"session"是整个消息列表,每次都发给大模型。
多轮对话的代码模式
理解了原理之后,多轮对话的实现模式就很清晰了:
// 用一个 List 维护对话历史
List<Map<String, String>> messages = new ArrayList<>();
// 添加系统提示
messages.add(Map.of("role", "system", "content", "你是一个友好的助手"));
// 第一轮对话
messages.add(Map.of("role", "user", "content", "我叫小明"));
String reply1 = callLLM(messages); // 调用大模型
messages.add(Map.of("role", "assistant", "content", reply1)); // 保存助手回复
// 第二轮对话
messages.add(Map.of("role", "user", "content", "你还记得我叫什么吗?"));
String reply2 = callLLM(messages); // 再次调用,带上历史
messages.add(Map.of("role", "assistant", "content", reply2));
是不是和你平时在 Web 应用中维护用户会话很像? 概念完全一样,只是存储的内容不同。
第四步:多模态对话
什么是多模态? 简单说,就是大模型不只能处理文本,还能处理图片、音频、视频等不同类型的数据。
但请注意:不是所有大模型都支持多模态。 在使用之前,你需要先确认你使用的大模型是否支持。
支持多模态的常见模型
| 模型 | 支持的多模态类型 |
|---|---|
| GPT-4V、GPT-4o | 图片 |
| Claude 3 | 图片 |
| Gemini Pro Vision | 图片、视频 |
| 通义千问 VL | 图片 |
| GLM-4V | 图片 |
图片理解示例
如果大模型支持图片,你可以这样发送请求:
curl https://api.openai.com/v1/chat/completions \
-H "Content-Type: application/json" \
-H "Authorization: Bearer YOUR_API_KEY" \
-d '{
"model": "gpt-4o",
"messages": [
{
"role": "user",
"content": [
{
"type": "text",
"text": "这张图片里有什么?"
},
{
"type": "image_url",
"image_url": {
"url": "https://example.com/image.jpg"
}
}
]
}
]
}'
注意 content 的变化: 从原来的字符串变成了一个数组,每个元素有不同的 type。
多模态的条件检查
在实际开发中,我们需要先检查大模型是否支持多模态:
/**
* 检查模型是否支持图片输入
* 类似于检查数据库是否支持某种特性
*/
boolean supportsVision(String modelName) {
// 已知支持视觉能力的模型列表
List<String> visionModels = Arrays.asList(
"gpt-4o", "gpt-4-vision-preview",
"claude-3-opus", "claude-3-sonnet",
"gemini-pro-vision"
);
return visionModels.stream()
.anyMatch(model -> modelName.toLowerCase().contains(model));
}
第五步:用 Java 调用大模型
前面我们用 curl 体验了 OpenAI Chat 协议,现在让我们把它变成真正的 Java 代码。
作为 Java 开发者,你可能已经在想: "这不就是一个 HTTP POST 请求吗?我用 HttpURLConnection 或者 OkHttpClient 就能搞定。"
没错,就是这样。 大模型 API 就是一个普通的 REST 接口,你用你熟悉的任何 HTTP 客户端都能调用。
下面是一个完整的 Java 示例:
import java.io.IOException;
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.util.List;
import java.util.Map;
/**
* 一个简单的 LLM 客户端
* 用 Java 11+ 的 HttpClient 实现,无需任何第三方依赖
*/
public class SimpleLlmClient {
private final String apiKey;
private final String apiUrl;
private final String model;
public SimpleLlmClient(String apiKey, String apiUrl, String model) {
this.apiKey = apiKey;
this.apiUrl = apiUrl;
this.model = model;
}
/**
* 调用大模型 API
*
* @param messages 消息列表,格式与 curl 示例一致
* @return 大模型的回复内容
*/
public String chat(List<Map<String, String>> messages) throws IOException, InterruptedException {
// 构建请求体(和 curl 的 -d 参数一模一样)
String requestBody = buildRequestBody(messages);
// 创建 HTTP 请求
HttpClient client = HttpClient.newHttpClient();
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create(apiUrl))
.header("Content-Type", "application/json")
.header("Authorization", "Bearer " + apiKey)
.POST(HttpRequest.BodyPublishers.ofString(requestBody))
.build();
// 发送请求并获取响应
HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());
// 解析响应,提取大模型的回复
return parseResponse(response.body());
}
/**
* 构建请求体
* 将 Java 对象转换为 JSON 字符串
*/
private String buildRequestBody(List<Map<String, String>> messages) {
StringBuilder sb = new StringBuilder();
sb.append("{\"model\":\"").append(model).append("\",\"messages\":[");
for (int i = 0; i < messages.size(); i++) {
Map<String, String> msg = messages.get(i);
if (i > 0) sb.append(",");
sb.append("{\"role\":\"").append(msg.get("role")).append("\",");
sb.append("\"content\":\"").append(escapeJson(msg.get("content"))).append("\"}");
}
sb.append("]}");
return sb.toString();
}
/**
* 解析响应 JSON,提取 choices[0].message.content
* 这里用简单的字符串操作,生产环境建议用 JSON 库
*/
private String parseResponse(String responseBody) {
// 简单解析,找到 "content":"..." 的内容
int contentStart = responseBody.indexOf("\"content\":\"") + 11;
int contentEnd = responseBody.indexOf("\"", contentStart);
return responseBody.substring(contentStart, contentEnd);
}
/**
* 转义 JSON 特殊字符
*/
private String escapeJson(String str) {
return str.replace("\\", "\\\\")
.replace("\"", "\\\"")
.replace("\n", "\\n")
.replace("\r", "\\r")
.replace("\t", "\\t");
}
/**
* 使用示例
*/
public static void main(String[] args) throws IOException, InterruptedException {
// 配置你的 API Key 和地址
String apiKey = "YOUR_API_KEY";
String apiUrl = "https://api.openai.com/v1/chat/completions";
String model = "gpt-3.5-turbo";
// 创建客户端
SimpleLlmClient client = new SimpleLlmClient(apiKey, apiUrl, model);
// 构建消息列表(和 curl 的 messages 一模一样)
List<Map<String, String>> messages = List.of(
Map.of("role", "system", "content", "你是一个友好的Java技术专家"),
Map.of("role", "user", "content", "你好,请简单介绍一下Spring Boot")
);
// 调用大模型
String reply = client.chat(messages);
System.out.println("大模型回复:" + reply);
}
}
运行这段代码,你会看到和 curl 一样的效果。 因为本质上,它们做的是同一件事——发送一个 HTTP POST 请求。
使用 OkHttp 的版本(更常用)
如果你的项目已经在用 OkHttp 或 Retrofit,可以用更简洁的方式:
/**
* 使用 OkHttp 调用大模型
* 更适合 Spring Boot 项目
*/
public String chatWithOkHttp(List<Map<String, String>> messages) throws IOException {
OkHttpClient client = new OkHttpClient();
String jsonBody = buildRequestBody(messages);
RequestBody body = RequestBody.create(jsonBody, MediaType.parse("application/json"));
Request request = new Request.Builder()
.url(apiUrl)
.addHeader("Authorization", "Bearer " + apiKey)
.post(body)
.build();
try (Response response = client.newCall(request).execute()) {
return parseResponse(response.body().string());
}
}
你会发现,无论是用原生的 HttpClient,还是 OkHttp,代码结构都是一样的:
- 构建请求体(JSON)
- 发送 POST 请求
- 解析响应
这就是标准化协议的好处——你只需要写一次对接逻辑,就能适配所有兼容 OpenAI 协议的大模型。
实战:用 curl 完成一次完整的对话流程
让我们把前面学到的知识串起来,完成一个完整的对话流程:
# 第1步:发送第一条消息,建立对话
curl https://api.openai.com/v1/chat/completions \
-H "Content-Type: application/json" \
-H "Authorization: Bearer YOUR_API_KEY" \
-d '{
"model": "gpt-3.5-turbo",
"messages": [
{"role": "system", "content": "你是一个Java技术专家"},
{"role": "user", "content": "你好,我是小明,我在学习Spring Boot"}
]
}'
# 第2步:根据响应,继续对话
curl https://api.openai.com/v1/chat/completions \
-H "Content-Type: application/json" \
-H "Authorization: Bearer YOUR_API_KEY" \
-d '{
"model": "gpt-3.5-turbo",
"messages": [
{"role": "system", "content": "你是一个Java技术专家"},
{"role": "user", "content": "你好,我是小明,我在学习Spring Boot"},
{"role": "assistant", "content": "你好小明!很高兴你对Spring Boot感兴趣..."},
{"role": "user", "content": "能给我一个简单的REST API例子吗?"}
]
}'
注意到第2步了吗? 我们把第1步的所有消息(包括大模型的回复)都放了进去,然后再加上新的用户消息。
总结
让我们回顾一下今天学到的内容:
- LLM 的三种主要对接协议:Chat 协议(最通用)、Response 协议(更简洁)、Claude 协议(独立格式)
- Chat 协议是事实上的行业标准——学会它,你就能对接几乎所有大模型
- 请求格式很简单——
model+messages数组 - 多轮对话的原理——把历史消息都放在
messages里一起发送 - 多模态需要先检查能力——不是所有模型都支持
- Java 调用大模型——就是发一个 HTTP POST 请求,用你熟悉的 HttpClient 或 OkHttp 都行
你已经掌握了对接大模型的核心技能。 接下来,我们就要把这些知识应用到 Agent 中,让你的 SimpleAgent 的 decide 方法接入真正的大模型。
下一步
在下一篇文章中,我们将:
- 封装一个通用的 LLM Client(支持多模型切换)
- 实现完整的对话记忆管理
- 把
SimpleAgent升级为真正能"思考"的智能体
请阅读:封装 LLM Client 与实现智能体 记住:你不是在学习一门全新的技术,而是在用你已经熟悉的 Java 技能,连接一个新的能力。 就像你在 Spring 项目中调用其他微服务一样——只是这次,那个"微服务"是一个拥有智能的大模型。
动手试试上面的 curl 命令和 Java 代码吧!当你看到大模型的回复时,你会对接下来的学习充满信心。