这学期有一门《运营与供应链管理》,第二次课留了个案例作业:南方超级医疗设备公司的设施选址。题目不难,标准的重心法(Center of Gravity)单仓库选址问题,考的是套公式 + ROI 判断。
我照例先让 Claude 做了一份,给了一个看起来挺漂亮的参考答案。觉得不放心,开了一个新的会话用 Codex 再独立跑了一遍。拿到结果之后我愣住了:两份答案的思路完全一致,计算出来的最优坐标相差不大,但最终 ROI 正负相反、决策完全相反。
- Claude:重心在 (7.42, 5.04),年运输节约 $46,838,ROI −188%,结论:续租堪萨斯城,别搬
- Codex:重心在 (7.58, 4.51),年运输节约 $223,646,ROI +41.22%,结论:应当搬迁
两个都是基于同一份 PDF 案例、同一个重心法公式,怎么会算出一正一负?
左右互博:把裁判位置也交给 AI
我本来的计划是自己动手验算一遍,但看了一会儿发现这事没那么简单——重心法有两个版本(一次加权平均 vs Weiszfeld 迭代)、费率 R 的单位有两种理解(美元/担 vs 美元/担/英里)、外向成本又有两种建模方式。要真人判裁谁对谁错,我得先把讲义从头翻一遍。
懒病发作,我干脆把两份答案一起丢回一个新的 Claude 会话,原话是这样的:
你完成的解答位于:南方医疗设备公司设施选址_参考答案-Claude.md 我使用其他模型解答的结果和你基本不一致,虽然思路是相同的,结果位于:南方医疗设备公司设施选址-案例解答-Codex.md 请检查双方究竟谁的逻辑存在问题,并给出支撑理由,如有必要,请修正完善最终版本结论
注意这里:Claude 开新会话后其实是没有前次对话记忆的,所以这份 _参考答案-Claude.md 对它而言就是一份陌生的答案,和 Codex 的那份在它眼里是对等的。这样它就没有「维护自己面子」的心理包袱。
Claude 翻出了自己的错
它做的第一件事是去读讲义 PDF 里重心法的原始公式。这一步关键——它不是在两份答案的输出之间找分歧,而是把两者都对照原题和教材做 ground truth 校对。
几分钟后它给出了结论:
Codex 的逻辑正确,Claude 的参考答案存在三处方法论错误。
三处错误它一条条列了:
1. R 的单位错。 讲义里目标函数是 TC = Σ Vᵢ·Rᵢ·dᵢ,要求 Rᵢ 为「美元/担/英里」。案例表里给的是「美元/担(当前线路总费率)」,必须除以当前距离才能换算。案例原文第 2 页其实写得很清楚:「可估计出以仓库为起点的外向运输平均成本为 0.0235 美元/担/英里」——这句话就是在提示学生做单位换算。前一次 Claude 把这段忽略了,直接把美元/担当权重用,相当于把当前距离"二次放大"进了权重里,让远距离客户影响被高估。
2. 没迭代。 讲义 p.13 把步骤写得很明白:先用 X₀ = Σ(V·R·X)/Σ(V·R) 算一个初始坐标,然后根据 dᵢ 做 Weiszfeld 迭代直到收敛。前一次 Claude 算完初始坐标就收工了。
3. 外向费率结构错。 案例文本明确给了一个统一的外向均值 0.0235。前一次 Claude 反其道而行之,按每个客户的当前总费率和距离回推了每客户不同的每英里率。这会让 Chicago 显得每英里 0.050、Montreal 每英里 0.019,两者相差 2.6 倍——这种人为的不一致把新选址的外向成本虚推高了,把真实 $223K 的年节约压成虚假的 $47K。
这三处错误串在一起,合力把最优点错误地向东北拉偏、又错误地抬高了新址的外向账单,最终得到"别搬"的结论。Codex 在这三点上都是按讲义和案例原文来的。
用 Python 作第三个独立裁判
到这一步其实已经够了,但我让 Claude 再做一件事:别用自己的文字推理下结论,写段 Python 从头算一遍,以程序的数值输出为准。
下面这段是它写的(我稍微整理了一下):
import math
k = 230.0 # 1 坐标单位 = 230 英里
# (V, R_per_mile, X, Y) — 内向用个别每英里率,外向统一 0.02346
nodes = [
(61500, 0.01438521, 3.60, 3.90), # 菲尼克斯
(120600, 0.00791246, 6.90, 1.00), # 蒙特雷
(17000, 0.02346330, 0.90, 9.10), # 西雅图
(32000, 0.02346330, 1.95, 4.20), # 洛杉矶
(12500, 0.02346330, 5.60, 6.10), # 丹佛
( 9500, 0.02346330, 7.80, 3.60), # 达拉斯
(29500, 0.02346330, 10.20, 6.90), # 芝加哥
(21000, 0.02346330, 11.30, 3.95), # 亚特兰大
(41300, 0.02346330, 14.00, 6.55), # 纽约
( 8600, 0.02346330, 12.70, 7.80), # 多伦多
(10700, 0.02346330, 14.30, 8.25), # 蒙特利尔
]
# 初始种子
X = sum(V*R*Xi for V,R,Xi,Yi in nodes) / sum(V*R for V,R,Xi,Yi in nodes)
Y = sum(V*R*Yi for V,R,Xi,Yi in nodes) / sum(V*R for V,R,Xi,Yi in nodes)
# Weiszfeld 迭代
for _ in range(100):
nx = ny = d = 0.0
for V,R,Xi,Yi in nodes:
di = k * math.sqrt((X-Xi)**2 + (Y-Yi)**2)
if di < 1e-6: continue
w = V*R / di
nx += w*Xi; ny += w*Yi; d += w
Xn, Yn = nx/d, ny/d
if abs(Xn-X) + abs(Yn-Y) < 1e-7: break
X, Y = Xn, Yn
def TC(Xp, Yp):
return sum(V*R * k*math.sqrt((Xp-Xi)**2 + (Yp-Yi)**2) for V,R,Xi,Yi in nodes)
print(f"optimum = ({X:.4f}, {Y:.4f})")
print(f"TC @ KC (8.20, 6.00) = {TC(8.20, 6.00):,.0f}")
print(f"TC @ Claude(7.42,5.04) = {TC(7.42, 5.04):,.0f}")
print(f"TC @ Codex (7.58,4.51) = {TC(7.58, 4.51):,.0f}")
print(f"TC @ optimum = {TC(X, Y):,.0f}")
跑出来的输出:
optimum = (7.5806, 4.5084)
TC @ KC (8.20, 6.00) = 6,983,017
TC @ Claude(7.42,5.04) = 6,792,698
TC @ Codex (7.58,4.51) = 6,759,371
TC @ optimum = 6,759,371
几件事一次性水落石出:
- Weiszfeld 迭代收敛到 (7.5806, 4.5084),和 Codex 的 (7.58, 4.51) 逐位对上
- Codex 那份的 TC $6,759,371 在正确方法下也正好是最优值
- Claude 前一份的 (7.42, 5.04) 在正确方法下 TC 是 $6,792,698——比最优值贵了 $33K/年,连局部最优都不是
- 堪萨斯城 TC $6,983,017 和题目给的 $6,982,104 差不到 0.02%(差异来自案例里距离和均值的四舍五入),说明用的参数都没偏
然后年节约 $223,646,减去每年 $10 万新增租金,一次性搬迁投入 $30 万,年 ROI 41.22%,远高于 25% 阈值。正确结论是应当搬迁。
然后它把自己之前写错的那份改了
我让它把修订版直接覆盖原文件,同时在文件顶上标注修订说明。它交出的文件顶部是这样的:
# 案例:南方超级医疗设备公司设施选址 —— 参考答案(修订版)
> 修订说明:本版本相对于初版修正了三处方法论错误——
> (1) 将运输费率 R 从「美元/担」换算为「美元/担/英里」;
> (2) 在初始种子 (X₀, Y₀) 的基础上补充 Weiszfeld 迭代;
> (3) 外向费率按案例原文统一取 0.02346 美元/担/英里,而非按客户回推。
> 修订后结论从「维持堪萨斯城」转为「应搬迁至 (7.58, 4.51) 附近」。
三个情景(Q1 基准、Q2 需求变化、Q3 费率上涨)的结论全部翻过来,ROI 从负值变成 41%–60% 区间,决策从"别搬"变成"都该搬"。
为什么「左右互博」比让单一模型深挖更靠谱
这件事让我后面做类似作业时固定下来了一套流程:一份答案让 A 模型做,一份让 B 模型做,再让 A 当裁判评 B,要求它必须回到原始材料(讲义/教材/题目原文)做 ground truth 校对,最后用 Python 当第三方复算。
为什么这么折腾?几个观察:
单一模型很难发现自己的方法论偏差。 如果我一开始就让 Claude 自己审自己那份答案,它大概率会在"既有框架"里找局部的算术错误,而不会去质疑 R 的单位、是否需要迭代这种框架本身的选择。Codex 那份答案的存在提供了一个不同的框架作为参照,这时候回到原题去校对才有意义。换句话说:两个答案的差异不在于谁算得更准,而在于把可能的解法空间暴露了出来。
当裁判比当选手更挑剔。 让 Claude 做题时它倾向于快速给出一个自洽的流程。让它当裁判时,它会去对照原文、逐字抠公式、做单位分析。同一个模型、同一个任务知识,输出质量差很远——角色不同,防御姿态不同。
Python 兜底是性价比最高的一步。 两个模型吵起来的时候,文字推理再漂亮也有可能都错。让它们最后落到一段可执行的代码上,数值一跑就有定论。这次 Python 确认 (7.5806, 4.5084) 是严格最优,也顺手把 Claude 自己给的 (7.42, 5.04) 彻底证伪了——文字说它"连局部最优都不是"显得没说服力,一行 TC(7.42, 5.04) = 6,792,698 > 6,759,371 谁都没法赖。
模型之间没有「面子」问题。 前面提到我没告诉新会话的 Claude 那份错答案是它自己写的。这是有意为之——同一个账号、同一个模型,它如果意识到是在审自己的作业,有没有可能措辞会软一点?我不敢保证。切掉身份线索,让它以陌生答卷的姿态去评,才放得开。
我有不担心的地方,也有担心的地方
不担心的是:作业本身是死题、有唯一答案、可以程序化验证。在这类场景下 AI + AI + 代码的组合已经比我自己从头算可靠得多——我自己算大概率也会在哪一步单位换算上犯 Claude 初版犯的那种错。
担心的是:不是所有作业都有"Python 可跑的 ground truth"。比如案例讨论题让写一份"定性分析报告"的时候,两个模型可能会写出两份看起来都通顺、实则都在偏差范围内的答卷,而我没有外部锚点去判对错。这种时候"左右互博"只能证伪明显的错误,不能正向保证正确。
但即便如此,让 AI 看它同行的作业依然比让 AI 看自己的作业要诚实。这是我这次学到的最实用的一招。
尾声
借助 AI ,我弄清楚了重心法为什么要迭代、为什么 R 必须是每英里率——这两件事要是我自己硬看讲义,大概率和 Claude 初版踩一样的坑。把作业交给 AI 之后,我反而把原理学得更明白了。
这件事我没推广到所有课——有些课的作业本来就是要自己算才算练到手(比如数理课)。但对那些"考的是方法,不是手算熟练度"的课,我现在的默认姿势就是两个模型互评 + 代码兜底。花的时间比自己硬做少得多,踩的坑反而更少。
下次有类似的折腾再来记录。
评论
评论使用 GitHub Discussions 承载;留言需要 GitHub 账号。