Skip to content

成本治理与预算告警:给老板一个能看懂的数字 ​

1. 本节产出 ​

一套平台级成本治理:成本模型表(可给老板看)、按租户/团队的成本归因与内部结算、预算预测与告警、以及五个可量化的降本手段及预期收益。

2. 前置依赖 ​

3. 为什么成本治理是「能不能持续」的关键 ​

一个真实的对话场景:

老板:「AI 这块一个月花多少?」 你:「大概……几万?具体要看各团队。」 老板:「花在哪?值不值?下个月会不会更多?」 你:「……我查一下。」

这三个问题答不上来,第二年的预算就没了。

老板真正想知道的你需要能提供的
花多少总成本 + 按团队/场景拆分
花在哪Top10 成本来源
值不值成本 vs 业务收益(人力节省/收入)
会不会更多趋势预测与预算告警

4. 核心原理 ​

4.1 成本模型表(给老板看的那张表) ​

| 场景     | 月调用量 | 单次成本 | 月成本  | 替代方案成本 | 净收益 |
|---------|---------|---------|--------|------------|-------|
| 客服问答 | 50,000  | 0.008   | 400    | 人力 8,000  | +7,600|
| 文档检索 | 20,000  | 0.006   | 120    | 人力 3,000  | +2,880|
| 工单处理 | 5,000   | 0.045   | 225    | 人力 6,000  | +5,775|
| 代码审查 | 800     | 0.032   | 26     | 人力 4,000  | +3,974|
| 合计     | —       | —       | 771    | 21,000      | +20,229|

「替代方案成本」这一列是说服力的关键。孤立地看「771 元」没感觉,对比「原本要 2.1 万人力」立刻有价值感。

注意:净收益要用保守口径。人力节省通常按「节省的时间 × 工时成本」估算,且要打个折扣(节省的时间不一定完全转化为产出)。

4.2 成本归因的三个维度 ​

必须能按以下维度出报表(缺一个都答不上老板的问题):
  ├─ 按租户/团队(内部分摊)
  ├─ 按场景/应用(哪个功能费钱)
  ├─ 按模型/厂商(议价与选型依据)
  └─ 按时间(趋势与预测)

归因数据来源是网关的用量记录(04-02)。这就是为什么统计必须在网关做。

4.3 预算预测 ​

简单预测:
  日均成本 × 30 = 月预测
  考虑增长:近 7 天斜率外推

告警阈值:
  实际 > 预测 × 1.2    → 提醒
  实际 > 预算 × 0.8    → 告警
  实际 > 预算 × 0.95   → 自动降级
  单日 > 日均 × 3      → 异常告警(可能是死循环或滥用)

「单日 > 日均 3 倍」这个异常检测很重要:它能抓住死循环、脚本刷接口这类突发问题,而月度预算告警发现时已经晚了。

4.4 五个降本手段及预期收益 ​

#手段预期降本代价
1缓存(精确+语义)30~40%需要失效机制
2模型分级路由20~35%需要任务分类
3上下文压缩15~25%需要摘要逻辑
4减少不必要调用(去重、合并)10~20%需要流程梳理
5Prompt 精简5~10%需要验证效果不降

按性价比排序:1 > 2 > 3 > 4 > 5。

注意:降本要以评测集守护质量。每个降本动作跑一次评测(02-17 / 03-18),确认召回率或完成率没有明显下降。省了钱但效果崩了,是最坏的结果。

5. 代码走查 ​

5.1 成本汇总 ​

java
// src/main/java/com/example/platform/cost/CostReportService.java
@Service
public class CostReportService {

    /** 按租户 × 场景的成本报表 */
    public List<CostRow> byTenantAndCase(YearMonth month) {
        return jdbc.query("""
                SELECT tenant_id,
                       COALESCE(case_name, 'unknown') AS case_name,
                       SUM(prompt_tokens)     AS pt,
                       SUM(completion_tokens) AS ct,
                       SUM(cost_yuan)         AS cost
                  FROM ai_usage
                 WHERE at >= ? AND at < ?
                 GROUP BY tenant_id, case_name
                 ORDER BY cost DESC
                """, mapper, month.atDay(1), month.plusMonths(1).atDay(1));
    }

    /** 预测:近 7 天日均外推 */
    public Forecast forecast() {
        double avg7 = jdbc.queryForObject("""
                SELECT COALESCE(SUM(cost_yuan),0)/7
                  FROM ai_usage WHERE at > now() - INTERVAL '7 days'
                """, Double.class);
        return new Forecast(avg7 * 30, avg7, budgetMonthly());
    }
}

5.2 预算告警 ​

java
// src/main/java/com/example/platform/cost/BudgetAlertJob.java
@Component
public class BudgetAlertJob {

    @Scheduled(cron = "0 0 9 * * *")     // 每天 9 点
    public void check() {
        double monthToDate = costRepo.monthToDate();
        double budget = props.monthlyBudget();
        double ratio = monthToDate / budget;

        // 1. 进度告警
        if (ratio > 0.8) {
            alert.send("AI 成本告警",
                    "本月已用 %.0f%%(%.0f / %.0f 元)".formatted(ratio*100, monthToDate, budget));
        }

        // 2. 异常检测:单日超日均 3 倍(可能是死循环或滥用)
        double today = costRepo.today();
        double dailyAvg = monthToDate / DayOfMonth();
        if (today > dailyAvg * 3 && today > props.minAbsoluteThreshold()) {
            alert.send("AI 成本异常",
                    "今日 %.0f 元,是日均的 %.1f 倍,请检查是否有异常任务".formatted(today, today/dailyAvg));
        }

        // 3. 进度与日期不匹配(月中就用了 90%)
        int dayOfMonth = LocalDate.now().getDayOfMonth();
        double expectedRatio = dayOfMonth / 30.0;
        if (ratio > expectedRatio * 1.5) {
            alert.send("AI 成本进度偏快",
                    "第 %d 天已用 %.0f%%,超出正常进度".formatted(dayOfMonth, ratio*100));
        }
    }
}

第三条「进度与日期不匹配」很实用:它能在月中就发现问题,而不是等到月底。

5.3 内部结算(chargeback) ​

java
/** 按租户生成月度账单,供内部结算 */
public Invoice invoice(String tenantId, YearMonth month) {
    List<CostRow> rows = byTenant(tenantId, month);
    double total = rows.stream().mapToDouble(CostRow::cost).sum();

    return new Invoice(tenantId, month, rows, total,
            pricingPlan.of(tenantId));      // 可能有套餐价
}

5.4 成本模型表导出 ​

java
/** 导出为 xlsx,供汇报使用 */
public void exportModel(Path out) {
    // 列:场景 / 月调用量 / 单次成本 / 月成本 / 替代成本 / 净收益
}

6. 跑起来 ​

bash
git checkout ch04-07-cost
docker compose up -d
mvn spring-boot:run
bash
# 1. 成本报表
curl "http://localhost:8080/api/cost/report?month=2026-10"
# 期望:按租户 × 场景的明细

# 2. Top10
curl "http://localhost:8080/api/cost/top?limit=10"

# 3. 预测
curl "http://localhost:8080/api/cost/forecast"
# 期望:{"dailyAvg":26.4,"monthlyForecast":792,"budget":2000,"ratio":0.396}

# 4. 告警验证(注入一笔大额用量)
curl -X POST http://localhost:8080/admin/cost/inject -d '{"yuan":500}'
# 期望:触发「成本异常」告警

# 5. 导出成本模型表
curl "http://localhost:8080/api/cost/export" -o cost-model.xlsx
检查项通过标准
三维归因租户/场景/模型都能出报表
预测可用给出月预测与预算占比
三类告警进度/异常/进度偏快都能触发
模型表含「替代方案成本」列
结算能按租户出账单

7. 生产避坑 ​

  1. 成本模型表必须有「替代方案成本」列。孤立的成本数字没有说服力,对比人力成本才能体现价值。这一列是预算审批能不能过的关键。
  2. 必须做单日异常检测,不能只看月度预算。月度预算告警发现问题时钱已经花完了。单日突增(3 倍日均)往往意味着死循环或滥用,早发现能省下大钱。
  3. 每一次降本都要用评测集验证质量。缓存、模型降级、上下文压缩都会影响效果。做法是:每次降本动作跑一次评测,召回率/完成率下降超过阈值就回滚。省了钱但效果崩了,比不省钱更糟。

8. 延伸与锚点 ​

  • 思考题:技术、成本、合规都讲完了,最后一件事——怎么证明团队用 AI 真的提效了?(04 篇最后一节)
  • 代码锚点:git checkout ch04-07-cost
  • 下一课时:04-08 团队与效能度量
  • 对应课件:L04-07 成本治理