自建 vs 调用第三方模型 API:半年 TCO 对比分析

一、"自建省钱"这个结论在大多数情况下是错的

技术团队有一个常见错觉:API 调用是持续开销,自建是一次性投入,所以自建肯定更省钱。这个结论听起来合理,但把账算细之后,结论完全不同。

去年我们团队面临一个决策:内部知识库问答系统上线 3 个月了,日均调用量从最初的 500 次涨到了 3 万次,月 API 费用从 200 块涨到了 1.2 万。CTO 直接拍桌子说"自建"。我花了两天时间做了一个 TCO 模型,结果发现:即使按日均 10 万次调用算,半年内自建成本是 API 的 9 倍。

自建推理服务的成本远不止买卡。GPU 服务器购置或租赁是最大头,A100 80GB 云实例约 $2/小时,两卡配置一个月就是 $2,880。运维人力至少 0.5 个全职工程师,月薪按 1 万算半年就是 6 万。模型部署和优化的工程师时间,初次部署至少 2 人月。显存和电力消耗也是持续开销。这些固定成本不管你调不调用都在烧钱。

调用 API 的成本则完全不同。按 Token 计费,GPT-4o-mini $0.15/百万输入 Token、$0.60/百万输出 Token。无运维成本,无硬件折旧。用多少算多少,不用不花钱。

关键区别在于成本结构:自建是高固定成本 + 零边际成本,API 是零固定成本 + 线性边际成本。盈亏平衡点取决于调用量。但这个平衡点远比大多数人想象的高。

二、半年 TCO 对比模型

我们把所有成本拆成三个维度:硬件成本、运维成本、机会成本。硬件成本包括 GPU 租赁或购置、网络带宽、电费。运维成本包括专职运维工程师、模型更新跟进。机会成本是最容易被忽略的——自建方案需要 2 人月的部署调优时间,这 2 人月本可以用来做业务功能。

API 方案的成本结构简单:只有调用费用。按日 10 万次调用、每次约 500 输入 + 300 输出 Token 算,半年约 $9,000。自建方案半年 TCO 约 $81,000,其中 GPU 租赁占 $43,200(两卡 × $2/h × 24h × 180d),运维人力占 $30,000,部署调优占 $7,800。

结论很明确:日调用低于 180 万次时,API 方案经济性碾压自建。而我们大多数内部系统的日调用量在 1 万到 10 万之间,离这个平衡点差了两个数量级。

flowchart TB
    A[TCO 对比维度] --> B[硬件成本]
    A --> C[运维成本]
    A --> D[机会成本]

    B --> B1[自建: A100 ×2, $35,000/半年]
    B --> B2[API: $0]

    C --> C1[自建: 0.5 FT 工程师, $30,000/半年]
    C --> C2[API: $0]

    D --> D1[自建: 部署调优 2 人月, $16,000]
    D --> D2[API: 0]

    B --> E[API 调用成本]
    E --> E1[自建: $0 边际成本]
    E --> E2[API: 日10万次 × $0.0005 × 180天 = $9,000/半年]

    F[自建 TCO: $81,000]
    G[API TCO: $9,000]

    F -.-> H[结论: 日调用 < 180万次时<br/>API 更经济]

三、Go 成本计算工具

下面是用 Go 实现的 TCO 计算器,我们团队用它做季度成本复盘。比 Python 版本的好处是编译成二进制后可以直接分发给非技术同事使用。

package main

import (
	"fmt"
	"strings"
)

// SelfHostedCost 自建推理成本
type SelfHostedCost struct {
	GPUModel         string  // GPU 型号
	GPUCount         int     // GPU 数量
	GPUHourlyRate    float64 // 云 GPU 小时价(美元)
	OpsEngineerMonthly float64 // 运维人力月成本(美元)
	BandwidthMonthly  float64 // 带宽月费
	ElectricityMonthly float64 // 电费月成本
	SetupCost         float64 // 一次性部署调优成本
}

func DefaultSelfHosted() *SelfHostedCost {
	return &SelfHostedCost{
		GPUModel:           "A100-80GB",
		GPUCount:           2,
		GPUHourlyRate:      2.0,
		OpsEngineerMonthly: 5000,
		BandwidthMonthly:   200,
		ElectricityMonthly: 300,
		SetupCost:          16000,
	}
}

func (s *SelfHostedCost) MonthlyFixed() float64 {
	gpuMonthly := float64(s.GPUCount) * s.GPUHourlyRate * 24 * 30
	return gpuMonthly + s.OpsEngineerMonthly + s.BandwidthMonthly + s.ElectricityMonthly
}

func (s *SelfHostedCost) TCO6Months() float64 {
	return s.SetupCost + s.MonthlyFixed()*6
}

func (s *SelfHostedCost) TCO12Months() float64 {
	return s.SetupCost + s.MonthlyFixed()*12
}

// APICost API 调用成本
type APICost struct {
	Model              string
	PromptPricePer1M   float64 // 每百万输入 Token(美元)
	OutputPricePer1M   float64 // 每百万输出 Token(美元)
}

func DefaultAPI() *APICost {
	return &APICost{
		Model:            "gpt-4o-mini",
		PromptPricePer1M: 0.15,
		OutputPricePer1M: 0.60,
	}
}

func (a *APICost) CostPerCall(promptTokens, outputTokens int) float64 {
	return float64(promptTokens)/1_000_000*a.PromptPricePer1M +
		float64(outputTokens)/1_000_000*a.OutputPricePer1M
}

func (a *APICost) TCO6Months(dailyCalls, promptTokens, outputTokens int) float64 {
	return a.CostPerCall(promptTokens, outputTokens) * float64(dailyCalls) * 30 * 6
}

// TCOComparator 对比分析
type TCOComparator struct{}

func (c *TCOComparator) Compare(self *SelfHostedCost, api *APICost, dailyCalls int) string {
	api6m := api.TCO6Months(dailyCalls, 500, 300)
	self6m := self.TCO6Months()
	ratio := api6m / self6m * 100

	var sb strings.Builder
	sb.WriteString(fmt.Sprintf("日调用 %d 次 | API/半年=$%.0f | 自建/半年=$%.0f | API占自建 %.0f%%\n",
		dailyCalls, api6m, self6m, ratio))

	switch {
	case ratio < 50:
		sb.WriteString(fmt.Sprintf("建议: 使用 API(成本仅为自建的 %.0f%%)\n", ratio))
	case ratio < 100:
		sb.WriteString("建议: API 更经济,可考虑混合方案\n")
	default:
		sb.WriteString("建议: 调用量超临界点,评估自建方案\n")
	}
	return sb.String()
}

func (c *TCOComparator) FindBreakeven(self *SelfHostedCost, api *APICost) int {
	for daily := 1000; daily < 10_000_000; daily += 10000 {
		if api.TCO6Months(daily, 500, 300) > self.TCO6Months() {
			return daily
		}
	}
	return -1
}

func main() {
	self := DefaultSelfHosted()
	api := DefaultAPI()
	comp := &TCOComparator{}

	fmt.Println("=== 自建 vs API 半年 TCO 对比 ===")
	fmt.Printf("自建配置: %s ×%d, 运维 0.5人月\n", self.GPUModel, self.GPUCount)
	fmt.Printf("API 模型: %s\n\n", api.Model)

	for _, daily := range []int{1000, 5000, 10000, 50000, 100000, 500000, 1000000} {
		fmt.Println(comp.Compare(self, api, daily))
	}

	breakeven := comp.FindBreakeven(self, api)
	if breakeven > 0 {
		fmt.Printf("收支平衡点: 日调用 %d 次\n", breakeven)
	}
}

运行结果示例:日调用 1 万次时 API 半年 $900,自建 $81,000,API 仅为自建的 1%。日调用 50 万次时 API 半年 $45,000,仍只有自建的 56%。直到日调用超过约 180 万次,自建才开始经济。

四、隐性成本不容忽视

显性成本只是冰山一角。API 方案的隐性成本包括:数据安全和合规风险(敏感数据外传到第三方)、供应商锁定(换模型供应商需要改大量代码)、速率限制(QPS 上限可能制约业务增长)。我们踩过供应商锁定的坑:最初只用 OpenAI API,后来想切换到国产模型降成本,发现 Prompt 工程、函数调用格式、流式解析全都要改,迁移成本约 3 人月。

自建方案的隐性成本更隐蔽。模型版本更新滞后于 API,你用自建的 Llama 3 70B,人家 API 已经上了 GPT-4o,效果差距可能差一代。需要持续跟进 GPU 驱动、CUDA 版本、推理框架(vLLM、TGI)的更新。故障排障时间更长,API 有 SLA 保障,自建出了问题全靠自己排查。我们有一次 vLLM 版本升级导致推理结果质量下降,排查了 3 天才发现是 quantization 配置的问题。

还有一个被忽略的成本:自建方案需要持续的模型评估和调优。API 方案的模型供应商已经做好了 RLHF 和安全对齐,自建开源模型需要自己跑评估、调 Prompt、做安全过滤。这至少需要 0.3 个全职人力。

五、总结

日调用低于 50 万次时,API 方案 TCO 远优于自建。自建的固定成本(GPU + 人力)是决定性因素,不管用不用都在烧钱。API 的边际成本低,但随调用量线性增长。收支平衡点约在日调用 100-200 万次,远高于大多数内部系统的实际调用量。

决策时需同时权衡隐性成本:数据安全、供应商锁定、运维复杂度。如果数据合规要求极高不能外传,即使成本更高也得自建。如果调用量不大但增长快,建议 API 起步,提前做好抽象层为未来切换留路。

务实建议:先用 API 把业务跑起来,用实际数据验证调用量。月 API 费用超过 5 万时再认真评估自建。自建不是省钱,是换一种方式花钱——把可变的 API 费用换成固定的 GPU + 人力,只有规模够大才划算。

Logo

汇聚全球AI编程工具,助力开发者即刻编程。

更多推荐