尧图建网站 尧图建网站 YAOTU WEB BUILD 免费咨询
ARTICLE DETAIL

资讯详情

深耕网站建设与建站编程的一线实战洞察。

MerchantBench智能体评测:从单轮问答走向长期经营连贯性

MerchantBench智能体评测:从单轮问答走向长期经营连贯性 在 LLM 智能体从演示走向业务系统的过程中单轮问答的评测方式很快遇到瓶颈。MerchantBench 正是围绕长期经营连贯性设计的一类智能体评测基准它把一个智能体放入模拟商户经营环境让其在连续多个经营周期内完成进货、定价、营销、回款等决策再通过状态一致性、目标一致性和行动合法性等指标判断智能体是否具备稳定经营的能力。本文以一个可运行的示例工程为主线拆解 MerchantBench 的环境设计、评测循环、指标计算和排查方法方便读者把它迁移到自己的智能体评测任务中。这类评测和传统知识问答评测的最大区别在于“过程”。知识问答关心答案是否正确经营评测关心一连串动作是否连续、状态是否对得上、目标有没有漂移。一个智能体可能在某一天的决策非常漂亮但第二天就忘了前一天刚设定的价格策略或者把库存状态理解错导致后续所有计划失效。MerchantBench 这类基准要暴露的正是这种“单步正确但长期失控”的问题。1. 为什么智能体评测要关注长期经营连贯性1.1 单轮评测的局限传统评测基准往往把任务切成独立样本输入一个问题输出一个答案然后与标准答案比对。这种方式在信息检索、代码生成、数学计算等领域仍然有效因为每次任务可以独立判断。但在智能体场景里任务天然是连续的多步决策。前一步的输出会变成后一步的输入前一个错误会被后续决策放大。举例来说一个销售客服智能体在第一轮对话中正确识别了用户身份第二轮如果因为上下文截断忘记了用户是会员就会给出非会员价格导致整段服务失败。单轮评测看不出这种问题因为第二轮的输入已经把“用户身份”这个信息重新提供了。而真实智能体系统里状态往往是分布式存储、长文本摘要、多轮压缩后的结果信息丢失是常态。MerchantBench 关注长期经营连贯性目的就是补上这个评测缺口不是问“这一步做对没有”而是问“连续经营 30 天后智能体是否还能保持一致的状态认知和经营策略”。1.2 长期经营连贯性到底在测什么长期经营连贯性可以拆成三个可计算的部分。记忆连贯性智能体在多个决策点之间是否保持对资金、库存、顾客、口碑等状态的准确认知。策略连贯性智能体是否围绕一个稳定目标经营而不是每隔几天彻底改变方向。行动连贯性智能体每个动作是否遵守环境规则动作参数是否在状态约束范围内。三者相互影响。记忆连贯性是基础如果智能体反复忘记库存数量就谈不上策略行动连贯性是约束即使智能体想激进扩张也不能让它突破现金限制。MerchantBench 的评测设计需要同时覆盖这三个维度而不是只给一个最终利润数字。1.3 MerchantBench 的定位MerchantBench 不是另一个抽象推理题集它更像一个“带规则和状态的模拟沙盒”。智能体每天收到当前经营状态选择一个动作环境根据规则更新状态然后进入下一天。这个定位带来的好处是评测更接近真实业务。智能体必须学会在同一份状态历史里做决策必须处理决策带来的长期后果。比如某天为了短期促销大幅降价可能会提高顾客数量但也会压缩利润如果连续多天如此最终现金可能大幅下降。单轮评测无法生成这种连锁反应只有带状态推进的环境才能做到。同时MerchantBench 也把“评测过程”本身透明化。每一次动作、状态变更、收益变化都可以记录成结构化日志便于事后回放和定位问题。这一点对评测结果的可信度非常重要。2. MerchantBench 的核心组成和工作流程2.1 三层架构场景层、交互层、评测层MerchantBench 的工程实现可以拆成三层每一层职责边界要清楚否则后续扩展会很痛苦。层级主要职责典型组件场景层定义经营任务、目标、初始参数场景配置、任务模板交互层维护模拟环境状态执行智能体动作环境类、状态对象、动作校验评测层记录日志、计算指标、生成报告评测循环、指标函数、报告输出场景层负责生成“今天卖什么、初始有多少钱、目标是什么”。交互层负责把智能体返回的动作转成状态变更。评测层只读取日志分析结果不直接修改环境。三层分离的价值在于可复用。如果后续想评测售后服务型智能体只需要替换场景层和状态字段交互层和评测层可以基本保留。如果只是想换一个模型只需要改智能体适配层不需要改动环境逻辑。2.2 模拟经营环境的状态模型模拟经营环境的核心是状态对象。MerchantBench 使用一组可量化的字段模拟商户资产和经营条件。# environment.py from dataclasses import dataclass, field dataclass class MerchantState: day: int 0 cash: float 10000.0 inventory: int 100 customers: int 50 reputation: float 60.0 price: float 100.0 history: list field(default_factorylist) def to_prompt(self): return { day: self.day, cash: round(self.cash, 2), inventory: self.inventory, customers: self.customers, reputation: round(self.reputation, 2), price: round(self.price, 2), }这个状态模型把“经营连贯性”落到可观测字段上。现金、库存、顾客数量、口碑、价格是五个最基础的经营指标。智能体每一轮都会拿到一份类似的 JSON 状态它必须基于这份状态做决策。状态模型设计的关键是既不能过细导致智能体被无关字段干扰也不能过粗否则评测无法区分“策略不同”和“状态理解错误”。上面五个字段对一个小型商户模拟已经足够实际项目可以按行业扩展比如增加“供应商账期”“退换货率”等字段。2.3 评测指标怎么算MerchantBench 示例工程建议使用四个核心指标。行动合法率所有动作中通过环境校验的比例。经济绩效最终现金相对于初始现金的变化率。记忆一致性每隔固定周期让智能体回述当前现金和库存计算回述值和真实值的误差。目标完成度经营周期结束时是否达到预设目标。其中记忆一致性是长期连贯性里最有代表性的指标。它不能只靠每一轮动作判断因为智能体可能用一个固定的动作序列碰巧获得高分但对状态本身一无所知。定期回述能强制暴露记忆问题。# evaluator.py def check_memory(agent, env, freq5): results [] for day in range(1, env.state.day 1): if day % freq 0: summary agent.recall(env.state.to_prompt()) expected_cash float(summary.get(cash, 0.0)) expected_inventory int(summary.get(inventory, 0)) cash_error abs(expected_cash - env.state.cash) / max(1.0, abs(env.state.cash)) inventory_error abs(expected_inventory - env.state.inventory) / max(1, abs(env.state.inventory)) results.append({ day: day, cash_error: round(cash_error, 4), inventory_error: round(inventory_error, 4), }) return results最终打分可以采用加权方式def episode_score(metrics): action_validity metrics[action_validity] economic_performance max(0.0, metrics[economic_performance]) memory_consistency 1.0 - metrics[memory_consistency] return 0.3 * action_validity 0.35 * min(economic_performance, 1.0) 0.35 * memory_consistency这里的权重是示例值。实际使用时要根据业务目标调整。如果更看重智能体是否遵守规则行动合法率权重提高如果更看重经营结果经济绩效权重提高如果重点评估长期记忆就提高记忆一致性权重。3. 搭建一个最小可运行的 MerchantBench 评测工程3.1 环境准备和依赖运行本文示例需要 Python 3.10 或更高版本。依赖库数量很少openai pandas pyyaml安装命令pip install openai pandas pyyaml注意这里没有写死版本。OpenAI 的 Python SDK 迭代比较快安装时应使用当前稳定版本。如果只做环境模拟和指标计算不调用模型那么可以不安装 openai只保留 pandas 和 pyyaml 即可。运行之前需要准备一个可访问的大模型 API 密钥。MerchantBench 的核心是调用 LLM 智能体接口示例工程通过环境变量读取密钥export OPENAI_API_KEY你的密钥如果使用的是兼容 OpenAI 接口的本地模型或第三方服务需要额外配置 base_url 和模型名称。不同服务商的接口格式可能有差异落地前要先确认兼容性。3.2 项目结构最小工程可以采用下面的目录结构。merchantbench/ ├── config.yaml ├── run_eval.py ├── merchantbench/ │ ├── __init__.py │ ├── environment.py │ ├── agent.py │ ├── evaluator.py │ └── utils.pyenvironment.py 负责状态和动作执行。agent.py 负责调用 LLM 并返回动作。evaluator.py 负责评测循环和指标计算。run_eval.py 是入口脚本。config.yaml 保存评测参数。这个结构足够支撑小规模实验。当场景变多之后场景配置可以拆成scenes/目录日志可以输出到runs/目录但最小闭环不需要这些。3.3 模拟环境实现环境类需要实现两个核心方法校验动作和推进状态。动作定义为 JSON 格式类型必须是set_price、purchase、advertise、collect_debt、wait之一。VALID_ACTIONS {set_price, purchase, advertise, collect_debt, wait} class ShopEnv: def __init__(self, initial_state: MerchantState | None None): self.state initial_state or MerchantState() def validate_action(self, action: dict): if not isinstance(action, dict): return False, action must be a dict action_type action.get(type) if action_type not in VALID_ACTIONS: return False, finvalid action type: {action_type} if action_type purchase: spend float(action.get(spend, 0.0)) if spend self.state.cash: return False, can not spend more than cash if action_type set_price: new_price float(action.get(price, self.state.price)) if new_price 0: return False, price must be positive return True, ok def step(self, action: dict): valid, msg self.validate_action(action) if not valid: return self.state, False, msg action_type action.get(type) if action_type set_price: self.state.price float(action.get(price, self.state.price)) elif action_type purchase: spend float(action.get(spend, 0.0)) unit_cost 50.0 purchased int(spend / unit_cost) self.state.cash - purchased * unit_cost self.state.inventory purchased elif action_type advertise: budget float(action.get(budget, 0.0)) self.state.cash - budget self.state.customers int(budget / 10) elif action_type collect_debt: self.state.cash float(action.get(amount, 0.0)) self._advance_day() return self.state, True, ok def _advance_day(self): self.state.day 1 demand max(0, self.state.customers * (1 - self.state.price / 200)) sales min(self.state.inventory, int(demand)) self.state.cash sales * self.state.price self.state.inventory - sales self.state.customers int(self.state.customers * 0.9 5) self.state.reputation 0.5这里的关键是校验与执行分离。校验保证智能体不能“凭空支出大于现金的采购预算”执行则统一走_advance_day推进每日结算。如果动作非法环境不推进状态评测循环就能记录一次失败。需要注意的是示例中的_advance_day是演示用简化模型。真实的经营模拟需要更细的需求计算和成本模型但核心结构不变动作先校验再执行状态按固定节奏推进。3.4 智能体适配层智能体适配层负责把状态对象转成 prompt再调用 LLM最后把模型返回的文本解析成 JSON 动作。import json import os from openai import OpenAI class BaseAgent: def __init__(self, namebase): self.name name def act(self, state_prompt: dict, instructions: str) - dict: raise NotImplementedError def recall(self, state_prompt: dict) - dict: raise NotImplementedError class OpenAIAgent(BaseAgent): def __init__(self, modelgpt-4o-mini, temperature0.0): super().__init__(namemodel) self.model model self.temperature temperature self.client OpenAI(api_keyos.getenv(OPENAI_API_KEY)) def _call(self, system, user): resp self.client.chat.completions.create( modelself.model, temperatureself.temperature, response_format{type: json_object}, messages[ {role: system, content: system}, {role: user, content: user}, ], ) return json.loads(resp.choices[0].message.content) def act(self, state_prompt: dict, instructions: str) - dict: system 你是模拟商铺经营智能体只能返回 JSON 动作。 user f当前状态{json.dumps(state_prompt, ensure_asciiFalse)}\n{instructions} return self._call(system, user) def recall(self, state_prompt: dict) - dict: user ( 只根据当前状态返回 JSON包含 cash 和 inventory 两个字段 不要输出其他内容。\n当前状态 json.dumps(state_prompt, ensure_asciiFalse) ) return self._call(你是状态记录器。, user)这里使用response_format{type: json_object}强制模型返回 JSON。这个参数只在部分模型上生效使用前要确认模型能力。如果模型不支持就不传该参数并额外写一个健壮的 JSON 解析函数。解析函数要容错。模型偶尔会输出代码块或前后有空白字符建议做一次清理def safe_parse_action(text): if not text: return {type: wait} text text.strip() if text.startswith(json): text text[7:] if text.endswith(): text text[:-3] try: return json.loads(text) except json.JSONDecodeError: return {type: wait}3.5 评测循环与指标计算评测循环是 MerchantBench 的主循环。它负责把智能体、环境、日志组合起来跑完一个完整的经营周期。def run_episode(agent, env, days30, instructions): logs [] for _ in range(days): state_before env.state.to_prompt() action agent.act(state_before, instructions) action safe_parse_action(str(action)) valid, msg env.validate_action(action) state_after, ok, step_msg env.step(action) logs.append({ day: state_after.day, state_before: state_before, action: action, valid: valid and ok, message: step_msg, state_after: state_after.to_prompt(), }) return logs每个日志条目都记录动作前后的状态。这样即使后续发现智能体策略有问题也可以回溯到具体某一天。指标计算函数如下def compute_metrics(logs, initial_cash, target_cash): valid_actions sum(1 for log in logs if log[valid]) action_validity valid_actions / len(logs) final_cash logs[-1][state_after][cash] if logs else initial_cash economic_performance (final_cash - initial_cash) / initial_cash goal_completion 1.0 if final_cash target_cash else final_cash / target_cash return { action_validity: round(action_validity, 4), economic_performance: round(economic_performance, 4), goal_completion: round(goal_completion, 4), }memory_consistency需要单独跑一次周期性回述检查见 2.3 节的check_memory函数。最终把这些指标合并成报告。4. 运行评测并解读报告4.1 配置评测参数运行之前先配置config.yaml。environment: initial_cash: 10000.0 initial_inventory: 100 initial_customers: 50 initial_reputation: 60.0 initial_price: 100.0 days: 30 agent: model: gpt-4o-mini temperature: 0.0 evaluation: target_cash: 15000.0 memory_check_freq: 5temperature建议设置为 0.0保证同一配置下的评测结果可复现。如果调高到 0.7模型输出会更有探索性但评测方差也会变大。对于基准评测优先保证稳定性。4.2 启动评测入口脚本run_eval.py读取配置文件创建智能体和环境跑完一个 episode输出日志和指标。import argparse import json import yaml from merchantbench.environment import ShopEnv, MerchantState from merchantbench.agent import OpenAIAgent from merchantbench.evaluator import run_episode, compute_metrics, check_memory def main(): parser argparse.ArgumentParser() parser.add_argument(--config, defaultconfig.yaml) args parser.parse_args() with open(args.config, r, encodingutf-8) as f: config yaml.safe_load(f) env_config config[environment] agent_config config[agent] eval_config config[evaluation] state MerchantState( cashenv_config[initial_cash], inventoryenv_config[initial_inventory], customersenv_config[initial_customers], reputationenv_config[initial_reputation], priceenv_config[initial_price], ) env ShopEnv(state) agent OpenAIAgent( modelagent_config[model], temperatureagent_config[temperature], ) instructions ( 你每天只能选择一个动作动作必须是 JSON 对象。 可以 set_price、purchase、advertise、collect_debt 或 wait。 目标是提升现金和库存的长期收益。 ) logs run_episode(agent, env, daysenv_config[days], instructionsinstructions) metrics compute_metrics( logs, initial_cashenv_config[initial_cash], target_casheval_config[target_cash], ) memory check_memory(agent, env, freqeval_config[memory_check_freq]) report {metrics: metrics, memory_checks: memory, logs: logs} with open(report.json, w, encodingutf-8) as f: json.dump(report, f, ensure_asciiFalse, indent2) print(json.dumps(metrics, ensure_asciiFalse, indent2)) if __name__ __main__: main()运行命令python run_eval.py --config config.yaml正常结束时终端会打印指标同时目录下生成report.json。4.3 结果解读一份简化报告可能长这样{ metrics: { action_validity: 0.97, economic_performance: 0.24, goal_completion: 0.83, memory_consistency: 0.08 } }解读时不要只看单个数字。行动合法率 0.97 说明智能体基本遵守规则经济绩效 0.24 说明最终现金比初始增加了 24%目标完成度 0.83 说明离 15000 的目标还有一点距离记忆一致性 0.08 说明回述误差平均为 8%属于可接受范围。把这些指标合并成最终评分之前建议先检查日志。如果智能体天天wait行动合法率可能很高但经济绩效很差这种模型显然不合格。所以经济绩效和记忆一致性必须同时考察。4.4 与单轮评测对比MerchantBench 和单轮评测不是替代关系而是互补关系。对比维度单轮评测MerchantBench任务结构一个输入一个输出多个连续决策回合状态管理无状态或状态无关有显式状态对象主要风险事实错误、逻辑错误状态遗忘、策略漂移、规则违规评测成本较低较高需要连续调用模型适用场景知识问答、代码生成经营模拟、客服工作流、自动化运营如果目标只是验证模型知道某个知识点单轮评测足够。如果目标是验证智能体在真实业务流程中的可靠性就必须引入长期经营连贯性评测。5. 常见问题与排查路径5.1 智能体答非所问现象智能体返回的不是 JSON 动作而是一段自然语言解释或者返回了包含多余字段的 JSON。可能原因prompt 约束不明确或者模型不支持response_format参数。检查方式打印原始返回内容确认是否存在代码块、反引号、多余文本。检查模型是否支持 JSON 输出模式。处理建议在 system prompt 中写死“只返回 JSON”并调用safe_parse_action做兜底解析。如果模型确实不支持 JSON 模式可以把示例动作直接放进 prompt可以返回的动作示例{type: set_price, price: 90}5.2 经营状态没有按步骤更新现象日志中连续多天输出相同的cash和inventory。可能原因动作没有通过校验环境直接返回原状态或者智能体一直在执行wait以外的动作但动作字段拼写错误。检查方式查看日志中每条记录的valid字段如果大量为false说明动作校验失败。再查看失败消息确认是动作类型不匹配还是参数超限。处理建议在validate_action中把失败原因写入日志。动作类型建议只允许小写字母避免大小写不一致导致校验失败。wait动作也不能一直被使用可以在评测中统计动作分布如果某个动作占比过高需要警惕。5.3 评测结果不稳定现象同一配置跑两次最终评分差距很大。可能原因temperature没有固定为 0模型版本变化prompt 中状态字段顺序变化环境初始状态没有重置。检查方式确认temperature0.0检查每次实验是否新建了ShopEnv实例确认模型名称完全一致。处理建议把 temperature 设为 0每次评测前重建环境对象并将模型名称、prompt 版本、配置内容记录到报告元数据中。对于需要方差分析的实验可以固定随机种子但要注意 LLM API 本身仍有不确定性不能完全消除波动。5.4 token 超限或上下文丢失现象经营天数一长模型开始忽略早期状态信息或者 API 调用报context length exceeded。可能原因每次 prompt 都塞入了全部历史日志导致 token 占用快速增长或者需要智能体记忆的关键状态没有压缩到当前状态对象中。检查方式看请求日志中的 token 使用量确认是否接近模型上下文上限。再看智能体回述误差是否随天数增加而上升。处理建议MerchantBench 状态对象只保留当前关键字段不把历史全部发给模型。如果业务确实需要历史记忆应单独实现记忆模块比如按天做摘要而不是把原始日志全部拼进 prompt。6. 从得分到改进6.1 场景设计比打分更重要MerchantBench 的价值上限由场景设计决定。如果只设计“初始资金 10000、经营 30 天”这样一个场景评测结果只能反映模型在该场景下的表现。不同行业的经营约束差异很大例如零售行业要控制库存周转服务行业要维护客户满意度跨境贸易要处理汇率波动。场景越贴近真实业务评测结论越有参考价值。设计场景时可以控制三个变量初始状态、目标函数、规则约束。比如把目标从“最终现金最大”改为“30 天内不低于破产线且现金增长最大”智能体就会采取更保守的策略。这就是场景对评测灵敏度的调节作用。6.2 用回放定位失败步骤报告中的指标只能告诉你“哪里不行”不能直接告诉你“为什么不行”。定位问题要靠回放日志。推荐把日志导出为 CSV方便在表格工具中筛选import csv def export_logs(logs, pathlogs.csv): with open(path, w, newline, encodingutf-8) as f: writer csv.DictWriter(f, fieldnames[day, action, valid, message, cash, inventory]) rows [] for log in logs: rows.append({ day: log[day], action: json.dumps(log[action], ensure_asciiFalse), valid: log[valid], message: log[message], cash: log[state_after][cash], inventory: log[state_after][inventory], }) writer.writeheader() writer.writerows(rows)回放时重点看三处第一次出现非法动作的回合、第一次出现较大记忆误差的回合、经济绩效突然下降的回合。这三处通常能覆盖大多数智能体问题。6.3 学习环境与生产环境的差异在小规模实验里跑通 MerchantBench 只是第一步。把它用于实际智能体评测时还要区分环境差异。环境关注点建议本地模拟快速验证环境逻辑固定 config跑 1 个 episode评测回归集持续监测模型效果使用多天期、多场景配置保留基线报告生产业务真实场景风险评估做影子评测用历史日志回放加入人工抽样生产环境还要额外考虑成本控制。MerchantBench 每个 episode 会调用几百次模型 API跑一组完整实验的成本不低。建议先做小规模试跑确认 prompt 和指标合理后再扩大样本量。另一个建议是保存每次评测的完整报告和模型版本否则比较不同模型时会因为上下文或模型别名不一致而得出错误结论。对于刚开始尝试智能体评测的团队MerchantBench 的最小示例是一个不错的练习起点。先把它跑通再把状态字段换成自己业务里的对象把动作集合替换成业务允许的操作最后围绕真实目标设计场景。这个过程比直接追求“高分模型”更有价值因为评测体系本身的质量决定了模型后续迭代的方向是否可靠。
返回列表