Skip to content
Spring AI 文档版本 2.1.0-SNAPSHOT 源快照 162eb0d2 更新于 2026-10-16

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 入门

自动配置

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-keyAWS 访问密钥-
spring.ai.bedrock.aws.secret-keyAWS 密钥-
spring.ai.bedrock.aws.session-token临时凭据的 AWS 会话令牌-
spring.ai.bedrock.aws.profile.nameAWS 配置档名。-
spring.ai.bedrock.aws.profile.credentials-pathAWS 凭据文件路径。-
spring.ai.bedrock.aws.profile.configuration-pathAWS 配置文件路径。-

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 会大于 0
  • cacheReadInputTokens 会是 0

当你(在 5 分钟 TTL 内)再次发送同一个被缓存的提示词时:

  • cacheWriteInputTokens 会是 0
  • cacheReadInputTokens 会大于 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

最佳实践

  1. 选择正确的策略:
  • 对可复用的系统提示词和指令使用 SYSTEM_ONLY(对所有模型有效)
  • 当你有大型稳定的工具但系统提示词动态时使用 TOOLS_ONLY(仅 Claude)
  • 当系统和工具都大而稳定时使用 SYSTEM_AND_TOOLS(仅 Claude)
  • 配合 ChatClient 记忆,对多轮对话使用 CONVERSATION_HISTORY
  • 用 NONE 显式禁用缓存
  1. 满足令牌要求:专注于缓存满足最小令牌要求的内容(多数模型 1024+ 令牌)。

  2. 复用完全相同的内容:缓存在提示词内容精确匹配时效果最佳。 即便是微小变化也会需要一个新的缓存条目。

  3. 监控令牌用量:使用元数据指标跟踪缓存有效性:

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"); }

  1. 战略性缓存放置:实现会根据你所选的策略自动把缓存断点放在最优位置,确保符合 AWS Bedrock 的 4 断点限制。

  2. 缓存寿命:AWS Bedrock 缓存默认有 5 分钟 TTL(存活时间),但它们也支持 1 小时的 TTL 时长。 每次缓存访问都会重置计时器。这可以通过 BedrockCacheOptions 上的 BedrockCacheTtl 或 spring.ai.bedrock.converse.chat.cache-options.ttl 属性来配置。

  3. 模型兼容性:留意模型特定的限制:

  • Claude 模型:支持所有缓存策略
  • Amazon Nova 模型:只支持 SYSTEM_ONLY 和 CONVERSATION_HISTORY(不支持工具缓存)
  1. 工具稳定性:当使用 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 中的提示词缓存实现遵循以下关键设计原则:

  1. 战略性缓存放置:缓存断点根据所选策略被自动放在最优位置,确保符合 AWS Bedrock 的 4 断点限制。

  2. 服务商可移植性:缓存配置通过 BedrockChatOptions 而非单条消息完成,在切换不同 AI 服务商时保持兼容。

  3. 线程安全:缓存断点跟踪以线程安全的机制实现,以正确处理并发请求。

  4. UNION 类型模式:AWS SDK 使用 UNION 类型,缓存点作为独立块而非属性添加。 这不同于直接 API 的方式,但确保了类型安全与 API 合规。

  5. 增量缓存: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 OutputConfig API 在 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();
    }
}

BytesRouter 是社区翻译站点,所有译文仅供参考,一切以官方英文原文为准。