每天中午十一点半,办公室总会上演同一幕:有人盯着外卖软件翻五分钟还没下单,有人在“黄焖鸡还是麻辣烫”之间反复横跳,还有人打开三个App轮流刷价格。我以前也是这德行,整个人被“中午吃啥”耗掉半管血。直到我顺手用豆包 Seed-2.1-pro-0915 做了个「今天吃啥」的小工具,这事真就翻篇了。
豆包大家应该不陌生,字节跳动那个国民级AI助手,而 Seed-2.1-pro-0915 是它背后新一代语言模型的一个版本号。我把它接上自己写的点菜决策逻辑:输入预算、口味、忌口、以及最近吃过啥,它三秒钟甩出三道不重样的午餐建议,每道还带着推荐理由和大致价格。我连选都懒得选,直接抄第一道菜下单。
这个工具特别适合三类人:一是每天外卖选择困难户,二是想带饭但不知道做什么的备餐党,三是办公室三五个人天天为“去哪吃”开小会的协作小组。模具不大,技术含量也不夸张,但思路值得掰开讲讲,核心其实是大家都能复刻的 Prompt 工程和调用姿势。
1. 先弄清楚:你纠结的到底是什么
1.1 选择过载才是罪魁祸首
很多人以为“中午吃啥”是一个口味问题,其实是一个心理问题。心理学里有个词叫“选择过载”,选项一多,大脑的决策成本指数级上升,最后要么随便选一个然后后悔,要么干脆不选。外卖App把全城几百家店塞到你面前,每家店又是一个好几十道菜的菜单,这等于逼着你在一万多个选项里做单选题。
我以前试过把菜名写在纸条上抓阄,结果发现没用——抓出来的那个答案我大概率不想吃。因为抓阄是纯随机,它不带任何约束:我想吃辣的时候它可能给我一碗白粥,我想省钱的时候它可能甩我一份七八十的日料。纠结的本质,是“约束条件太多”和“方案太少”之间的冲突,而人脑在中午那种血糖偏低的状态下,根本没法做这种多条件检索。
1.2 用随机数轮盘为什么不行
有人可能会说,搞个轮盘指针转一下不就完了,还要什么大模型?真不行。轮盘转出来的结果和抓阄一样,没有推理过程。它能知道“你昨天刚吃过火锅,今天最好清淡点”吗?它能判断“预算三十块钱以内,还要有蛋白质和蔬菜”吗?这些不是随机数能解决的,它们是约束满足问题。
大模型的价值就在于这些常识推理。它不是凭空给你编一个菜,而是结合你的历史记录、预算、口味偏好、甚至当前季节做判断。同样是“预算30”,冬天它可能推荐热汤面,夏天它可能推凉面配小菜。这种上下文感知的能力,是任何花哨的随机算法都做不到的。
1.3 为什么是豆包 Seed-2.1-pro-0915
模型选型和工具本身的逻辑同样重要。我一开始也想过用别家模型,最后固定用 Seed-2.1-pro-0915,不是图新鲜,是三个现实理由。
第一,中文语料强。中餐菜名的翻译是很多国外模型的老大难,“蚂蚁上树”翻成“蚂蚁爬树”的段子一抓一把。豆包这个底座的训练数据对中餐菜名、小吃名、家常菜的语境理解非常准,不会闹出“麻婆豆腐+草莓酱”这种洋不洋土不土的组合。第二,指令跟随性好。我这套小工具的核心不是代码,是给模型的指令。Seed-2.1-pro-0915 这类新版本在遵循复杂格式约束上明显比以前稳,你说“只要JSON”,它就极少给你夹带私货。第三,成本低。每天中午一次请求,输入几十个token、输出一两百个token,折合下来一次一两分钱,一个月就想不出一顿早饭钱。
当然,这串编号里的“pro”我理解是更完整的版本,后缀“0915”多半是版本迭代的快照日期。细节不必较真,只需知道这个版本的中文与指令跟随能力,用来做点菜决策绰绰有余。
| 方案 | 是否能带约束条件 | 是否知道历史记录 | 中文菜品理解 | 自动化能力 |
|---|---|---|---|---|
| 抓阄纸条 | 否 | 否 | 不适用 | 否 |
| 随机数轮盘 | 否 | 否 | 不适用 | 部分 |
| 豆包网页版对话 | 能,但每次复制粘贴聊天记录 | 弱,聊天记录会忘 | 强 | 弱 |
| Seed-2.1-pro-0915 API | 能,结构化传入 | 强,可通过对话记忆 | 强 | 强 |
2. 核心不是代码,是那四版 Prompt
2.1 把“随便”翻译成约束条件
很多人用AI第一反应是问“今天吃什么”,然后AI回一大段废话。问题在于“吃”这个信息根本不够。你要把“随便”翻译成人话,AI才能给你有价值的建议。我给自己定的输入模板是这四个字段:
- 预算:我现在能花多少钱,单位是元。
- 口味:今天想吃辣、清淡、酸甜还是重口?
- 忌口:不吃什么,不限于过敏,还包括我不爱吃的东西。
- 历史记录:过去三天吃过什么,模型需要避开它。
一开始我拿这套模板直接问豆包网页版,发现它偶尔能给出好东西,但更多时候像“薛定谔的推荐”,可能这次推荐鱼香肉丝,你说不要,下次它还推荐鱼香肉丝。因为网页版没有稳定的上下文把约束串在一起。后来我意识到,不是模型不行,是我的提问方式太散。
2.2 第一版:能跑,但脑子不记事
我写的第一版 Prompt 简单粗暴:
你是一个熟悉中餐和外餐的美食顾问。我会给你今天的预算、口味、忌口、 以及过去三天吃过的菜。请推荐3道不同的午餐选择,每道一行, 格式:“菜名 | 一句话理由 | 预估价格”。不要推荐我过去三天吃过的菜。跑了一次,输出是这样的:
辣子鸡丁 | 下饭神器,辣度够劲 | 28 番茄鸡蛋面 | 清淡顺口,适合不太想吃重口的时刻 | 18 三文鱼波奇饭 | 蛋白质和蔬菜都有,带点轻食感 | 38看起来还行,但用几天就发现两个问题:第一,它嘴上答应“不要推荐过去三天的菜”,真到输出时偶尔还是会把前几天出现过的菜混进来,尤其是热门菜如“番茄炒蛋”。第二,三道菜风格容易扎堆,有时候连出三道米饭配肉类,完全没有菜蔬。这不是模型笨,是我没有把“不要重复”和“风格差异化”变成足够的硬约束。
2.3 Prompt 的“角色+任务+格式”三段式重构
后来我把 Prompt 重写成更结构化的版本,这也是这套工具真正的灵魂所在:
# 角色 你是一个实战型美食顾问,熟悉家常菜、外卖档口、便当搭配和食堂菜品。 你只推荐现实中最常见、最容易吃到、价格靠谱的菜品,不推荐花哨新菜。 # 任务 用户会提供四个字段:预算、口味、忌口、最近吃过的菜。 请推荐3道午餐,要求: 1. 总价格在预算范围内,至少一道低于预算一半; 2. 避开忌口和最近吃过三次内的菜; 3. 三道菜在品类上拉开差距:一道偏主食类、一道偏下饭类、 一道偏轻食或含绿叶菜; # 输出格式 只输出一个 JSON 数组,不要任何解释和前后缀文字。数组元素包含: - name: 菜名 - reason: 一句话推荐理由(必须结合用户口味) - est_price: 预估价格 - category: 主食 / 下饭 / 轻食三处关键改动:一是任务里明确“避开最近吃过三次内的菜”,范围从“过去三天”变成“吃过三次”,记忆逻辑更清楚;二是要求“至少一道低于预算一半”,直接压住超支问题;三是用 category 字段强制三道菜拉开差距。从那之后,输出质量明显上了一个台阶,很少再出现“三道都是盖饭”的尴尬场面。
2.4 三个模式:日常、翻牌、戒断
光有一个基础 Prompt 还不够,我顺手做了三个变体,随时切换:
日常模式就是上面那个,推荐三道菜,适合“今天虽然纠结但还想正经选一选”的状态。翻牌模式则把 temperature 拉到很高,Prompt 里只写“随机推荐一道你绝对想得到但我大概率没吃过的菜,不要家常菜,不要面条米饭”,适合想突破舒适区、让味蕾受点刺激的时刻。戒断模式则是把“低卡、少油、高蛋白、避塘”几个字段写死,专门应对连续吃胖三斤之后的日子。
这三个模式本质就是三套不同的 Prompt 模板。模板这种东西,一旦你掌握了“换场景就换约束”的思路,可以无限扩展。比如加一个“约会模式”,推荐适合两人吃的环境友好的下馆子菜品;或者加一个“酷热模式”,专门筛冷面、凉皮、拌菜。
3. 实操:从 API Key 到手搓一个小工具
3.1 准备环境与账号接入
要先说明一点:网页版豆包够日常聊天用,但你要做自动化工具,最好走 API。我当时在火山引擎的方舟平台创建了一个应用,拿到 API Key,然后把模型接入点开通,那里能看到模型名称或接入点ID。不同项目在控制台上展现形式略有差异,我的建议是你手头拿到的接入点标识为准,它可能长成“ep-xxxx”也可能直接是模型名,只要填对地方就行。
环境就 Python 3.10+,装一个 openai 库,因为它兼容豆包服务的接口协议。
pip install openai需要注意,Python 的openai库在 1.0 版本之后用法有变化。下面的代码我按新版写法来,老版本的话把参数名改一下即可。
3.2 核心请求代码怎么写
核心代码其实短得惊人,总共二十几行。把 API Key 放在环境变量里,避免硬编码进脚本:
import os import json from openai import OpenAI client = OpenAI( api_key=os.getenv("ARK_API_KEY"), base_url="https://ark.cn-beijing.volces.com/api/v3" ) SYSTEM_PROMPT = """你是一个实战型美食顾问,熟悉家常菜、外卖档口、便当搭配和食堂菜品。 你只推荐现实中最常见、最容易吃到、价格靠谱的菜品。 用户会提供预算、口味、忌口、最近吃过的菜四个字段。 请推荐3道午餐,要求总价格在预算范围内,至少一道低于预算一半; 避开忌口和最近吃过三次内的菜;三道菜在品类上拉开差距。 只输出一个 JSON 数组,包含 name、reason、est_price、category 字段。""" def recommend(budget, taste, avoid, history): user_msg = ( f"预算:{budget}元\n" f"口味:{taste}\n" f"忌口:{avoid}\n" f"最近吃过的菜:{history}" ) resp = client.chat.completions.create( model="seed-2.1-pro-0915", temperature=0.8, messages=[ {"role": "system", "content": SYSTEM_PROMPT}, {"role": "user", "content": user_msg}, ], ) content = resp.choices[0].message.content match = re.search(r"\[.*\]", content, re.S) return json.loads(match.group())model这一栏可以直接写模型名,也可以填你在方舟后台生成的接入点ID,按实际拿到的为准。temperature=0.8是我调出来的最佳区段,既有随机性又不至于乱来,后面会专门讲。
注意我用正则从返回内容里抠出 JSON 数组。虽然 Prompt 里强调“只输出 JSON”,但实际偶尔还是会带两句“好的,已按你的要求推荐”之类的废话,正则是兜底方案,专门治这种不听话。
3.3 用本地文件记住“昨天吃了啥”
模型是无状态的,这次调用完它就把你忘了。所以要在工具外面做一个简单的记忆模块。我这里用文本文件,够用。
history.txt 里一行一道菜:
辣子鸡丁 番茄鸡蛋面 三文鱼波奇饭每次调用前读这个文件,转成逗号分隔的字符串放进 user_msg 的“最近吃过的菜”字段。调用成功后,把推荐的三道菜名 append 进去,再只保留最近 20 行,保证记忆不会太长也不会太短。
后来我发现一个问题:如果模型推荐的菜用户最后没吃,它也会把推荐的菜写进历史,导致第二天误以为“吃过”。所以我又改成这样:推荐出来后先别急着写入,等用户回复选择哪道,再把那道菜真正写进 history.txt。多了一个人工确认的步骤,换来的是记忆的高准确度,非常划算。
3.4 三次真实测试跑出来的效果
我拿几个典型输入做过试验,挑三次记录放在这里,你们可以感受一下前后对比。
测试一:预算30元,口味“想吃辣但胃今天有点怂”,忌口“不吃香菜和折耳根”,历史记录“小炒肉、担担面、汉堡”。模型返回:
[ {"name": "番茄炖牛腩", "reason": "想吃辣但胃怂,就选带点酸甜微辣的炖菜,牛腩蛋白质足", "est_price": 28, "category": "下饭"}, {"name": "菌菇鸡汤面", "reason": "汤面温和,没有香菜,符合今日清淡解压的需求", "est_price": 20, "category": "主食"}, {"name": "凉拌鸡丝", "reason": "绿叶菜配鸡丝,低负担轻食,给胃留点余量", "est_price": 16, "category": "轻食"} ]测试二:预算60元,口味“清淡但要有肉”,历史“沙拉、寿司、米线”。模型开始推清蒸鲈鱼、鸡胸藜麦饭、家常豆腐,品类分散得相当好看。
测试三:预算15元,口味“越便宜越硬核”,历史“兰州拉面”。模型推荐的蛋炒饭、肉夹馍配凉皮、食堂两荤一素,准确GET到了“便宜硬核”的调性。
这三轮下来模型没有给我任何一道“全网消费趋势榜”里的悬浮菜,全是附近的日常选项。这就是 Prompt 里那句“只推荐现实中最常见、最容易吃到”在起作用。
4. 踩坑七日谈:这些问题我挨个遇过
4.1 推荐结果高频重复
用三天之后第一个撞见的问题就是模型疯狂推荐同类型菜,尤其是番茄、土豆这类百搭食材,它恨不得天天见面。我一开始以为它没看历史记录,后来翻代码才发现,历史记录确实传了,但我在历史里只存菜名,没有存做法和食材维度,模型只能从字面上排除,番茄鸡蛋面和番茄炒蛋对模型来说是两道不同的菜。
解决办法是两个动作叠加:一是历史记录里不仅写菜名,还要补食材说明,每次写“番茄鸡蛋面(含番茄,鸡蛋,面条)”;二是在 Prompt 里加了一句“如果无法完全避开,优先避开食材和风味组合相同的菜”。从那以后重复率骤降。
4.2 JSON 输出混入文字导致解析崩掉
有一天下班时我的脚本收到一条“抱歉,可能是我的网络不好,重新生成一次”的返回文中带一个JSON乱码。一查,模型偶尔会在 JSON 前后加散装句子,我的 json.loads 直接炸了。后来加了正则抽取第一组方括号中间的内容,再交给 json.loads,同时 try-except 兜底。只要 JSON 字段本身是合法的,散装文字就不会影响解析。
还有一个隐藏坑:JSON 内部的字符串里可能出现中文逗号、多余空格,甚至把“3道菜”写成“三道菜”,这些都不至于解析失败,但如果你在用老版本 SDK,注意别把 response.choices[0].message.content 里可能为 None 的情况给漏掉。
4.3 三道菜全是主食
这个问题在早期特别明显。模型觉得“你穷,那就碳水管饱”,于是给你推荐炒饭、炒面、炒河粉三件套。安排上“至少一道轻食或含绿叶菜”之后,情况好转,但还是偶尔出“三菜全荤”。我最后用的方法是给三个 category 字段做最直白的映射,同时要求模型自己给菜品做分类,如果不满足就判定输出不合格,重试一次。
4.4 预算超支是常态
模型对价格天生没概念。你给它“30元预算”,它照样推你一份58的煲仔饭。单纯在 Prompt 里说“不要超预算”效果很差,因为模型没有实时物价数据。我的对策是两步:第一步,在 Prompt 里明确给出价格锚点,“15元上下可选择:蛋炒饭、肉夹馍、食堂快餐;20元上下:盖饭、汤面;25-30元下:家常小炒、轻食沙拉”;第二步,在生成的 JSON 外面加一层后置校验,如果 est_price 超过预算,就再调用一次并把超预算的菜名放进“排除”字段。双保险之后基本不会出现低级超支。
4.5 temperature 调的越高越不靠谱
我尝试过 temperature=1.3,想让推荐更大胆。结果它给我推荐了一碗“仰望星空派”,虽然我承认那是英国菜,但和“中餐+外卖+30元”这个场景差得太远。反过来,temperature=0.2 的时候非常保守,连续几次都推“黄焖鸡米饭”。后来我把温度定在0.75到0.8之间,发现是最适合点菜场景的区间:既能拉开新鲜感,又不至于飞出大气层。
4.6 一个人吃饱全家不饿?团队场景启动
后来办公室几个同事看到了也想用,但每个人的忌口、预算都不一样,直接套用一个人版本的 prompt 根本不work。于是加了一个群模式:把所有人的忌口写成一个段落,模型必须返回“在场所有人都不吃的食材为零”的菜品,然后每个人用投票决定吃哪道。
这个过程让我意识到一个很本质的问题:点菜模型的Prompt其实不是在“生成菜名”,而是在人际约束和社会偏好之间做加权妥协。它要处理的信息,跟你在会议室里协调两个部门的资源分配没有区别。
4.7 网页版和 API 完全不是一回事
还有一个小经验:有段时间我图省事,直接在豆包网页版开一个新的对话窗口用相同 Prompt,结果出来效果一次好一次坏。后来才反应过来,网页版和 API 虽然模型同源,但产品层面的系统提示词和上下文策略完全不一样。网页版自带很多隐形的指令,比如安全过滤、对话引导;API 走的是最原始的模型接口,你对 Prompt 的控制力强得多。如果你想做精细控制,必须上 API,别想着用网页版凑合。
5. 进阶玩法:从“吃饭工具”到“吃饭方案”
5.1 封装成豆包技能,一句话唤起
如果你不想每次敲 Python 脚本,可以把这套 Prompt 封装成豆包里的“技能”或“Skill”。现在不少 AI 平台支持自定义技能菜单,你把“今天吃啥”这个技能绑定上面那段 Prompt,之后在对话窗口里说一句“豆包,今天吃啥”,它就会自动索要预算和口味。这相当于是让用户从“自己拼 Prompt”解放出来,接入门槛降低到爷爷奶奶也能用的程度。
体验上有个小技巧:技能命名最好带动作,比如“帮我点个菜”,而不是只有一个“菜单”,后者容易被聊天助手当成普通词汇忽略掉。我把技能描述写成“当用户说不知吃啥时,自动收集预算与忌口,输出三道结构化推荐”,唤醒率明显更高。
5.2 团队版:一次输入,大家一起结束纠结
团队版实际就是一个多行输入:“预算总共多少”“有几个人”“谁不吃辣”“谁最近减肥”“上次聚餐在哪家店”。模型会把这些人条件折叠成推荐维度,输出要么是“几人可以一起去的餐厅类型”,要么是“三份可以共存的外卖点单组合”。
我后来还加了一个投票环节:模型推荐出4道菜之后,大家用表情符号选定唯一一道,选定结果写回本地文件,下次推荐时会自动排除这个选项。因为每次这么干,时间消耗最长的不再是点菜,而是“谁来投最后那一票”。
5.3 用数据驯养你的“口味曲线”
历史记录攒了三周后,我顺手写了个统计脚本,把每周吃的菜按“辣度、油脂、碳水、蔬菜”打标记,生成了我个人的口味曲线。结果我发现:周一到周三我明显偏重口,周四开始自动想清淡,周五则是“随便吃但别排队”的草率型消费。这条曲线倒推回去给了模型一个新字段“本周第几天”,推荐质量再度提升。
这就是这个项目里最让我兴奋的部分。模型本来只是解决一个“今天吃啥”的临时问题,当你把历史数据喂给它后,它就慢慢变成了一个知道你口味规律、会在你明天情绪性想吃炸鸡之前提前推一份青菜汤面的私人营养参谋。
5.4 我理解的小工具哲学
工具本身不复杂,一个 API 请求加一个 Prompt 模板,但背后那个想法挺值得聊:“把琐碎决策外包给机器,把意志力留给真正重要的判断。”人每天能做的有效决策是有限的,中午点菜这种低收益高消耗的问题,交给大模型去纠结,反而是在保护自己的精力。
我把这个项目跑到今天已经两个月,午休前点菜从五分钟缩到三十秒。有时候模型推出一道菜,我知道那不是最好吃的,但它至少帮我选好了,午餐这件事就这么轻松地完成了。如果你也受够了“选择过载”,不妨照着这套思路做一个自己的“今天吃啥”,它会比你想象得更早地改变中午十二点的心情。