第1771篇:AI应用的成本核算模型——如何建立Token消耗的ROI分析体系
第1771篇:AI应用的成本核算模型——如何建立Token消耗的ROI分析体系
最近和几个做AI产品的朋友聊,发现大家都在头疼同一件事:老板问"这个AI功能每个月花了多少钱,带来了多少价值",结果大部分人都答不上来。
不是不想算,是真的不知道怎么建体系。
Token消耗有了,账单每个月也有,但业务价值在哪里?怎么把两者对应起来?靠拍脑袋说"AI提升了效率",这话在季度复盘会上根本站不住脚。
这篇文章,我来认真聊一下怎么建立一套可落地的Token消耗ROI分析体系。不是理论框架,是我们团队在实际项目里摸出来的东西。
先说清楚问题在哪
很多团队对AI成本的认知停留在"账单层",就是每个月OpenAI或者Anthropic发来的账单。这个账单能告诉你花了多少钱,但告诉不了你钱花在哪里、值不值得。
我见过几种常见的混乱状态:
第一种:全放一个Key。整个公司所有AI功能都用同一个API Key,月底账单一来,谁也不知道哪个功能烧的钱。
第二种:有功能维度但没有业务维度。知道"智能客服"每月花了5万token,但不知道这5万token处理了多少工单,平均每工单成本多少,相比人工处理省了多少。
第三种:有数据没分析。埋了日志,能查询,但没有自动汇总,没有对比基准,数据只是数据,不是洞察。
真正能做ROI分析的,需要把成本数据和业务数据打通,建立"AI动作→资源消耗→业务产出"的完整链路。
ROI分析体系的四个层次
我把AI应用的成本ROI分析分成四个层次,从简单到复杂:
大部分团队卡在第一层或第二层,跳不到第三层。原因不复杂:业务价值量化比技术指标难,需要和产品、运营、财务一起定义口径。
第一层:成本归因的工程实现
成本归因说白了就是:每一笔Token消耗,都要能找到对应的业务来源。
调用链路追踪
每次API调用必须带上业务上下文,这个上下文在后续统计时是分析的钥匙。
@Data
@Builder
public class AICallContext {
private String traceId; // 全局追踪ID
private String userId; // 用户ID
private String featureCode; // 功能代码,如 "smart_reply" / "doc_summary"
private String productLine; // 产品线
private String tenantId; // 租户ID(多租户场景)
private String bizObjectId; // 业务对象ID,如工单ID/文档ID
private String modelName; // 调用的模型
private Map<String, String> extInfo; // 扩展信息
}这个 Context 对象需要在整个调用链路里传递,最终写入成本日志。
Token消耗记录服务
@Service
@Slf4j
public class TokenCostRecorder {
@Autowired
private TokenCostRepository repository;
@Autowired
private ModelPriceConfig priceConfig;
/**
* 记录一次AI调用的Token消耗
*/
public void record(AICallContext context, TokenUsage usage) {
TokenCostRecord record = TokenCostRecord.builder()
.traceId(context.getTraceId())
.userId(context.getUserId())
.featureCode(context.getFeatureCode())
.productLine(context.getProductLine())
.tenantId(context.getTenantId())
.bizObjectId(context.getBizObjectId())
.modelName(context.getModelName())
.inputTokens(usage.getPromptTokens())
.outputTokens(usage.getCompletionTokens())
.inputCostUsd(calculateCost(context.getModelName(), "input", usage.getPromptTokens()))
.outputCostUsd(calculateCost(context.getModelName(), "output", usage.getCompletionTokens()))
.callTime(LocalDateTime.now())
.extInfo(JSON.toJSONString(context.getExtInfo()))
.build();
record.setTotalCostUsd(record.getInputCostUsd().add(record.getOutputCostUsd()));
repository.save(record);
// 异步更新实时统计
updateRealtimeStats(record);
}
private BigDecimal calculateCost(String modelName, String tokenType, int tokenCount) {
ModelPrice price = priceConfig.getPrice(modelName);
if (price == null) {
log.warn("未找到模型价格配置: {}", modelName);
return BigDecimal.ZERO;
}
BigDecimal pricePerToken = "input".equals(tokenType)
? price.getInputPricePerToken()
: price.getOutputPricePerToken();
return pricePerToken.multiply(new BigDecimal(tokenCount));
}
}这里有个容易踩的坑:价格配置要独立维护,不要硬编码。各家模型价格会调整,而且同一个模型在不同时期、不同用量层级价格不同,用一个配置中心管理价格,更新时不用改代码。
模型价格配置
@Configuration
@ConfigurationProperties(prefix = "ai.model.price")
@Data
public class ModelPriceConfig {
// 配置格式:modelName -> ModelPrice
private Map<String, ModelPrice> prices;
@Data
public static class ModelPrice {
private String modelName;
// 每1000个token的价格(美元)
private BigDecimal inputPricePerK;
private BigDecimal outputPricePerK;
public BigDecimal getInputPricePerToken() {
return inputPricePerK.divide(new BigDecimal("1000"), 10, RoundingMode.HALF_UP);
}
public BigDecimal getOutputPricePerToken() {
return outputPricePerK.divide(new BigDecimal("1000"), 10, RoundingMode.HALF_UP);
}
}
public ModelPrice getPrice(String modelName) {
return prices.get(modelName);
}
}对应的YAML配置:
ai:
model:
price:
prices:
gpt-4o:
modelName: gpt-4o
inputPricePerK: 0.005
outputPricePerK: 0.015
gpt-4o-mini:
modelName: gpt-4o-mini
inputPricePerK: 0.00015
outputPricePerK: 0.0006
claude-3-5-sonnet-20241022:
modelName: claude-3-5-sonnet-20241022
inputPricePerK: 0.003
outputPricePerK: 0.015
claude-3-haiku-20240307:
modelName: claude-3-haiku-20240307
inputPricePerK: 0.00025
outputPricePerK: 0.00125第二层:单位成本计算
有了原始的Token消耗数据,下一步是聚合成有业务意义的单位成本指标。
常见的单位成本维度:
- 每次AI调用平均成本:最基础的指标
- 每个业务动作的AI成本:如每张工单AI处理成本、每篇文档摘要成本
- 每个活跃用户的月均AI成本:判断商业模式是否成立
- 每个功能模块的月均成本:功能优先级决策依据
@Service
public class UnitCostAnalysisService {
@Autowired
private TokenCostRepository repository;
/**
* 按功能统计单位成本
*/
public List<FeatureUnitCost> analyzeByFeature(LocalDate startDate, LocalDate endDate) {
List<Object[]> rawData = repository.aggregateByFeature(startDate, endDate);
return rawData.stream().map(row -> {
String featureCode = (String) row[0];
long callCount = ((Number) row[1]).longValue();
long totalInputTokens = ((Number) row[2]).longValue();
long totalOutputTokens = ((Number) row[3]).longValue();
BigDecimal totalCost = (BigDecimal) row[4];
return FeatureUnitCost.builder()
.featureCode(featureCode)
.callCount(callCount)
.totalInputTokens(totalInputTokens)
.totalOutputTokens(totalOutputTokens)
.totalCostUsd(totalCost)
.avgCostPerCall(callCount > 0
? totalCost.divide(new BigDecimal(callCount), 6, RoundingMode.HALF_UP)
: BigDecimal.ZERO)
.avgInputTokensPerCall(callCount > 0 ? totalInputTokens / callCount : 0)
.avgOutputTokensPerCall(callCount > 0 ? totalOutputTokens / callCount : 0)
.build();
}).collect(Collectors.toList());
}
/**
* 计算每个业务对象的AI成本
* 比如:每张工单的AI处理成本
*/
public Map<String, BigDecimal> calcCostPerBizObject(
String featureCode,
LocalDate startDate,
LocalDate endDate) {
List<Object[]> data = repository.aggregateByBizObject(featureCode, startDate, endDate);
return data.stream().collect(Collectors.toMap(
row -> (String) row[0], // bizObjectId
row -> (BigDecimal) row[1] // totalCost
));
}
/**
* 按用户计算月均AI成本
*/
public BigDecimal calcAvgMonthlyCostPerUser(int year, int month) {
BigDecimal totalCost = repository.sumCostByMonth(year, month);
long activeUserCount = repository.countActiveUsersByMonth(year, month);
if (activeUserCount == 0) return BigDecimal.ZERO;
return totalCost.divide(new BigDecimal(activeUserCount), 4, RoundingMode.HALF_UP);
}
}第三层:价值量化——最难也最关键的一步
前两层是技术活,这一层是业务活。
AI价值来源通常有两类:
替代成本(Cost Saving):AI做了原来需要人做的事,省出来的人力成本就是价值。
增量收入(Revenue Generation):AI带来了原来不存在的收入,比如提升转化率、客单价。
先聊替代成本的计算方式。
替代成本模型
@Data
@Builder
public class CostSavingModel {
private String featureCode;
private String description;
// 人工处理参数
private int humanTimeSeconds; // 人工处理一次需要多少秒
private BigDecimal humanHourlyCost; // 人工每小时综合成本(含社保等)
// AI处理的质量系数(AI处理效果相当于人工的比例)
private BigDecimal qualityFactor; // 0.0~1.0,1.0表示完全等效
/**
* 计算节省的人力成本
*/
public BigDecimal calcSaving(long aiCallCount, BigDecimal aiTotalCost) {
// 人工处理等量任务的成本
BigDecimal humanTotalCost = humanHourlyCost
.multiply(new BigDecimal(humanTimeSeconds))
.divide(new BigDecimal(3600), 4, RoundingMode.HALF_UP)
.multiply(new BigDecimal(aiCallCount));
// 考虑质量系数,AI处理的有效价值
BigDecimal effectiveHumanCost = humanTotalCost.multiply(qualityFactor);
// 净节省 = 有效人工成本 - AI成本
return effectiveHumanCost.subtract(aiTotalCost);
}
/**
* 计算ROI
*/
public BigDecimal calcROI(long aiCallCount, BigDecimal aiTotalCost) {
BigDecimal saving = calcSaving(aiCallCount, aiTotalCost);
if (aiTotalCost.compareTo(BigDecimal.ZERO) == 0) return BigDecimal.ZERO;
return saving.divide(aiTotalCost, 4, RoundingMode.HALF_UP);
}
}举个真实案例:我们做过一个智能工单分类功能,原来每张工单需要人工分类大约90秒,人力综合成本约60元/小时。AI每次分类消耗大约800 input token + 100 output token,用 gpt-4o-mini,大约0.00014美元。
每天处理1000张工单:
- 人工成本:1000 × 90秒 × (60/3600) = 1000 × 1.5元 = 1500元/天
- AI成本:1000 × 0.00014美元 ≈ 0.14美元 ≈ 约1元/天
ROI = (1500 - 1) / 1 ≈ 1499倍
当然,AI分类准确率不可能100%,要引入质量系数。假设AI准确率85%,需要人工复核15%的工单,真实ROI还是非常可观的。
第四层:ROI决策支撑
有了前三层的数据,就可以建立一个简单的ROI决策矩阵。
ROI阈值怎么定?这个要看行业和公司,但一个基本原则是:AI功能的ROI至少要高于公司其他技术投入的平均ROI,否则资源用到其他地方更值。
我们通常用3个关键指标来做决策:
@Data
@Builder
public class AIFeatureROIReport {
private String featureCode;
private String featureName;
private LocalDate reportPeriod;
// 成本指标
private BigDecimal totalCostUsd; // 总AI成本
private BigDecimal avgCostPerCall; // 每次调用平均成本
private BigDecimal costTrend; // 成本环比变化(正数=上升)
// 价值指标
private BigDecimal totalSavingCny; // 节省人力成本(人民币)
private BigDecimal totalRevenueImpactCny; // 带动收入变化
// ROI指标
private BigDecimal roi; // 综合ROI
private BigDecimal paybackDays; // 回收期(天)
// 决策建议
private String recommendation; // "SCALE_UP" / "OPTIMIZE" / "SUNSET"
public void generateRecommendation() {
if (roi.compareTo(new BigDecimal("5")) > 0
&& costTrend.compareTo(new BigDecimal("0.3")) < 0) {
recommendation = "SCALE_UP"; // 加大投入
} else if (roi.compareTo(new BigDecimal("1")) > 0) {
recommendation = "OPTIMIZE"; // 优化成本
} else {
recommendation = "SUNSET"; // 考虑下线
}
}
}数据库设计
把前面的逻辑落到存储层,给个参考表结构:
-- Token消耗明细表
CREATE TABLE ai_token_cost_detail (
id BIGINT AUTO_INCREMENT PRIMARY KEY,
trace_id VARCHAR(64) NOT NULL COMMENT '追踪ID',
user_id VARCHAR(64) COMMENT '用户ID',
feature_code VARCHAR(64) NOT NULL COMMENT '功能代码',
product_line VARCHAR(64) COMMENT '产品线',
tenant_id VARCHAR(64) COMMENT '租户ID',
biz_object_id VARCHAR(128) COMMENT '业务对象ID',
model_name VARCHAR(64) NOT NULL COMMENT '模型名称',
input_tokens INT NOT NULL DEFAULT 0 COMMENT '输入Token数',
output_tokens INT NOT NULL DEFAULT 0 COMMENT '输出Token数',
input_cost_usd DECIMAL(12,8) NOT NULL DEFAULT 0 COMMENT '输入成本(美元)',
output_cost_usd DECIMAL(12,8) NOT NULL DEFAULT 0 COMMENT '输出成本(美元)',
total_cost_usd DECIMAL(12,8) NOT NULL DEFAULT 0 COMMENT '总成本(美元)',
call_time DATETIME NOT NULL COMMENT '调用时间',
ext_info JSON COMMENT '扩展信息',
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
INDEX idx_feature_time (feature_code, call_time),
INDEX idx_user_time (user_id, call_time),
INDEX idx_tenant_time (tenant_id, call_time),
INDEX idx_trace (trace_id)
) COMMENT 'AI Token消耗明细';
-- 每日统计汇总表(加速查询)
CREATE TABLE ai_cost_daily_summary (
id BIGINT AUTO_INCREMENT PRIMARY KEY,
stat_date DATE NOT NULL COMMENT '统计日期',
feature_code VARCHAR(64) NOT NULL COMMENT '功能代码',
tenant_id VARCHAR(64) COMMENT '租户ID',
model_name VARCHAR(64) NOT NULL COMMENT '模型名称',
call_count INT NOT NULL DEFAULT 0 COMMENT '调用次数',
total_input_tokens BIGINT NOT NULL DEFAULT 0,
total_output_tokens BIGINT NOT NULL DEFAULT 0,
total_cost_usd DECIMAL(16,6) NOT NULL DEFAULT 0,
active_user_count INT DEFAULT 0 COMMENT '活跃用户数',
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
UNIQUE KEY uk_date_feature_tenant_model (stat_date, feature_code, tenant_id, model_name)
) COMMENT 'AI成本每日汇总';可视化仪表板
光有数据还不够,要让老板和业务方能看懂,需要一个简单的报表。
我们用的是 Spring Boot + ECharts 的方案,后端提供数据接口:
@RestController
@RequestMapping("/api/ai-cost")
public class AICostDashboardController {
@Autowired
private AICostDashboardService dashboardService;
/**
* 获取成本趋势数据(30天)
*/
@GetMapping("/trend")
public ApiResponse<CostTrendVO> getCostTrend(
@RequestParam(required = false) String featureCode,
@RequestParam(required = false) String tenantId) {
LocalDate endDate = LocalDate.now();
LocalDate startDate = endDate.minusDays(29);
CostTrendVO trend = dashboardService.getCostTrend(featureCode, tenantId, startDate, endDate);
return ApiResponse.success(trend);
}
/**
* 获取功能维度ROI报告
*/
@GetMapping("/roi-report")
public ApiResponse<List<AIFeatureROIReport>> getROIReport(
@RequestParam int year,
@RequestParam int month) {
List<AIFeatureROIReport> reports = dashboardService.generateMonthlyROIReport(year, month);
return ApiResponse.success(reports);
}
/**
* 获取成本构成分析(按功能/模型/产品线)
*/
@GetMapping("/breakdown")
public ApiResponse<CostBreakdownVO> getCostBreakdown(
@RequestParam String dimension, // "feature" / "model" / "product_line"
@RequestParam String startDate,
@RequestParam String endDate) {
CostBreakdownVO breakdown = dashboardService.getCostBreakdown(
dimension,
LocalDate.parse(startDate),
LocalDate.parse(endDate)
);
return ApiResponse.success(breakdown);
}
}几个落地经验
最后说几个从实际操盘中摸出来的经验,书本上看不到的那种。
经验一:先做归因,再做优化。 很多团队上来就想降成本,但连钱花在哪里都不清楚,优化就是瞎优化。我见过一个团队花了一个月优化某个功能的Prompt,省了20%,结果发现那个功能只占总成本的3%,意义不大。先把归因做清楚,找到最大的成本中心,再去优化,事半功倍。
经验二:质量系数是个主观数字,要和业务对齐。 我们在算替代人力成本时,AI准确率怎么和人工比?这个没有客观标准,需要业务方认可。建议早期先用保守估计,ROI依然高,这样汇报时底气更足。
经验三:日志要异步写,不能影响主流程。 Token消耗记录是分析用的,千万别同步写数据库,一旦数据库慢了影响AI接口响应,用户体验直接崩。用 MQ 异步消费,即使日志系统挂了,主业务也不受影响。
经验四:成本单位要在团队内统一换算。美元账单给产品和运营看没意义,要换算成人民币,而且要用汇率乘以一个缓冲系数(建议1.1-1.2倍),因为汇率波动和平台服务费容易被忽略。
这套体系搭起来之后,你在季度复盘会上说"智能客服这个月AI成本3.2万元,节省了人工成本约18万元,ROI约5.6倍,建议继续投入",比说"AI提升了效率"要有说服力得多。
数据能说话,才是工程师的底气。
