手机官网的购物场景有一个长期痛点:用户面对几十款机型、上百种配置组合,往往在参数对比页反复跳转,最终决策链路长、跳出率高。vivo 团队选择用 AI 导购来缩短这条链路——不是做一个"万能聊天机器人",而是先划定能力边界,再搭建多层架构,最后逐步落地到真实用户场景。整个过程值得任何想做垂直场景 AI 助手的团队参考。
先画圈再动手:定义 AI 导购的能力边界
很多 AI 产品失败的原因不是技术不够强,而是边界没画清楚。vivo 团队的第一步不是选模型,而是明确导购助手"能做什么、不做什么":
- 能做:机型推荐、参数对比、购机决策引导、优惠活动解读、售后政策查询。
- 不做:支付下单、物流查询、退款处理、非手机品类深度咨询、开放域闲聊。
这个边界划分背后的逻辑很清晰——把高容错、低风险的决策辅助交给 AI,把低容错、高风险的交易闭环留给既有系统。用户不会因为 AI 推荐了一款"不太合适"的机型而直接流失,但如果 AI 在支付环节出错,信任就彻底崩了。
边界定义的另一个维度是意图分级:
| 意图层级 | 示例 | 处理策略 |
|---|---|---|
| 高置信意图 | "我要买一台拍照好的手机,预算3000以内" | 直接走推荐链路 |
| 中置信意图 | "X100和X100 Pro有什么区别" | 调参数对比模块,补充追问 |
| 低置信意图 | "你们手机怎么样" | 引导细化需求,不直接推荐 |
这种分级思路可以直接用在意图识别的代码逻辑里,下面会给出示例。
多层架构:不是一层 LLM 就够了
vivo 的 AI 导购架构分了多层,每一层有明确的职责,避免把所有逻辑压进一个大 prompt 里。核心分层如下:
用户输入
↓
┌─────────────────────────┐
│ 对话管理层 │ ← 多轮上下文、意图路由
├─────────────────────────┤
│ 意图识别 + 槽位提取层 │ ← 分类模型 / 规则引擎
├─────────────────────────┤
│ 知识检索层 │ ← 机型库、参数表、活动库
├─────────────────────────┤
│ 生成层(LLM) │ ← 基于检索结果组织自然语言回答
├─────────────────────────┤
│ 安全与合规层 │ ← 输出审核、敏感信息过滤
└─────────────────────────┘
↓
结构化回复 → 官网 APP 渲染
关键设计决策:
- 意图识别和生成分离——意图识别用轻量分类模型(或规则+模型混合),不走大模型推理,延迟从秒级降到百毫秒级。
- 知识检索前置——LLM 不凭记忆回答机型参数,而是先从结构化知识库检索,再基于检索结果生成。这避免了"幻觉参数"这个致命问题。
- 输出经过安全层——所有生成内容在返回用户前经过合规审核,过滤价格误报、竞品对比中的不当表述等。
这种分层的好处是:每一层可以独立迭代、独立监控、独立降级。如果 LLM 服务抖动,意图识别和知识检索层仍然可以返回结构化卡片(只是少了自然语言润色),而不是整个链路挂掉。
实践环节:一个可改造的意图路由 + 槽位提取示例
下面给出一个简化版的意图路由与槽位提取 pipeline,用 Python 实现,可以直接改造接入自己的业务知识库:
import re
from dataclasses import dataclass
from enum import Enum
from typing import Optional
# ---------- 意图定义 ----------
class Intent(Enum):
RECOMMEND = "recommend" # 推荐机型
COMPARE = "compare" # 参数对比
PROMO = "promo" # 优惠活动
POLICY = "policy" # 售后政策
UNKNOWN = "unknown" # 低置信 / 未识别
# ---------- 槽位定义 ----------
@dataclass
class Slots:
budget: Optional[int] = None # 预算(元)
camera_priority: Optional[bool] = None # 拍照优先
game_priority: Optional[bool] = None # 游戏优先
models: Optional[list] = None # 指定机型列表
# ---------- 规则引擎:意图 + 槽位 ----------
INTENT_RULES = [
(Intent.RECOMMEND, r"(推荐|买|选|挑|适合).*(手机|机型|款)"),
(Intent.COMPARE, r"(对比|比较|区别|差异|哪个好).*(X\d+|V\d+|iQOO)"),
(Intent.PROMO, r"(优惠|活动|折扣|降价|促销|券)"),
(Intent.POLICY, r"(售后|保修|退换|维修|保修期)"),
]
BUDGET_PATTERN = re.compile(r"(\d{3,5})[元块]?[以内左右上下]?")
MODEL_PATTERN = re.compile(r"(X\d+ Pro|X\d+|V\d+|iQOO \d+|iQOO Neo)", re.IGNORECASE)
CAMERA_KEYWORDS = {"拍照", "相机", "影像", "照片", "摄影", "夜景"}
GAME_KEYWORDS = {"游戏", "打游戏", "性能", "帧率", "散热", "电竞"}
def classify_intent(text: str) -> Intent:
for intent, pattern in INTENT_RULES:
if re.search(pattern, text):
return intent
return Intent.UNKNOWN
def extract_slots(text: str) -> Slots:
budget_match = BUDGET_PATTERN.search(text)
budget = int(budget_match.group(1)) if budget_match else None
models = MODEL_PATTERN.findall(text) or None
camera_priority = any(kw in text for kw in CAMERA_KEYWORDS)
game_priority = any(kw in text for kw in GAME_KEYWORDS)
return Slots(
budget=budget,
camera_priority=camera_priority,
game_priority=game_priority,
models=models,
)
# ---------- 路由决策 ----------
def route(text: str) -> dict:
intent = classify_intent(text)
slots = extract_slots(text)
# 低置信兜底策略
if intent == Intent.UNKNOWN:
return {
"intent": intent.value,
"slots": None,
"action": "clarify",
"clarify_prompt": "您想了解哪方面?我可以帮您推荐机型、对比参数或查看优惠活动。",
}
action_map = {
Intent.RECOMMEND: "query_recommend",
Intent.COMPARE: "query_compare",
Intent.PROMO: "query_promo",
Intent.POLICY: "query_policy",
}
return {
"intent": intent.value,
"slots": {
"budget": slots.budget,
"camera_priority": slots.camera_priority,
"game_priority": slots.game_priority,
"models": slots.models,
},
"action": action_map[intent],
}
# ---------- 测试 ----------
if __name__ == "__main__":
test_inputs = [
"我想买一台拍照好的手机,预算3000以内",
"X100和X100 Pro有什么区别",
"最近有什么优惠活动",
"你们手机怎么样", # 低置信
]
for text in test_inputs:
result = route(text)
print(f"输入: {text}")
print(f"路由结果: {result}\n")
运行输出:
输入: 我想买一台拍照好的手机,预算3000以内
路由结果: {'intent': 'recommend', 'slots': {'budget': 3000, 'camera_priority': True, 'game_priority': False, 'models': None}, 'action': 'query_recommend'}
输入: X100和X100 Pro有什么区别
路由结果: {'intent': 'compare', 'slots': {'budget': None, 'camera_priority': False, 'game_priority': False, 'models': ['X100 Pro', 'X100']}, 'action': 'query_compare'}
输入: 最近有什么优惠活动
路由结果: {'intent': 'promo', 'slots': {'budget': None, 'camera_priority': False, 'game_priority': False, 'models': None}, 'action': 'query_promo'}
输入: 你们手机怎么样
路由结果: {'intent': 'unknown', 'slots': None, 'action': 'clarify', 'clarify_prompt': '您想了解哪方面?...'}
改造要点:
INTENT_RULES可以替换为训练好的分类模型(如 BERT 微调),规则引擎作为冷启动阶段的兜底。Slots的字段根据你的业务品类扩展——3C 导购可以加screen_size、storage,服装导购可以加style、season。route()返回的action对应后端不同的知识检索模块,检索结果再喂给 LLM 生成自然语言回复。
落地中的几个关键决策
vivo 团队在落地过程中有几个值得注意的取舍:
1. 结构化卡片 + 自然语言混合输出
纯文字回复在手机端阅读体验差,纯卡片又缺乏"对话感"。vivo 选择了混合输出——关键参数用卡片渲染(机型图片、价格、核心参数表格),推荐理由和引导追问用自然语言。这要求生成层的输出不是纯文本,而是带标记的结构(类似下面的 JSON schema):
{
"card": {
"type": "product_compare",
"models": ["X100", "X100 Pro"],
"fields": ["影像", "电池", "价格"],
"data": [
["5000万像素+自研V2", "5000万像素+自研V3", ""],
["5000mAh", "5400mAh", ""],
["3999元起", "4999元起", ""]
]
},
"text": "X100 Pro 在影像芯片和电池上更强,如果预算充足且拍照是核心需求,建议选 Pro。日常使用 X100 已经足够。需要我帮您看看优惠吗?"
}
2. 多轮对话的上下文管理
导购场景天然是多轮的——用户先说"预算3000",再追问"有没有拍照更好的",再问"这款和那款比怎么样"。vivo 的对话管理层维护了一个槽位状态机,每一轮更新槽位而不是重新提取。这意味着第二轮的"拍照更好的"会继承第一轮的 budget=3000,不需要用户重复说。
3. 降级策略
LLM 服务不可用时,系统降级到"规则推荐 + 卡片直出"模式——根据已提取的槽位直接查知识库,返回排序后的机型卡片列表,跳过自然语言生成。用户仍然能拿到推荐结果,只是少了对话体验。
上线前的检查清单
如果你也在做类似的垂直场景 AI 导购,落地前建议逐项确认:
- 边界清单:列出 AI 明确能做和不能做的场景,和业务方对齐签字。
- 幻觉防线:机型参数、价格、活动信息必须走检索而非 LLM 记忆,上线前用测试集跑一遍参数准确率。
- 意图兜底:低置信意图必须有 clarify 策略,不能硬猜。
- 槽位继承:多轮对话的槽位状态机要测试——故意拆散需求说(第一轮说预算,第二轮说偏好),验证槽位是否正确累积。
- 降级通路:LLM 挂了之后用户还能拿到什么?降级路径要单独测试。
- 合规审核:价格误报、竞品贬损、虚假承诺——这些在导购场景是红线,输出审核层必须覆盖。
- 埋点设计:意图分布、槽位提取成功率、推荐采纳率、多轮完成率——没有这些数据,迭代就是盲猜。
vivo 的实践说明一件事:AI 导购的价值不在于"能聊天",而在于"能帮用户更快做决策"。划清边界、分层架构、检索优先、降级兜底——这些工程决策比选哪个大模型更影响最终效果。