模型可以把“给孩子买两本书,哪种组合划算”理解得更顺,却不应该直接决定收银台扣多少钱。金额一旦进入支付链路,规则版本、商品范围和取整方式都必须可核对。
我会把这条边界放在接口上:模型提交候选购物车,代码重新核算,展示确认后才进入结算。模型说得再有道理,也不能跳过一次确定性计算。
候选、校验与执行分开
假设一家演示商店只有“满 300 元减 50 元”这一条规则。这里的金额都是分,数量有明确上限。模型可能提出两种商品组合,但价格必须来自服务端商品表,不能信模型传回来的单价。
package main import ( "errors" "fmt" ) type Item struct { SKU string; Qty int64 } func payable(cart []Item, prices map[string]int64) (int64, error) { if len(cart) == 0 || len(cart) > 20 { return 0, errors.New("invalid cart size") } var gross int64 seen := map[string]bool{} for _, item := range cart { price, ok := prices[item.SKU] if !ok || seen[item.SKU] || item.Qty < 1 || item.Qty > 10 || price < 0 || price > 1000000 { return 0, errors.New("invalid item") } seen[item.SKU] = true gross += price * item.Qty } if gross >= 30000 { return gross - 5000, nil } return gross, nil } func main() { prices := map[string]int64{"A":15000, "B":14999} for _, c := range [][]Item{ {{"A",1},{"B",1}}, {{"A",2}}, {{"A",1},{"A",1}}, } { n, err := payable(c, prices) fmt.Printf("pay=%d accepted=%t\n", n, err == nil) } }保存为 main.go 后运行 go run main.go。三个构造输入分别检查门槛以下、刚好达到门槛和重复 SKU。价格、条目数与数量上限也使本例的整数求和范围有界。真实商城还要加入库存、税费、互斥券、运费和价格快照;这里没有假装覆盖这些规则。
服务端重新计算还有一个好处:模型提了一个不存在的优惠,计算器会直接拒绝或忽略不存在的规则,而不是继续为它编理由。
推理成本要和任务质量放在一起看
OpenAI 推理模型文档区分推理 token 与可见输出。请求即使没有返回可用答案,也可能消耗推理预算。对用户来说,等待时间也包括网络、工具、排队和重试,不能只看首字出现得快不快。
下面两件事值得做,但本文没有模型接口实测:
- 用同一批带标准答案的购物车测试不同推理设置,分别统计合法候选率、规则核验通过率、总耗时和完整任务费用。
- 将“生成建议”与“找全局最优解”分开验收。前者可能足够改善搜索体验;后者需要明确约束、穷举基线或可靠求解器,不能靠一段解释证明。
DeepSeek 当前模型与计价文档还区分模型版本、缓存与峰谷价格。一次测试的费用不能仅写“某模型每次多少钱”,至少要带日期、版本、输入/输出量和计费时段。
模型出错后,系统能不能停在正确的位置
候选无效时,产品可以继续问一个澄清问题,或者展示已核验的商品组合。最不该做的是拿上一次优惠结果凑一个“看起来顺畅”的答案。
我更看重一个朴素的验收条件:无论模型如何改写商品名、数量和优惠解释,进入付款的金额都由同一套业务规则重新算出。它并不保证规则本身没有 bug,所以仍需边界测试和订单对账;但它让模型的错误停在建议层,而不是直接变成资损。
本地验证采用 Go 1.26.5,只运行上述构造案例。没有调用真实模型,没有评估转化率,也没有得出任何生产降本比例。