直接用 net/http + json 即可调用 openai chat completions 接口,需正确设置 authorization、content-type、messages 格式、超时、代理及 resp.body.close(),并校验响应字段与错误体。

直接用 net/http + json 调 OpenAI Chat Completions 接口就行
OpenAI 官方没有 Go SDK,但 ChatGPT(即 gpt-3.5-turbo 或 gpt-4)的 API 是标准 RESTful 接口,net/http 完全够用,加 encoding/json 解析响应即可。不用引入第三方 HTTP 客户端(比如 resty),除非你已有统一封装层。
常见错误现象:401 Unauthorized(密钥错或没传)、400 Bad Request(body 缺字段或格式错)、context deadline exceeded(没设超时,卡死在 connect/read)。
- 必须在请求头里带
Authorization: Bearer <your-api-key></your-api-key>,不是api_key=xxx -
Content-Type必须是application/json,漏了会返回 400 且提示模糊 - 请求体必须是 JSON 对象,不能是字符串;
messages是数组,至少含一个{"role": "user", "content": "..."} - 别硬编码
https://api.openai.com/v1/chat/completions—— 后续可能要切代理或自建网关,建议抽成配置项
代理设置必须显式配在 http.Client.Transport 上
国内直连 api.openai.com 大概率失败,但「用代理域名」不等于「改 Host 或 DNS」——HTTP 代理生效靠的是 http.Transport.Proxy。不设它,http.DefaultClient 会直连,哪怕你 hosts 里写了代理 IP 也没用。
使用场景:公司内网走企业代理、本地用 Clash/Proxifier 的 HTTP 端口、或对接自建反向代理(如 https://your-proxy.com/v1)。
- 正确写法是新建
http.Client,改它的Transport.Proxy字段,例如:http.ProxyURL(&url.URL{Scheme: "http", Host: "127.0.0.1:7890"}) - 如果代理是 HTTPS(如某些企业网关),Scheme 得用
https,否则会报unsupported protocol scheme "https" - 别只设
HTTP_PROXY环境变量就以为万事大吉 —— Go 默认不读它,除非你手动调http.ProxyFromEnvironment - 代理超时和 dial timeout 是两回事:前者控制 CONNECT 阶段,后者控制 TLS 握手和首字节响应,都得设
response.Body 不 close 会导致连接泄漏
这是 Go 初学者高频翻车点。每次 http.Do() 后,不管状态码多少,只要 resp.Body != nil,就必须 defer resp.Body.Close()。漏掉它,底层 TCP 连接不会释放,跑几轮就卡住(too many open files 或持续 pending)。
性能影响:默认 http.DefaultClient 复用连接,但不 close body 会让连接卡在 idle 状态无法复用,实际变成短连接,QPS 直接受损。
- 哪怕你只取
resp.StatusCode就 return,也得先 close body - 推荐写法:
if resp != nil && resp.Body != nil { defer resp.Body.Close() } - 如果后续要用
ioutil.ReadAll或json.NewDecoder,记得它内部不自动 close,还是得你自己来
解析 response 要检查 error 和 choices[0].message.content
OpenAI 返回的 JSON 结构固定,但字段可能为空或缺失。直接 json.Unmarshal 后取 resp.Choices[0].Message.Content 很危险 —— 比如流式响应(stream: true)返回的是 SSE,不是 JSON object;或者模型拒答时 choices 是空数组。
容易踩的坑:panic: runtime error: index out of range [0] with length 0,就因为没判空。
- 永远先检查
err != nil(解码失败)和len(resp.Choices) == 0 -
resp.Choices[0].FinishReason是"stop"才代表正常结束;如果是"length",说明被max_tokens截断了 - 别忽略
resp.Usage.PromptTokens和resp.Usage.CompletionTokens—— 计费和限流都靠它,调试时很有用 - 如果要支持流式,别用
json.Unmarshal,得按行 parse SSE event,每行以data:开头
最常被忽略的其实是错误响应体本身:OpenAI 出错时(比如 429)也会返回 JSON,含 error.message 和 error.type,但很多人只看 status code 就 panic,反而丢掉了关键提示。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











