chat - 模型的响应与计费
2026年6月10日 下午5:02:49
在 Solon AI 中,所有的模型调用结果都通过 ChatResponse 接口承载。它不仅包含模型输出的消息,还负责追踪 Token 的消耗情况。
1、响应接口 (ChatResponse)
ChatResponse 统一了同步调用和流式调用的返回结构。针对目前流行的“推理模型”(如 DeepSeek-R1, OpenAI o1),它提供了专门的方法来区分思维链(Thinking)和最终答案。
提示: 对于支持深度思考的模型,推荐使用 getResultContent() 直接获取给用户看的结果,而 getContent() 常用于日志记录或调试分析。
package org.noear.solon.ai.chat;
import org.noear.solon.ai.AiUsage;
import org.noear.solon.ai.chat.message.AssistantMessage;
import org.noear.solon.lang.Nullable;
import org.noear.solon.lang.Preview;
import java.util.List;
public interface ChatResponse {
/**
* 获取配置(只读)
*/
ChatConfigReadonly getConfig();
/**
* 获取选项
*/
ChatOptions getOptions();
/**
* 获取响应数据
*/
@Nullable
String getResponseData();
/**
* 获取模型
*/
String getModel();
/**
* 获取错误
*/
@Nullable
ChatException getError();
/**
* 是否为空(没有内容,也没有工具调用)
*/
boolean isEmpty();
/**
* 是否有选择
*/
boolean hasChoices();
/**
* 最后一个选择
*/
@Nullable
ChatChoice lastChoice();
/**
* 获取所有选择
*/
@Nullable
List<ChatChoice> getChoices();
/**
* 获取消息
*/
@Nullable
AssistantMessage getMessage();
/**
* 获取聚合消息(流响应完成时可用)
*/
@Nullable
AssistantMessage getAggregationMessage();
/**
* 获取聚合内容(流响应完成时可用)
*/
String getAggregationContent();
/**
* 是否有消息内容
*/
boolean hasContent();
/**
* 获取消息原始内容
*/
String getContent();
/**
* 获取消息结果内容(清理过思考)
*/
String getResultContent();
/**
* 获取使用情况(完成时,才会有使用情况)
*/
@Nullable
AiUsage getUsage();
/**
* 是否完成
*/
boolean isFinished();
/**
* 是否为流响应
*/
boolean isStream();
}
2、计费与使用统计 (AiUsage)
AiUsage 用于记录单次对话消耗的 Token 资源。不同的模型服务商提供的原始 JSON 结构差异很大,Solon AI 将其标准化为三个核心指标。
package org.noear.solon.ai;
import org.noear.snack4.ONode;
public class AiUsage {
private final long promptTokens;
private final long thinkTokens;
private final long completionTokens;
private final long totalTokens;
private final long cacheCreationInputTokens;
private final long cacheReadInputTokens;
private final ONode source;
public AiUsage(long promptTokens, long thinkTokens, long completionTokens, long totalTokens, ONode source) {
this(promptTokens, thinkTokens, completionTokens, totalTokens, 0L, 0L, source);
}
public AiUsage(long promptTokens, long thinkTokens, long completionTokens, long totalTokens,
long cacheCreationInputTokens, long cacheReadInputTokens, ONode source) {
this.promptTokens = promptTokens;
this.thinkTokens = thinkTokens;
this.completionTokens = completionTokens;
this.totalTokens = totalTokens;
this.cacheCreationInputTokens = cacheCreationInputTokens;
this.cacheReadInputTokens = cacheReadInputTokens;
this.source = source;
}
/**
* 获取提示语消耗令牌数
*/
public long promptTokens() {
return promptTokens;
}
/**
* 获取思考消耗令牌数
*/
public long thinkTokens() {
return thinkTokens;
}
/**
* 获取完成消耗令牌数
*/
public long completionTokens() {
return completionTokens;
}
/**
* 获取总消耗令牌数
*/
public long totalTokens() {
return totalTokens;
}
/**
* 获取缓存创建输入令牌数 (Claude Prompt Caching)
*/
public long cacheCreationInputTokens() {
return cacheCreationInputTokens;
}
/**
* 获取缓存读取输入令牌数 (Claude Prompt Caching)
*/
public long cacheReadInputTokens() {
return cacheReadInputTokens;
}
/**
* 源数据
*/
public ONode getSource() {
return source;
}
}
3、代码示例
获取清理后的内容
ChatResponse resp = chatModel.call(prompt);
// 自动处理思考过程,只打印结果
System.out.println("Result: " + resp.getResultContent());
// 打印计费信息
AiUsage usage = resp.getUsage();
if (usage != null) {
System.out.println("Cost Tokens: " + usage.totalTokens());
}
处理原始数据 如果你需要获取厂商特有的计费细节(例如 DeepSeek 的 prompt_cache_hit_tokens):
long cacheHit = resp.getUsage().getSource()
.get("usage")
.get("prompt_cache_hit_tokens").getLong();