1. 从一场直播演示说起:AI购物到底在演示什么
Gemini那场直播我反复看了三遍。第一遍看热闹,第二遍看交互细节,第三遍专门盯着它调用工具时的参数传递和状态流转。很多人看完的第一反应是"这不就是个语音助手加购物车吗",但如果你真的动手搭过类似的Agent,就会知道这场演示里藏着的技术密度远比表面看起来高得多。
所谓"用AI购物",拆开来看其实是三件事的串联:意图理解、工具调用、多轮状态维护。用户说一句"帮我找一双适合雨天跑步的鞋,预算800以内",系统要做的不是关键词匹配,而是先把这句话翻译成结构化的查询条件——场景是雨天跑步、品类是跑鞋、价格上限800、隐含需求是防滑和防水。然后它需要调用商品检索接口,拿到候选集,再根据用户后续的反馈("太丑了""有没有轻一点的")动态调整查询参数,而不是重新开始一轮对话。
这场演示真正让我感兴趣的点在于,Gemini在购物场景里展示的是一种**"边聊边操作"的混合模式**。它不是先问完所有问题再给结果,而是在对话过程中就把筛选、比价、加购这些动作穿插进去。这背后依赖的是函数调用(Function Calling)机制和会话状态的持久化。我在自己的项目里复现过类似流程,踩过的坑后面会细说。
这篇文章适合三类人看:一是想搞清楚AI Agent在电商场景怎么落地的开发者;二是正在做AI应用、想知道工具调用链路怎么设计的同学;三是对Gemini能力边界好奇、想自己动手试一把的爱好者。我会从演示拆解讲到技术原理,再给出一套可以跑通的实操方案,最后把我在实际搭建中遇到的坑和解决思路完整摊开。
2. 拆解直播里的购物链路:意图、检索、决策三段式
2.1 意图理解不是"听懂话",而是"翻译成结构化查询"
大多数人以为AI购物的第一步是语音识别,其实语音转文字只是最外层。真正的核心是把自然语言翻译成可执行的查询对象。直播里用户说"我想买一个送给刚搬新家的朋友的礼物,不要太贵",Gemini没有直接搜"礼物",而是先追问了朋友的兴趣、预算范围、是否需要包装。这个追问行为本身就是意图理解的一部分——它识别出当前信息不足以构成有效查询,于是主动补全槽位(Slot Filling)。
我在自己的实现里用的是"槽位+意图"的双层结构。意图层判断用户是要搜索、比价、加购还是售后;槽位层负责收集品类、价格区间、场景标签、偏好属性这些字段。Gemini的演示里没有暴露它的内部结构,但从交互节奏能看出来,它至少维护了五到六个槽位,并且支持槽位的动态增删。比如用户后来说"对了,朋友对气味敏感",系统立刻新增了一个"无香型"的筛选条件,而不是把这个信息丢掉。
这里有个容易忽略的细节:槽位不是越多越好。我一开始设计了十几个槽位,结果用户每说一句话系统都要追问三四个问题,体验极差。后来改成"核心槽位必填、扩展槽位按需触发",核心槽位只有品类和预算,其他槽位在检索结果不理想时才主动询问。这个策略调整之后,平均对话轮次从7.2轮降到了3.8轮。
2.2 商品检索接口的调用时机与参数构造
直播里Gemini调用检索的时机很有意思。它不是等所有信息收集完才调用,而是在信息足够构成一次有效查询时就先调一次,拿到结果后再根据用户反馈细化。这个策略的好处是用户能快速看到反馈,不会觉得系统在"审问"自己。
参数构造这块,我实测下来最关键的是查询条件的优先级排序。假设用户说了五个条件,但商品库里有三个条件同时满足的结果为零,系统需要知道先放宽哪个。我的做法是给每个槽位设一个权重:品类和价格是硬约束,场景标签是软约束,品牌偏好是弱约束。检索时先按硬约束过滤,软约束用于排序,弱约束只在结果充足时作为加分项。
Gemini的演示里有一个细节:当用户说"有没有便宜点的"时,系统没有重新检索,而是在已有结果集里按价格升序重排。这说明它维护了一个会话级的结果缓存,而不是每次都打接口。这个设计在真实场景里非常重要,因为电商接口的延迟通常在200到500毫秒,如果每轮对话都重新请求,用户体验会明显卡顿。
2.3 决策环节:AI怎么"替用户做选择"
购物场景里最难的不是找到商品,而是帮用户做决策。直播里Gemini展示了一个能力:当用户面对三个候选商品犹豫时,它会主动对比关键差异,比如"第一款防水等级更高但重了80克,第二款轻但只有两个配色,第三款价格最低但评价里提到尺码偏小"。这种对比不是简单的参数罗列,而是基于用户已表达的偏好做加权。
我在实现这个功能时用了一个简单的评分模型:每个商品在用户关注的维度上打分,然后加权求和。权重来自用户在对话中提到的偏好词频,比如用户三次提到"轻",那重量维度的权重就调高。这个方法很粗糙,但实测效果比让大模型直接"凭感觉推荐"要稳定得多,因为大模型的推荐理由经常前后矛盾。
提示:决策环节一定要保留"用户否决"的出口。直播里用户说"都不太满意",Gemini立刻回到检索环节并询问是否放宽某个条件。如果系统只会推荐不会回退,用户很快就会失去耐心。
3. 工具调用背后的技术骨架:Function Calling怎么串起来
3.1 函数定义的设计原则:少而精,参数要扁平
Gemini的Function Calling机制允许你注册一组函数,模型根据对话内容决定调用哪个、传什么参数。我在项目里注册了六个函数:search_products、get_product_detail、compare_products、add_to_cart、get_cart、apply_coupon。听起来不多,但每个函数的参数设计花了我最多时间。
核心原则是参数扁平化。我一开始把搜索参数设计成嵌套对象,比如{"filters": {"price": {"max": 800}, "category": "shoes"}},结果模型经常传错层级。改成扁平结构{"category": "shoes", "price_max": 800, "price_min": 0}之后,参数准确率从七成提升到了九成五以上。大模型对嵌套结构的处理能力确实弱一些,这是实测结论。
另一个原则是给参数设默认值。比如price_min默认0,sort_by默认"relevance"。这样即使用户没说,模型也不会因为缺参数而调用失败。我在函数描述里明确写了"如果用户未指定,使用默认值",模型基本能遵守。
3.2 多轮对话中的状态管理:别把历史全塞进上下文
直播里Gemini能记住用户三轮前说的"预算800",这说明它做了状态管理。但状态管理不等于把全部对话历史塞进上下文。我试过把最近十轮对话原封不动传给模型,结果token消耗暴涨,而且模型经常被早期信息干扰,比如用户一开始说"随便看看",后来明确说要买,模型还在纠结"随便看看"这个表述。
我的方案是维护一个结构化的会话状态对象,只把关键槽位和最近两轮对话传给模型。状态对象长这样:
{ "intent": "search", "slots": { "category": "running_shoes", "price_max": 800, "scene": "rainy_running", "preferences": ["lightweight", "non_slip"] }, "last_results": ["sku_001", "sku_002", "sku_003"], "turn_count": 4 }每次模型返回后,我用一个轻量的解析逻辑更新这个对象,而不是让模型自己维护。这样做的原因是模型对结构化状态的读写不如代码可靠,让它专注于"理解"和"决策",状态维护交给程序。
3.3 错误处理:工具调用失败时AI该怎么接话
这是直播里没展示但实际开发中最头疼的部分。商品接口超时、库存查询返回空、优惠券校验失败,这些情况在真实环境里天天发生。如果工具调用失败后模型直接说"抱歉我做不到",用户体验就断了。
我的做法是给每个函数定义降级返回。比如search_products超时后返回一个空结果加错误码,同时在函数描述里告诉模型"如果返回错误码TIMEOUT,告知用户稍后重试并建议放宽条件"。实测下来,模型能根据错误码生成合理的回复,比如"刚才查询有点慢,要不我们把价格范围放宽一点再试试?"
还有一个坑是模型会编造工具返回结果。我遇到过模型在接口返回空列表时,自己虚构了三个商品推荐给用户。解决办法是在系统提示里明确写"只能使用工具返回的数据,不得自行生成商品信息",并且在代码层做校验,如果模型输出的商品ID不在返回结果里,直接拦截重试。
4. 自己动手复现:一套可跑通的AI购物Agent方案
4.1 环境准备与依赖选择
要复现这套流程,你需要三样东西:一个支持Function Calling的大模型接口、一个商品数据源、一个会话管理层。商品数据源我用的是本地SQLite,建了一张商品表包含品类、价格、场景标签、重量、防水等级等字段,大概两千条模拟数据。真实场景接电商平台的开放接口也行,但调试阶段本地数据更可控。
模型接口这块,Gemini的API支持函数调用,文档里有完整示例。如果你用其他模型,逻辑是相通的,关键是确认它支持并行函数调用和多轮函数调用。并行调用是指模型一次返回多个函数调用请求,比如同时查商品和查库存;多轮调用是指模型拿到函数结果后继续发起新的调用。这两个能力决定了你的Agent能不能处理复杂购物流程。
会话管理我用的是Redis存状态对象,key是会话ID,过期时间设30分钟。如果你只是本地测试,用内存字典也够,但要注意进程重启后状态会丢。
4.2 核心代码结构:从用户输入到商品推荐
整个流程分四步:接收用户输入、更新会话状态、调用模型、执行函数并回传。我用Python写了一个简化版,核心逻辑大概长这样:
def handle_user_input(session_id, user_text): state = load_state(session_id) messages = build_messages(state, user_text) response = model.generate( messages=messages, tools=TOOL_DEFINITIONS, tool_config={"mode": "AUTO"} ) if response.has_tool_calls: results = [] for call in response.tool_calls: result = execute_tool(call.name, call.args) results.append(format_tool_result(call.id, result)) state = update_state(state, response, results) save_state(session_id, state) return handle_user_input(session_id, None) # 带着工具结果再问模型 state = update_state(state, response) save_state(session_id, state) return response.text这段代码里最关键的是递归调用那一步。模型返回工具调用请求后,程序执行工具,把结果作为新的消息追加到对话里,再让模型生成最终回复。Gemini的API里这个流程有标准写法,注意工具结果的格式要严格符合接口要求,否则模型会解析失败。
4.3 提示词设计:让模型知道"什么时候该问,什么时候该做"
系统提示词我改了十几版,最后稳定下来的版本核心是三条规则:第一,信息不足时优先追问,但一次最多问两个问题;第二,能通过工具获取的信息不要问用户;第三,推荐商品时必须给出至少一个具体理由,理由必须来自工具返回的数据。
第三条规则特别重要。我一开始没加这条,模型推荐商品时说"这款很适合你",完全是空话。加上规则后,它会说"这款防水等级IPX6,重量240克,符合你提到的雨天和轻量需求",可信度完全不一样。
还有一个小技巧:在提示词里给几个少样本示例(Few-shot),展示一轮完整的"用户说→模型调用工具→模型回复"流程。我放了两个示例,一个简单查询一个多轮筛选,模型的表现明显更稳定。示例不用长,每个控制在五行以内。
5. 实测中暴露的问题与我的处理方式
5.1 模型"过度热情":不该加购的时候加购
测试初期遇到一个典型问题:用户只是问"这个多少钱",模型就自动调用了add_to_cart。原因是我的函数描述里写了"当用户对商品表达兴趣时可加入购物车",模型把"问价格"理解成了"表达兴趣"。
修复方式是把触发条件写得更严格:"仅当用户明确说出'加入购物车''我要买''下单'等指令时才调用此函数"。同时我在代码层加了一道校验,如果用户输入里没有购买意图关键词,即使模型请求加购也拦截。这个双重保险很有必要,因为模型对模糊表述的判断确实不稳定。
5.2 价格计算的精度问题:别让AI做算术
直播里有个环节是计算优惠后的价格,我特意留意了Gemini的表现。它算对了,但我在自己项目里用其他模型测试时,遇到过算错的情况,比如满减和折扣叠加时顺序搞反。后来我把价格计算全部放到代码里,模型只负责决定"用哪张优惠券",具体金额由程序算好再告诉模型。
这个原则可以推广到所有涉及数值计算的场景:模型做决策,代码做计算。折扣、税费、运费、分期,这些统统不要让模型碰。我在函数里加了一个calculate_final_price,输入商品ID和优惠券ID,返回精确到分的价格,模型直接引用这个结果。
5.3 会话超时后的状态恢复
用户聊到一半去干别的事,二十分钟后回来说"就刚才那个吧",这时候会话状态可能已经过期了。我的处理是保留最近一次的结果快照,即使主状态过期,也能从快照里恢复出用户上次看的商品列表。快照存的是商品ID和关键属性,不存完整对话,存储成本很低。
如果快照也过期了,系统会礼貌地请用户重新描述需求,同时尽量从用户这句话里提取线索。比如"就刚才那个"提取不出有效信息,但如果用户说"就刚才那双跑鞋",系统能提取出"跑鞋"这个品类,重新检索后大概率能找到同一批商品。
注意:会话恢复功能一定要做,但不要做得太"聪明"。我试过让模型根据过期会话的残留信息猜测用户意图,结果经常猜错,反而让用户困惑。老老实实说"抱歉刚才的会话已结束,您说的是哪类商品"比自作聪明要好。
6. 从演示到产品:还差哪些关键能力
直播演示和真实产品之间有巨大的鸿沟,我踩过之后总结出三个必须补齐的能力。
第一个是商品数据的实时性。演示里商品信息是静态的,真实场景里价格会变、库存会变、促销会变。我的方案是在检索函数里加一层缓存,缓存时间设60秒,同时给每个商品返回一个"数据更新时间"字段,模型在推荐时可以提及"当前价格"而不是"标价"。
第二个是多模态输入。用户可能发一张图片说"我想要类似的",这时候纯文本的Function Calling就不够了。Gemini支持图片输入,但要把图片特征转成检索条件需要额外的处理。我的做法是用图片描述模型先生成文字描述,再把描述作为搜索关键词,实测召回率大概七成,够用但不算理想。
第三个是个性化记忆。演示里每次对话都是独立的,真实产品需要记住用户的历史偏好。我在会话状态之外加了一个用户画像层,记录用户常买的品类、价格带、品牌偏好。这个画像不直接传给模型,而是在检索时作为排序权重使用,避免模型被历史信息带偏。
最后分享一个我在调试中总结的小技巧:每次修改提示词或函数定义后,用同一组测试用例跑一遍回归。我准备了二十条典型用户输入,覆盖简单查询、多轮筛选、模糊表达、错误恢复等场景,每次改动后跑一遍,看通过率有没有下降。这个习惯帮我避免了好几次"改了一个问题引入两个新问题"的情况。