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

聊天响应元数据

使用 AI(例如 OpenAI 的 ChatGPT)会消耗资源,并由 AI 服务商基于通过 API 对 AI 发起的用量与请求返回相应指标。 消耗通常以某个时间范围(如每月)内发起的请求数或使用的令牌数来计量,AI 服务商以此衡量消耗并重置限额。 你的速率限额直接由你注册时所选择的 AI 服务商套餐决定。 例如,你可以通过链接查阅 OpenAI 的速率限额和套餐详情。

为了帮助你洞察自己的 AI(模型)消耗与整体用量,Spring AI 提供了一个 API,用于内省 AI 服务商在其 API 中返回的元数据。

Spring AI 定义了 3 个用于检视这些指标的主要类型:ChatResponseMetadata、RateLimit 和 Usage。 这些类型都可以通过程序方式,从由某个 AI 请求发起并返回的 ChatResponse 中访问。

ChatResponseMetadata

当通过 AI 服务商的 API 发起 AI 请求并返回 AI 响应时,Spring AI 会自动创建一个 ChatResponseMetadata 实例。 你可以用如下方式,从 ChatResponse 中访问 AI 服务商的元数据:

.从 ChatResponse 获取 ChatResponseMetadata

java
ChatResponse response = chatModel.call(prompt);

ChatResponseMetadata metadata = response.getMetadata();

String id = metadata.getId();
String model = metadata.getModel();
RateLimit rateLimit = metadata.getRateLimit();
Usage usage = metadata.getUsage();
PromptMetadata promptMetadata = metadata.getPromptMetadata();

你或许可以设想,用 AI 为自己的 Spring 应用做速率限制,或以自动化、智能且实时的方式限制影响令牌用量的 Prompt 大小。 退一步说,你至少可以收集这些指标来监控并报告你的消耗情况。

速率限额

RateLimit 接口提供对 AI 服务商在发起 AI 请求时就你的 API 用量所返回的真实信息的访问。

.RateLimit 接口

java
interface RateLimit {

	Long getRequestsLimit();

	Long getRequestsRemaining();

	Duration getRequestsReset();

	Long getTokensLimit();

	Long getTokensRemaining();

	Duration getTokensReset();

}

requestsLimit 和 requestsRemaining 让你知道:基于你注册时所选择的 AI 服务商套餐,你总共可以发起多少个 AI 请求,以及在给定时间范围内还剩多少余额。 requestsReset 返回一个 Duration,表示在时间范围到期、你的限额依据所选套餐重置之前还剩余的时间。 tokensLimit、tokensRemaining 和 tokensReset 这些方法与此类似,只是聚焦于令牌的限额、余额与重置。

.从 ChatResponseMetadata 获取 RateLimit

java
RateLimit rateLimit = metadata.getRateLimit();

Long tokensRemaining = rateLimit.getTokensRemaining();

// do something interesting with the RateLimit metadata

诸如 OpenAI 这样的 AI 服务商,会在其 REST API 响应的 HTTP 头中返回速率限额信息。 当某个模型实现映射了这些头时,它们会通过 RateLimit 暴露出来。

支持程度因模型实现而异。 当速率限额信息不可用时,getRateLimit() 返回一个 EmptyRateLimit,其访问器返回零而非 null。 用 instanceof EmptyRateLimit 来区分「缺失的速率限额」与「真实的零值」。

用量

Usage 数据可以从 ChatResponseMetadata 对象获取。 Usage 接口定义如下:

.Usage 接口

java
interface Usage {

	Integer getPromptTokens();

	Integer getCompletionTokens();

	default Integer getTotalTokens() { ... }

	Object getNativeUsage();

}

这些方法名不言自明:它们告诉你 AI 处理 Prompt 并生成响应所需的令牌数。 totalTokens 是 promptTokens 与 completionTokens 之和。 Spring AI 默认会计算这个值,但该信息也会随 OpenAI 等服务商的 AI 响应一并返回。

与 ChatModel 一起使用

下面是一个完整示例,展示如何用 OpenAI 的 ChatModel 跟踪用量:

java
@SpringBootConfiguration
public class Configuration {

	@Bean
	public OpenAiChatModel openAiChatModel() {
		return OpenAiChatModel.builder()
			.options(OpenAiChatOptions.builder()
				.apiKey(System.getenv("OPENAI_API_KEY"))
				.build())
			.build();
	}

}

@Service
public class ChatService {

	private final OpenAiChatModel chatModel;

	public ChatService(OpenAiChatModel chatModel) {
		this.chatModel = chatModel;
	}

	public void demonstrateUsage() {
		// Create a chat prompt
		Prompt prompt = new Prompt("What is the weather like today?");

		// Get the chat response
		ChatResponse response = this.chatModel.call(prompt);

		// Access the usage information
		Usage usage = response.getMetadata().getUsage();

		// Get standard usage metrics
		System.out.println("Prompt Tokens: " + usage.getPromptTokens());
		System.out.println("Completion Tokens: " + usage.getCompletionTokens());
		System.out.println("Total Tokens: " + usage.getTotalTokens());

		// Access native OpenAI usage data with detailed token information
		if (usage.getNativeUsage() instanceof com.openai.models.completions.CompletionUsage nativeUsage) {

			// Detailed prompt token information
			nativeUsage.promptTokensDetails().ifPresent(details -> {
				System.out.println("Prompt Tokens Details:");
				details.audioTokens().ifPresent(tokens -> System.out.println("- Audio Tokens: " + tokens));
				details.cachedTokens().ifPresent(tokens -> System.out.println("- Cached Tokens: " + tokens));
			});

			// Detailed completion token information
			nativeUsage.completionTokensDetails().ifPresent(details -> {
				System.out.println("Completion Tokens Details:");
				details.reasoningTokens().ifPresent(tokens -> System.out.println("- Reasoning Tokens: " + tokens));
				details.acceptedPredictionTokens().ifPresent(tokens -> System.out.println("- Accepted Prediction Tokens: " + tokens));
				details.audioTokens().ifPresent(tokens -> System.out.println("- Audio Tokens: " + tokens));
				details.rejectedPredictionTokens().ifPresent(tokens -> System.out.println("- Rejected Prediction Tokens: " + tokens));
			});
		}
	}
}

与 ChatClient 一起使用

如果你使用的是 ChatClient,可以通过 ChatResponse 对象访问用量信息:

java
ChatResponse response = chatClient.prompt("What is the weather like today?")
		.call()
		.chatResponse();

Usage usage = response.getMetadata().getUsage();

提示缓存用量指标

对于支持提示缓存(prompt caching)的服务商,Usage 接口提供对缓存指标的统一访问,无需进行服务商特定的强制转换:

java
Usage usage = response.getMetadata().getUsage();

// Unified cache metrics — works across all providers
Long cacheReadTokens = usage.getCacheReadInputTokens();
Long cacheWriteTokens = usage.getCacheWriteInputTokens();

if (cacheReadTokens != null && cacheReadTokens > 0) {
	System.out.println("Cache hit: " + cacheReadTokens + " tokens read from cache");
}
if (cacheWriteTokens != null && cacheWriteTokens > 0) {
	System.out.println("Cache write: " + cacheWriteTokens + " tokens written to cache");
}

对于不支持提示缓存的服务商,这些方法返回 null。

下表按服务商展示了提示缓存指标的可用性:

服务商缓存读取令牌缓存写入令牌
Anthropic是是(cacheCreationInputTokens)
AWS Bedrock是是
OpenAI是(cachedTokens)否
Google Gemini是(cachedContentTokenCount)否
DeepSeek否否
Mistral否否
Ollama否否

NOTE

关于服务商特定的详细缓存指标(例如 Gemini 中按模态划分的缓存细分),请使用 getNativeUsage() 来访问服务商的原生用量对象。

多步流程中的累计用量

当响应是通过多步流程(如工具调用循环)产出时,getUsage() 报告的是该轮交互中每一次模型调用的累计令牌用量,而不仅仅是最后一次。 例如,一次触发单个工具调用的 ChatClient 对话至少会进行两次模型调用,返回的 getUsage() 反映的是两者之和。

java
ChatResponse response = chatClient.prompt("What is the weather in Paris?")
		.tools(new WeatherTools())
		.call()
		.chatResponse();

// Cumulative across all model calls in the tool-calling loop
Usage usage = response.getMetadata().getUsage();
int totalTokens = usage.getTotalTokens();

累计总量由 org.springframework.ai.support.UsageCalculator 计算,它对标准令牌计数和统一缓存指标(getCacheReadInputTokens() / getCacheWriteInputTokens())求和。

DANGER

服务商特定的原生用量对象无法跨响应合并。

因此,一旦用量被聚合到超过一次模型调用之后(例如在工具调用循环之后),getNativeUsage() 会返回 null;它只在单次调用的响应中被保留。 如果你需要服务商的原生用量对象,请从单个 ChatModel 调用而非多步 ChatClient 交互中读取它。

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