柠檬水摊挑战:用经营模拟评测GPT与Claude的决策能力 把 GPT 和 Claude 同时扔进同一个柠檬水摊让它们在连续三十天里自己决定卖多少钱、进多少货、要不要做促销最后看谁账户里的现金更多。这个看似娱乐性的“柠檬水摊挑战”实际上是一种很有价值的大模型评测方式它不要求模型回答问题而是要求模型在一个动态环境里反复做经营决策并且直接承担自己决策造成的财务后果。这类实验经常以视频形式出现在技术社区里很多人看完只记住了“谁赢了”却忽略了实验背后的设计逻辑。真正值得复现的不是输赢而是那套“环境模拟、决策接口、结果采集、策略复盘”的完整链路。本文会从零搭建一个可运行的柠檬水摊经营模拟器把 GPT 和 Claude 分别封装成决策客户端让它们在相同天气、相同客流、相同成本条件下独立经营最后对比两组经营指标。读完这篇文章你会得到一个可以反复修改和扩展的评测框架。你既能把它当作大模型 Agent 能力的对比实验也能换成其他模型或换成其他经营场景比如咖啡店、小超市、电商库存管理。文章核心会落在四个问题上这个实验到底在测什么、环境怎么搭、提示词怎么做到公平、结果怎么分析和排查。1. 先理解柠檬水摊挑战在测什么1.1 柠檬水摊模拟考察的不是语言能力而是决策闭环传统的大模型评测比如知识问答或代码生成考察的是模型“一次性的输出质量”。模型给出一个答案评测结束答案不会反过来影响后续输入。柠檬水摊实验完全不同它是一个多轮决策闭环模型先读取当前状态今天是晴天还是雨天、温度多高、客流大概多少、原料成本是多少。模型输出经营决策定什么价、生产多少杯、是否做促销。环境根据决策计算销量、收入和损耗生成新的现金和库存。第二天模型带着前一天的结果继续做决策。这里的核心难点在于“承担后果”。如果模型第一天定价过高导致滞销第二天它就必须面对现金减少和原料浪费的现实。优秀的模型会从结果里修正策略而只会“讲道理”的模型则可能连续犯错。这种连续闭环能力正是 Agent 类应用最需要验证的部分。1.2 与常见基准测试相比这类实验的差异在哪评测类型典型形式主要考察点天然局限知识问答MMLU 等分类题目知识记忆和单步推理静态问题无交互、无后果代码生成HumanEval 等函数题单点编码能力只看答案不看长期维护工具调用Function Calling 测试参数提取和调用正确性通常不检查策略合理性经营模拟柠檬水摊挑战连续决策、复盘、资源约束模拟环境与现实总有偏差知识问答考的是“模型知不知道”经营模拟考的是“模型能不能把知道的东西用在一连串动态决策里”。两者并不矛盾但适用场景不同。如果你想评估一个模型能不能胜任价格决策助手、库存管理 Agent 或营销策略建议工具单纯刷题式评测远远不够柠檬水摊这类沙盒实验反而更接近真实业务。1.3 评测目标要提前定清楚否则实验会失去比较意义动手之前先明确这次实验要回答什么问题。常见目标有三种不同目标对应不同设计比总利润看谁在 30 天里赚得更多设计时要把定价、备货、促销等因素都放在同一套需求模型里。比策略稳定性看谁在天气变化和成本波动下不会频繁“翻车”设计时要加入足够多的随机扰动。比解释质量看谁的决策理由更符合经营常识设计时需要在输出里保留 reason 字段并单独分析。目标一旦确定所有指标、随机种子、运行轮数都要围绕它固定下来。不要跑完再挑一个对自己有利的指标那是无效对比。后续所有代码示例都以“比总利润并兼顾策略稳定性”作为主线。2. 搭建同时驱动 GPT 和 Claude 的经营模拟环境2.1 模拟环境的四层结构先想清楚再写代码很多人在这个环节容易直接开写结果代码越来越乱。先把模拟器拆成四层逻辑会清晰很多世界层负责生成每天天气、温度、客流、原料成本相当于真实世界的“外部环境”。状态层用一个数据结构保存现金、库存、当日价格、销量、浪费量等经营状态。决策层把当前状态和历史摘要拼成提示词交给 GPT 或 Claude拿到结构化决策。结果层根据需求函数计算销量、更新现金和库存并记录日志。其中最容易做错的是决策层和结果层的边界。模型只负责“提出计划”比如“我打算卖 3.5 元一杯生产 80 杯”。环境负责“执行计划”也就是根据需求模型算出到底能卖出去多少杯。千万不要让模型自己计算销量因为大模型做算术并不可靠而且由模型自己算结果就等于给了它作弊和幻觉的空间。2.2 需求模型不能太简单否则实验一眼看穿需求模型是整个模拟器最核心的公式它决定了“经营得好不好”到底由什么因素决定。一个过于简单的需求模型比如“每天固定卖 60 杯”会让任何策略都失去意义。实际建议至少包含四个因子基础客流、天气系数、价格敏感度、随机扰动。import random def estimate_demand(traffic: int, weather: str, price: float) - int: weather_factor { sunny: 1.3, cloudy: 1.0, rainy: 0.5, }.get(weather, 1.0) # 价格越高需求越低以 3.0 元为基准价 price_factor max(0.1, 1.0 - (price - 3.0) * 0.4) demand int(traffic * weather_factor * price_factor) noise random.randint(-5, 5) return max(0, demand noise)这段代码的逻辑是晴天客流转化率明显更高雨天只有一半左右价格每比基准价高 1 元需求会下降约四成随机扰动模拟真实经营中的偶然因素。这样设计之后模型如果能在雨天主动降价、在晴天增加产量利润就会明显改善评测结果就有了区分度。2.3 状态数据结构和主循环是模拟器的主干状态对象建议用 dataclass 保存字段要少而清晰方便日志展示和后续扩展。from dataclasses import dataclass dataclass class DayState: day: int weather: str temperature: int traffic: int unit_cost: float price: float 0.0 produce: int 0 sold: int 0 waste: int 0 cash: float 50.0主循环按“生成状态、提交决策、执行结算、记录日志”四步推进def run_experiment(client, days: int 30, seed: int 42): rng random.Random(seed) history [] cash 50.0 for day in range(1, days 1): state DayState( dayday, weatherrng.choices( [sunny, cloudy, rainy], weights[0.4, 0.4, 0.2], )[0], temperaturerng.randint(22, 35), trafficmax(30, int(rng.gauss(100, 20))), unit_costrng.choice([1.2, 1.5, 1.8]), cashcash, ) decision client.decide(build_prompt(state, history[-7:])) state.price float(decision[price]) state.produce int(decision[produce]) state.sold min( estimate_demand(state.traffic, state.weather, state.price), state.produce, ) state.waste max(0, state.produce - state.sold) state.cash cash state.sold * state.price - state.produce * state.unit_cost cash state.cash history.append(state) return history这里有一个容易忽略的细节生产量超过需求量时超出部分直接算作浪费也就是原料损耗。如果模型每次都生产很多即使销售额很高浪费成本也会把它拖垮。这个机制逼着模型必须平衡“怕不够卖”和“怕卖不完”两个风险这正是库存管理的核心矛盾。2.4 依赖清单和学习环境差异组件用途建议版本方向Python运行模拟器3.11 及以上openai SDK调用 GPT 系列接口1.xanthropic SDK调用 Claude 系列接口最新稳定版pydantic校验模型输出2.xpytest回归测试模拟器7.x注意由于本文写作时不同模型和 SDK 版本更新较快代码示例只展示接口设计思路。实际落地前需要先确认你申请的 API Key 可用模型名、SDK 版本和计费方式再替换示例中的模型参数。学习环境可以只用固定种子、单次运行、日志输出到控制台。生产环境要额外考虑 API Key 隔离、调用失败重试、成本预算、结果落库和多轮实验的统计显著性这些会在第 7 节展开。3. 提示词和接口封装公平性的关键3.1 公平性从“同一套提示词”开始很多对比实验之所以不可信是因为两个模型收到的提示词不一样。有的实验给 GPT 写了详细背景给 Claude 只丢了一句话有的实验悄悄加了“你是资深经营顾问”这种身份词。结果差异根本说不清是模型能力差异还是提示词差异。正确的做法是提示词模板只有一个模型标识和客户端不同其他全部一致。模板里至少包含以下信息你的角色柠檬水摊主目标是 30 天后现金余额最大。当前日期、天气、温度、预计客流、原料成本。当前现金和最近 7 天的简要经营记录。输出要求必须是合法 JSON字段固定为 price、produce、promotion、reason。def build_prompt(state: DayState, recent: list[DayState]) - str: history_text \n.join( f第{s.day}天 {s.weather} 温度{s.temperature} 客流{s.traffic} f定价{s.price} 生产{s.produce} 售出{s.sold} 浪费{s.waste} f现金{s.cash:.2f} for s in recent[-7:] ) return f 你是一个柠檬水摊主需要在连续 30 天里做出定价、生产和促销决策。 目标第 30 天结束时现金余额最大。 今天是第 {state.day} 天。 天气{state.weather} 温度{state.temperature} 度 预计客流约 {state.traffic} 人 单杯原料成本{state.unit_cost} 元 当前现金{state.cash:.2f} 元 最近经营记录越近越新 {history_text} 请输出 JSON字段含义 - price今天的售价单位元保留一位小数 - produce今天生产的杯数整数 - promotion促销策略只能选 none、discount、bundle - reason一句话解释你的决策理由 只输出 JSON不要输出其他文字。 这里有一个关键的心理机制不要把“策略建议”写在提示词里。比如模板里写“雨天应该降价”会直接泄露答案两个模型都会照做实验就失去意义。提示词只给状态和规则不给策略倾向。3.2 决策输出强制使用结构化 JSON模型输出必须稳定才能被模拟器解析。最稳妥的方式是要求 JSON 格式并用 SDK 的结构化输出能力兜底。若你的模型接口支持 JSON Schema 约束建议直接开启不支持时则在提示词末尾强调“只输出 JSON”。{ price: 3.5, produce: 80, promotion: none, reason: 晴天且温度较高客流预计充足维持基准价并适度增产 }解析时要防御三种坏情况模型输出里多了 Markdown 代码块标记、字段名变为英文同义词、价格或产量出现负数。下面这段解析函数能处理大部分问题import json import re def parse_decision(raw: str) - dict: raw re.sub(r^(?:json)?|$, , raw.strip()) data json.loads(raw) return { price: float(data[price]), produce: int(data[produce]), promotion: data.get(promotion, none), reason: str(data.get(reason, )), }如果解析仍然失败不要直接崩溃应该把原始输出写入日志并返回一个保守决策比如按基准价和最近三天平均产量生产确保实验能继续推进。3.3 用统一客户端接口隐藏两个模型的差异为了让主循环完全不区分 GPT 和 Claude需要定义一个统一接口。Python 里用 Protocol 描述接口再分别实现两个客户端。from typing import Protocol class DecisionClient(Protocol): def decide(self, prompt: str) - dict: ...class GPTClient: def __init__(self, api_key: str, model: str): self.model model self.client OpenAI(api_keyapi_key) def decide(self, prompt: str) - dict: resp self.client.responses.create( modelself.model, inputprompt, text{ format: { type: json_object } }, ) return parse_decision(resp.output_text)class ClaudeClient: def __init__(self, api_key: str, model: str): self.model model self.client Anthropic(api_keyapi_key) def decide(self, prompt: str) - dict: resp self.client.messages.create( modelself.model, max_tokens300, system只输出 JSON 格式的决策结果。, messages[{role: user, content: prompt}], ) return parse_decision(resp.content[0].text)两个客户端实现相同的 decide 方法主循环就能直接复用。实际调用代码里还需要处理限流、超时和重试这里为了突出结构省略了异常分支。3.4 历史信息给多长需要权衡提示词里放最近 7 天经营记录是为了让模型有足够的复盘信息。但这个长度直接影响 Token 消耗和决策噪声。历史太短比如只看昨天模型容易只看眼前策略短视。历史太长比如 30 天全放进去Prompt 越来越大API 成本上升模型也容易被老数据干扰。比较合理的方式是最近 7 天并在每一天前面附带当天天气和现金让模型能大致判断趋势。如果希望进一步压缩成本可以用一段自然语言摘要代替完整记录例如“最近三天天气晴朗销量稳定但昨天因为降价利润下降”。这种摘要可以由环境自动生成适合批量实验场景。4. 运行连续经营并采集结果4.1 用同一随机种子跑完整月实验运行方式很简单创建两个客户端分别调用 run_experiment使用同一个 seed然后输出三十天的记录。gpt_history run_experiment(GPTClient(api_key, gpt-4.1-mini)) claude_history run_experiment(ClaudeClient(api_key, claude-sonnet-4-5))使用同一个 seed 意味着两个模型面对完全相同的天气序列、客流序列和成本序列。这是对比实验的基本前提变量只有模型本身环境完全一致。否则天气好的模型天然占便宜结果没有说服力。4.2 运行过程中的即时检查点实验不能只盯着最后一天的现金数。每一天都应当有可检查的约束否则模型可能用“非法操作”钻空子价格必须大于 0且建议限制在 0.5 到 6.0 元之间。生产量必须是非负整数。促销字段必须是 none、discount、bundle 之一。现金余额不能为负如果连续多天亏损到无法采购按破产处理并停止实验。这些检查点建议直接放在 parse_decision 之后和结算之前。发现非法决策时记录日志并替换为保守决策而不是把异常直接抛到主程序避免一轮实验失败后整段数据作废。4.3 核心指标的计算和对比实验结束后汇总指标并输出成表格。以下是一组演示数据仅用于展示分析方式不代表某个模型的固定结论指标GPT 示例Claude 示例计算方式期末现金512.6 元468.2 元历史最后一天的 cash总销售额1380.0 元1246.0 元每天 price 乘 sold 求和总原料成本702.0 元618.0 元每天 produce 乘 unit_cost 求和累计浪费杯数46 杯112 杯每天 waste 求和平均毛利率49.1%50.4%1 - 总成本 / 总销售额决策变更次数18 次11 次相邻两天价格或产量发生变化的次数从这个表格能读出两类信息一是结果谁赚得更多二是原因赚得多的那个到底赢在定价、成本控制还是减少浪费。只看期末现金很容易被误导比如某个模型销售额很高但浪费也高净利润反而不理想。指标不能只看一个要看一组。4.4 决策日志是分析策略的关键素材除了数值指标模型每天输出的 reason 字段是理解策略变化的宝贵素材。建议在日志里单独输出两列当天天气、模型给出的理由。第10天 sunny 定价3.6 生产85 理由晴天高温客流预计较高适当提价并增产 第11天 rainy 定价2.8 生产45 理由雨天客流下降降价促销并减少生产这种日志能帮你快速定位问题模型是否学会了根据天气调整策略是否在连续亏损后改变思路是否只是机械重复前一天的决策。数值告诉你“结果如何”日志告诉你“为什么会出现这个结果”。5. 从结果看模型决策风格差异5.1 不同模型在固定提示词下容易表现出不同风格在固定提示词和固定环境下不同模型面对同一段历史经营记录会做出不同风格的决策。常见观察是某个模型倾向于维持稳定价格只在天气突变时小幅调整另一个模型更倾向于频繁促销、频繁变价用试探的方式寻找最优区间。没有哪一方绝对正确。稳定定价减少了顾客对价格的敏感但可能错过雨天促销带来的销量频繁促销能清库存但会让毛利率下降。在真实经营里这两种风格对应不同的品牌策略。这类实验最有价值的产出之一就是把模型的“性格倾向”暴露出来而不是简单给它打分。5.2 模型对数字运算并不擅长幻觉会在模拟器里原形毕露柠檬水摊实验中有一个现象很常见模型会在 reason 里写“预计利润为 150 元”但实际售价乘销量根本对不上还有模型会引用根本不存在的“昨日销量”比如昨天实际卖出的数字是 70它却写成 90。这类幻觉在纯文本问答里不容易被发现但在模拟器里会直接体现在现金计算上导致策略失真。解决思路是“让模型出计划让环境出结果”。模型永远不需要自己算利润它只需要输出价格、产量和促销策略销量和利润全部由模拟器计算。这样即使模型的理由写错了最终现金也只有一套客观数据不会因为模型算错而污染实验结果。5.3 提示词的一句话可能被模型放大成极端策略提示词里的措辞会显著影响决策而且影响程度可能超出直觉。比如提示词里写“避免浪费”模型可能连续多天只生产最低数量导致每天都过早售罄、损失大量潜在收入写“最大化销售额”模型又可能天天生产 200 杯浪费严重。这提示一个更普适的结论LLM Agent 对目标函数非常敏感评测时必须把目标函数写得完整、明确、不可误解。目标最好是单一指标比如“第 30 天现金余额最大”而不是“既要多卖又少浪费”否则模型会自己权衡出一套无法预期的策略。如果要评估多目标能力请用打分函数显式定义好每个目标的权重。6. 常见问题与排查链路6.1 模型输出解析失败导致实验中断现象某个模型从某一天开始返回空内容、Markdown 代码块或者重复 JSONparse_decision 抛异常实验中断。可能原因提示词里没有强调“只输出 JSON”。模型接口的 max_tokens 太小输出被截断。该模型对 JSON 格式支持不稳定偶尔输出注释或额外说明。API 返回的内容被 SDK 包装在特定字段里读取位置写错。排查方式打印原始响应内容不要直接看解析后的 dict确认读取字段是 output_text、content[0].text 还是 message.content。解决方式是先清洗原始文本再解析解析失败时回落为保守决策并记录日志。6.2 模型连续多天输出完全相同的决策现象从第 5 天开始价格、产量、促销连续 10 天完全一样仿佛模型睡着了。可能原因历史记录里的信息增量太小模型认为最稳妥就是维持原状。提示词里历史部分过长模型被最近一次的决策“锚定”。温度参数设置为 0模型输出完全确定没有探索行为。模型在模拟器中没有感知到亏损压力因为它不看现金变化。排查方式检查历史摘要是否包含足够的差异化信息检查现金是否在持续下降但模型没有调整适当提高采样温度到 0.5 附近或者在提示词里要求“比较昨天预测和实际销量的差异后再决策”。6.3 API 花费超出预期现象10 轮对比实验跑完账单比预期高很多。可能原因提示词里放入了全部 30 天历史每条 prompt 都随天数增长。每个决策调用都开启了多次重试失败重试同样计费。使用大尺寸模型跑低频小决策成本性价比不高。没有设置每天或每轮的预算上限。排查方式统计每天平均 prompt token 数检查是否线性增长把历史改为最近 7 天摘要在客户端统一加超时和重试上限给实验脚本加预算计数超过阈值自动停止。6.4 排查顺序速查表问题现象优先检查再检查处理建议输出解析失败原始响应内容SDK 字段读取位置清洗文本、保守回落、记录日志决策不变历史信息增量采样温度和提示词锚定压缩历史、提高温度、增加对比要求结果异常高是否绕过约束需求模型是否有漏洞检查非法决策日志补约束条件成本超预算prompt 长度重试次数和模型规格历史摘要化、限制重试、预算计数两个模型结果几乎一样提示词是否泄露策略随机种子是否相同去掉策略暗示确认环境一致7. 最佳实践与扩展方向7.1 评测 LLM Agent 的可复用清单这类实验以后会经常遇到把检查清单固定下来能减少无谓的返工明确评测目标比利润、比稳定性还是比解释质量只选一个主线。固定随机种子确保不同模型面对完全相同的环境序列。提示词只给状态和规则不泄露任何策略偏好。模型只输出计划环境负责计算销量、利润和浪费。校验每个决策的合法性非法输入要回落并记录。不以单次期末现金做结论至少结合毛利、浪费、策略变化一起看。同一轮实验至少跑 3 到 5 次观察方差避免被随机扰动误导。保存完整决策日志包括 raw 输出、解析后决策、当天结算结果。控制 API 成本给每轮实验设定预算上限。发布结论时注明模型版本、提示词版本、随机种子和运行次数。7.2 从柠檬水摊扩展到复杂业务模拟柠檬水摊只是最小可用的经营沙盒。它的核心设计可以扩展到很多场景咖啡店经营增加时间段、门店租金、员工排班参数。电商库存管理加入采购提前期、退换货率、仓储费用。广告投放策略用点击率、转化率、预算约束替代客流和价格。供应链决策让多个模型分别扮演批发商、零售商、物流方形成多 Agent 博弈。扩展时只要守住一条原则环境必须给出可计算的反馈模型必须承担决策后果。没有后果反馈的 Agent 模拟本质上还是在聊天。7.3 学习环境与生产环境的明显分界线学习环境下单次运行、固定种子、控制台输出足够理解整个流程。如果要把这套机制投入生产比如用大模型辅助真实定价或库存补货必须补齐以下能力决策约束模型输出的价格和产量要先过业务规则校验超出合法区间自动拒绝并告警。人工审批涉及真实资金的操作保留“模型建议、人工确认”的审批环节。可回滚每次决策都要有版本号出现异常时能回滚到上一版策略。监控指标把决策日志、异常率、成本和业务结果接入监控系统实时告警。灰度对比先用一个门店或一组商品做小流量对照实验再决定是否全量启用。需要在日志、权限、监控和回滚上投入的时间通常会超过模拟器本身的开发时间。这部分不能省否则实验里的好结果很难安全平稳地变成生产收益。回到最初的问题GPT 和 Claude 谁会经营柠檬水摊真实答案是单次实验的输赢没有普适意义因为结果高度依赖提示词、模型版本、需求模型和随机种子。有意义的是这套可复现、可扩展、可核查的评测方法。它把“哪个模型更强”这种模糊争论变成了“在固定条件下哪个模型赚到更多现金、浪费更少、策略更稳定”的具体问题。能用工程方法回答一个原本靠直觉争论的问题这才是柠檬水摊挑战最大的价值。下一步建议从最小改动开始先在自己电脑上把单模型的 30 天模拟跑通然后复制成双模型对比再加一个模型版本对照。当你能看到不同模型在同一片晴天和雨天里做出不同选择时你就会比任何“谁强谁弱”的结论都更了解它们的实际行为。