
Anthropic 最近抛出了一个足以让零售业和 AI 圈都抬头看的方向在一年内推出由 AI 运营的自动售货机、商店和咖啡馆。乍看像是一句“AI 新零售”的营销话术但从工程视角看这其实是大模型走出聊天框、进入物理世界的最典型场景之一。如果只是把一个聊天机器人的 API 接到售货机上那并不新鲜真正难的是让 AI 自己理解顾客的模糊指令、判断库存、解锁货道、处理异常并且在整个过程中不出现“货出了、钱没收到”这种致命问题。这篇文章不打算评价 Anthropic 的商业计划是否激进而是希望借这个方向聊一聊一个普通开发者能实际落地的简化版本用 Claude API 做一个“AI 零售终端代理”。我们会从一个最小可运行的 Python 脚本开始拆解从顾客说“我要一瓶可乐”到货道真实解锁的完整链路然后讨论环境搭建、代码实现、验证方式和生产环境必须注意的坑。读完这篇文章你会知道 AI 运营自动售货机这件事的本质是什么也会有一个可以继续扩展的实验原型。1. 这篇文章真正要解决的问题很多同学看到“AI 运营商店”的第一反应是“用机器人搬货”这是误解。如果仅仅是把商品从货架拿给顾客工业机械臂早就做到了不需要大模型。真正的变革发生在“决策层”传统售货机是预先写死的按钮和规则顾客只能选择编号而 AI 售货机允许顾客用自然语言表达需求甚至能处理退货、推荐、库存盘点这类复杂任务。所以这篇文章要解决的核心问题不是“怎么造一台售货机”而是AI 在无人零售里到底扮演什么角色一个 “AI 运营” 的系统需要哪些上游和下游组件用现有的大模型 API以 Anthropic Claude 为例怎么把意图识别、工具调用、设备控制串起来实际开发中哪些环节最容易出问题如果你正在做 AI Agent 应用或者想尝试大模型与物联网硬件结合的项目这篇文章会很有价值。即便你不关心零售场景文中的“大模型 工具调用 外部系统”模式也能直接迁移到智能客服、机器人控制、运维自动化等方向。我的判断是AI 零售的核心不是“模型多聪明”而是“决策闭环有多可靠”。模型可以犯错但系统设计必须让错误不造成实际损失。这也是为什么我们需要在代码层面明确划分“AI 建议”和“设备执行”的边界。2. AI 运营零售场景的基础概念与核心原理2.1 从传统售货机到 AI 售货机传统自动售货机的工作方式很直接用户投币或扫码按下对应货道的编号机器收到数字信号后电机旋转把商品推落。这个过程的逻辑是完全确定的业务规则的任何一次变更哪怕只是调整价格都需要人工修改控制器程序。AI 售货机的变化在于把“按下编号”升级成“自然语言对话 动态决策”。系统需要理解用户说“来瓶冰的不要太甜的”是什么意思然后结合商品库、库存、口味偏好甚至实时折扣决定推荐哪个商品再触发物理设备出货。这要求系统具备三个能力感知通过摄像头、传感器、用户输入获取现场信息。决策调用大模型对信息进行理解和推理生成下一步动作。执行把模型输出的动作转换成设备指令并确保结果正确。这三个能力对应的就是一个典型的 AI Agent 架构LLM 作为大脑工具Tools作为手脚外部系统库存、支付、硬件作为环境。2.2 大模型与工具调用Function Calling要让 Claude 这类模型控制售货机关键机制是工具调用。所谓工具调用其实就是让模型在回答问题时不只输出文本还可以输出一个“结构化动作”比如{ name: unlock_slot, arguments: {slot_id: 3} }模型本身不直接操作硬件它只负责“决策”。当它判断顾客要买可乐时它不知道可乐在哪个货道于是先调用“查询库存”工具得到结果后再调用“解锁货道”工具。整个过程可以由一个循环完成用户输入消息。调用模型 API携带工具定义。如果模型返回工具调用请求则执行对应工具把结果回传给模型。模型根据工具结果决定继续调用工具还是输出最终回复。这就是 Agent 的核心循环。传统编程中的“if-else”逻辑被模型动态生成的动作取代而工具本身仍然是确定性代码这既保留了 AI 的灵活性又保证了硬件的安全性。2.3 为什么选择 Claude API 作为示例用 Anthropic 的 Claude 作为示例不是因为它是唯一选择而是因为它非常适合这类场景上下文窗口够大可以容纳较长的商品说明、库存历史和对话记录工具调用稳定返回的 JSON 结构清晰不容易出现格式崩坏模型在中文自然语言理解上表现不错适合做零售客服API 风格简洁Python SDK 上手成本低。需要说明的是下面示例里的模型名称我会写一个具体的版本但你实际部署时请以官方最新文档为准。因为模型迭代很快有的示例代码运行到一半可能发现模型名称已过期这属于正常现象。3. 环境准备与前置条件在写代码之前先确认你的开发环境。这里用 Python 3.10因为新版 Anthropic SDK 和 pydantic 都要求较新的 Python。3.1 获取 Anthropic API Key首先你需要一个 Anthropic 开发者账号并在控制台创建一个 API Key。创建之后把它保存到环境变量或.env文件中不要直接硬编码在代码里。如果你在公网演示更要注意 Key 泄露的问题。3.2 安装 Python 依赖建议使用 virtualenv 或 conda 管理环境。需要安装的包如下anthropic官方 Python SDKfastapiWeb 服务框架用于对外提供 APIuvicornASGI 服务器python-dotenv读取 .env 文件pydanticFastAPI 依赖用于数据校验安装命令pip install anthropic fastapi uvicorn python-dotenv pydantic如果你只是想在本地跑通最小示例只安装anthropic python-dotenv就够。FastAPI 是可选的但它能让你更方便地模拟“用户通过手机/语音终端发来请求”的场景。3.3 硬件环境可选如果你有真实硬件可以使用树莓派 继电器板 直流电机来模拟货道。但本文演示的是逻辑层硬件层面我会提供一个模拟函数把“解锁货道”打印到控制台。这样即使没有任何硬件也能验证整个 Agent 链路。真实硬件接入时只需要把unlock_slot函数里的print替换成 GPIO 操作即可。3.4 准备一个目录结构我建议先建一个干净的目录方便后续管理ai-vending-machine/ ├── .env ├── agent_order.py ├── hardware_controller.py ├── app.py └── requirements.txt其中agent_order.py是核心 Agent 逻辑hardware_controller.py是模拟硬件层app.py是基于 FastAPI 的对外服务。4. 核心流程拆解从“我要可乐”到货道解锁现在开始拆解核心流程。为了让你不迷路我先用文字描述一遍完整时序再给出具体代码。一个顾客走到 AI 售货机前按下语音按钮说“给我来一瓶可乐要冰的。”系统执行以下步骤音频/文本输入语音识别模块把语音转成文本或者用户直接在触摸屏输入文字。调用 Claude API把文本和工具定义发给模型并要求模型扮演售货员角色。模型返回工具调用Claude 发现这句话里没有具体货道编号于是调用query_inventory工具查询可乐库存。执行工具系统查询本地库存表返回“可乐还有 5 瓶冰柜温度正常”。再次调用 Claude模型拿到库存信息后认为可乐可以直接出货于是调用unlock_slot工具参数为货道编号比如 3。执行工具硬件模块解锁 3 号货道电机旋转可乐掉落。模型输出最终回复“好的已为您出货请取走商品。”注意在第 5 步中如果查询库存发现可乐缺货模型就会调用另一个工具比如recommend_alternative或者直接输出“抱歉可乐卖完了试试雪碧吧”。这就是 AI 决策比传统售货机灵活的地方。为了安全我们还要加一个硬性约束模型只会“建议”解锁货道真正执行前必须由本地代码再次检查库存是否充足、设备是否在线否则报错。不要无条件相信模型输出这是生产级 Agent 最重要的原则。4.1 工具定义的规范Anthropic API 的工具定义使用 JSON Schema 格式。每一个工具需要包含名称、描述和输入结构。描述写得好不好直接影响模型调用的准确率。例如unlock_slot的描述应该写清楚“解锁货道让商品掉落通常在用户确认购买后调用”而不是简单写“解锁”。下面是一个工具定义示例我们在代码里实际会用到{ name: query_inventory, description: 查询某个商品在售货机中的实时库存, input_schema: { type: object, properties: { product: { type: string, description: 商品名称例如可乐、雪碧 } }, required: [product] } }好的工具命名应该具备“动词 宾语”的特征比如query_inventory、unlock_slot、create_order、refund模型更容易理解。4.2 Agent 循环的设计Agent 循环是用代码写死的。因为模型一次请求可能只返回一个工具调用也可能连续返回多个所以循环不能只处理一次。正确做法是把模型的响应追加到消息列表直到stop_reason不再是tool_use。每次执行完工具都需要把结果以tool_result块的形式传回模型模型才能继续推理。这里特别容易踩坑很多新手把工具结果放在一个普通的系统消息里或者用错误格式回传导致模型无法理解工具执行结果。在 Anthropic API 中tool_result是消息体里的一个特殊内容块必须包含原始tool_use块的 id。5. 完整示例与代码实现以下代码可以直接复制到项目里按照注释修改模型名称和 API Key 后即可运行。为了保持示例简洁我把业务规则写死在一个字典里真实项目应该换成数据库或缓存。5.1 依赖文件 requirements.txtanthropic0.40.0 fastapi0.115.0 uvicorn0.30.0 python-dotenv1.0.0 pydantic2.0.0版本号是大致下限实际安装最新版即可。5.2 环境变量文件 .envANTHROPIC_API_KEYsk-ant-你的key MODEL_NAMEclaude-3-5-sonnet-20241022如果你用的模型比这个新直接替换MODEL_NAME不用改代码。5.3 模拟硬件控制 hardware_controller.py这个模块模拟底层硬件。为了让你在本地无硬件也能跑这里先把“解锁”动作打印出来但保留真实对接的接口。实际树莓派项目里可以把print替换成控制 GPIO 的代码。# hardware_controller.py 模拟售货机硬件控制层。 在生产环境中unlock_slot 应该通过串口、GPIO 或 HTTP 控制真实设备。 这里只打印日志方便在没有硬件时验证 Agent 决策链路。 INVENTORY { 可乐: {price: 3.0, slot: 1, stock: 5}, 雪碧: {price: 3.0, slot: 2, stock: 0}, 矿泉水: {price: 2.0, slot: 3, stock: 8}, } def query_inventory(product: str) - dict: 查询某个商品的库存和货道信息。 item INVENTORY.get(product) if item is None: return {available: False, message: f没有找到商品{product}} return { available: item[stock] 0, stock: item[stock], slot: item[slot], price: item[price], } def unlock_slot(slot_id: int) - dict: 解锁指定货道。 # 模拟硬件动作 # 真实场景树莓派 GPIO 输出高电平驱动继电器让电机旋转 print(f[硬件执行] 解锁货道 {slot_id}电机旋转商品掉落) return {success: True, slot: slot_id}这个文件里的INVENTORY是一个全局字典多个请求共享且没有加锁。真实项目里要注意并发和事务问题后面会专门讲。5.4 核心 Agent 逻辑 agent_order.py这是整篇文章的核心。它完成以下事情加载环境变量定义 Claude 工具实现process_message循环把工具执行结果回传给模型。# agent_order.py 基于 Claude API 的 AI 售货机 Agent。 import os from dotenv import load_dotenv from anthropic import Anthropic from hardware_controller import query_inventory, unlock_slot load_dotenv() client Anthropic(api_keyos.getenv(ANTHROPIC_API_KEY)) MODEL os.getenv(MODEL_NAME, claude-3-5-sonnet-20241022) TOOLS [ { name: query_inventory, description: 查询某个商品在售货机中的实时库存、价格和货道编号。, input_schema: { type: object, properties: { product: { type: string, description: 商品名称例如可乐、雪碧。 } }, required: [product] } }, { name: unlock_slot, description: 解锁指定货道让商品掉落。通常用于用户确认购买且库存充足时。, input_schema: { type: object, properties: { slot_id: { type: integer, description: 货道编号。 } }, required: [slot_id] } } ] SYSTEM_PROMPT 你是智能售货机助手。你的职责是帮助顾客选购商品。 在调用工具时必须遵守以下规则 1. 如果顾客没有指定具体商品先询问需求。 2. 如果顾客指定了商品先调用 query_inventory 查询库存。 3. 只有当库存充足且顾客明确要购买时才调用 unlock_slot。 4. 如果库存不足推荐替代商品但不要擅自解锁货道。 5. 不要编造工具执行结果所有结果必须来自工具返回值。 def execute_tool(tool_name: str, tool_input: dict): 执行具体的工具函数。 if tool_name query_inventory: return query_inventory(tool_input[product]) if tool_name unlock_slot: return unlock_slot(tool_input[slot_id]) return {error: f未知工具: {tool_name}} def process_message(user_message: str) - str: 处理用户消息并返回最终回复。 messages [{role: user, content: user_message}] loop_count 0 while True: loop_count 1 if loop_count 10: return 处理超时已终止请求。 response client.messages.create( modelMODEL, max_tokens1024, systemSYSTEM_PROMPT, toolsTOOLS, messagesmessages, ) # 把模型回复追加到消息列表 messages.append({ role: assistant, content: response.content, }) # 如果模型没有要求调用工具说明已生成最终回复结束循环 if response.stop_reason ! tool_use: break # 处理模型返回的所有工具调用 tool_result_blocks [] for block in response.content: if block.type tool_use: result execute_tool(block.name, block.input) tool_result_blocks.append({ type: tool_result, tool_use_id: block.id, content: str(result), }) # 将工具结果以 user 消息形式回传给模型 messages.append({ role: user, content: tool_result_blocks, }) # 提取最终文本内容 final_text for block in messages[-1][content]: if block.type text: final_text block.text return final_text.strip() if __name__ __main__: # 本地测试 while True: user_input input(顾客说) if user_input in (exit, quit): break reply process_message(user_input) print(售货机, reply)代码逻辑并不复杂但有几个细节必须解释response.content可能是一个多个块的列表其中既有文本块也有工具调用块。必须遍历处理而不能只取response.content[0]。工具结果必须放在roleuser的消息里并使用tool_result内容块。这样 Claude 才能把工具执行结果和之前的请求关联起来。loop_count是一个保险机制防止模型陷入无限循环。5.5 对外 Web 服务 app.py为了让手机、平板等终端能调用这个 Agent可以包一层 FastAPI 服务。用户提交文本服务返回售货机回复。# app.py from fastapi import FastAPI from pydantic import BaseModel from agent_order import process_message app FastAPI() class OrderRequest(BaseModel): text: str app.post(/api/order) def create_order(req: OrderRequest): reply process_message(req.text) return {reply: reply} if __name__ __main__: import uvicorn uvicorn.run(app, host0.0.0.0, port8000)启动后你可以用 curl 测试curl -X POST http://127.0.0.1:8000/api/order \ -H Content-Type: application/json \ -d {text: 给我来一瓶可乐}返回结果是一个 JSON包含售货机的回复。6. 运行结果与效果验证6.1 本地命令行测试先运行agent_order.pyexport ANTHROPIC_API_KEYsk-ant-你的key python agent_order.py输入顾客说给我来一瓶可乐如果一切正常你会看到类似输出[硬件执行] 解锁货道 1电机旋转商品掉落 售货机 好的已为您解锁1号货道请取走可乐。这里的关键是模型先调用了query_inventory确认库存后再调用unlock_slot。你在终端看到“解锁货道”日志说明 Agent 闭环已经跑通。如果输入顾客说我要雪碧由于库存里雪碧的stock是 0模型应该回复类似售货机 抱歉雪碧暂时缺货建议您试试矿泉水或可乐。并且不会打印任何解锁日志。这验证了模型的行为约束。6.2 Web 服务测试启动 FastAPIuvicorn app:app --reload打开另一个终端curl -X POST http://127.0.0.1:8000/api/order \ -H Content-Type: application/json \ -d {text: 我要一瓶矿泉水}返回{ reply: 好的已为您解锁3号货道请取走矿泉水。 }如果query_inventory和unlock_slot任何一个环节出错返回内容会明显不同比如模型会请求用户重试或者直接报错说工具调用失败。6.3 如何判断成功判断这套最小原型是否成功有三个标准用户说“要可乐”最终控制台出现“解锁货道”。用户说“要雪碧”库存为 0控制台没有出现“解锁货道”且模型给出替代建议。多次调用同一个商品库存不会出现负数也不会出现“卖完还在卖”的情况。第三点在当前简单实现里其实还不完美因为INVENTORY是全局变量没有扣减库存。你可以在unlock_slot中增加库存扣减逻辑并在query_inventory里返回最新数据。这是第一个可以升级的地方。6.4 如果失败先看哪里如果第一次运行就报错不要慌按以下顺序排查是否设置了ANTHROPIC_API_KEY没设置会报认证错误。模型名称是否有效换了版本要改.env。网络能否连通api.anthropic.com如果你在企业内网可能需要配置代理。工具描述是否写清楚如果模型老是调错工具多半是工具描述有歧义。有没有给tool_result传tool_use_id少了这个模型会认为工具调用没有返回。7. 常见问题与排查思路下面是这个项目实战中最常见的五个问题以及对应的排查方法。这些坑不是我编的而是所有做 LLM Agent 的人几乎都会遇到的。问题现象可能原因排查方式解决方案调用 API 时报unable to connect to anthropic services或failed to connect to api.anthropic.com网络不通、DNS 解析异常、代理配置错误、防火墙拦截检查网络连通性ping api.anthropic.com检查环境变量中的代理配置尝试关闭代理后重试确保服务器能访问公网如果必须走代理确认代理支持 HTTPS联系本地运维放行域名模型一直不调用工具而是直接回复文本工具描述不够清晰或提示词里没有强调必须调用工具查看最终回复内容打印response.stop_reason是否为end_turn优化工具描述的动词和场景在 system prompt 中增加“调用工具前先分析”的步骤调用工具报错tool_use_id不存在工具结果没有正确回传使用了错误的格式检查messages中是否包含tool_result块且tool_use_id来自原工具调用块严格按照 Anthropic 文档的格式循环时保存每个 tool_use 的 id模型返回的 JSON 参数解析失败模型把参数名写错或 LLM 误传了额外字段打印block.input确认字段名与input_schema是否一致增加参数校验兜底解析失败时构造一个说明错误的结果返回给模型让模型重试本地跑通但 FastAPI 服务高并发时库存错乱全局变量没有加锁多个请求同时扣减库存添加多进程压测查看日志中的库存值使用 Redis 或数据库原子操作管理库存给硬件操作加分布式锁排查时优先关注“模型视角”和“环境视角”。很多问题不是代码 bug而是模型没有理解工具用法。这时最好的调试方法是把messages完整打印出来看模型上一次回复里有没有工具调用块以及工具结果有没有正确回传。8. 最佳实践与工程建议8.1 永远不要让 AI 直接控制设备在上面的示例中unlock_slot函数确实是被 Agent 循环调用的但真实生产环境里不能这么裸奔。更安全的设计是模型只生成“意图”由本地的调度器校验后再执行。比如模型说“我想解锁 1 号货道”调度器会先检查1 号货道是否存在库存是否 0用户是否已经完成支付售货机是否处于在线状态。只有全部通过才真正给继电器通电。这个思路在机器人、医疗、金融等所有 AI 落地场景都适用LLM 是决策建议者不是最终执行者。8.2 把工具粒度设计得足够细工具粒度是 Agent 系统最容易出问题的设计点。粒度太粗比如一个do_everything工具模型不知道什么时候该调用粒度太细比如每个传感器一个工具模型需要多次调用既慢又容易错。建议按“业务动作”划分工具查询库存、解锁货道、生成订单、退款、上报异常每个工具只做一件事描述字段写清楚前置条件和后果。8.3 库存一致性必须用事务保证示例里用字典模拟库存生产环境要换成数据库。一次出货涉及两个操作扣减库存和触发硬件。这两个操作不是原子的如果扣了库存但电机没转用户会投诉。推荐做法是在数据库中锁定库存记录调用硬件设备根据硬件返回值决定提交事务还是回滚。如果硬件调用返回超时不要立刻回滚要查询设备状态确认是否已经出货再做补偿。8.4 建立完整的日志和审计体系AI Agent 的一个潜在风险是“不可解释”。顾客说“我要可乐”系统为什么推荐了矿泉水当出现纠纷时你必须有能力回放整个过程。建议记录用户输入原文每次模型请求的完整消息列表工具调用的输入输出硬件执行结果最终回复。日志不仅要存还要能快速检索。出现问题时先定位是哪一步的决策导致异常再决定是优化 prompt、修改工具还是回滚代码。8.5 控制模型延迟和成本零售场景对延迟很敏感。如果顾客说话后三秒都没反应体验就崩了。降低延迟的方法包括使用速度更快的模型比如 Haiku 类模型处理简单查询把“查询库存”这类确定性操作放在本地预判避免每次都让模型调用工具对常见商品可乐、矿泉水做意图缓存直接映射到货道设置合理的max_tokens不要给模型太多废话空间。成本控制方面可以给每天的交易量设置预算监控 API 调用量。异常时主动熔断防止某个用户刷爆你的额度。8.6 先做模拟仿真再上真机如果你真要接实体售货机先在模拟器里跑至少一周。把各种极端情况都测一遍缺货、卡货、网络断连、用户反复取消、支付成功但出货失败。模拟环境里能发现绝大多数逻辑漏洞而且调试成本远低于真机。8.7 注意安全边界和用户隐私售货机如果带摄像头就涉及人脸和用户行为数据。不要随意把视频流传到大模型 API最好本地先做脱敏只传必要的商品识别结果。用户对话内容也不建议长期保存除非你有明确合规需求。9. 总结与后续学习方向Anthropic 提出的“AI 运营自动售货机、商店和咖啡馆”计划具体执行细节可能还存在变数但背后的技术方向已经非常清晰大模型正在从“回答问题”走向“替你在真实世界里办事”。这篇文章用一个最小可运行的 Claude Agent 原型演示了售货机场景下的核心链路——自然语言指令、库存查询、工具调用、硬件解锁。你会发现真正的难点并不在调用 API 本身而在于如何设计工具、如何管理状态、如何处理异常。下一步你可以从这几个方向继续深入把语音识别接入系统让用户真正能语音下单增加摄像头视觉识别让 AI 能“看到”商品和用户结合 Claude 的多模态能力做商品推荐把库存字典换成 Redis 或 MySQL并封装成原子操作用 LangGraph 或自研状态机管理复杂多轮对话而不是简单 while 循环多个售货机组成集群用同一个 Agent 平台调度探索“商店和咖啡馆”的集中式运营。AI 零售最终会变成什么样现在没人能给出确定答案。但可以确定的是如果你能把今天这套“大模型 工具 硬件”的闭环跑通你就已经掌握了理解未来所有 AI 实体应用的基础。建议你先复制代码跑一遍把模型换成最新版本再试着加入一个自己的工具函数比如“推荐商品”或“生成优惠券”。动手之后你会对这个领域的感知完全不同。