Gin 框架对接 Claude API:用 Go 搭一个高并发 AI 网关

不少教程讲「Go 怎么调用 Claude API」时,通常会从最基础的地方开始:安装 SDK、初始化 Client、发起请求,然后把结果打印出来。作为入门,这当然没问题。但如果你的目标不是写一个 demo,而是给前端、内部系统,甚至多个业务团队提供统一的 AI 能力,那只会调用 Claude API 显然还不够。

更实际的做法,是在服务端用 Gin 封装一层 AI 网关,把 Claude API 的调用、鉴权、限流、日志、成本统计、流式转发和错误处理都收进来。这样一来,前端不用接触上游 API Key,业务系统也不需要理解 Claude Messages API 里那些具体字段和调用细节。

本文主要讨论服务端直接对接 Anthropic Claude API 的工程实现。如果你使用的是 ClaudeAPI 这类第三方 Claude API 兼容接入平台,需要先明确一点:它不是 Anthropic 官方服务,更适合需要兼容接入、多线路选择、中文支持、企业充值、开票以及基础技术协助的场景。具体支持哪些能力、有哪些调用规则,还是要以平台官网的最新说明为准。

为什么不建议在前端直接调用 Claude API?

前端直接调用 Claude API,最大的问题就是安全。API Key 一旦出现在浏览器代码、移动端包体或者小程序里,基本就很难保证不被复制、泄露或者滥用。

在企业项目里,一个 Gin Claude API 网关通常至少要负责这些事情:

  • 保护上游 Claude API Key,只把它放在后端环境变量或 Secret 中;
  • 使用业务 token 给调用方做鉴权,而不是把上游密钥发给前端;
  • 按业务方做限流、额度控制、模型路由和调用审计;
  • 统一注入 system prompt,避免每个业务系统都重复拼提示词;
  • 记录 request_id、耗时、状态码和 token 用量;
  • 对 429、5xx、超时等错误做统一处理。

所以,Go AI 网关并不是简单地把 Claude API 代理一遍。更准确地说,它是把「模型能力」包装成一个可治理、可观测、也方便扩展的内部服务。

整体架构:Gin + Claude API + AI 网关调用链

一个真正能上线的 Gin Claude API 网关,调用链可以设计成下面这样:

Client / Frontend
      |
      v
Gin Router
      |
      v
Auth / RateLimit / Logger / Recovery
      |
      v
Chat Handler
      |
      v
Claude Service
      |
      v
Claude Client
      |
      v
Anthropic Claude API 或兼容接入服务

这里每一层最好都保持边界清楚。

  • Router 主要负责注册路由,比如 /api/v1/chat/api/v1/chat/stream
  • Middleware 处理鉴权、限流、日志、请求体大小限制等通用逻辑;
  • Handler 负责参数绑定、基础校验和响应封装;
  • Service 放业务逻辑,比如上下文处理、模型路由、错误转换;
  • Client 才真正去调用 Claude API,同时负责连接复用、超时控制和流式响应处理。

这样拆分的好处很明显:今天底层接的是 Claude,明天如果要换成 OpenAI、Gemini、Bedrock 或 Vertex,只要接口抽象做得还可以,就不用把 Gin Handler 全部推倒重写。

技术选型:官方 Anthropic Go SDK 还是第三方 Claude Go Client?

Go 调用 Claude API 时,可选方案其实不少。比较稳妥的方式,是根据场景来选。

方案 适合场景 优点 注意点
Anthropic 官方 Go SDK 直接调用 Claude API 官方维护,接口更新通常更及时 网关层需要自己封装
liushuangls/go-anthropic 想使用非官方 wrapper 示例较多,封装也比较灵活 模型和 beta 参数可能会滞后
claude-agent-sdk-go 调用 Claude Code CLI 适合做 Claude Code 自动化 不是常规 Claude API 服务端调用方式
psanford/claude 兼容 Anthropic、Bedrock、Vertex provider 抽象比较好 对单一 Gin 网关来说可能稍重

如果是从零开始搭 Gin Claude API 网关,通常建议优先用官方 SDK。同时,在业务代码里留一层接口抽象,例如:

type LLMClient interface {
    Chat(ctx context.Context, req ChatRequest) (*ChatResponse, error)
    StreamChat(ctx context.Context, req ChatRequest) (<-chan StreamChunk, error)
}

这样做的好处是,当前实现不会变复杂,但以后要接多模型、多供应商时,也不会被某个 SDK 彻底绑死。

初始化 Gin 项目与目录结构

项目目录可以按下面这种方式组织:

gin-claude-gateway/
├── cmd/server/main.go
├── internal/config/config.go
├── internal/handler/chat_handler.go
├── internal/service/claude_service.go
├── internal/client/claude_client.go
├── internal/middleware/auth.go
├── internal/middleware/ratelimit.go
├── internal/model/chat.go
├── internal/response/response.go
├── go.mod
├── .env.example
├── Dockerfile
└── README.md

先把 Gin 和 Claude SDK 这些基础依赖装好:

go mod init gin-claude-gateway
go get github.com/gin-gonic/gin
go get github.com/anthropics/anthropic-sdk-go
go get golang.org/x/time/rate

配置不要直接写死在代码里。至少准备下面这些环境变量:

ANTHROPIC_API_KEY=sk-ant-xxx
CLAUDE_MODEL=claude-3-5-sonnet-latest
SERVER_PORT=8080
REQUEST_TIMEOUT=60s
MAX_CONCURRENCY=100
RATE_LIMIT_RPS=20
GIN_MODE=release

模型名称会随着官方更新发生变化,所以最好放在配置里。实际使用时,以 Anthropic 官方文档,或者兼容接入平台的最新说明为准。

封装 Claude Client:连接复用、超时和模型配置

在高并发场景下,千万不要每来一个请求就重新创建 HTTP Client 或 Claude Client。Client 应该在程序启动时初始化好,然后通过依赖注入传给 Service 使用。

这里有几个原则值得注意。

第一,API Key 只能从环境变量、Secret Manager、Kubernetes Secret 或 CI/CD 注入,不要写进代码,更不要提交到 Git 仓库。

第二,所有上游调用都要带上 context.Context。在 Gin Handler 里可以直接使用 c.Request.Context()。这样用户断开连接后,上游请求也能尽快取消,避免无意义地继续消耗资源。

第三,超时一定要设置合理。AI 请求通常比普通接口慢,但也不能无限等下去。比较常见的写法是给每次调用包一层 timeout:

ctx, cancel := context.WithTimeout(c.Request.Context(), cfg.RequestTimeout)
defer cancel()

如果你使用自定义 HTTP Client,还需要配置连接池、空闲连接数和超时时间。否则在高并发下频繁建连,性能和稳定性都会受到影响。

实现 Gin Chat 接口:Go 调用 Claude API 示例

对外接口可以先设计成:

POST /api/v1/chat

请求体大概长这样:

{
  "messages": [
    {"role": "user", "content": "用 Go 写一个 Gin 路由示例"}
  ],
  "max_tokens": 1024,
  "temperature": 0.7
}

核心流程并不复杂:Handler 绑定请求参数,Service 把它转换成 Claude Messages API 需要的结构,Client 调用上游,最后再返回统一格式的 JSON。

这里不太建议直接把上游原始响应透传给前端。更好的做法,是封装成网关自己的响应格式:

{
  "request_id": "gw_123456",
  "model": "claude-xxx",
  "content": "这里是模型输出",
  "usage": {
    "input_tokens": 123,
    "output_tokens": 456
  }
}

这样前端和业务系统依赖的是网关协议,而不是某个上游 SDK 的字段结构。以后上游字段变了,网关内部适配一下就行。

多轮对话怎么做:无状态、Redis 会话与上下文裁剪

单轮对话很容易做,真正麻烦的是多轮对话。常见做法大致有三种。

无状态网关模式

客户端每次都提交完整的 messages,服务端只负责转发、鉴权、限流和记录日志。这种方式最简单,适合 Web 前端自己维护上下文的场景。

有状态会话模式

客户端只传 session_id 和本轮输入,服务端从 Redis 或数据库里取出历史消息,拼接后再调用 Claude API。这种模式更适合多端同步、客服系统、企业内部助手等场景。

摘要压缩模式

长对话会持续消耗 token,成本也会越来越高。超过一定长度后,可以把较早的消息压缩成摘要,再保留最近几轮原文。这样既能降低成本,也能减少触发上下文上限的概率。

不管采用哪种模式,都应该限制最大上下文长度,并由服务端统一注入 system prompt。普通用户不应该有权限直接覆盖系统指令。

Gin 实现 Claude 流式响应:SSE 转发实战

对聊天类产品来说,流式输出的体验通常比一次性返回好很多。可以单独提供一个接口:

POST /api/v1/chat/stream

在 Gin 里做 SSE 转发时,需要设置响应头:

c.Header("Content-Type", "text/event-stream")
c.Header("Cache-Control", "no-cache")
c.Header("Connection", "keep-alive")

上游每返回一个 chunk,服务端就写入一段 SSE 数据,并及时 Flush。同时,还要监听 ctx.Done()。如果用户关闭页面,或者网络已经断开,服务端应该立刻取消上游 Claude stream,避免 goroutine 和连接泄漏。

流式接口的错误处理要提前想好。如果响应还没开始,可以返回标准 JSON 错误;但如果已经开始输出,就只能通过 SSE event 发送错误,比如 event: error,然后让前端结束渲染。

高并发网关治理:限流、并发控制、重试和熔断

Go 和 Gin 的性能确实不错,但不能把「高并发」理解成无限放量。Claude API 是外部上游服务,真正的瓶颈往往在上游延迟、额度限制和网络稳定性上。

一般建议至少做两层控制。

第一层是 QPS 限流。可以用 rate.Limiter 控制全局请求速率,也可以按业务方分别控制。

第二层是最大并发控制。可以用 semaphore 防止瞬时请求把上游连接和本机资源打满:

select {
case sem <- struct{}{}:
    defer func() { <-sem }()
case <-ctx.Done():
    return ctx.Err()
}

错误处理也要分清楚哪些能重试,哪些不能重试:

上游错误 网关状态码 是否可重试 处理建议
invalid_request 400 检查请求参数
authentication_error 502/500 检查服务端 API Key
rate_limit_error 429 限流或退避重试
500/529 overloaded 503 熔断、重试或降级
context deadline exceeded 504 优化超时配置和请求大小

重试要谨慎,尤其是流式响应和非幂等业务。通常只对短暂网络错误、429、部分 5xx 做指数退避,并且一定要设置最大重试次数,不能一直重试下去。

统一错误码与日志追踪

网关不应该把上游错误原样暴露给用户。更合适的方式,是返回统一错误结构:

{
  "code": "CLAUDE_RATE_LIMITED",
  "message": "Claude API rate limit exceeded",
  "request_id": "gw_123456",
  "retryable": true
}

日志里至少要记录这些信息:

  • request_id;
  • app_id 或 tenant_id;
  • 模型名称;
  • HTTP 状态码;
  • 上游耗时;
  • input_tokens 和 output_tokens;
  • 是否命中限流、超时或重试。

需要特别注意的是,不要在日志里记录完整 API Key、业务 token、敏感 prompt 或隐私数据。生产环境最好做采样、脱敏和字段过滤。

安全设计:API Key 保护、业务鉴权与请求限制

Gin Claude API 网关的安全边界一定要清楚:上游 Claude API Key 只属于服务端,前端最多只能拿业务 token。

Auth Middleware 可以校验类似这样的请求头:

Authorization: Bearer <business-token>

不同业务 token 可以绑定不同额度、模型权限和限流策略。如果是公开 Web 页面,还要配合 CORS 白名单、请求体大小限制、IP 风控、验证码等措施。

Prompt 注入也不能忽略。服务端 system prompt 应该和用户输入分离,不能允许普通用户通过参数直接覆盖。对于敏感业务,还可以增加输出审计、关键词过滤,甚至人工复核流程。

成本控制:max_tokens、模型路由与用量统计

AI 网关必须关注成本。每次请求都应该设置 max_tokens,防止模型输出失控。对于特别长的输入,可以在进入 Claude API 之前做长度校验、截断、摘要,必要时直接拒绝请求。

模型路由可以按照任务复杂度来设计:

  • 简单分类、改写、摘要,可以使用成本较低的模型;
  • 复杂推理、代码分析、长上下文任务,再使用能力更强的模型;
  • 不同业务方可以配置不同的模型白名单和每日额度。

响应头里也可以返回一些用量信息,方便前端或调用方排查问题:

X-Request-ID: gw_xxx
X-Model: claude-xxx
X-Input-Tokens: 1234
X-Output-Tokens: 567

如果使用第三方兼容接入服务,比如 ClaudeAPI,也要以平台实际返回字段和计费说明为准。不要在代码或文档里假设固定价格、固定额度,避免后面维护时出问题。

本地运行、curl 测试与 Docker 部署

本地运行可以直接执行:

go run ./cmd/server

健康检查接口建议保留一个:

GET /healthz

用 curl 测试时,可以这样请求:

curl -X POST http://localhost:8080/api/v1/chat \
  -H "Authorization: Bearer test-token" \
  -H "Content-Type: application/json" \
  -d '{"messages":[{"role":"user","content":"解释一下 Gin middleware"}],"max_tokens":512}'

Dockerfile 可以采用多阶段构建。生产环境里,通过 docker run -e 或 Kubernetes Secret 注入 API Key:

docker build -t gin-claude-gateway .
docker run -p 8080:8080 \
  -e ANTHROPIC_API_KEY=sk-ant-xxx \
  -e CLAUDE_MODEL=claude-xxx \
  gin-claude-gateway

不要把 .env 打进镜像,也不要把它提交到 Git 仓库。这一点看似基础,但线上事故里其实很常见。

压测与性能优化建议

可以用 heywrk 做压测。不过要注意,真实 Claude API 调用会受到上游限额、网络延迟和模型生成速度影响,所以压测结果不能简单理解成固定性能承诺。

示例命令如下:

hey -n 1000 -c 50 -m POST \
  -H "Authorization: Bearer test-token" \
  -H "Content-Type: application/json" \
  -d '{"messages":[{"role":"user","content":"hello"}],"max_tokens":128}' \
  http://localhost:8080/api/v1/chat

压测时重点看 P95、P99、错误率、429 数量、超时数量和上游平均耗时。流式响应会占用更长时间的连接,所以不能只盯着 QPS,还要关注并发连接数、内存、goroutine 数量,以及客户端断开后资源是否能正常释放。

常见问题 FAQ

Gin 可以直接调用 Claude API 吗?

可以。Gin 本质上就是 HTTP Web 框架,在 Handler 里当然可以调用 Claude SDK 或 HTTP API。不过生产环境更建议通过 Service 和 Client 分层封装,不要把上游调用逻辑全堆在 Handler 里。

Go 调用 Claude API 用哪个 SDK?

优先考虑 Anthropic 官方 Go SDK。如果需要兼容 Bedrock、Vertex 或第三方兼容平台,可以在网关内部抽象一个 LLMClient 接口,再根据实际场景替换具体实现。

Claude API Key 应该放前端还是后端?

应该放后端。前端只调用 Gin 网关,并使用业务 token 做鉴权。上游 API Key 应该从环境变量、Secret Manager 或 Kubernetes Secret 中读取。

Gin 如何实现 Claude 流式输出?

可以使用 SSE 或 fetch stream。Gin 侧设置 text/event-stream,逐块写入数据并 Flush,同时监听请求上下文的取消信号。客户端断开后,服务端应立即停止上游 stream。

Claude API 返回 429 怎么办?

这通常说明触发了限流或额度约束。网关应该返回统一错误码,必要时做指数退避重试,同时降低调用方 QPS。不要无限重试,否则只会把问题放大。

如何保存 Claude 多轮对话上下文?

小项目可以让客户端每次传完整 messages;中大型项目更建议用 Redis 或数据库保存会话历史,并对长上下文做裁剪或摘要压缩。

Go AI 网关和普通后端接口有什么区别?

普通接口更多关注业务数据的读写;AI 网关除了这些,还要处理模型路由、prompt 模板、token 成本、流式响应、上游限流、输出审计,以及多供应商扩展等问题。

如何兼容 OpenAI、Gemini 或 Bedrock?

不要让 Handler 直接依赖某个具体 SDK。可以定义统一的 LLMClient 接口,把 Claude、OpenAI、Gemini、Bedrock 分别做成不同实现,再通过配置或路由策略选择。

为什么本地能调用,部署后却超时?

常见原因包括出口网络不通、代理配置缺失、容器环境变量没注入、服务端超时时间太短、上游限流,或者云平台安全组限制。排查时可以结合 request_id、上游耗时和错误日志一起看。

如何统计 Claude API token 成本?

从上游响应里读取 usage 字段,然后按 app、tenant、user、model 和日期聚合。再结合实际计费规则做成本核算。具体价格和额度,要以官方或接入平台的最新说明为准。

总结:从 Go SDK Demo 到生产级 AI 网关

Go 调用 Claude API 只是第一步。一个真正能上线的 Gin Claude API 网关,还需要同时解决安全、鉴权、限流、并发控制、流式转发、统一错误码、日志追踪、token 成本和部署运维这些问题。

比较稳妥的落地路径是:先用官方 SDK 打通 /api/v1/chat,然后补上 SSE 流式接口、限流和统一错误码;再往后加入会话管理、模型路由、成本统计和多供应商扩展。这样既能快速上线,也不会把系统困在一个简单 demo 结构里。

Logo

欢迎加入DeepSeek 技术社区。在这里,你可以找到志同道合的朋友,共同探索AI技术的奥秘。

更多推荐