最近我一直在琢磨一个问题:同样是顶级大语言模型,GPT 和 Claude 在“动手做事”的时候,差别到底有多大?单纯问它们“怎么做柠檬水”,答案大概率大差不差。但如果让它们各自独立经营一个柠檬水摊,自己做定价、选址、进货、促销、应对天气变化,它们的决策风格和最终收益会有什么不同?
这个想法来自一个很有意思的视频实验:给 GPT 和 Claude 分别创建独立的柠檬水摊模拟环境,让它们在完全相同的规则和随机事件下各自经营一段时间,最后对比谁赚得更多、谁更稳健。这类实验的价值在于:它把大模型从“聊天机器人”还原成了“决策 Agent”,让我们看到模型在目标导向、资源约束、多步推理场景下的真实表现。
本文将完整拆解这个实验的设计思路、提示词编写方法、评测指标和对比结论,并附上可复现的 Python 模拟框架。无论你是对大模型 Agent 能力感兴趣的研究者,还是想在业务中引入 AI 决策的开发者,这篇文章都能给你一个可落地的评测思路。
1. 背景与核心概念
1.1 为什么选择“柠檬水摊”作为评测场景
柠檬水摊是一个非常经典的经营模拟场景,在很多商业课程和游戏里都出现过,比如 Lemonade Stand Game。它的特点是:规则简单,但决策空间足够丰富。
玩家每天需要做几个关键决策:
- 决定制作多少杯柠檬水(受库存和天气影响)。
- 决定每杯卖多少钱(受需求弹性和竞争影响)。
- 决定投放多少广告(影响客流量)。
- 决定购买多少原材料(柠檬、糖、杯子,受保质期和成本影响)。
- 根据天气预报调整策略(下雨天 vs 晴天)。
这个场景天然适合评测 AI Agent,因为它把“大语言模型”从纯粹的文本生成任务拉到了“连续多轮决策任务”里。模型不能只靠一次回答取胜,而是要在一个动态环境里持续做出几十次决策,每次决策都会影响后续状态。
1.2 什么是 Agent 评测中的“决策一致性”
在普通对话评测里,我们关心的是模型生成文本的质量、相关性、准确性。但在 Agent 评测里,我们更关心“决策一致性”(Decision Consistency)。它的意思是:模型在连续多轮决策中,是否始终围绕目标函数(比如总利润)做优化,而不是被单轮文本生成带偏。
举个例子,模型第一天定价 3 元卖光了,第二天涨价到 5 元后销量暴跌。一个决策一致性的模型会分析原因:可能是价格弹性太高,或者天气影响了需求,然后调整策略。而一个只擅长文本生成的模型,可能会在第三天再次把价格调到 5 元,因为它“觉得”高端定价更好,完全忽略之前的销量数据。
柠檬水摊实验的核心,就是观察 GPT 和 Claude 在几十轮连续决策中的一致性、策略性和鲁棒性。
1.3 GPT 与 Claude 的能力差异背景
在做实验之前,我们先大致了解两个模型的能力定位。GPT 系列(如 GPT-4、GPT-4o)在工具调用、结构化输出、多模态理解方面积累很深,API 生态完善,适合任务型 Agent。Claude 系列(如 Claude 3.5 Sonnet、Claude 3 Opus)在长上下文理解、指令跟随、写作质量和逻辑推理方面口碑很好,尤其是处理复杂指令时的稳定性。
注意:这里说的能力差异是模型本身的预训练侧重不同。但在具体任务中,它们的表现受提示词、环境复杂度、工具可用性的影响非常大。我们这篇文章的实验,就是要用统一的环境和提示词模板,尽量公平地观察二者的差异。
2. 实验设计与评测框架
2.1 实验目标
我们设计这个实验的核心目的有三个:
- 对比 GPT 和 Claude 在相同模拟环境下的经营决策能力。
- 收集两组模型在定价、进货、广告投入等方面的策略倾向。
- 量化对比最终收益、稳定性和风险控制能力。
2.2 模拟环境的基本规则
为了保证公平,我们需要让两个模型运行在完全相同的模拟环境里。基本规则如下:
- 模拟周期:连续 30 天(也可以跑 90 天看长期表现)。
- 初始资金:每个摊位 20 美元。
- 天气系统:每天随机生成天气,晴天、多云、小雨、大雨的概率固定在权重里。
- 需求模型:客流量 = 基础客流量 × 天气系数 × 广告系数 × 价格系数。
- 成本模型:每杯柠檬水的原材料成本 = 柠檬成本 + 糖成本 + 杯子成本。
- 库存模型:原材料有保质期,过度采购会浪费;但也可能因为缺货损失销售机会。
每天开始时,模型收到当天的天气和库存信息,需要做出三个决策:生产多少杯、定价多少、广告投入多少。模拟器根据固定公式计算当天销量和利润,更新资金和库存,然后进入第二天。
2.3 公平性控制
在多模型对比实验里,公平性是最容易出问题的地方。为了尽量控制变量,我们需要做到以下几点:
- 完全相同的初始状态:资金、库存、天气序列必须一致。
- 完全相同的提示词模板:两个模型收到的指令格式和字段完全一致。
- 完全相同的决策接口:模型输出统一为 JSON 格式,避免自由文本带来的解析误差。
- 完全相同的随机种子:天气序列、随机事件在两组实验中复用相同种子。
- 多次重复实验:每个模型至少跑 5 轮,取平均值,避免单次随机波动导致误判。
2.4 评测指标
我们使用以下指标来评估两个模型的经营表现:
| 指标 | 说明 | 计算方式 |
|---|---|---|
| 总利润 | 30 天结束时的累计净利润 | 总收入 - 总成本 - 总广告费 |
| 日均利润 | 每天平均利润 | 总利润 / 30 |
| 库存周转率 | 原材料使用效率 | 售出杯数 / 采购原料可制作杯数 |
| 缺货率 | 因库存不足损失销售的比例 | 缺货天数 / 总天数 |
| 广告转化效率 | 每美元广告带来的收入增量 | 广告带来的收入 / 广告费用 |
| 价格稳定性 | 调价频率与幅度 | 价格标准差 |
这些指标覆盖了盈利能力、运营效率、风险控制三个维度,比单纯看最终利润更全面。
3. 环境准备与提示词设计
3.1 实验环境
这个实验可以用 Python 实现闭环模拟。推荐环境如下:
- Python 3.10+
- openai 库(GPT API 调用)
- anthropic 库(Claude API 调用)
- pandas(数据处理)
- matplotlib(可视化对比)
pip install openai anthropic pandas matplotlib注意:你需要准备好 OPENAI_API_KEY 和 ANTHROPIC_API_KEY 环境变量。出于安全和合规考虑,建议把 key 放在本地 .env 文件里,不要提交到代码仓库。
3.2 系统提示词模板
以下是我们使用的基础提示词,它是整个实验的灵魂。这里给出完整中文版本,方便读者阅读:
你是一个柠檬水摊经营者。你的摊位的初始资金是 20 美元。 每天你会收到一条 JSON 消息,包含以下信息: - day: 当前是第几天 - weather: 今天的天气(晴天/多云/小雨/大雨) - forecast: 明天天气预报 - cash: 当前现金 - inventory: 当前库存(单位:份原料,每份可制作一杯柠檬水) - sales_yesterday: 昨天的销量 - price_yesterday: 昨天的定价 - ad_yesterday: 昨天的广告投入 - profit_yesterday: 昨天的利润 - historical_sales: 过去 7 天的销量列表 每天你需要输出一个 JSON 对象,包含以下决策字段: - production: 今天制作多少杯柠檬水(整数,不能超过库存上限) - price: 每杯售价(美元,保留一位小数) - ad_spend: 广告投入(美元,保留一位小数,不能超过现金的 20%) 你的目标是:30 天结束后总利润最大化。 规则: 1. 每杯柠檬水原材料成本约 0.5 美元。 2. 晴天的客流量是雨天的 3 倍。 3. 广告投入每 0.1 美元大约能带来 1% 的客流量提升。 4. 如果你定价过高,销量会明显下降。 5. 库存会过期,第 3 天未使用的原料将作废。 请基于数据和规则做决策,不要随意猜测。输出必须是合法的 JSON 格式。这个提示词有几个关键设计:
- 明确目标:告诉模型要最大化总利润,而不是单日利润。
- 提供结构化数据:让模型基于历史数据做决策,这是评测 Agent 能力的关键。
- 给出规则:让模型在明确约束下做优化,而不是自由发挥。
- 限制输出格式:强制 JSON 输出,方便解析和对比。
3.3 调用代码示例
下面是一个调用 GPT 的核心代码片段,Claude 的调用方式类似,只需要替换 API 客户端和模型名称:
import os import json from openai import OpenAI client = OpenAI(api_key=os.getenv("OPENAI_API_KEY")) def get_gpt_decision(system_prompt, state_json): response = client.chat.completions.create( model="gpt-4o", messages=[ {"role": "system", "content": system_prompt}, {"role": "user", "content": json.dumps(state_json)} ], temperature=0.2, response_format={"type": "json_object"} ) content = response.choices[0].message.content return json.loads(content)Claude 版本:
import anthropic client = anthropic.Anthropic(api_key=os.getenv("ANTHROPIC_API_KEY")) def get_claude_decision(system_prompt, state_json): response = client.messages.create( model="claude-3-5-sonnet-20241022", max_tokens=1024, system=system_prompt, messages=[ {"role": "user", "content": json.dumps(state_json)} ] ) content = response.content[0].text return json.loads(content)这段代码的核心是:把系统提示词固定,把每天的动态状态以 JSON 形式传给模型,模型返回 JSON 决策结果。这样我们就能在一个循环里连续跑 30 天。
4. 完整实战:构建柠檬水摊经营模拟器
4.1 模拟器整体流程
我们可以把模拟器拆成几个模块:
- 状态管理模块:维护现金、库存、天气、历史销量。
- 决策模块:调用 GPT 或 Claude API 获取决策。
- 经济模拟模块:根据决策和天气计算当天销量、利润。
- 数据记录模块:每天记录所有状态,用于后续分析。
为了便于理解,下面给出核心代码框架。
4.2 模拟器核心代码
import random import json from dataclasses import dataclass, asdict @dataclass class State: day: int weather: str forecast: str cash: float inventory: int sales_yesterday: int price_yesterday: float ad_yesterday: float profit_yesterday: float historical_sales: list class LemonadeStandSimulator: def __init__(self, seed=42, initial_cash=20.0): self.seed = seed self.rng = random.Random(seed) self.cash = initial_cash self.inventory = 20 # 初始 20 份原料 self.total_profit = 0.0 self.sales_history = [] self.state = None def get_weather(self, day): weather_pool = ["晴天", "多云", "小雨", "大雨"] weights = [0.35, 0.30, 0.20, 0.15] weather = self.rng.choices(weather_pool, weights=weights)[0] forecast = self.rng.choices(weather_pool, weights=weights)[0] return weather, forecast def get_state(self, day, weather, forecast, last_decision, last_profit): self.state = State( day=day, weather=weather, forecast=forecast, cash=self.cash, inventory=self.inventory, sales_yesterday=last_decision["sales"] if last_decision else 0, price_yesterday=last_decision["price"] if last_decision else 0, ad_yesterday=last_decision["ad_spend"] if last_decision else 0, profit_yesterday=last_profit, historical_sales=self.sales_history[-7:] ) return self.state def step(self, decision, weather): production = int(decision["production"]) price = float(decision["price"]) ad_spend = float(decision["ad_spend"]) # 校验:生产量不能超过库存 production = min(production, self.inventory) # 基础客流量 weather_factor = {"晴天": 3.0, "多云": 2.2, "小雨": 1.2, "大雨": 1.0} base_visitors = 50 * weather_factor[weather] # 广告系数:每 0.1 美元提升 1% ad_factor = 1.0 + (ad_spend / 0.1) * 0.01 # 价格系数:价格越贵,转化率越低 price_factor = max(0.2, 1.5 - price * 0.3) visitors = base_visitors * ad_factor * price_factor demand = int(visitors) # 实际销量 = min(需求, 库存) sales = min(demand, production) revenue = sales * price cost = production * 0.5 + ad_spend profit = revenue - cost # 更新状态 self.inventory -= production self.cash += profit self.total_profit += profit self.sales_history.append(sales) return { "sales": sales, "demand": demand, "revenue": revenue, "cost": cost, "profit": profit, "cash": self.cash, "inventory": self.inventory, "production": production, "price": price, "ad_spend": ad_spend }这段代码实现了一个基础但完整的经营模拟循环。需要注意几个关键点:
- 校验机制:model 输出的 production 不能超过库存,这是在模拟真实生产约束。
- 需求公式:客流量受天气、广告、价格三个因素影响,模型需要通过试错来理解这个公式。
- 成本结构:每杯成本 0.5 美元,广告费单独计算。
4.3 主循环代码
def run_experiment(decision_fn, days=30, seed=42): sim = LemonadeStandSimulator(seed=seed) last_decision = None last_profit = 0.0 log = [] for day in range(1, days + 1): weather, forecast = sim.get_weather(day) state = sim.get_state(day, weather, forecast, last_decision, last_profit) decision = decision_fn(state) # 简单的 JSON 校验 if not all(k in decision for k in ["production", "price", "ad_spend"]): decision = {"production": 0, "price": 1.0, "ad_spend": 0.0} result = sim.step(decision, weather) log.append({**asdict(state), **result}) last_decision = result last_profit = result["profit"] return sim, log这里的 decision_fn 传入的是我们上面定义的 get_gpt_decision 或 get_claude_decision 的包装函数,这样同一个模拟器可以跑任何模型。
4.4 运行与结果输出
import pandas as pd gpt_sim, gpt_log = run_experiment( lambda state: get_gpt_decision(SYSTEM_PROMPT, state) ) claude_sim, claude_log = run_experiment( lambda state: get_claude_decision(SYSTEM_PROMPT, state) ) print("GPT 总利润:", gpt_sim.total_profit) print("Claude 总利润:", claude_sim.total_profit) df_gpt = pd.DataFrame(gpt_log) df_claude = pd.DataFrame(claude_log) # 可视化对比每日利润 import matplotlib.pyplot as plt plt.plot(df_gpt["day"], df_gpt["profit"].cumsum(), label="GPT") plt.plot(df_claude["day"], df_claude["profit"].cumsum(), label="Claude") plt.xlabel("Day") plt.ylabel("Cumulative Profit") plt.legend() plt.show()可视化收益曲线后,你会直观地看到两个模型的利润累积趋势:早期可能差异不大,但后期策略分化会非常明显。
4.5 预期观察点
由于环境影响和 API 响应差异,每次实验的具体数据会有所不同。但从方法论角度,你可以重点关注以下几个观察点:
- 模型是否理解了价格弹性:涨价后销量是否回落?如果连续几天涨价后销量暴跌,说明模型在利用历史数据调整。
- 模型是否关注库存过期:如果模型每天生产量远大于销量,库存积压过期,说明它没有把“保质期”约束纳入决策。
- 模型是否根据天气调整产量:遇到大雨天,减少生产是理性的;如果模型在雨天还大量生产,说明策略不够灵活。
- 广告投入是否合理:广告能带来客流量,但也直接消耗现金。理性模型会根据边际收益决定是否投广告。
5. 对比结果与策略分析
5.1 策略风格差异
基于我在类似实验中的观察(注意:你的实验结果可能因为模型版本、温度参数、提示词微调而不同),GPT 和 Claude 往往会表现出不同的策略倾向:
GPT 的策略倾向:
- 偏向“数据驱动”,会更频繁地参考 historical_sales 数据。
- 定价调整幅度较大,倾向于试探价格上限。
- 对广告投入的敏感性较高,愿意在晴天加大广告投放。
- 在库存管理上偶尔出现过度生产,对保质期约束不够敏感。
Claude 的策略倾向:
- 对指令的跟随更稳定,更倾向于严格遵循提示词里给出的规则。
- 在生产决策上更保守,倾向于“安全库存”策略,缺货率通常较低。
- 对价格调整更谨慎,价格曲线平滑。
- 广告投入策略偏稳健,不太会大幅调整。
这些差异可以概括为:GPT 更像“激进型玩家”,愿意冒风险换取高收益;Claude 更像“稳健型玩家”,优先保证不翻车。
5.2 量化对比维度
建议你从下面这些维度来量化对比两个模型的表现:
| 对比维度 | 观察方式 |
|---|---|
| 累计利润 | 30 天结束时谁的总利润更高 |
| 最大回撤 | 单日最大亏损是多少,出现在第几天 |
| 缺货率 | 有多少天因为生产不足而损失销量 |
| 库存浪费率 | 有多少原料因过期作废 |
| 价格调整频率 | 每两天调价一次还是每天调价 |
| 广告效率 | 每投入 1 美元广告带来多少收入 |
价格曲线和库存曲线的可视化能帮助你快速判断模型策略是否合理。
5.3 为什么结果不代表模型“智力”
需要特别强调一个误区:如果 GPT 最终利润更高,不代表 GPT 比 Claude 更聪明;如果 Claude 更稳健,也不代表它更适合所有 Agent 场景。原因是:
- 该实验只测试了经营决策能力的一个窄面。
- 不同模型的预训练数据、指令微调方式、甚至 API 的温度默认值都会影响结果。
- 我们使用的提示词是同一套,但这套提示词可能对某个模型更“友好”,对另一个模型则不一定。
- 模型的输出存在随机性,单次实验不能说明任何问题。
正确的解读方式是:这个实验观察的是“在当前模拟环境和提示词下,两个模型的决策行为差异”。
6. 常见问题与排查思路
6.1 模型返回的不是合法 JSON
这是最常见的坑。虽然我们设置了response_format={"type": "json_object"},但有时模型还是会输出一段文字包裹的 JSON,或者字段名不对。
解决思路: 1. 在解析 JSON 前,先尝试用正则提取第一个 { 到最后一个 } 之间的内容。 2. 如果解析失败,进入“降级策略”,使用昨天的决策或默认决策。 3. 在实际代码里,最好用一个包装函数统一处理异常。下面是一个健壮的 JSON 解析函数:
import re import json def safe_parse_json(content): try: return json.loads(content) except json.JSONDecodeError: match = re.search(r"\{.*\}", content, re.DOTALL) if match: try: return json.loads(match.group()) except json.JSONDecodeError: return None return None6.2 模型连续输出相同决策
有时候模型会陷入“重复模式”,每天输出几乎相同的 production、price、ad_spend。这在经营模拟里很难说完全错误,因为稳定的价格在需求平稳时是合理策略。但如果连续多天库存告急或积压,仍然不调整,说明模型可能没有在认真利用历史数据。
排查方法:打印模型每天的决策日志,观察它在销量剧烈变化后的第二天是否调整策略。如果没有,可以考虑在提示词里加一句“请注意根据昨天的销量调整今天的生产量”。
6.3 请求超时或限流
使用 API 跑 30 天循环时,可能会遇到限流或超时。建议在每次调用之间加一个小延时,并实现重试机制:
import time def call_with_retry(fn, retries=3, delay=2): for i in range(retries): try: return fn() except Exception as e: if i == retries - 1: raise e time.sleep(delay * (i + 1))6.4 实验结果波动大
如果你跑多次实验发现结果方差很大,这是正常的。LLM 的决策本身带有随机性,再加上天气序列的随机生成,结果必然有波动。建议固定随机种子、增加重复次数,并报告平均值和标准差,而不是只报告单次结果。
7. 最佳实践与工程建议
7.1 设计 Agent 评测任务的核心原则
通过这个柠檬水摊实验,可以总结出几条适用于更广泛 Agent 评测的原则:
- 任务可量化:必须有明确的数值目标(如总利润、成功率、完成时间),否则无法对比模型。
- 环境可控:使用固定的种子和规则,让所有模型在完全相同条件下运行。
- 提示词中立:不要为某个模型优化提示词,而是用同一套提示词尽可能公平地测试。
- 连续多轮:单轮问答无法评测决策能力,必须设计连续决策任务。
- 多次重复:LLM 输出有随机性,至少重复 3 到 5 次取平均。
7.2 代码工程层面建议
在实际写 Agent 评测系统时,有几个工程层面的建议:
- 状态管理独立:把模拟器状态和模型调用解耦,这样你可以随时替换模型,也不会污染历史数据。
- 日志完整:每天记录模型的原始输出、解析后决策、模拟结果。没有日志很难排查问题。
- 决策校验:模型输出不一定满足约束(比如生产数量大于库存、广告费超过现金),必须在系统层面做硬校验,而不是依赖模型自觉。
- 并发控制:如果跑多组实验,注意 API 限流,做好退避重试。
7.3 安全与合规提醒
虽然这里是模拟实验,但用 LLM 做真实经营决策时,有几个风险需要提前规避:
- 不要直接让模型控制真实资金。先在小规模、可回滚的模拟环境里验证策略。
- 对模型的输出做业务规则校验。模型可能会给出不合规的定价或过高的广告预算。
- 记录审计日志。在真实业务场景里,AI 的每次决策都应可追溯,方便出问题时回滚。
- 人工复核关键节点。在高金额、高风险决策时,建议保留人工审批环节。
7.4 提示词迭代的注意事项
提示词对模型表现影响极大,这既是好事也是坏事。好的方面是,我们可以通过调提示词让模型更符合任务需求;坏的方面是,提示词的小改动可能导致结果大幅变化。
在实际做提示词迭代时,建议遵循以下流程:
- 写一版基础提示词,跑通完整流程。
- 记录基线结果。
- 每次只改一个变量(比如增加一条规则、改变措辞),做 A/B 对比。
- 保存每个版本的提示词和结果,方便复盘。
不要一次改多处,否则出了问题很难定位是哪个改动导致的。
8. 扩展方向:从柠檬水摊到真实业务 Agent
8.1 将模拟器替换成业务环境
柠檬水摊实验的最大价值在于它的迁移性。你可以把“生产柠檬水”换成“采购商品”“投放广告”“预约排产”等任何真实业务动作。模拟器的核心框架不变,只需要替换:
- 动作空间:从 {production, price, ad_spend} 换成业务需要的决策字段。
- 环境反馈:从天气、销量换成业务指标,如转化率、客单价、库存天数。
- 约束条件:从原料库存换成供应商交期、物流时效、预算上限。
这个思路可以用于测试 LLM 在供应链优化、促销策略、库存管理、定价调整等场景中的表现。
8.2 加入反思机制
GPT 和 Claude 在基础实验中暴露出的一个共同问题是:它们很少主动“复盘”。如果在模拟器中加入一个“每日复盘”环节,让模型在每天结束后回顾当天的决策并输出反思,然后在下一天决策前参考前一天的反思,效果通常会更好。
示例:
今天是第 5 天,昨天的销量低于预期。你的复盘是:昨天定价过高,且没有考虑到小雨天气的影响。请基于昨天的复盘,给出今天的决策。这种“行动 - 观察 - 反思 - 调整”的循环,也是目前 Agent 领域比较流行的 ReAct 模式的核心思想。
8.3 加入工具调用
更高阶的玩法是给模型提供“工具调用”能力。比如,允许模型查询历史数据的统计摘要、计算价格弹性系数、甚至调用外部天气 API。此时模型不再单纯依赖提示词里给的数据,而是主动获取信息,这更接近真实业务 Agent 的形态。
{ "tools": [ {"name": "calculate_price_elasticity", "description": "计算过去 N 天的价格弹性"}, {"name": "get_weather_forecast", "description": "获取未来 3 天天气预报"} ] }两个模型在工具调用能力上的差异会直接影响任务表现,这也是一个很有意思的对比点。
9. 总结与动手建议
通过这个 GPT 和 Claude 经营柠檬水摊的对比实验,我们能看到几件有价值的事情:
- 大模型可以作为决策 Agent 运行在模拟环境里,不只是聊天。
- 不同模型在相同任务上会展现出不同的策略风格,有的激进、有的稳健。
- 评测 Agent 需要完整的模拟器、提示词和指标体系,不能只看一个最终数字。
- 柠檬水摊是一个很好的入门实验环境,规则简单但足够复杂,能暴露很多 Agent 决策问题。
如果你也想复现这个实验,我建议你按下面的顺序动手:
- 先跑通模拟器本身,不接任何 LLM,手动输入决策验证经济公式是否合理。
- 接入 GPT,跑 30 天,记录日志。
- 接入 Claude,跑同样的 30 天,对比结果。
- 重复 5 次实验,取平均值,看看结论是否稳定。
- 尝试调整提示词,观察两个模型的表现变化。
实践出真知。比起直接相信文档或测评榜单,亲自让两个模型在相同规则下“经营”一次,你会对它们的差异有更直观的感受。
建议你从本文的模拟器代码出发,先跑通一个最小闭环,再逐步增加复杂度。如果你在实验过程中遇到问题,欢迎在评论区把报错信息和运行日志贴出来,大家一起排查。