AI购物助手技术拆解:从Gemini直播看意图理解与向量检索实战
1. 从一场直播说起AI购物到底在演示什么Google那场Gemini直播我反复看了三遍。不是因为演示有多炫酷而是它展示的交互路径跟我过去半年自己折腾AI Agent的体验高度重合——一个用户对着手机说“帮我找一双适合扁平足的跑鞋预算800以内”Gemini在几秒内完成了商品检索、参数对比、用户评价摘要、价格区间筛选最后给出三个推荐并附上购买链接。整个过程没有打开任何购物App没有手动输入关键词没有在几十个商品页面之间来回切换。这就是AI购物最核心的价值把“人找货”变成“货找人”。传统电商的搜索框需要你明确知道自己要什么而AI购物助手需要的是你描述你的需求场景。这两者之间的差距就是AI能发挥价值的地方。我写这篇东西是想把这场直播背后的技术逻辑拆开来看。不是复述Google的官方话术而是从一个实际做过AI应用开发的人的角度分析它用了什么技术、为什么这么设计、如果你想自己复现一个类似的AI购物助手需要怎么做。适合有基本编程概念、对AI应用开发感兴趣、或者正在考虑把AI能力接入自己业务的人阅读。完全不懂代码也能看懂思路部分实操部分可以跳过。2. AI购物的技术底座为什么是Gemini而不是传统搜索2.1 传统电商搜索的天花板在哪里先说说为什么传统搜索搞不定这件事。电商平台的搜索本质上是关键词匹配排序。你输入“跑鞋 扁平足 800元以下”搜索引擎做的事情是从商品库中找出标题或标签包含这些词的SKU然后按照销量、评分、广告出价等因子排序。这套逻辑的问题在于它不理解“扁平足”意味着什么——不知道扁平足需要什么类型的鞋底支撑、什么材质的鞋垫、什么结构的后跟。它只是把“扁平足”当成一个字符串去匹配。我做过一个测试在某主流电商平台搜索“扁平足跑鞋”前20个结果里有7个的标题里根本没有“扁平足”这个词只是跑鞋品类下的热销款。而真正针对扁平足设计的鞋款因为标题里写的是“足弓支撑”“稳定型跑鞋”这类专业术语反而排不到前面。这就是关键词匹配的局限——它匹配的是文字不是意图。Gemini在直播中做的事情完全不同。它把用户的自然语言描述先做意图解析提取出结构化的需求参数足弓类型扁平足、运动类型跑步、预算上限800元。然后它知道扁平足需要的是“稳定型”或“控制型”跑鞋对应的是特定的中底密度、后跟支撑结构、鞋垫材质。这些知识来自Gemini的训练数据不需要用户自己说出来。2.2 Gemini的多模态能力在购物场景中的具体应用直播里有一个细节被很多人忽略了用户拍了一张自己旧跑鞋磨损情况的照片Gemini通过分析鞋底磨损模式判断出用户是过度内旋overpronation这正好对应扁平足人群的典型步态特征。这个能力来自Gemini的多模态理解——它能同时处理图像和文本信息。具体来说Gemini在购物场景中调用了以下几层能力视觉识别识别商品图片中的品类、颜色、款式、材质纹理文本理解解析用户评价中的情感倾向和关键信息点知识推理将用户需求映射到商品属性比如“扁平足”映射到“稳定型跑鞋”多轮对话在用户说“第一双太贵了”之后自动调整预算参数重新筛选这四层能力叠加起来才构成了一个完整的AI购物体验。单独拿出任何一层效果都会打折扣。比如只有视觉识别没有知识推理你拍一张鞋的照片它只能告诉你“这是一双跑鞋”但不知道这双鞋适不适合你。2.3 为什么是现在三个前提条件同时成熟了AI购物这个概念不新鲜十年前就有“智能导购”的说法。但为什么现在才真正可用因为三个前提条件同时成熟了第一大模型的推理成本降到了可接受范围。2023年初调用一次GPT-4的成本大约是现在的几十倍。直播中Gemini完成一次完整购物咨询背后可能调用了多次模型推理包括意图解析、商品匹配、评价摘要、推荐生成。如果单次成本太高这个场景在商业上就不成立。第二商品数据的结构化程度提高了。过去电商平台的商品详情页是一堆HTML标签堆砌现在很多平台提供了标准化的商品API返回JSON格式的结构化数据。这让AI模型能直接读取商品参数而不需要去解析网页。第三用户对AI交互的接受度上来了。我身边非技术背景的朋友现在遇到问题第一反应是打开AI对话窗口而不是搜索引擎。这个习惯迁移是AI购物能成立的用户基础。3. 拆解直播中的核心技术环节3.1 意图理解从“我想要”到结构化参数用户说“帮我找一双适合扁平足的跑鞋预算800以内”这句话在技术实现上需要被拆解成什么{ category: running_shoes, attributes: { arch_type: flat_foot, support_type: stability, budget_max: 800, currency: CNY }, intent: product_search, confidence: 0.94 }这个结构化过程是AI购物的第一步也是最关键的一步。如果意图解析错了后面所有推荐都是白搭。Gemini在这里用的是函数调用Function Calling机制——模型不是直接生成一段文字回复而是生成一个结构化的函数调用请求由后端系统去执行实际的商品检索。注意函数调用的准确性高度依赖提示词设计。我在自己的项目中测试过同样的模型提示词里有没有给出明确的参数枚举值准确率能差30%以上。3.2 商品检索与匹配向量数据库的角色Gemini拿到结构化参数后怎么从海量商品中找到匹配的直播中没有展示后端细节但根据我的经验大概率用的是向量检索传统过滤的混合方案。具体流程是这样的每个商品在入库时会被转换成一段向量embedding这段向量编码了商品的品类、属性、适用场景、用户评价摘要等信息。当用户查询到来时查询语句也被转换成向量然后在向量数据库中做相似度检索找出最接近的N个商品。之后再根据预算、库存、评分等硬性条件做过滤和排序。为什么不用传统的关键词检索因为向量检索能处理语义相似性。比如用户说“适合扁平足的跑鞋”向量检索能找到“足弓支撑跑鞋”“稳定型跑鞋”“控制型跑鞋”这些语义相近但字面不同的商品。关键词检索做不到这一点。我实测过的一个方案是用text-embedding-3-small做商品向量化存入Qdrant向量数据库检索时用余弦相似度排序Top-20结果再交给大模型做精排。这个方案在几千个商品的测试集上召回率比纯关键词检索高了40%左右。3.3 评价摘要从几百条评论中提取关键信息直播中有一个我很喜欢的细节Gemini在推荐每双鞋时都附了一句“用户普遍反映...”的摘要。这个摘要不是随便截取一条评论而是对几百条评价做了情感分析和要点提取。技术实现上这一步通常是这样做的先把商品的所有评价拉取下来按时间倒序取最近200条然后用大模型做摘要生成。提示词大概是这样的以下是某款跑鞋的用户评价请提取 1. 用户最满意的三个点 2. 用户最不满意的两个点 3. 关于尺码选择的建议 4. 关于耐用性的反馈 用简洁的中文输出每条不超过20字。这个摘要的质量直接决定了用户对推荐的信任度。我踩过的坑是早期版本没有限制输出长度模型生成了一大段话用户根本没耐心看。后来改成每条不超过20字信息密度上来了点击率明显提升。3.4 多轮对话中的参数继承与修改直播中用户说“第一双太贵了有没有便宜点的”Gemini立刻把预算从800降到了500重新推荐了两款。这个看似简单的操作背后涉及对话状态管理。系统需要维护一个会话级别的参数对象每次用户输入后更新这个对象而不是从头解析。具体来说# 初始状态 session { category: running_shoes, arch_type: flat_foot, budget_max: 800 } # 用户说太贵了 # 模型识别出意图是修改预算 session[budget_max] 500 # 或者按比例下调 # 重新检索 results search_products(session)这里有个容易忽略的点预算下调的幅度怎么定是直接砍到500还是按比例降20%我的经验是让模型根据用户语气判断。如果用户说“太贵了”但没有给具体数字通常下调20%-30%比较合适如果用户说“有没有500以内的”那就直接设500。4. 如果你想自己搭一个AI购物助手完整实操路径4.1 技术选型为什么我最终选了这套组合过去半年我做过三个版本的AI购物助手从最简陋的提示词工程到现在的完整Agent架构踩了不少坑。最终稳定下来的技术栈是这样的组件选型理由大模型Gemini 1.5 Pro多模态能力强函数调用稳定长上下文适合处理大量商品数据向量数据库Qdrant轻量Docker一键部署Python SDK好用后端框架FastAPI异步支持好适合处理并发对话请求商品数据源电商平台开放API结构化数据省去爬虫维护成本前端Streamlit原型/ React生产原型阶段快速验证生产环境用React做交互选Gemini而不是其他模型主要原因是它的函数调用准确率在我的测试中最高。我用同一套提示词测试了Gemini、GPT-4和Claude在“从用户自然语言中提取购物参数”这个任务上Gemini的字段提取准确率是94%GPT-4是91%Claude是89%。差距不大但Gemini的多模态能力在“拍照识鞋”这个场景上是决定性的。4.2 商品数据准备向量化与入库假设你已经通过电商平台的开放API拿到了商品数据每条数据大概长这样{ product_id: B08XYZ123, title: 男子稳定型跑鞋 足弓支撑 透气网面, price: 699, category: running_shoes, attributes: { support_type: stability, arch_type: flat_foot, drop: 8mm, weight: 280g }, rating: 4.5, review_count: 2341 }接下来需要把每条商品转换成向量。我用的方法是把标题、品类、属性拼接成一段文本然后调用embedding接口import google.generativeai as genai def get_embedding(product): text f{product[title]} {product[category]} text .join([f{k}:{v} for k, v in product[attributes].items()]) result genai.embed_content( modelmodels/text-embedding-004, contenttext ) return result[embedding]然后把向量和商品ID一起存入Qdrantfrom qdrant_client import QdrantClient from qdrant_client.models import PointStruct client QdrantClient(hostlocalhost, port6333) points [] for product in products: vector get_embedding(product) points.append(PointStruct( idproduct[product_id], vectorvector, payloadproduct )) client.upsert(collection_nameproducts, pointspoints)提示embedding的维度要和Qdrant集合创建时指定的维度一致。text-embedding-004的维度是768创建集合时要指定size768。4.3 对话流程的完整实现整个对话流程可以拆成五个步骤我用一个简化的代码框架来说明async def shopping_assistant(user_input, session): # 第一步意图解析 intent await parse_intent(user_input, session) if intent[action] search: # 第二步更新会话参数 session.update(intent[params]) # 第三步向量检索 query_vector get_embedding_from_text(user_input) candidates qdrant.search( collection_nameproducts, query_vectorquery_vector, limit20 ) # 第四步硬性条件过滤 filtered [c for c in candidates if c.payload[price] session[budget_max]] # 第五步生成推荐 recommendation await generate_recommendation( filtered[:5], session ) return recommendation elif intent[action] modify: # 处理参数修改 session.update(intent[params]) return await shopping_assistant(重新搜索, session)这个框架看起来简单但每个步骤都有细节需要打磨。比如parse_intent的提示词设计我改了十几版才稳定下来。核心是要给模型明确的输出格式约束并且提供足够的示例。4.4 提示词设计让模型稳定输出结构化数据意图解析的提示词是整个系统中最关键的部分。我最终稳定下来的版本是这样的你是一个购物助手负责解析用户的购物需求。 当前会话状态 {session_json} 用户输入{user_input} 请输出JSON格式的解析结果包含以下字段 - action: search | modify | compare | detail - params: 需要更新的参数对象 - confidence: 0-1之间的置信度 参数枚举值 - category: running_shoes, basketball_shoes, casual_shoes, ... - arch_type: flat_foot, normal, high_arch - support_type: stability, neutral, motion_control 只输出JSON不要有其他内容。这个提示词的关键在于给出了明确的枚举值。早期版本我没有列枚举值模型有时候会生成“扁平足”有时候生成“flat_foot”导致后端处理逻辑要写很多兼容代码。加上枚举值约束后输出稳定性大幅提升。5. 实际运行中会遇到的问题与排查方法5.1 模型返回的JSON格式错误这是最常见的问题。即使提示词里写了“只输出JSON”模型有时候还是会加一句“好的以下是解析结果”然后再输出JSON。解决方法有两个一是用API提供的JSON模式Gemini支持response_mime_type: application/json二是在代码里做容错解析。import json import re def safe_parse_json(text): # 尝试直接解析 try: return json.loads(text) except json.JSONDecodeError: pass # 尝试提取JSON块 match re.search(r\{.*\}, text, re.DOTALL) if match: try: return json.loads(match.group()) except json.JSONDecodeError: pass # 解析失败返回默认值 return {action: search, params: {}, confidence: 0}我实测下来加上JSON模式后格式错误率从15%降到了2%以下。剩下的2%用上面的容错解析也能兜住。5.2 向量检索结果不相关有时候用户搜“轻量跑鞋”返回的结果里混进了篮球鞋。排查下来通常是两个原因一是embedding模型对品类区分不够敏感二是商品数据中品类字段的权重太低。解决方法是在拼接embedding文本时给品类字段加权重text f{product[title]} * 2 # 标题重复一次提高权重 text f品类:{product[category]} * 3 # 品类重复三次 text .join([f{k}:{v} for k, v in product[attributes].items()])这个技巧是我从信息检索的老论文里翻出来的叫“字段加权”。原理很简单重复出现的词在embedding中占的权重更大。实测下来品类准确率从78%提升到了91%。5.3 多轮对话中参数丢失用户说“第一双太贵了”系统需要知道“第一双”指的是上一轮推荐中的第一个商品。如果会话状态管理没做好模型可能会重新搜索而不是在当前结果中筛选。我的做法是在会话状态中保存上一轮的推荐结果ID列表session[last_results] [B08XYZ123, B08ABC456, B08DEF789]然后在提示词中告诉模型“如果用户提到‘第一双’‘第二个’等指代词请从last_results中选取对应的商品ID。”这样模型就能正确理解指代关系。5.4 常见问题速查表问题现象可能原因排查方法解决方案模型返回非JSON格式提示词约束不够强打印原始返回内容启用JSON模式容错解析检索结果品类错误embedding权重分配不合理检查Top-10结果的品类分布品类字段加权重复多轮对话参数丢失会话状态未正确传递打印每轮session内容保存last_results并传入提示词推荐结果重复向量检索未去重检查候选集是否有重复ID检索后按product_id去重响应速度慢串行调用过多打日志看各步骤耗时意图解析和向量检索并行执行6. 几个我踩过的坑和对应的经验6.1 不要试图让模型做所有事我第一版的设计是让Gemini直接生成推荐列表不经过向量检索。结果模型开始“编造”商品——生成一些不存在的商品名称和价格。这个问题的根源是模型在生成任务上会“幻觉”而购物场景对准确性要求极高编造的商品会直接摧毁用户信任。后来改成“模型负责理解和生成检索交给向量数据库”准确性问题就解决了。模型只做它擅长的事情理解自然语言、提取参数、生成摘要。商品匹配这种需要精确性的任务交给专门的检索系统。6.2 评价摘要要控制信息密度早期版本的评价摘要我让模型自由发挥结果生成的内容又长又空比如“这款鞋整体表现不错用户反馈良好值得购买”。这种摘要没有任何信息量。后来我把提示词改成强制提取具体信息点从评价中提取 - 尺码偏大/偏小/正常必须选一个 - 最常被提到的优点具体到材质或设计 - 最常被提到的缺点具体到部位或场景 - 适合的脚型如果有提到这样生成的摘要才有参考价值。用户看到“尺码偏小半码建议买大半码”比看到“用户反馈良好”有用得多。6.3 预算修改的幅度要合理用户说“太贵了”的时候如果直接把预算砍半推荐结果可能全是低端款用户体验反而不好。我的做法是让模型根据上下文判断如果当前推荐的价格是800用户说“太贵”通常下调到600-650比较合适如果用户明确说“500以内”那就设500。这个逻辑我写在了提示词里如果用户表达价格不满但没有给出具体数字 将预算下调20%-30%但不要低于当前预算的50%。6.4 处理“打不开”和“地区限制”这类问题做AI应用开发的人经常会遇到模型服务在某些地区不可用的情况。我的经验是永远准备一个降级方案。如果主模型调用失败自动切换到备用模型如果向量检索超时降级到关键词检索。用户不需要知道后端出了什么问题他们只需要得到一个可用的结果。具体实现上我用了一个简单的重试降级逻辑async def call_with_fallback(prompt): try: return await call_gemini(prompt) except Exception as e: logger.warning(fGemini调用失败: {e}) try: return await call_backup_model(prompt) except Exception as e2: logger.error(f备用模型也失败: {e2}) return {error: 服务暂时不可用}这个逻辑看起来简单但在实际运行中救了我好几次。特别是演示的时候主模型突然超时降级方案能保证演示不中断。7. 这套方案还能怎么扩展AI购物助手只是一个起点。同样的技术架构可以迁移到很多场景本地生活服务推荐把商品库换成餐厅、酒店、景点用户说“找一家适合带小孩的餐厅人均100以内”系统做同样的意图解析和向量检索。企业采购助手把商品库换成办公用品、IT设备加上审批流程和预算控制就是一个企业内部采购Agent。二手交易匹配用户描述“想收一台九成新的Switch OLED”系统从二手商品库中匹配并且根据成色、价格、卖家信用做综合排序。我在实际项目中发现这套架构的复用率很高。核心的意图解析、向量检索、多轮对话管理三个模块换个数据源就能跑。真正需要重新做的是提示词中的枚举值定义和商品属性的映射关系。最后分享一个我在调试过程中总结的小技巧每次修改提示词后用同一组测试用例跑一遍记录准确率变化。我维护了一个包含50条用户输入的测试集覆盖了搜索、修改、对比、详情四种意图。每次改提示词跑一遍测试集准确率下降就回滚。这个习惯帮我避免了很多“改了一个问题引入两个新问题”的情况。