用 Python、Node、Go、Java、curl 调 /chat/completions 的复制即用手册
本文提供 Python、Node.js、Go、Java 和 curl 调用 /chat/completions 的最小可运行示例,对比鉴权头的写法与错误处理差异,帮助开发者快速接入。
为什么需要这份手册
许多聚合 API 服务兼容 OpenAI 的 /chat/completions 接口,但不同语言在 HTTP 客户端、鉴权头拼写和错误处理范式上各有习惯。本文用五种最常见的调用方式,给出可直接复制粘贴的最小示例,并指出容易踩坑的细节。
注意:鉴权头通常是 Authorization: Bearer <API_KEY>,但不同语言里字符串拼接方式不同,下文会分别说明。
通用约定
- 请求方法:
POST - 端点:
/chat/completions(多数服务兼容 OpenAI 风格) - 请求体:JSON,包含
model、messages等字段 - 成功响应:JSON,通常在
choices[0].message.content中返回文本 - 失败响应:HTTP 状态码非 2xx,响应体多为 JSON 错误对象
Python 示例(requests)
import requests
API_KEY = "YOUR_API_KEY"
URL = "https://api.example.com/v1/chat/completions"
headers = {
"Authorization": f"Bearer {API_KEY}",
"Content-Type": "application/json",
}
payload = {
"model": "gpt-3.5-turbo",
"messages": [
{"role": "user", "content": "你好,介绍一下你自己"}
],
}
try:
resp = requests.post(URL, headers=headers, json=payload, timeout=30)
resp.raise_for_status()
data = resp.json()
print(data["choices"][0]["message"]["content"])
except requests.HTTPError as e:
print("HTTP 错误:", e.response.status_code, e.response.text)
except requests.RequestException as e:
print("请求异常:", str(e))
鉴权头写法:f-string 直接拼接 Bearer 前缀,注意 Bearer 后有一个空格。
错误处理:用 raiseforstatus() 抛出异常,再分别捕获 HTTPError 和网络异常。HTTPError 的 response.text 通常包含服务端错误详情。
Node.js 示例(原生 fetch)
const API_KEY = "YOUR_API_KEY";
const URL = "https://api.example.com/v1/chat/completions";
async function main() {
const resp = await fetch(URL, {
method: "POST",
headers: {
"Authorization": `Bearer ${API_KEY}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
model: "gpt-3.5-turbo",
messages: [{ role: "user", content: "你好,介绍一下你自己" }],
}),
});
if (!resp.ok) {
const text = await resp.text();
throw new Error(`HTTP ${resp.status}: ${text}`);
}
const data = await resp.json();
console.log(data.choices[0].message.content);
}
main().catch((err) => {
console.error("请求失败:", err.message);
});
鉴权头写法:模板字符串 ` Bearer ${API_KEY} `,注意保留空格。
错误处理:fetch 只在网络层出错时 reject,HTTP 状态码错误需要手动检查 resp.ok。建议读取 resp.text() 而不是 resp.json(),因为有些错误响应可能不是 JSON。
Go 示例(net/http)
package main
import (
"bytes"
"encoding/json"
"fmt"
"io"
"net/http"
"time"
)
func main() {
apiKey := "YOUR_API_KEY"
url := "https://api.example.com/v1/chat/completions"
payload := map[string]interface{}{
"model": "gpt-3.5-turbo",
"messages": []map[string]string{
{"role": "user", "content": "你好,介绍一下你自己"},
},
}
body, _ := json.Marshal(payload)
req, err := http.NewRequest("POST", url, bytes.NewBuffer(body))
if err != nil {
panic(err)
}
req.Header.Set("Authorization", "Bearer "+apiKey)
req.Header.Set("Content-Type", "application/json")
client := &http.Client{Timeout: 30 * time.Second}
resp, err := client.Do(req)
if err != nil {
fmt.Println("请求异常:", err)
return
}
defer resp.Body.Close()
respBody, _ := io.ReadAll(resp.Body)
if resp.StatusCode < 200 || resp.StatusCode >= 300 {
fmt.Printf("HTTP 错误 %d: %s\n", resp.StatusCode, string(respBody))
return
}
var data map[string]interface{}
json.Unmarshal(respBody, &data)
choices := data["choices"].([]interface{})
msg := choices[0].(map[string]interface{})["message"].(map[string]interface{})
fmt.Println(msg["content"])
}
鉴权头写法:"Bearer " + apiKey,Go 中字符串拼接用 +,注意空格。
错误处理:Go 需要手动检查 resp.StatusCode,网络错误通过 err 返回。读取 resp.Body 后要记得 defer Close()。
Java 示例(HttpClient,Java 11+)
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 ChatDemo {
public static void main(String[] args) throws Exception {
String apiKey = "YOUR_API_KEY";
String url = "https://api.example.com/v1/chat/completions";
String jsonBody = "{" +
"\"model\": \"gpt-3.5-turbo\"," +
"\"messages\": [{\"role\": \"user\", \"content\": \"你好,介绍一下你自己\"}]" +
"}";
HttpClient client = HttpClient.newBuilder()
.connectTimeout(Duration.ofSeconds(30))
.build();
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create(url))
.timeout(Duration.ofSeconds(30))
.header("Authorization", "Bearer " + apiKey)
.header("Content-Type", "application/json")
.POST(HttpRequest.BodyPublishers.ofString(jsonBody))
.build();
HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());
int status = response.statusCode();
if (status < 200 || status >= 300) {
System.out.println("HTTP 错误 " + status + ": " + response.body());
return;
}
System.out.println(response.body());
}
}
鉴权头写法:"Bearer " + apiKey,Java 中字符串拼接用 +。
错误处理:client.send 可能抛出 IOException 或 InterruptedException,需在方法签名中声明或捕获。状态码需手动判断。
curl 示例
curl -X POST "https://api.example.com/v1/chat/completions" \
-H "Authorization: Bearer $API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "gpt-3.5-turbo",
"messages": [
{"role": "user", "content": "你好,介绍一下你自己"}
]
}'
鉴权头写法:使用环境变量 $API_KEY 避免密钥泄露,注意 Bearer 后的空格。
错误处理:curl 默认只输出响应体,需要加 -i 或 -w "%{http_code}" 查看状态码。若返回非 2xx,响应体通常是 JSON 错误信息。
常见差异与避坑
- 鉴权头空格:
Bearer后面必须有一个空格,否则服务端可能返回 401。 - Content-Type:必须为
application/json,否则部分服务端会解析失败。 - 超时设置:LLM 响应可能较慢,建议客户端超时不低于 30 秒。
- 错误响应格式:不同服务端错误结构可能不同,建议先打印原始响应文本再解析。
- 重试策略:对 429 或 5xx 错误可做有限次退避重试,但 4xx(如 401、400)通常不应重试。
小结
五种语言的调用骨架高度一致,差异主要集中在鉴权头拼接方式和错误处理习惯上。复制对应示例后,只需替换 API_KEY、URL 和 model 字段即可运行。建议先在 curl 中验证连通性,再移植到项目中。