主题
Bedrock Converse API
Amazon Bedrock Converse API 为对话式 AI 模型提供了一个统一接口,带有增强的能力,包括函数/工具调用、多模态输入和流式响应。
Bedrock Converse API 有以下高层特性:
- 工具/函数调用:在对话期间支持函数定义与工具使用
- 多模态输入:能在对话中同时处理文本与图像输入
- 流式支持:模型响应的实时流式
- 系统消息:支持系统级指令与上下文设定
TIP
Bedrock Converse API 在多个模型服务商之间提供统一接口,同时处理 AWS 特定的认证与基础设施事务。
目前,Converse API 受支持的模型包括: Amazon Titan、Amazon Nova、AI21 Labs、Anthropic Claude、Cohere Command、Meta Llama、Mistral AI。
NOTE
遵循 Bedrock 的建议,Spring AI 正把所有聊天对话实现迁移到使用 Amazon Bedrock 的 Converse API。 尽管现有的 InvokeModel API 支持对话应用,我们强烈建议对所有聊天对话模型采用 Converse API。 Converse API 不支持嵌入操作,因此这些会留在当前 API 中,现有 InvokeModel API 中的嵌入模型功能将被维护
前置条件
关于设置 API 访问,参阅 Amazon Bedrock 入门
获取 AWS 凭据:如果你还没有 AWS 账户和配置好的 AWS CLI,这个视频指南能帮你完成配置:AWS CLI & SDK Setup in Less Than 4 Minutes!。你将能获取你的访问密钥与安全密钥。
启用要使用的模型:前往 Amazon Bedrock,从左侧的 Model Access 菜单,配置对你将要使用模型的访问。
自动配置
NOTE
Spring AI 的自动配置、starter 模块的制品名称发生了重大变更。 更多信息请参阅 升级说明。
把 spring-ai-starter-model-bedrock-converse 依赖添加到你项目的 Maven pom.xml 或 Gradle build.gradle 构建文件:
xml
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-starter-model-bedrock-converse</artifactId>
</dependency>TIP
参见 依赖管理 一节,把 Spring AI BOM 添加到你的构建文件。
聊天属性
前缀 spring.ai.bedrock.aws 是配置到 AWS Bedrock 连接的属性前缀。
| 属性 | 描述 | 默认值 |
|---|---|---|
| spring.ai.bedrock.aws.region | 要使用的 AWS 区域 | us-east-1 |
| spring.ai.bedrock.aws.timeout | 整个 API 调用的 AWS 最大时长 | 5m |
| spring.ai.bedrock.aws.connection-timeout | 建立连接时等待的最大时长 | 5s |
| spring.ai.bedrock.aws.connection-acquisition-timeout | 从池中等待新连接的最大时长 | 30s |
| spring.ai.bedrock.aws.async-read-timeout | 读取异步响应花的最大时长 | 30s |
| spring.ai.bedrock.aws.access-key | AWS 访问密钥 | - |
| spring.ai.bedrock.aws.secret-key | AWS 密钥 | - |
| spring.ai.bedrock.aws.session-token | 临时凭据的 AWS 会话令牌 | - |
| spring.ai.bedrock.aws.profile.name | AWS 配置档名。 | - |
| spring.ai.bedrock.aws.profile.credentials-path | AWS 凭据文件路径。 | - |
| spring.ai.bedrock.aws.profile.configuration-path | AWS 配置文件路径。 | - |
NOTE
聊天自动配置的启用与禁用,现通过以 spring.ai.model.chat 为前缀的顶层属性来配置。
要启用,spring.ai.model.chat=bedrock-converse(默认启用)
要禁用,spring.ai.model.chat=none(或任何不匹配 bedrock-converse 的值)
做出此变更是为了支持配置多个模型。
前缀 spring.ai.bedrock.converse.chat 是为 Converse API 配置聊天模型实现的属性前缀。
| 属性 | 描述 | 默认值 |
|---|---|---|
| spring.ai.bedrock.converse.chat.enabled(已移除,不再生效) | 启用 Bedrock Converse 聊天模型。 | true |
| spring.ai.model.chat | 启用 Bedrock Converse 聊天模型。 | bedrock-converse |
| spring.ai.bedrock.converse.chat.model | 要使用的模型 ID。你可以使用受支持的模型与模型特性 | 无。从 AWS Bedrock 控制台选择你的 modelId。 |
| spring.ai.bedrock.converse.chat.temperature | 控制输出的随机性。取值范围 [0.0,1.0] | 0.8 |
| spring.ai.bedrock.converse.chat.top-p | 采样时要考虑令牌的累计概率最大值。 | AWS Bedrock 默认 |
| spring.ai.bedrock.converse.chat.top-k | 生成下一个令牌时的令牌候选数。 | AWS Bedrock 默认 |
| spring.ai.bedrock.converse.chat.max-tokens | 生成响应中的最大令牌数。 | 500 |
运行时选项
使用可移植的 ChatOptions 或 BedrockChatOptions 可移植构建器来创建模型配置,例如 temperature、maxToken、topP 等。
在启动时,可通过 BedrockConverseProxyChatModel(api, options) 构造器或 spring.ai.bedrock.converse.chat.* 属性配置默认选项。
在运行时,你可以通过向 Prompt 调用添加新的、请求特定的选项来覆盖默认选项:
java
var options = BedrockChatOptions.builder()
.model("us.anthropic.claude-haiku-4-5-20251001-v1:0")
.temperature(0.6)
.maxTokens(300)
.toolCallbacks(List.of(FunctionToolCallback.builder("getCurrentWeather", new WeatherService())
.description("Get the weather in location. Return temperature in 36°F or 36°C format. Use multi-turn if needed.")
.inputType(WeatherService.Request.class)
.build()))
.build();
String response = ChatClient.create(this.chatModel)
.prompt("What is current weather in Amsterdam?")
.options(options)
.call()
.content();提示词缓存
AWS Bedrock 的提示词缓存特性让你能缓存常用提示词,以在重复交互中降低成本并改善响应时间。 当你缓存一个提示词后,后续相同的请求可以复用被缓存的内容,显著减少被处理的输入令牌数。
NOTE
受支持的模型 提示词缓存在通过 AWS Bedrock 提供的 Claude 3.x、Claude 4.x 和 Amazon Nova 模型上受支持。 令牌要求 不同模型对缓存有效性有不同的最小令牌阈值:
- Claude Sonnet 4 及多数模型:1024+ 令牌
- 模型特定的要求可能不同 - 请查阅 AWS Bedrock 文档
缓存策略
Spring AI 通过 BedrockCacheStrategy 枚举提供战略性缓存放置:
NONE:完全禁用提示词缓存(默认)SYSTEM_ONLY:只缓存系统消息内容TOOLS_ONLY:只缓存工具定义(仅 Claude 模型)SYSTEM_AND_TOOLS:同时缓存系统消息和工具定义(仅 Claude 模型)CONVERSATION_HISTORY:在聊天记忆场景中缓存整个对话历史
这种战略性方式确保在不超过 AWS Bedrock 4 断点限制的前提下,实现最优的缓存断点放置。
NOTE
Amazon Nova 限制 Amazon Nova 模型(Nova Micro、Lite、Pro、Premier)只支持对 system 和 messages 内容做缓存。 它们不支持对 tools 做缓存。 如果你尝试对 Nova 模型使用 TOOLS_ONLY 或 SYSTEM_AND_TOOLS 策略,AWS 会返回一个 ValidationException。 对 Amazon Nova 模型请使用 SYSTEM_ONLY 策略。
启用提示词缓存
通过在 BedrockChatOptions 上设置 cacheOptions 并选择一个 strategy 来启用提示词缓存。
仅系统缓存
最常见的用例——在多个请求间缓存系统指令:
java
// Cache system message content
ChatResponse response = chatModel.call(
new Prompt(
List.of(
new SystemMessage("You are a helpful AI assistant with extensive knowledge..."),
new UserMessage("What is machine learning?")
),
BedrockChatOptions.builder()
.model("us.anthropic.claude-haiku-4-5-20251001-v1:0")
.cacheOptions(BedrockCacheOptions.builder()
.strategy(BedrockCacheStrategy.SYSTEM_ONLY)
.build())
.maxTokens(500)
.build()
)
);仅工具缓存
在保持系统提示词动态的同时缓存大型工具定义(仅 Claude 模型):
java
// Cache tool definitions only
ChatResponse response = chatModel.call(
new Prompt(
"What's the weather in San Francisco?",
BedrockChatOptions.builder()
.model("us.anthropic.claude-haiku-4-5-20251001-v1:0")
.cacheOptions(BedrockCacheOptions.builder()
.strategy(BedrockCacheStrategy.TOOLS_ONLY)
.build())
.toolCallbacks(weatherToolCallbacks) // Large tool definitions
.maxTokens(500)
.build()
)
);NOTE
此策略只在 Claude 模型上受支持。
Amazon Nova 模型会返回一个 ValidationException。
系统与工具缓存
同时缓存系统指令和工具定义以获得最大复用(仅 Claude 模型):
java
// Cache system message and tool definitions
ChatResponse response = chatModel.call(
new Prompt(
List.of(
new SystemMessage("You are a weather analysis assistant..."),
new UserMessage("What's the weather like in Tokyo?")
),
BedrockChatOptions.builder()
.model("us.anthropic.claude-haiku-4-5-20251001-v1:0")
.cacheOptions(BedrockCacheOptions.builder()
.strategy(BedrockCacheStrategy.SYSTEM_AND_TOOLS)
.build())
.toolCallbacks(weatherToolCallbacks)
.maxTokens(500)
.build()
)
);NOTE
此策略使用 2 个缓存断点(一个用于工具,一个用于系统)。
只在 Claude 模型上受支持。
对话历史缓存
为多轮聊天机器人和助手缓存不断增长对话历史:
java
// Cache conversation history with ChatClient and memory
ChatClient chatClient = ChatClient.builder(chatModel)
.defaultSystem("You are a personalized career counselor...")
.defaultAdvisors(MessageChatMemoryAdvisor.builder(chatMemory).build())
.build();
String response = chatClient.prompt()
.user("What career advice would you give me?")
.advisors(a -> a.param(ChatMemory.CONVERSATION_ID, conversationId))
.options(BedrockChatOptions.builder()
.model("us.anthropic.claude-haiku-4-5-20251001-v1:0")
.cacheOptions(BedrockCacheOptions.builder()
.strategy(BedrockCacheStrategy.CONVERSATION_HISTORY)
.build())
.maxTokens(500)
.build())
.call()
.content();多块系统消息缓存
当使用顾问(例如 RAG 上下文注入)时,你的系统提示词可能由多个 SystemMessage 实例组成:一个静态前缀(你的基础指令)和一个动态后缀(每次请求都在变的顾问注入上下文)。
默认情况下,当启用 SYSTEM_ONLY 或 SYSTEM_AND_TOOLS 缓存时,每个 SystemMessage 被发出为一个独立的 SystemContentBlock,缓存点被放在最后一个块之后。 如果该内容在任何部分在请求间发生变化,整个系统缓存就被作废——导致每个请求产生 1.25x 的缓存写入成本,而非便宜得多的缓存读取。
multiBlockSystemCaching 选项通过把缓存点放在倒数第二个系统块之后(即在静态前缀与动态后缀之间的边界)来解决这一点,从而即便最后一个块自由变化,静态部分仍保持被缓存。
java
BedrockCacheOptions cacheOptions = BedrockCacheOptions.builder()
.strategy(BedrockCacheStrategy.SYSTEM_ONLY)
.multiBlockSystemCaching(true)
.ttl(BedrockCacheTtl.ONE_HOUR)
.build();
BedrockChatOptions chatOptions = BedrockChatOptions.builder()
.model("us.anthropic.claude-haiku-4-5-20251001-v1:0")
.cacheOptions(cacheOptions)
.build();
// Static system prompt (will be cached)
SystemMessage staticPrompt = new SystemMessage("You are an expert assistant...");
// Dynamic RAG context (injected by advisor, changes each request)
SystemMessage dynamicContext = new SystemMessage("Relevant context: " + ragResults);
Prompt prompt = new Prompt(List.of(staticPrompt, dynamicContext, new UserMessage("...")), chatOptions);这会产生一个形如 [text(static), cachePoint, text(dynamic)] 的 Bedrock system 数组,因此只有静态前缀参与缓存键。
NOTE
如果只存在单个 SystemMessage,缓存点被放在那一个块之后(与默认行为相同)。
multiBlockSystemCaching 选项只在存在两个或更多系统消息时才改变放置。
WARNING
消息顺序很重要。缓存点始终被放在倒数第二个系统块之后,因此静态内容必须排在消息列表中动态内容之前。如果你把基础指令注册为第一个 SystemMessage 并让顾问在其后追加动态上下文,这一顺序自然得到满足。
使用 ChatClient 流式 API
java
String response = ChatClient.create(chatModel)
.prompt()
.system("You are an expert document analyst...")
.user("Analyze this large document: " + document)
.options(BedrockChatOptions.builder()
.model("us.anthropic.claude-haiku-4-5-20251001-v1:0")
.cacheOptions(BedrockCacheOptions.builder()
.strategy(BedrockCacheStrategy.SYSTEM_ONLY)
.build())
.build())
.call()
.content();用法示例
下面是一个演示带成本跟踪的提示词缓存的完整示例:
java
// Create system content that will be reused multiple times
String largeSystemPrompt = "You are an expert software architect specializing in distributed systems...";
// (Ensure this is 1024+ tokens for cache effectiveness)
// First request - creates cache
ChatResponse firstResponse = chatModel.call(
new Prompt(
List.of(
new SystemMessage(largeSystemPrompt),
new UserMessage("What is microservices architecture?")
),
BedrockChatOptions.builder()
.model("us.anthropic.claude-haiku-4-5-20251001-v1:0")
.cacheOptions(BedrockCacheOptions.builder()
.strategy(BedrockCacheStrategy.SYSTEM_ONLY)
.build())
.maxTokens(500)
.build()
)
);
// Access cache-related token usage from metadata
Integer cacheWrite1 = (Integer) firstResponse.getMetadata()
.getMetadata()
.get("cacheWriteInputTokens");
Integer cacheRead1 = (Integer) firstResponse.getMetadata()
.getMetadata()
.get("cacheReadInputTokens");
System.out.println("Cache creation tokens: " + cacheWrite1);
System.out.println("Cache read tokens: " + cacheRead1);
// Second request with same system prompt - reads from cache
ChatResponse secondResponse = chatModel.call(
new Prompt(
List.of(
new SystemMessage(largeSystemPrompt), // Same prompt - cache hit
new UserMessage("What are the benefits of event sourcing?")
),
BedrockChatOptions.builder()
.model("us.anthropic.claude-haiku-4-5-20251001-v1:0")
.cacheOptions(BedrockCacheOptions.builder()
.strategy(BedrockCacheStrategy.SYSTEM_ONLY)
.build())
.maxTokens(500)
.build()
)
);
Integer cacheWrite2 = (Integer) secondResponse.getMetadata()
.getMetadata()
.get("cacheWriteInputTokens");
Integer cacheRead2 = (Integer) secondResponse.getMetadata()
.getMetadata()
.get("cacheReadInputTokens");
System.out.println("Cache creation tokens: " + cacheWrite2); // Should be 0
System.out.println("Cache read tokens: " + cacheRead2); // Should be > 0令牌用量跟踪
AWS Bedrock 通过响应提供缓存特定的指标。缓存指标可通过两种方式访问:
原生 Usage 对象(推荐用于可观测性)
对于可观测性处理器和指标收集,通过原生 TokenUsage 对象访问缓存指标:
java
import software.amazon.awssdk.services.bedrockruntime.model.TokenUsage;
ChatResponse response = chatModel.call(/* ... */);
// Access cache metrics from native TokenUsage object
TokenUsage tokenUsage = (TokenUsage) response.getMetadata()
.getUsage()
.getNativeUsage();
if (tokenUsage != null) {
Integer cacheWrite = tokenUsage.cacheWriteInputTokens();
Integer cacheRead = tokenUsage.cacheReadInputTokens();
System.out.println("Cache write: " + cacheWrite + ", Cache read: " + cacheRead);
}元数据 Map(向后兼容)
为向后兼容,缓存指标也可通过元数据 Map 获得:
java
ChatResponse response = chatModel.call(/* ... */);
// Access cache metrics from metadata Map
Integer cacheWrite = (Integer) response.getMetadata()
.getMetadata()
.get("cacheWriteInputTokens");
Integer cacheRead = (Integer) response.getMetadata()
.getMetadata()
.get("cacheReadInputTokens");缓存特定的指标包括:
cacheWriteInputTokens:返回创建缓存条目时使用的令牌数cacheReadInputTokens:返回从既有缓存条目读取的令牌数
当你首次发送一个被缓存的提示词时:
cacheWriteInputTokens会大于 0cacheReadInputTokens会是 0
当你(在 5 分钟 TTL 内)再次发送同一个被缓存的提示词时:
cacheWriteInputTokens会是 0cacheReadInputTokens会大于 0
真实世界用例
法律文档分析
通过在多个问题间缓存文档内容,高效分析大型法律合同或合规文档:
java
// Load a legal contract (PDF or text)
String legalContract = loadDocument("merger-agreement.pdf"); // ~3000 tokens
// System prompt with legal expertise
String legalSystemPrompt = "You are an expert legal analyst specializing in corporate law. " +
"Analyze the following contract and provide precise answers about terms, obligations, and risks: " +
legalContract;
// First analysis - creates cache
ChatResponse riskAnalysis = chatModel.call(
new Prompt(
List.of(
new SystemMessage(legalSystemPrompt),
new UserMessage("What are the key termination clauses and associated penalties?")
),
BedrockChatOptions.builder()
.model("us.anthropic.claude-haiku-4-5-20251001-v1:0")
.cacheOptions(BedrockCacheOptions.builder()
.strategy(BedrockCacheStrategy.SYSTEM_ONLY)
.build())
.maxTokens(1000)
.build()
)
);
// Subsequent questions reuse cached document - 90% cost savings
ChatResponse obligationAnalysis = chatModel.call(
new Prompt(
List.of(
new SystemMessage(legalSystemPrompt), // Same content - cache hit
new UserMessage("List all financial obligations and payment schedules.")
),
BedrockChatOptions.builder()
.model("us.anthropic.claude-haiku-4-5-20251001-v1:0")
.cacheOptions(BedrockCacheOptions.builder()
.strategy(BedrockCacheStrategy.SYSTEM_ONLY)
.build())
.maxTokens(1000)
.build()
)
);批量代码评审
以一致的评审标准处理多个代码文件,同时缓存评审准则:
java
// Define comprehensive code review guidelines
String reviewGuidelines = """
You are a senior software engineer conducting code reviews. Apply these criteria:
- Security vulnerabilities and best practices
- Performance optimizations and memory usage
- Code maintainability and readability
- Testing coverage and edge cases
- Design patterns and architecture compliance
""";
List<String> codeFiles = Arrays.asList(
"UserService.java", "PaymentController.java", "SecurityConfig.java"
);
List<String> reviews = new ArrayList<>();
for (String filename : codeFiles) {
String sourceCode = loadSourceFile(filename);
ChatResponse review = chatModel.call(
new Prompt(
List.of(
new SystemMessage(reviewGuidelines), // Cached across all reviews
new UserMessage("Review this " + filename + " code:\n\n" + sourceCode)
),
BedrockChatOptions.builder()
.model("us.anthropic.claude-haiku-4-5-20251001-v1:0")
.cacheOptions(BedrockCacheOptions.builder()
.strategy(BedrockCacheStrategy.SYSTEM_ONLY)
.build())
.maxTokens(800)
.build()
)
);
reviews.add(review.getResult().getOutput().getText());
}
// Guidelines cached after first request, subsequent reviews are faster and cheaper带知识库的客户支持
创建一个缓存你产品知识库的客户支持系统,以获得一致、准确的响应:
java
// Load comprehensive product knowledge
String knowledgeBase = """
PRODUCT DOCUMENTATION:
- API endpoints and authentication methods
- Common troubleshooting procedures
- Billing and subscription details
- Integration guides and examples
- Known issues and workarounds
""" + loadProductDocs(); // ~2500 tokens
@Service
public class CustomerSupportService {
public String handleCustomerQuery(String customerQuery, String customerId) {
ChatResponse response = chatModel.call(
new Prompt(
List.of(
new SystemMessage("You are a helpful customer support agent. " +
"Use this knowledge base to provide accurate solutions: " + knowledgeBase),
new UserMessage("Customer " + customerId + " asks: " + customerQuery)
),
BedrockChatOptions.builder()
.model("us.anthropic.claude-haiku-4-5-20251001-v1:0")
.cacheOptions(BedrockCacheOptions.builder()
.strategy(BedrockCacheStrategy.SYSTEM_ONLY)
.build())
.maxTokens(600)
.build()
)
);
return response.getResult().getOutput().getText();
}
}
// Knowledge base is cached across all customer queries
// Multiple support agents can benefit from the same cached content多租户 SaaS 应用
在不同租户间缓存共享的工具定义,同时按租户定制系统提示词:
java
// Shared tool definitions (cached once, used across all tenants)
List<FunctionToolCallback> sharedTools = createLargeToolRegistry(); // ~2000 tokens
// Tenant-specific configuration
@Service
public class MultiTenantAIService {
public String processRequest(String tenantId, String userQuery) {
// Load tenant-specific system prompt (changes per tenant)
String tenantPrompt = loadTenantSystemPrompt(tenantId);
ChatResponse response = chatModel.call(
new Prompt(
List.of(
new SystemMessage(tenantPrompt), // Tenant-specific, not cached
new UserMessage(userQuery)
),
BedrockChatOptions.builder()
.model("us.anthropic.claude-haiku-4-5-20251001-v1:0")
.cacheOptions(BedrockCacheOptions.builder()
.strategy(BedrockCacheStrategy.TOOLS_ONLY)
.build())
.toolCallbacks(sharedTools) // Shared tools - cached
.maxTokens(500)
.build()
)
);
return response.getResult().getOutput().getText();
}
}
// Tools cached once, each tenant gets customized system prompt最佳实践
- 选择正确的策略:
- 对可复用的系统提示词和指令使用
SYSTEM_ONLY(对所有模型有效) - 当你有大型稳定的工具但系统提示词动态时使用
TOOLS_ONLY(仅 Claude) - 当系统和工具都大而稳定时使用
SYSTEM_AND_TOOLS(仅 Claude) - 配合 ChatClient 记忆,对多轮对话使用
CONVERSATION_HISTORY - 用
NONE显式禁用缓存
满足令牌要求:专注于缓存满足最小令牌要求的内容(多数模型 1024+ 令牌)。
复用完全相同的内容:缓存在提示词内容精确匹配时效果最佳。 即便是微小变化也会需要一个新的缓存条目。
监控令牌用量:使用元数据指标跟踪缓存有效性:
Integer cacheWrite = (Integer) response.getMetadata().getMetadata().get("cacheWriteInputTokens"); Integer cacheRead = (Integer) response.getMetadata().getMetadata().get("cacheReadInputTokens"); if (cacheRead != null && cacheRead > 0) { System.out.println("Cache hit: " + cacheRead + " tokens saved"); }
战略性缓存放置:实现会根据你所选的策略自动把缓存断点放在最优位置,确保符合 AWS Bedrock 的 4 断点限制。
缓存寿命:AWS Bedrock 缓存默认有 5 分钟 TTL(存活时间),但它们也支持 1 小时的 TTL 时长。 每次缓存访问都会重置计时器。这可以通过
BedrockCacheOptions上的BedrockCacheTtl或spring.ai.bedrock.converse.chat.cache-options.ttl属性来配置。模型兼容性:留意模型特定的限制:
- Claude 模型:支持所有缓存策略
- Amazon Nova 模型:只支持
SYSTEM_ONLY和CONVERSATION_HISTORY(不支持工具缓存)
- 工具稳定性:当使用
TOOLS_ONLY、SYSTEM_AND_TOOLS或CONVERSATION_HISTORY策略时,确保工具保持稳定。 由于级联作废,改变工具定义会作废所有下游的缓存断点。
缓存作废与级联行为
AWS Bedrock 遵循一个带级联作废的分层缓存模型:
缓存层次:Tools → System → Messages
在每一层发生的更改会作废该层及其后所有层:
| 更改内容 | 工具缓存 | 系统缓存 | 消息缓存 |
|---|---|---|---|
| 工具 | ❌ 无效 | ❌ 无效 | ❌ 无效 |
| 系统 | ✅ 有效 | ❌ 无效 | ❌ 无效 |
| 消息 | ✅ 有效 | ✅ 有效 | ❌ 无效 |
使用 SYSTEM_AND_TOOLS 策略的示例:
java
// Request 1: Cache both tools and system
ChatResponse r1 = chatModel.call(
new Prompt(
List.of(new SystemMessage("System prompt"), new UserMessage("Question")),
BedrockChatOptions.builder()
.cacheOptions(BedrockCacheOptions.builder()
.strategy(BedrockCacheStrategy.SYSTEM_AND_TOOLS)
.build())
.toolCallbacks(tools)
.build()
)
);
// Result: Both caches created
// Request 2: Change only system prompt (tools same)
ChatResponse r2 = chatModel.call(
new Prompt(
List.of(new SystemMessage("DIFFERENT system prompt"), new UserMessage("Question")),
BedrockChatOptions.builder()
.cacheOptions(BedrockCacheOptions.builder()
.strategy(BedrockCacheStrategy.SYSTEM_AND_TOOLS)
.build())
.toolCallbacks(tools) // SAME tools
.build()
)
);
// Result: Tools cache HIT (reused), system cache MISS (recreated)
// Request 3: Change tools (system same as Request 2)
ChatResponse r3 = chatModel.call(
new Prompt(
List.of(new SystemMessage("DIFFERENT system prompt"), new UserMessage("Question")),
BedrockChatOptions.builder()
.cacheOptions(BedrockCacheOptions.builder()
.strategy(BedrockCacheStrategy.SYSTEM_AND_TOOLS)
.build())
.toolCallbacks(newTools) // DIFFERENT tools
.build()
)
);
// Result: BOTH caches MISS (tools change invalidates everything downstream)实现细节
Spring AI 中的提示词缓存实现遵循以下关键设计原则:
战略性缓存放置:缓存断点根据所选策略被自动放在最优位置,确保符合 AWS Bedrock 的 4 断点限制。
服务商可移植性:缓存配置通过
BedrockChatOptions而非单条消息完成,在切换不同 AI 服务商时保持兼容。线程安全:缓存断点跟踪以线程安全的机制实现,以正确处理并发请求。
UNION 类型模式:AWS SDK 使用 UNION 类型,缓存点作为独立块而非属性添加。 这不同于直接 API 的方式,但确保了类型安全与 API 合规。
增量缓存:
CONVERSATION_HISTORY策略把缓存断点放在最后一个用户消息上,实现增量缓存,让每个对话回合都在先前缓存前缀之上构建。
成本考量
AWS Bedrock 对提示词缓存的定价(近似,因模型而异):
- 缓存写入:比基础输入令牌贵约 25%
- 缓存读取:便宜约 90%(只为基础输入令牌价格的 10%)
- 盈亏平衡点:仅一次缓存读取后你就已省钱
示例成本计算:
java
// System prompt: 2000 tokens
// User question: 50 tokens
// Without caching (5 requests):
// Cost: 5 × (2000 + 50) = 10,250 tokens at base rate
// With caching (5 requests):
// Request 1: 2000 tokens × 1.25 (cache write) + 50 = 2,550 tokens
// Requests 2-5: 4 × (2000 × 0.10 (cache read) + 50) = 4 × 250 = 1,000 tokens
// Total: 2,550 + 1,000 = 3,550 tokens equivalent
// Savings: (10,250 - 3,550) / 10,250 = 65% cost reduction工具调用
BedrockProxyChatModel 支持工具调用。模型可以请求执行工具,但它自己并不运行工具——由 Spring AI 处理执行。
对于大多数应用,请搭配自动注册的 ToolCallingAdvisor 使用 ChatClient —— 参见工具调用。若需要在由你自己驱动该循环的底层控制下(例如把工具调用与提示词缓存结合时),参见 ChatModel 工具调用。
推理与工具调用并用
当启用扩展思考且模型决定调用一个工具时,该响应中带签名的推理块会被自动保留,并在随工具结果一起发起的后续请求中重放。 Amazon Bedrock 要求重放中存在那些推理块——省略它们会导致 API 错误。
无需特殊配置。 无论使用自动管理的工具循环(带 ToolCallingAdvisor 的 ChatClient)还是通过 ToolCallingManager 手动驱动该循环,Spring AI 都会透明地处理重放。
结构化输出
AWS Bedrock 通过 JSON Schema 支持原生结构化输出,确保模型生成的响应严格符合你指定的结构。 此特性对受支持的模型可用,包括 Anthropic Claude 和 Amazon Nova。
用 ChatClient 配合原生结构化输出
使用结构化输出最简单的方式,是用 ChatClient 高层 API 和 ENABLE_NATIVE_STRUCTURED_OUTPUT 顾问:
java
record ActorsFilms(String actor, List<String> movies) {}
ActorsFilms actorsFilms = ChatClient.create(chatModel).prompt()
.options(ToolCallingChatOptions.builder()
.model("us.anthropic.claude-haiku-4-5-20251001-v1:0")
.build())
.advisors(AdvisorParams.ENABLE_NATIVE_STRUCTURED_OUTPUT)
.user("Generate the filmography for a random actor.")
.call()
.entity(ActorsFilms.class);这种方式会自动:
- 从你的 Java 类生成一个 JSON 模式
- 通过 AWS Bedrock
OutputConfigAPI 在BedrockChatOptions上设置outputSchema - 把 JSON 响应解析为你指定的类型
直接使用 outputSchema
要获得更多控制,你可以直接在 BedrockChatOptions 上设置 JSON 模式:
java
String jsonSchema = """
{
"type": "object",
"properties": {
"actor": { "type": "string" },
"movies": {
"type": "array",
"items": { "type": "string" }
}
},
"required": ["actor", "movies"],
"additionalProperties": false
}
""";
ChatResponse response = chatModel.call(
new Prompt("Generate the filmography for a random actor.",
BedrockChatOptions.builder()
.model("us.anthropic.claude-haiku-4-5-20251001-v1:0")
.outputSchema(jsonSchema)
.build()));
String content = response.getResult().getOutput().getText();NOTE
AWS Bedrock 结构化输出在构造 OutputConfig 时内部使用一个固定的模式名 response_schema。
模式 JSON 被直接传给 AWS SDK 的 JsonSchemaDefinition。
更多信息参见结构化输出文档。
多模态
多模态指的是模型同时理解和处理来自各种来源信息的能力,包括文本、图像、视频、pdf、doc、html、md 及更多数据格式。
Bedrock Converse API 支持多模态输入,包括文本和图像输入,并能基于组合输入生成文本响应。
你需要一个支持多模态输入的模型,例如 Anthropic Claude 或 Amazon Nova 模型。
图像
对于支持视觉多模态的模型,例如 Amazon Nova、Anthropic Claude、Llama 3.2,Bedrock Converse API 允许你在负载中包含多个图像。这些模型能分析所传入的图像并回答问题、对图像分类,以及根据提供的指令总结图像。
目前,Bedrock Converse 支持 image/jpeg、image/png、image/gif 和 image/webp mime 类型的 base64 编码图像。
Spring AI 的 Message 接口通过引入 Media 类型支持多模态 AI 模型。 它包含消息中媒体附件的数据与信息,使用 Spring 的 org.springframework.util.MimeType 和一个 java.lang.Object 表示原始媒体数据。
下面是一个简单代码示例,演示把用户文本与一张图像结合。
java
String response = ChatClient.create(chatModel)
.prompt()
.user(u -> u.text("Explain what do you see on this picture?")
.media(Media.Format.IMAGE_PNG, new ClassPathResource("/test.png")))
.call()
.content();
logger.info(response);它以 test.png 图像作为输入:
连同文本消息 "Explain what do you see on this picture?",并生成一个像这样的响应:
The image shows a close-up view of a wire fruit basket containing several pieces of fruit.
...视频
Amazon Nova 模型允许你在负载中包含单个视频,可以 base64 格式或经 Amazon S3 URI 提供。
目前,Bedrock Nova 支持 video/x-matroska、video/quicktime、video/mp4、video/webm、video/x-flv、video/mpeg、video/x-ms-wmv 和 video/3gpp mime 类型的视频。
Spring AI 的 Message 接口通过引入 Media 类型支持多模态 AI 模型。 它包含消息中媒体附件的数据与信息,使用 Spring 的 org.springframework.util.MimeType 和一个 java.lang.Object 表示原始媒体数据。
下面是一个简单代码示例,演示把用户文本与一个视频结合。
java
String response = ChatClient.create(chatModel)
.prompt()
.user(u -> u.text("Explain what do you see in this video?")
.media(Media.Format.VIDEO_MP4, new ClassPathResource("/test.video.mp4")))
.call()
.content();
logger.info(response);它以 test.video.mp4 图像作为输入:
连同文本消息 "Explain what do you see in this video?",并生成一个像这样的响应:
The video shows a group of baby chickens, also known as chicks, huddled together on a surface
...文档
对于某些模型,Bedrock 允许你通过 Converse API 文档支持在负载中包含文档,可以字节形式提供。文档支持有两种不同的变体,如下所述:
- 文本文档类型(txt、csv、html、md 等),其重点在文本理解。这些用例包括基于文档的文本要素来回答。
- 媒体文档类型(pdf、docx、xlsx),其重点在基于视觉的理解来回答问题。这些用例包括基于图表、图形等来回答。
目前 Anthropic PDF 支持(beta) 和 Amazon Bedrock Nova 模型支持文档多模态。
下面是一个简单代码示例,演示把用户文本与一个媒体文档结合。
java
String response = ChatClient.create(chatModel)
.prompt()
.user(u -> u.text(
"You are a very professional document summarization specialist. Please summarize the given document.")
.media(Media.Format.DOC_PDF, new ClassPathResource("/spring-ai-reference-overview.pdf")))
.call()
.content();
logger.info(response);它以 spring-ai-reference-overview.pdf 文档作为输入:
连同文本消息 "You are a very professional document summarization specialist. Please summarize the given document.",并生成一个像这样的响应:
**Introduction:**
- Spring AI is designed to simplify the development of applications with artificial intelligence (AI) capabilities, aiming to avoid unnecessary complexity.
...示例控制器
创建一个新 Spring Boot 项目,并把 spring-ai-starter-model-bedrock-converse 添加到你的依赖。
在 src/main/resources 下添加一个 application.properties 文件:
properties
spring.ai.bedrock.aws.region=eu-central-1
spring.ai.bedrock.aws.timeout=10m
spring.ai.bedrock.aws.access-key=${AWS_ACCESS_KEY_ID}
spring.ai.bedrock.aws.secret-key=${AWS_SECRET_ACCESS_KEY}
# session token is only required for temporary credentials
spring.ai.bedrock.aws.session-token=${AWS_SESSION_TOKEN}
spring.ai.bedrock.converse.chat.temperature=0.8
spring.ai.bedrock.converse.chat.top-k=15下面是使用聊天模型的一个示例控制器:
java
@RestController
public class ChatController {
private final ChatClient chatClient;
@Autowired
public ChatController(ChatClient.Builder builder) {
this.chatClient = builder.build();
}
@GetMapping("/ai/generate")
public Map generate(@RequestParam(value = "message", defaultValue = "Tell me a joke") String message) {
return Map.of("generation", this.chatClient.prompt(message).call().content());
}
@GetMapping("/ai/generateStream")
public Flux<ChatResponse> generateStream(@RequestParam(value = "message", defaultValue = "Tell me a joke") String message) {
return this.chatClient.prompt(message).stream().content();
}
}