
1. 项目缘起从“AI垃圾”到可信赖的成本计算器做云成本估算的开发者大概都听过用户这样的反馈“这数据准吗看着像AI生成的。” 我开发的 Azure 成本计算器 AzureCalc.uk 在最初上线时收到的第一条评论就是如此。说实话当时我有点被刺痛但也不得不承认用户说得有道理。最初的版本我为了快速上线直接把 Azure 官方文档里的定价常量硬编码进了程序页面上的说明文字也是通用模板整个工具缺乏透明的方法论——它看起来就像一个没有灵魂、只会吐数字的黑盒这恰恰是当下许多“AI生成内容”的典型特征看似正确但经不起推敲背后没有可验证的逻辑。用户的这句“AI垃圾”成了项目最重要的转折点。我意识到在数据工具领域信任不是靠华丽的界面或夸张的宣传建立的而是靠“展示你的工作过程”。就像解数学题只给最终答案没人信服必须把每一步的推导、每一个数据的来源都摊开来看。这个项目从此不再只是一个计算器而变成了一场关于如何构建可信数据系统的技术实践。我的目标很明确要让计算器输出的每一个英镑、每一分钱都能追溯到官方的价格源并且能与真实的账单对得上。这不仅仅是技术问题更是一种产品哲学——对抗“AI垃圾”感的解药就是极致的透明与可验证性。2. 数据基石构建可靠、可追溯的价格数据管道任何成本计算工具其生命线都是数据。如果底层数据不可靠那么无论前端交互多么炫酷计算结果都是空中楼阁。我的首要任务就是建立一个稳定、自动更新且源头可追溯的数据管道。2.1 放弃爬虫与手动维护拥抱官方 API最初我也考虑过从 Azure 定价页面爬取数据或者手动维护一个 CSV 文件。但这两种方式都不可持续。页面结构会变手动更新既繁琐又容易出错。幸运的是微软提供了官方的Azure Retail Prices APIprices.azure.com。这是微软自身用于发布全球各区域、各种货币定价的权威数据源其权威性和及时性是任何第三方爬虫都无法比拟的。使用它意味着我的工具与 Azure 官方的定价信息保持了同步。然而这个 API 返回的数据量是巨大的。仅“英国南部”区域、英镑计价的 SKU库存单位就有超过 5 万行而且每月都可能更新。显然我们不可能在用户每次打开计算器时都去实时查询这个庞大的 API那将导致无法接受的延迟。2.2 架构设计从 API 到可查询的数据库为了解决性能问题我设计了一个基于 Cloudflare Workers 和 D1Cloudflare 的 SQLite 数据库的数据管道。其核心工作流程如下定时触发通过 Cloudflare 的 Cron Triggers 设置一个每日任务例如 UTC 时间凌晨 2 点自动触发数据更新 Worker。数据获取与过滤Worker 启动后会调用 Azure Retail Prices API但并非全量抓取。我会在请求中附加严格的过滤器例如armRegionName eq ‘uksouth’和currencyCode eq ‘GBP’确保只拉取与我的计算器相关区域和货币的数据。这极大地减少了需要处理的数据量。数据清洗与入库获取到 JSON 数据后Worker 会进行必要的清洗和格式化然后执行INSERT OR REPLACE语句将数据存入 Cloudflare D1 数据库的prices表中。这张表的结构经过精心设计包含了 SKU 名称、产品名、计价单位、单价、区域、货币以及最重要的lastUpdated时间戳。注意这里使用INSERT OR REPLACE而非简单的INSERT是关键。它能确保当同一个 SKU 的价格更新时新数据会覆盖旧数据同时保持其他关联信息如 SKU ID不变避免了数据重复或引用失效的问题。这个管道带来的好处是立竿见影的性能所有前端计算请求现在都直接查询本地的 D1 数据库响应时间在毫秒级。可追溯性数据库中的每一条价格记录都带有获取时间戳我们可以清楚地知道某个价格是什么时候从官方 API 抓取的。可靠性即使 Azure 的 API 临时不可用我们的计算器依然可以使用最近一次成功抓取的数据提供服务保证了工具的可用性。3. 核心陷阱硬编码常量与动态数据的对决有了可靠的数据管道接下来就要解决最初版本的核心问题硬编码。在“Sprint 0”的草率版本中我犯了一个经典错误。例如对于 Log Analytics 的按需付费PAYG价格我直接从某篇 Azure 文档里找到了“£2.76/GB”这个数字并把它写死在了代码里。// ❌ 过去的错误做法硬编码常量 const LOG_ANALYTICS_PRICE_PER_GB 2.76;这种做法看似省事但隐患巨大。云服务的定价是动态的文档更新可能滞后甚至可能存在笔误。当我将数据管道搭建好首次从真实的 API 数据中查询 Log Analytics 的价格时结果让我吃了一惊实际价格是£2.10/GB。数据源Log Analytics 价格 (GB/月)偏差硬编码常量 (来自旧文档)£2.76基准Azure Retail Prices API (实际)£2.10-23.9%一个简单的硬编码导致了近 24% 的成本估算误差。对于动辄每月成千上万英镑的云账单来说这个误差是完全不可接受的也直接导致了用户对工具信任的崩塌。3.1 解决方案D1-First 开发原则痛定思痛我确立了一条铁律“D1-First”开发原则。任何价格在进入代码逻辑之前必须首先存在于 D1 数据库的prices表中。这成为了所有计算器组件开发的强制前置条件。具体的工作流变成了这样开发新计算器时首先在 D1 数据库中查询目标服务所有可用的 SKU 和价格确认所需的数据已存在。前后端匹配前端下拉菜单或输入框的选项SKU 名称、层级必须与数据库中的skuName等字段精确匹配。这里使用了严格的字符串比对避免因大小写或空格导致的匹配失败。查询验证在计算逻辑中不是使用常量而是动态构建 SQL 查询语句根据用户输入去 D1 中查找对应的单价。这个查询语句本身会被记录下来。UI 展示最终在用户界面展示的价格必须与数据库查询结果完全一致并在公式披露区域展示出来。这套流程彻底杜绝了“拍脑袋”定价格的可能确保了计算逻辑的每一个数字都有据可查、有源可溯。4. 终极验证用真实发票校准计算模型API 价格是“标价”而发票是“实付金额”。两者之间可能存在差距原因包括企业协议EA、云解决方案提供商CSP折扣、微软客户协议MCA中的优惠条款等。对于我的计算器首要目标是准确反映公开的按需付费PAYG价格。那么如何证明计算器标价与 Azure 实际账单的 PAYG 部分是一致的答案就是进行“发票对账”。我建立了一个季度性的手动验证流程这就像为计算器做定期的“体检”部署测试负载在独立的、纯净的 Azure 订阅中部署一个结构明确的测试工作负载。例如一个包含特定配置的虚拟机、一个设置了数据保留策略的 Log Analytics 工作区以及一个应用服务计划。这些资源都严格使用 PAYG 费率。运行完整计费周期让这个测试环境运行整整一个月一个完整的 Azure 计费周期产生足够的、可计费的使用量。捕获发票数据在 Azure 门户下载该订阅该计费周期的详细发票CSV 格式。这份发票是来自微软的、具有法律效力的计费事实。计算器模拟使用完全相同的资源配置区域、SKU、使用量在我的计算器中进行模拟。逐行比对将发票上的每一行项目Service Meter、资源 ID、数量、扩展成本与计算器输出的明细进行逐行比对。实操心得比对的关键在于“计量单位”和“资源 ID”的映射。Azure 发票上的MeterId需要与 Retail Prices API 中的meterId对应起来。第一次做对账时我花了大量时间建立这个映射表。现在这个映射关系已经成为我验证数据管道准确性的核心资产。通过多次对账我的计算器在 PAYG 资源上的估算与真实发票的差异被控制在0%四舍五入到小数点后两位。这个结果是反驳“数据不准”质疑的最有力证据。当然我也在工具的/methodology页面明确说明此精度仅适用于 PAYG 费率EA/CSP 等合约价格因折扣而异不在本工具的直接计算范围内。5. 透明化设计公式披露组件——展示每一步计算信任的建立光有后台的严谨还不够必须让用户在前台就能“看见”。我认为最有效的信任信号就是在给出计算结果的同时清晰地展示这个结果是如何算出来的。为此我设计并实现了FormulaDisclosure组件。这个组件不是一个静态的、写死的文本块。它是一个动态的 React 组件接收两个核心输入1) 从 D1 数据库查询到的原始价格数据对象2) 用户在前端输入的所有参数如区域、SKU、数量、运行时间。它的工作流程如下数据绑定组件内部解析价格数据对象提取出productName,skuName,unitPrice,unitOfMeasure等关键字段。公式生成根据服务类型如虚拟机是按小时计费存储是按月计费动态组装出人类可读的计算公式字符串。实时渲染将生成的公式和所有关键参数包括价格获取时间直接展示在计算结果旁边。例如一个计算了 730 小时一个月的 B2s 虚拟机成本其公式披露会显示计算明细 • 层级按需付费 · 区域英国南部 • SKU: Standard_B2s · 单价£0.0424 /小时 (数据获取于2023-10-27来源Azure Retail Prices API) • 计算公式£0.0424/小时 × 1 个实例 × 730 小时 £30.952 • 总计约£30.95 /月这个组件一举两得验证懂行的工程师或财务人员可以立刻拿这个单价去 Azure 官网或自己的账单核对瞬间完成验证。教育对于不熟悉云计费模式的用户它直观地展示了 Azure 的计费逻辑单价 × 数量 × 时间帮助他们理解账单的构成而不仅仅是一个神秘的数字。6. 系统活性证明价格历史与信任状态栏一个数据工具最怕的就是“看起来被遗弃了”。静态的页面、陈旧的数据日期都会让用户心生疑虑“这价格还是一年前的吗” 为了证明 AzureCalc.uk 是一个“活”的系统我引入了两个关键功能。6.1 价格历史页面我创建了一个独立的/history页面。在后台每次数据管道 Worker 更新价格时如果检测到某个 SKU 的价格发生了变化不仅会更新主prices表还会向price_history表插入一条记录包含skuId,oldPrice,newPrice,changedAt。这个历史页面就展示最近 10 次或 20 次价格变动。用户可以看到“Azure Database for PostgreSQL 灵活服务器Gen5 2 vCore 的价格在 2023年10月15日从 £0.204/小时 降至 £0.192/小时。”“标准 SSD 托管磁盘P10 的价格在 2023年9月1日更新。”这个页面无声地传递了三个信息1) 数据不是静态的2) 系统在自动监控价格变化3) 我们有完整的变化审计日志。这比任何“我们数据最新”的标语都更有说服力。6.2 全局信任状态栏在网站的每个计算器页面的页脚都有一个固定的TrustBar组件。它非常简洁只显示三行信息系统状态活跃 最新价格同步2023-10-27 02:00 UTC 当前缓存价格数52,147 (UK South, GBP)这同样不是装饰。最新价格同步的时间戳直接来自最后一次成功运行的数据管道 Worker 日志。缓存价格数是实时从 D1 数据库SELECT COUNT(*)查询得到的。如果数据管道崩溃这个时间戳就会停滞数量也可能异常。它就像一个持续跳动的心率监测仪让系统的健康状态一目了然。7. 防御性编程用 Zod 模式验证防止数据漂移在分布式或前后端分离的系统中一个常见的失败模式是“数据漂移”前端新增了一个功能或选项但后端 API 并没有做好接收它的准备。在我的架构里前端是 React 应用后端是 Cloudflare Worker API。如果前端计算器新增了一个“E64 v3”虚拟机型号但后端的 Worker 在处理请求时其数据验证模式Schema不认识这个新 SKU请求就会以 HTTP 400 错误失败用户在前端只会看到冰冷的 “Error /mo”。为了避免这种情况我引入了Zod这个 TypeScript 模式验证库并将其作为前后端契约的强制守护者。7.1 共享验证模式我在代码库中创建了一个共享文件workers/api/validation.ts。在这个文件中我使用 Zod 为每一个计算器 API 的请求体和响应体定义了严格的模式。例如虚拟机成本计算器的请求模式可能如下import { z } from zod; export const VmCalculatorSchema z.object({ region: z.enum([uksouth, ukwest]), // 只允许明确的区域 skuName: z.string().min(1), // SKU名称必须非空 instanceCount: z.number().int().positive(), // 实例数必须是正整数 hoursPerMonth: z.number().min(0).max(744), // 每月小时数在合理范围 }); export type VmCalculatorInput z.infertypeof VmCalculatorSchema;7.2 开发流程整合这个共享模式成为了开发流程中的强制关卡当产品需要新增一个 SKU 或计算维度时开发者必须首先更新这个共享的 Zod 模式。后端 Worker 会在处理请求的第一时间用这个模式验证输入。任何不匹配的请求都会被立即拒绝并返回清晰的错误信息如”Invalid skuName: ‘Standard_E64_v3’. Expected one of …”。前端在构建请求时也可以使用同样的 Zod 模式进行运行时验证甚至在开发阶段通过 TypeScript 类型获得智能提示和编译时检查。我将“更新并验证共享 Zod 模式”加入了每个冲刺Sprint结束前的检查清单。这种做法将潜在的运行时错误提前到了开发或编译阶段极大地提高了系统的健壮性和开发体验的一致性。8. 实践复盘从质疑到认可的转变与持续构建经过三个开发冲刺的迭代工具发生了根本性的变化。最让我有成就感的时刻是看到最初那个“AI垃圾”评论的同一讨论串里出现了一位云架构师的新留言“其中的 KQL 查询构建器部分是我认为最应该深入发展的功能……如果你能将计算器的输出与能反映用户工作空间实际成本的 KQL 查询配对你就实现了一个‘紧密闭环’这是目前该领域其他工具都做得不够好的。”这段评论点出了工具价值跃升的关键它从一个孤立的、单向的“计算器”进化成了一个能与用户真实运维环境通过 KQL 查询日志和账单数据进行比对、验证的“诊断系统”。这个闭环正是“感觉像被维护的工具”和“感觉像被生成的内容”之间的本质区别。我的“方法论”不再是一篇写在静态页面上的文档而是每晚 UTC 2:00 准时运行的 Cron 任务是每次价格变动都被记录的历史表是每个计算结果旁那个可点击展开的公式框。这些持续运行的、可见的“操作证据”共同构建了用户的信任。如果你也在构建数据驱动的工具无论领域是否相同我都强烈建议引入这种“可验证性”的 rigor严谨性数据溯源每个关键数字都要能说清从哪里来、何时来的。过程透明把计算过程、验证方法尽可能展示出来。持续验证建立与真实世界数据如发票、日志的定期核对机制。系统活性通过时间戳、变更日志等方式证明你的系统是“活”的。用户可能永远不会去仔细阅读你的技术架构文档但他们一定能感受到一个数据陈旧、反馈迟缓的静态页面与一个数据新鲜、反馈透明、持续演进的“活系统”之间的巨大差异。这种差异就是信任的基石。构建可信的工具是一场始于技术、终于体验的持久旅程。