[控场AI]
· 7 分钟阅读· 3,589 字

让LLM负责语言,脚本负责计算:决策型Agent的三段式架构

让LLM负责语言,脚本负责计算:决策型Agent的三段式架构

用三段式架构(LLM理解+脚本计算+MCP数据)解决麦当劳优惠券最优组合选餐问题,核心原则是确定性计算绝不交给概率模型。

本文以「麦当劳选餐+最优券匹配」为切入点,阐述了一套适用于决策型Agent的三段式架构:LLM负责将模糊自然语言翻译成硬约束参数并最终解说结果,MCP实时拉取门店菜单与优惠券等可信数据,本地Python脚本承担枚举、过滤、多目标排序等全部确定性计算。作者给出三条核心理由说明为何不让模型自行计算:枚举本质是笛卡尔积加约束过滤、券匹配需要精确算每单实际优惠额、三种排序目标相互矛盾只能靠脚本处理。此外文章还覆盖了WorkBuddy的MCP配置实操坑(type驼峰、配置路径、手动启用)和服务端重新定价这一常被跳过的关键步骤,最终提炼出可迁移到选品、排班、比价等同类决策场景的通用方法论。

从一个麦当劳优惠券问题说起

假设你手里有两张麦当劳优惠券,只能用一张:一张是「满40减10」,另一张是「汉堡单品88折」。你点了一个25元的汉堡——用满减券得凑够40元,实付30元;用折扣券则是25×0.88=22元,这时折扣券更划算。

但如果汉堡本身就是40元呢?满减券直接实付30元胜出,折扣券反而要35.2元。

哪张券更便宜,完全取决于订单里装了什么,没有通用答案。 这还只是两张券、一件商品的情形。真实订单可能是三张券、二十件商品,再叠加「两个人、总价50元以内、热量低于1100大卡、蛋白质至少35克」这样的约束。满足所有条件的组合数轻松上千。

决策型Agent三段式架构

把这种问题直接丢给大模型,几乎必然得到三种结果:慢(上千次乘加运算逐token生成)、贵(每一步都在烧token)、错(凡是让LLM算过数的人都懂)。于是这个开源项目从一条铁律出发:

LLM负责理解与表达,脚本负责计算与确定性。

三段式架构:各司其职

整个流程被拆成清晰的三个角色协作链路:用户用大白话说出需求(「两个人,50块以内,别太油腻」)→ LLM解析意图并转化为硬约束(max_payable=45、max_calories=1100、min_protein=35)→ 麦当劳MCP拉取真实门店数据(菜单/营养/优惠券)→ 本地脚本 optimize_meal.py 枚举组合并做最优券匹配 → LLM再把结果翻译回人话,给出三套方案。

职责边界划得很干净:

  • LLM:解读「别太油腻」这类模糊表达、把结果讲成人话;它不碰钱的计算、热量计算,也不负责记菜单。
  • MCP:提供结构化、可信的数据;它不做决策。
  • 本地脚本:枚举、过滤、券匹配、排序;它不处理自然语言。

这样做的收益很直接:脚本在毫秒级跑完,结果可复现,零token成本,而且钱永远不会算错。

MCP(Model Context Protocol)是Anthropic于2024年底提出的开放协议,旨在为大模型提供标准化的外部工具与数据接入方式。它相当于AI应用的「USB接口」——服务端按协议暴露工具(tool)和资源(resource),客户端(如WorkBuddy广告、Claude Desktop)通过JSON-RPC调用这些工具,模型无需知道数据从哪里来,只需要声明它想调用哪个工具、传什么参数。这种解耦让数据层可以独立升级:菜单更新、新增门店,只需服务端改动,模型侧完全不受影响。在本项目中,麦当劳MCP服务端负责按门店ID实时返回菜单、营养信息和当前可用优惠券,确保模型拿到的永远是最新、最准确的结构化数据,而不是训练时记住的过时快照。

为什么不让模型自己算

作者给出了三条相当硬核的理由。

第一,枚举是算术而非语言。 它本质是笛卡尔积加约束过滤:12个主食 + 01个小食 + 01个饮料 + 01个甜点。这个过程确定、无歧义、不需要任何「理解」。用一个概率模型去做确定性计算,是选错了工具。

第二,券匹配有一条模型容易漏掉的约束——每单只能用一张券。 所以不能简单挑面值最大的那张,必须计算「这个具体订单实际能省多少」:满减门槛是否达到、折后总价多少、品类限制是否适用。只有枚举才能真正算对。

第三,排序目标有三种,而且互相矛盾。 最便宜(总价升序)、最健康(偏离营养目标的程度升序)、最高性价比(每元蛋白质降序)。三种排序跑在同一批候选集上,这正是脚本该干的活。

笛卡尔积(Cartesian Product)是组合枚举的数学基础:将多个集合中的元素逐一配对,得到所有可能的组合。以本项目为例,若主食有8种选择、小食有5种、饮料有6种、甜点有4种,全量枚举的组合数为 8×5×6×4=960 种(再乘以「是否选该类别」的选项则更多)。这个数量对Python脚本来说是毫秒级任务,但对语言模型而言则意味着需要在自回归生成过程中逐token维持所有中间状态——模型并没有内置的"变量存储",每一步的计算都依赖上下文窗口里的文字表示,极易在多步骤算术中累积误差或遗漏边界条件。这正是「确定性计算交给确定性工具」这条原则的根本依据。

LLM真正擅长的那一半

拆分的另一面,是在不削减模型价值的前提下减轻它的负担。模型保留两个关键能力:

把模糊语言翻译成硬约束。「两个人」→ 提高蛋白质目标;「别太油腻」→ 可能加个热量上限,可能排除油炸品类;「50块以内」→ max_payable=45,给客户端看不见的配送费、包装费留出余量。这种语义到参数的映射,脚本做不了,模型却做得很好。

为结构化输出做解说。 脚本吐出JSON,模型负责解释「为什么推荐这套而不是那套」。边界始终清楚:模型负责翻译和叙述,不负责计算和记忆。

数据层的坑:别信模型的记忆

项目里有一条硬规则:所有数字都来自MCP,不许硬编码。 大模型对快餐菜单的记忆往往过时、不全,偶尔还会凭空编造。更关键的是:

同一连锁品牌的不同门店,卖的东西和能用的券都不一样。

所以流程必须是「先确认门店,再拉菜单和优惠券」。作者在测试时差点用关键词搜索自动选店——结果搜出来的全是火车站里的门店。要是真替用户选了,用户根本不在那附近,整单就废了。这成了整个skill里最硬的一条规则:门店必须由人确认,绝不能让系统猜。

MCP配置的几个实操陷阱

针对WorkBuddy客户端,作者踩了几个值得记录的坑:

type字段大小写。 麦当劳文档写的是小写 streamablehttp,但WorkBuddy只认驼峰式 streamableHttp。作者是通过阅读WorkBuddy内置连接器列表反推出来的——所有内置项都用驼峰。文档和客户端打架时,去对照内置项。

配置路径。 很多教程说放在 ~/.workbuddy/mcp.json,真正生效的却是 ~/.workbuddy-ai/mcp.json。前一个目录确实存在,但只放 device-id 和日志,不放配置。

配好不等于启用。 写完配置还得在自定义连接器面板里手动启用服务,否则客户端不会加载。验证时别信UI,直接发一次原始JSON-RPC的 tools/list 调用——作者这边返回了35个工具,才算真连上。

JSON-RPC(JSON Remote Procedure Call)是一种轻量级远程调用协议,使用JSON格式编码请求和响应。MCP底层即采用JSON-RPC 2.0:客户端发送包含 method、params 和 id 字段的请求体,服务端返回 result 或 error。文章中提到的 tools/list 调用,就是向MCP服务端发送一个方法名为 tools/list 的JSON-RPC请求,服务端返回其支持的所有工具定义(名称、描述、参数schema)。直接用原始JSON-RPC验证连通性,比依赖UI状态指示更可靠——UI可能缓存了旧状态,而成功收到工具列表响应则证明整条链路(网络、认证、协议版本)都已正常工作。调试MCP连接问题时,这是最值得优先尝试的验证手段。

容易被跳过的一步:服务端重新定价

本地引擎算出的总价只是估算。配送费、包装费、门店级促销只存在于服务端。所以在把方案展示给用户之前,必须把商品编码送进定价工具重新核对一次——两边不一致时以服务端为准。 跳过这一步,用户就会发现你算的钱和实际付款对不上。

可迁移的方法论

这个项目真正有价值的部分,不是算出最便宜的套餐,而是那套分工:

LLM管理解与表达,脚本管计算与确定性,MCP管可信数据。

凡是「决策形状」的任务——选餐、选品、排班、比价——这套三段式结构都能直接套用。项目已在GitHub开源(minxin88/mcd-meal-optimizer),MIT协议,纯Python标准库零依赖,运行 python scripts/optimize_meal.py --demo 就能用内置离线样本数据体验完整的枚举与券匹配流程,不消耗任何token。需要说明的是,项目范围不含下单、领券、取消订单,门店选择始终需要人工确认,仅限个人非商用。

这篇文章给做Agent的人提了个醒:别迷信让大模型包办一切。把确定性计算交回给确定性的工具,模型反而能在它真正擅长的地方发光。

#决策型Agent#LLM架构#MCP配置#AI Agent设计#确定性计算#WorkBuddy#大模型工具调用#开源项目
分享:

相关推荐