本文档面向已经熟悉 OpenAI / Anthropic 原生 API、第一次接 AWS Bedrock 的开发者。它会把 Bedrock 与其它两家协议的差异讲清楚,帮你避开本项目接入过程中实际踩过的坑。
文档分为四部分:
- Bedrock 是什么 / 它跟"调 Claude"的区别
- 关键概念:双端点、双 ID 体系、双鉴权方式
- 五类核心 API(列模型 / 列 inference profile / 调用 / 流式 / 控制面 vs 运行面)
- 与 OpenAI、Anthropic 原生协议的逐项对比
1. Bedrock 是什么
Bedrock 是 AWS 的"统一大模型网关"。
- Anthropic 原生 API(
api.anthropic.com):Anthropic 自己的服务,直接卖 Claude。 - OpenAI API(
api.openai.com):OpenAI 自己的服务,卖 GPT 系列。 - AWS Bedrock(
bedrock-runtime.{region}.amazonaws.com):AWS 把 Anthropic / Meta / Mistral / Amazon 自家 Titan / Stability / AI21 等多家模型统一打包,通过 AWS 自己的网关、计费、IAM、合规体系卖给你。
所以"在 Bedrock 上调 Claude"和"直接调 Anthropic 官方 API"得到的模型本身是同一个,但接入协议、鉴权、计费、配额、可用模型版本、合规属性全部不同。Bedrock 走的是 AWS 的"AWS Service API"风格,跟 S3 / DynamoDB 同一套规矩。
一句话总结:Bedrock 是 AWS 风格的 API,外形像 S3,里面装的是 Claude / Llama。
2. 关键概念
2.1 两个端点(host)—— 别搞混
Bedrock 有两个完全不同的域名,对应两类操作:
| 用途 | 域名 | 协议风格 | 例子 |
|---|---|---|---|
| 控制面(管理 / 元数据) | bedrock.{region}.amazonaws.com |
AWS Service JSON | 列模型、列 inference profile、查配额、置 guardrail |
| 运行面(真的发对话请求) | bedrock-runtime.{region}.amazonaws.com |
REST 调用 | invoke 模型、流式输出 |
⚠️ 本项目踩过的坑:把
bedrock.us-east-1.amazonaws.com填进了"API 地址"字段,然后用它去 invoke。结果 AWS 网关返回 HTTP 200 +{"Output": {"__type": "...UnknownOperationException"}, "Version": "1.0"}—— 状态码是 200 但实际失败。记住:runtime 域名一定带-runtime。
2.2 两套模型 ID —— 也别搞混
这是 Bedrock 最反直觉的地方。同一个 Claude 模型,AWS 给你两个 ID:
Foundation model ID(裸 ID)
形如:anthropic.claude-sonnet-4-6、anthropic.claude-3-5-sonnet-20241022-v2:0、meta.llama3-3-70b-instruct-v1:0。
- 由
ListFoundationModelsAPI 返回。 - 代表"这个模型存在"。
- 不一定能直接 invoke。
- 老一点 / 不紧俏的模型(Claude 3 Haiku、Llama 3.2)可以拿它直接 invoke(叫 "on-demand throughput")。
- 新 / 紧俏模型(Claude 4 全系列、Claude 3.7、Sonnet 4.5)禁用直接 invoke,必须走 inference profile。
Inference profile ID(带前缀)
形如:us.anthropic.claude-sonnet-4-6、global.anthropic.claude-sonnet-4-6、eu.anthropic.claude-3-5-sonnet-20240620-v1:0。
- 由
ListInferenceProfilesAPI 返回。 - 把一个 foundation model "路由 + 多 region 容量池"起来。
- 前缀是 region 池:
us.*→ us-east-1 / us-east-2 / us-west-2 之间自动调度eu.*→ 欧洲多 region 池apac.*→ 亚太多 region 池global.*→ 全球池(最大可用容量,最高延迟变化)
- 必须用这个 ID 去 invoke 才能调通 Claude 4 系列。
怎么判断该用哪个
看 invoke 时报不报这条错:
Invocation of model ID ... with on-demand throughput isn't supported.
Retry your request with the ID or ARN of an inference profile that contains this model.
报了 → 找一个匹配该 foundation 的 profile(前缀加 us. 不一定对,得查 ListInferenceProfiles,profile ID 不是简单加前缀,例如 apac.amazon.nova-micro-v1:0 才是真名)。
2.3 两套鉴权方式
Bedrock 鉴权有两条路,任选其一:
a. SigV4(IAM AK/SK)—— AWS 标准方式
- 你有 IAM 用户的 access_key_id + secret_access_key。
- 每个请求都用 AWS Signature V4 算法签名(
Authorization: AWS4-HMAC-SHA256 Credential=AK.../...)。 - 支持 STS 临时凭证(额外加
X-Amz-Security-Token头)。 - 优点:和 AWS 其它服务统一,可走 IAM Role / 跨账户委托 / VPC 内网。
- 缺点:客户端必须自己实现 SigV4,不是简单的
Bearer xxx。
本项目实现:
backend/app/infrastructure/proxy/aws_signing.py是纯 stdlib 手写 SigV4,不依赖 boto3。
b. Bedrock API Key(ABSK)—— 简化的 Bearer 模式
- AWS 在 2024 年底推出的简化方式。
- 在 Bedrock console 生成一个短时令牌(叫 ABSK,长得像
ABSKQmVkcm9ja0FQSUtleS0...)。 - 直接用
Authorization: Bearer ABSK...就能调用。 - 控制面 / 运行面都支持。
- 优点:客户端逻辑跟调 OpenAI 一模一样,零迁移成本。
- 缺点:是短期 token(默认 12 小时到 24 小时过期,AWS 在轮转),不适合长期跑的服务;权限粒度也不如 IAM Role 细。
两种方式覆盖的能力
| 能力 | SigV4 | ABSK (Bearer) |
|---|---|---|
ListFoundationModels |
✅ | ✅ |
ListInferenceProfiles |
✅ | ✅ |
| invoke / invoke-with-response-stream | ✅ | ✅ |
| Knowledge Base / Agent runtime | ✅ | ✅ |
| 复杂 IAM 策略(按模型 / 按 region 限权) | ✅ | 部分 |
项目里两条路都已经接好。在 /config 编辑 Bedrock 渠道时切换"鉴权方式"即可。
3. 五类核心 API
下面所有例子都假定 us-east-1,{ABSK} 是 Bedrock API Key。SigV4 替换鉴权头即可。
3.1 列出 foundation models
GET https://bedrock.us-east-1.amazonaws.com/foundation-models
Authorization: Bearer {ABSK}
Accept: application/json
返回(节选):
{
"modelSummaries": [
{
"modelId": "anthropic.claude-sonnet-4-6",
"modelArn": "arn:aws:bedrock:us-east-1::foundation-model/anthropic.claude-sonnet-4-6",
"modelName": "Claude Sonnet 4.6",
"providerName": "Anthropic",
"inputModalities": ["TEXT", "IMAGE"],
"outputModalities": ["TEXT"],
"responseStreamingSupported": true,
"inferenceTypesSupported": ["INFERENCE_PROFILE"],
"modelLifecycle": { "status": "ACTIVE" }
}
]
}
注意 inferenceTypesSupported:
["ON_DEMAND"]→ 可以拿 modelId 直接 invoke["INFERENCE_PROFILE"]→ 只能通过 inference profile invoke["ON_DEMAND", "INFERENCE_PROFILE"]→ 两种都行
3.2 列出 inference profiles
GET https://bedrock.us-east-1.amazonaws.com/inference-profiles?typeEquals=SYSTEM_DEFINED
Authorization: Bearer {ABSK}
返回(节选):
{
"inferenceProfileSummaries": [
{
"inferenceProfileId": "us.anthropic.claude-sonnet-4-6",
"inferenceProfileArn": "arn:aws:bedrock:us-east-1:320039031994:inference-profile/us.anthropic.claude-sonnet-4-6",
"inferenceProfileName": "US Anthropic Claude Sonnet 4.6",
"models": [
{ "modelArn": "arn:aws:bedrock:us-east-1::foundation-model/anthropic.claude-sonnet-4-6" },
{ "modelArn": "arn:aws:bedrock:us-east-2::foundation-model/anthropic.claude-sonnet-4-6" },
{ "modelArn": "arn:aws:bedrock:us-west-2::foundation-model/anthropic.claude-sonnet-4-6" }
],
"type": "SYSTEM_DEFINED",
"status": "ACTIVE"
}
]
}
SYSTEM_DEFINED 是 AWS 预置的;还有 APPLICATION_INFERENCE_PROFILE 是用户自己建的(用于细粒度成本归集)。
本项目"获取模型"按钮就是把这两个 API 的结果合并返回给前端:profile ID 排前面,foundation ID 排后面。
3.3 调用模型(非流式)
POST https://bedrock-runtime.us-east-1.amazonaws.com/model/{model-or-profile-id}/invoke
Authorization: Bearer {ABSK}
Content-Type: application/json
{
"anthropic_version": "bedrock-2023-05-31",
"messages": [
{ "role": "user", "content": "Hi" }
],
"max_tokens": 100
}
注意:
- model ID 在 URL 里,不在 body 里(跟 OpenAI / Anthropic 完全相反)
- body 是 Anthropic Messages API 格式 + 一个新字段
anthropic_version anthropic_version必须是"bedrock-2023-05-31"(魔法字符串,AWS 自定义)- 没有
model字段 —— 强行写也会被忽略 - 没有
stream字段 —— 流式靠下面 3.4 的不同 URL
返回(节选):
{
"id": "msg_bdrk_01ABC...",
"type": "message",
"role": "assistant",
"model": "claude-sonnet-4-6",
"content": [{ "type": "text", "text": "Hi! How can I help?" }],
"stop_reason": "end_turn",
"usage": {
"input_tokens": 8,
"output_tokens": 12
}
}
响应 body 跟 Anthropic 原生几乎一样。响应头里有 AWS 特有的 x-amzn-requestid,本项目用它做请求追踪。
3.4 调用模型(流式)
只改一处:URL 后缀换成 invoke-with-response-stream。
POST https://bedrock-runtime.us-east-1.amazonaws.com/model/{model-or-profile-id}/invoke-with-response-stream
body 不变。响应不是 SSE,是 AWS Event Stream(application/vnd.amazon.eventstream)—— 一种二进制帧格式。
每个事件帧解码后 payload 是一个 base64 字符串,再 base64 decode 才是 Anthropic 风格的 JSON:
{"type":"message_start","message":{...}}
{"type":"content_block_delta","delta":{"type":"text_delta","text":"Hi"}}
{"type":"content_block_delta","delta":{"type":"text_delta","text":"!"}}
{"type":"message_delta","usage":{"output_tokens":2}}
{"type":"message_stop"}
事件类型跟 Anthropic SSE 流一致,区别只在外面那层封装协议。
本项目走"透明转发"路线:BedrockProxy 把二进制流原样转给客户端,由客户端 SDK(boto3 或自写解析器)处理。优点是不丢任何字段、零额外延迟;缺点是客户端必须懂 Event Stream 协议(boto3 帮你处理掉了,自己写要有
aws-event-stream-python之类的库)。
3.5 控制面 vs 运行面 —— 一张图记牢
你的请求
│
┌─────────────────┴──────────────────┐
▼ ▼
┌─────────────────────────┐ ┌────────────────────────────────┐
│ bedrock.{region} │ │ bedrock-runtime.{region} │
│ 控制面(管理 API) │ │ 运行面(推理 API) │
│ AWS Service JSON 风格 │ │ REST 风格 │
└─────────────────────────┘ └────────────────────────────────┘
│ │
列模型 / 列 profile / Guardrail invoke / invoke-stream
Knowledge Base 管理 Converse / Converse Stream
⚠️ 把 invoke 打到控制面 → 状态码 200 + UnknownOperationException
4. 与 OpenAI / Anthropic 原生协议的对比
按"日常用得最多的几条线"逐项对比。
4.1 模型列表
| 项 | OpenAI | Anthropic | Bedrock |
|---|---|---|---|
| 端点 | GET /v1/models |
GET /v1/models |
GET /foundation-models + GET /inference-profiles |
| 域名 | api 主机本身 | api 主机本身 | 控制面主机(不是 runtime) |
| 返回结构 | {"data": [{"id":"gpt-4o", ...}]} |
{"data": [{"id":"claude-...","display_name":...}]} |
{"modelSummaries":[{"modelId":..., "providerName":..., "inferenceTypesSupported":...}]} |
| 是否分两套 ID | 否 | 否 | 是(foundation + inference profile) |
| 是否要鉴权 | 是 | 是 | 是(连"列模型"也要) |
关键差异:
- OpenAI / Anthropic:列出来的 ID 就是 invoke 的 ID,无脑用。
- Bedrock:foundation ID 不一定能 invoke,要自己判断
inferenceTypesSupported,新模型基本要走 profile ID。
4.2 对话调用
| 项 | OpenAI | Anthropic | Bedrock |
|---|---|---|---|
| 端点 | POST /v1/chat/completions |
POST /v1/messages |
POST /model/{id}/invoke |
| model 在哪 | body 里 "model": "gpt-4o" |
body 里 "model": "claude-..." |
URL path 里,body 里没有 |
| 鉴权 header | Authorization: Bearer sk-... |
x-api-key: sk-ant-... |
Authorization: Bearer ABSK... 或 SigV4 |
| body 风格 | OpenAI Chat Completions | Anthropic Messages | Anthropic Messages + anthropic_version |
| 必填特殊字段 | 无 | max_tokens |
max_tokens、anthropic_version: "bedrock-2023-05-31" |
| stream 怎么开 | body 里 "stream": true |
body 里 "stream": true |
换 URL:尾巴加 -with-response-stream |
| stream 协议 | SSE (text/event-stream) |
SSE | AWS Event Stream(二进制) |
| 响应 body | OpenAI choices 风格 | Anthropic content blocks 风格 | 跟 Anthropic 一样 |
| Request ID 头 | x-request-id、openai-request-id |
request-id、anthropic-request-id |
x-amzn-requestid |
| Token 用量 | usage.prompt_tokens / completion_tokens |
usage.input_tokens / output_tokens |
跟 Anthropic 一样 |
口诀:
- Bedrock invoke = "拿 Anthropic 的 body,按 AWS 的方式塞 URL 和签名"
- 想象一下"Anthropic API 长得像 REST,被 AWS 套了一层壳",差不多就是这个感觉
4.3 流式协议
| 项 | OpenAI / Anthropic SSE | Bedrock Event Stream |
|---|---|---|
| Content-Type | text/event-stream |
application/vnd.amazon.eventstream |
| 帧格式 | 文本 data: {json}\n\ndata: [DONE]\n\n |
二进制:12B 前缀 + headers + payload + 4B CRC |
| 客户端解析 | 字符串 split | 需要专用库(botocore.eventstream / aws-event-stream-python) |
| 失败时是否 SSE 友好 | 是(错误事件也是 data:) |
不是(二进制错误帧) |
| payload 内容 | 同非流式(chunk 是 delta) | 同 Anthropic 流(content_block_delta 等) |
4.4 错误反馈风格
| 协议 | 失败时 |
|---|---|
| OpenAI | HTTP 4xx/5xx + {"error":{"message":..., "type":..., "code":...}} |
| Anthropic | HTTP 4xx/5xx + {"type":"error","error":{"type":..., "message":...}} |
| Bedrock | 可能 200 + 错误 payload({"Output":{"__type":"...Exception"},"Version":"1.0"}),也可能 4xx + {"message":"..."},还会用 x-amzn-errortype 头标错误类 |
所以 Bedrock 不能只看 HTTP 状态码判定成功失败,还要看 body 有没有
__type或message。
4.5 鉴权方式
| 协议 | 鉴权 | 凭证生命周期 | 难度 |
|---|---|---|---|
| OpenAI | Authorization: Bearer sk-... |
长期(手动转 key) | 极简 |
| Anthropic | x-api-key: sk-ant-... |
长期 | 极简 |
| Bedrock SigV4 | Authorization: AWS4-HMAC-SHA256 Credential=... + 一堆 amz-* 头 |
长期 IAM 或短期 STS | 高(每个请求重签) |
| Bedrock ABSK | Authorization: Bearer ABSK... |
短期(小时级,AWS 自动轮转) | 极简 |
ABSK 是 AWS 给"想用 OpenAI 体验的人"开的方便门。生产环境如果想要细粒度 IAM 策略,还是得用 SigV4。
4.6 计费
| 协议 | 计费方 | 维度 |
|---|---|---|
| OpenAI | OpenAI 直接出账 | 按 token,模型分级 |
| Anthropic | Anthropic 直接出账 | 按 token,模型分级 |
| Bedrock | AWS 出账(合并到你的 AWS bill) | 按 token,同模型 Bedrock 价 ≈ 原厂价(个别可能 +5%~10%) |
Bedrock 主要卖点不是便宜,是"已经在 AWS 体系里":
- 单一发票 / 单一 IAM / 单一 VPC 内网 / 合规审计统一
- 适合大企业,对个人开发者吸引力不大
5. 速查清单(避坑)
接 Bedrock 时按这个 checklist 自检:
- [ ]
base_url用bedrock-runtime.{region}.amazonaws.com,不是bedrock.{region}.amazonaws.com - [ ] model 字段填的是 inference profile ID(带
us./global./eu./apac.前缀),不是裸 foundation ID(除非这模型支持 ON_DEMAND) - [ ] body 里有
"anthropic_version": "bedrock-2023-05-31" - [ ] body 里没有
"model"字段(在 URL 里) - [ ] body 里有
"max_tokens"(必填) - [ ] 流式:URL 用
invoke-with-response-stream后缀,不要在 body 里写"stream": true - [ ] 解析响应时同时检查 HTTP 状态码 + body 的
__type/message字段 - [ ] ABSK 是短期 token,定期到 console 续期;生产用 SigV4 + IAM Role 更稳
- [ ] 同一个 Claude 模型在不同 region 的可用性 / inference profile 名字不同,跨 region 切换前先重列 profile
6. 本项目的实现位置
| 关注点 | 文件 |
|---|---|
| Bedrock 实例配置 schema | backend/app/core/config.py BedrockInstance |
| SigV4 签名(纯 stdlib 实现) | backend/app/infrastructure/proxy/aws_signing.py |
透明代理 + /api/chat → invoke 转换 |
backend/app/infrastructure/proxy/llm_proxy.py BedrockProxy |
| 健康检查 | backend/app/infrastructure/health/checker.py check_bedrock_instance |
| 列模型(控制面,foundation + profile 合并) | backend/app/application/services/admin_checks.py check_adhoc_instance_health 的 bedrock 分支 |
| 测试对话(含 SigV4 签名) | backend/app/application/services/admin_checks.py _build_chat_test_request |
| 配置保存(双鉴权切换、控制面 host 拒绝) | backend/app/application/services/admin_config.py |
| 前端配置表单 | frontend/src/app/config/page.tsx、frontend/src/app/chat-config/page.tsx |
