
当绝大多数 AI 购物智能体还在帮你“种草”时TickClip 这类产品提供了一个更反直觉的方向在合适的时机给出“不要买”的建议。这个定位让 AI 购物 agent 从渠道推销员变成了用户身边的理性顾问。本文先拆解这类产品的决策逻辑再用 Python 实现一个最小可运行原型完整跑通从需求识别、商品检索、价格分析到“不购买/等待/购买”建议生成的链路最后给出生产环境下的工程化注意点。1. 购物智能体为什么需要“不购买”决策1.1 从“搜索推荐”到“决策辅助”的转变传统购物推荐的核心指标是点击率、转化率和 GMV。系统越了解用户就越倾向于把用户推向付款页。这对平台是好事但对用户不一定是最优解。用户常常遇到的情况是原本只想查一下商品信息结果半小时内浏览了大量同类产品明明家里已经有功能重叠的设备却因为“券后划算”“限时折扣”而下单买完之后发现使用频率很低最后变成闲置。AI 购物 agent 出现后常见的产品形态仍然是“你说需求我给你推荐”本质还是搜索推荐的对话版。它优化的是“找到商品”的准确率而不是“是否应该买”的决策质量。TickClip 这类思路的关键转变是把评价目标从“帮用户找到东西”改成“帮用户做出正确决策”。正确决策可能是购买也可能是购买更便宜的替代品还可能是不购买。这个转变看似简单但会直接影响推荐链路中每一步的设计。1.2 StrikeClip 的核心卖点拆解以 TickClip 这类 AI 购物 agent 为背景它的价值主张可以用一句话概括当数据判断“现在买不是好选择”时agent 要敢于说 No并且给出可验证的理由。这个能力拆开来看包含四个子能力需求理解用户说“想买个平板”可能是一个模糊兴趣也可能是一个紧急学习工具。agent 要区分这两者。商品与价格判断目标商品是否处于合理价格区间是否接近历史低价是否存在明显溢价。替代方案评估有没有更便宜、更够用、功能重叠度高的产品或者用户自己已有的设备。建议表达能力结论要清晰理由要具体不能让用户觉得“AI 在说教”。四个子能力缺一不可。如果没有价格判断拒绝建议就没有说服力如果没有替代方案用户听完“别买了”之后会不知道怎么行动如果表达能力差再正确的结论也可能被用户忽略。1.3 “不购买”建议背后的产品逻辑从产品角度看一个购物 agent 主动推荐“不要买”短期内会损失一笔佣金或成交但换来的是用户信任。购物决策不是一次性的用户如果发现 AI 的建议总是站在自己这边后续高客单价商品的购买决策会更依赖这个 agent。还有一层实际原因退货成本。冲动购买带来的退货、换货、客服成本对平台和商家都不低。如果 agent 在购买前就拦截掉一部分冲动消费长期来看可以减少售后压力。技术上也更接近“决策智能”的范畴。传统推荐系统优化的是行为概率购物 agent 优化的是决策质量。后者更难评估但更贴近用户真实利益。形态优化目标典型输出用户感知传统推荐系统点击率、转化率商品卡片、相似推荐被推送普通购物 agent推荐相关性对话式商品列表被引导TickClip 式购物 agent决策质量购买/等待/不购买 理由被理解2. 设计一个最小可复刻的 TickClip 原型2.1 能力框架四条链路要复刻 TickClip 的核心思路不需要一开始就接真实电商平台。可以先用 mock 数据跑通决策链路再逐步替换真实数据源。整个 agent 的数据流分为四段需求解析把用户的自然语言输入转成结构化需求。商品检索根据需求从商品库中筛出候选商品并补充价格信息。决策评估用评分卡模型对每个候选商品打分输出总分数和决策结论。解释生成把结构化决策转成自然语言建议。这个链路的特点是“模型负责理解和表达规则负责决策”。不建议让 LLM 直接拍板“买或不买”因为 LLM 的输出不稳定用户无法追溯判断依据。更稳妥的做法是让 LLM 抽取特征让确定性代码计算分数最后再让 LLM 基于分数生成解释。2.2 技术选型与运行环境原型使用 Python 3.10核心依赖只有两个httpx用于调用 LLM API。pydantic用于解析和校验结构化输出。如果你本地有 OpenAI 兼容的 API 服务或者使用 Ollama 跑本地模型都可以直接替换请求地址和模型名称。下面的代码都以 OpenAI 兼容接口为例实际项目要结合自己的模型网关调整。依赖版本建议用途Python3.10运行环境httpx0.27LLM API 请求pydantic2.x结构化数据校验一个 LLM API任意兼容 OpenAI 格式需求解析和解释生成生产环境至少还需要日志组件、缓存、限流和 API 密钥管理学习阶段先不引入。2.3 目录结构和数据模型先建立项目结构tickclip/ ├── main.py # CLI 入口 ├── config.py # 配置 ├── models.py # 数据结构 ├── requirements.py # 需求解析 ├── products.py # 商品检索与价格分析 ├── decision.py # 决策引擎 ├── explain.py # 建议生成 ├── prompts.py # Prompt 模板 └── data/ └── products.json # mock 商品数据商品数据使用一个 JSON 文件模拟。每个商品记录包含价格、原价、评分和历史价格序列历史价格用于判断当前价格是否处于低位。[ { id: ipad-10gen, name: iPad 第10代 64GB Wi-Fi, category: tablet, price: 3999, original_price: 4799, rating: 4.6, weekly_sales: 320, history: [4599, 4499, 4299, 4199, 4099, 3999] }, { id: xiaomi-pad-6, name: 小米平板6 128GB, category: tablet, price: 2299, original_price: 2499, rating: 4.4, weekly_sales: 510, history: [2499, 2399, 2299, 2299, 2299, 2299] }, { id: printer-hp-2700, name: HP 打印机 2720 无线款, category: printer, price: 799, original_price: 999, rating: 4.3, weekly_sales: 180, history: [999, 949, 899, 849, 799, 829] } ]这个数据模型足够支撑后续的价格分析和决策评分。真实项目中历史价格需要从第三方比价平台或电商开放接口获取并注意采集合规性。3. 核心模块实现从用户输入到“不要买”结论3.1 需求解析提炼预算、紧迫度、已有物品需求解析的目标是把“我最近想买个平板家里已经有一个旧的就是看剧用”这样一段输入变成结构化字段。先定义需求数据结构# models.py from dataclasses import dataclass, field from typing import List, Optional dataclass class Requirement: category: str budget: Optional[float] None urgency: float 0.5 # 0-11表示非常紧急 usage_frequency: float 0.5 # 0-1使用强度 existing_related: List[str] field(default_factorylist) impulse_indicators: List[str] field(default_factorylist) raw_text: str 然后写一个函数调用 LLM 把文本转成 JSON# requirements.py import json from models import Requirement def parse_requirement(text: str, llm_client, model: str) - Requirement: user_prompt f分析下面这段购物需求输出 JSON\n{text} resp llm_client.chat.completions.create( modelmodel, temperature0, messages[ {role: system, content: REQUIREMENT_SYSTEM_PROMPT}, {role: user, content: user_prompt}, ], response_format{type: json_object}, ) data json.loads(resp.choices[0].message.content) return Requirement( categorydata.get(category, ), budgetdata.get(budget), urgencyfloat(data.get(urgency, 0.5)), usage_frequencyfloat(data.get(usage_frequency, 0.5)), existing_relateddata.get(existing_related, []), impulse_indicatorsdata.get(impulse_indicators, []), raw_texttext, )这里的 Prompt 很关键。REQUIREMENT_SYSTEM_PROMPT要明确告诉模型每个字段的语义尤其是urgency和impulse_indicators。# prompts.py REQUIREMENT_SYSTEM_PROMPT 你是一个需求分析器。根据用户的购物描述输出 JSON字段如下 - category: 商品品类简短英文例如 tablet, printer, laptop。 - budget: 用户提到的预算金额数字若未提到则为 null。 - urgency: 0 到 1 的浮点数代表需求紧急程度。如果用户提到马上要用坏了急用给 0.8 以上如果只是看看以后再说给 0.3 以下。 - usage_frequency: 0 到 1代表预计使用频率。每周多次给 0.7 以上很少用给 0.3 以下。 - existing_related: 用户已经拥有的同类或功能重叠商品列表。 - impulse_indicators: 用户表达中的冲动信号例如降了很多必须入手刚好看到限时没有则给空列表。 只输出 JSON不要输出解释。 3.2 商品检索与价格分析商品检索可以先按category过滤。重点是价格分析因为“不要买”的很大一部分理由是“当前价格不划算”。计算两个指标折扣率(original_price - price) / original_price用于判断是否真的在打折。价格位置当前价格在history中的相对位置0 表示接近历史最低1 表示接近历史最高。# products.py import json from pathlib import Path from models import Requirement def load_products(path: str data/products.json): return json.loads(Path(path).read_text(encodingutf-8)) def search_products(category: str, products: list): return [p for p in products if p[category] category] def price_position(product) - float: history product.get(history, [product[price]]) lo min(history) hi max(history) if hi lo: return 0.5 return (product[price] - lo) / (hi - lo) def discount_rate(product) - float: original product.get(original_price, product[price]) if original 0: return 0.0 return round((original - product[price]) / original, 4)price_position是判断“现在是不是好价格”的核心。折扣率只能说明商品相对于吊牌价的折扣但有些商家会把吊牌价标高再用大折扣制造促销感。历史价格序列能够拆穿这种假促销。3.3 决策引擎评分卡 阈值决策引擎不直接使用 LLM而是使用一个可解释的加权评分模型。这样便于调参、便于出日志、便于向用户解释。# decision.py WEIGHTS { urgency: 0.25, substitution: 0.2, price_fairness: 0.2, impulse: -0.15, inventory: -0.1, budget_impact: 0.1, } class DecisionEngine: def __init__(self, weightsNone, buy_threshold0.6, wait_threshold0.35): self.weights weights or WEIGHTS self.buy_threshold buy_threshold self.wait_threshold wait_threshold def evaluate(self, factors: dict) - dict: score sum(self.weights[k] * factors.get(k, 0) for k in self.weights) score round(max(0.0, min(1.0, score)), 4) if score self.buy_threshold: decision buy elif score self.wait_threshold: decision wait else: decision not_buy return {score: score, decision: decision, factors: factors}factors中的每个值都来自上游模块。在原型里它们由需求和商品信息计算出def build_factors(requirement: Requirement, product: dict) - dict: discount discount_rate(product) pos price_position(product) # 价格公平度折扣越高、价格越接近历史低位得分越高 price_fairness 0.5 * discount * 2 0.5 * (1 - pos) price_fairness max(0.0, min(1.0, price_fairness)) # 替代性用户已有同类商品时替代性高不买的理由更充分 substitution 0.6 if requirement.existing_related else 0.2 # 预算影响如果商品价格明显超过预算则预算影响得分高更不建议现在买 budget_impact 0.0 if requirement.budget and product[price] requirement.budget * 1.1: budget_impact 0.8 # 冲动信号出现越多冲动词越要压低购买分数 impulse min(1.0, len(requirement.impulse_indicators) * 0.3) return { urgency: requirement.urgency, substitution: substitution, price_fairness: price_fairness, impulse: impulse, inventory: 0.3 if requirement.existing_related else 0.1, budget_impact: budget_impact, }注意impulse因子在权重里是负数。这意味着用户表达里冲动信号越多总分越低agent 越倾向于给出等待或不买的建议。3.4 解释生成让“不买”有说服力评分卡输出的是一堆数字用户看不懂。需要一个模块把这些数字翻译成自然语言建议。最简单的做法是使用模板但这容易显得机械。更自然的方式是把结构化评估结果交给 LLM让 LLM 基于数据生成解释。# explain.py def generate_advice(requirement, product, result, llm_client, model: str) - str: advice_prompt f 需求原文{requirement.raw_text} 候选商品{product[name]}当前价格 {product[price]}原价 {product[original_price]}。 决策结果{result[decision]}综合得分 {result[score]}。 评分因子{result[factors]} 用户已有{requirement.existing_related} 冲动信号{requirement.impulse_indicators} 请给出简短购物建议规则 1. 第一句直接说结论建议买 / 建议等待 / 不建议现在买。 2. 理由最多三条每条必须引用上面数据。 3. 如果结论是不建议买给一个具体行动项。 4. 不要使用夸张促销语气。 resp llm_client.chat.completions.create( modelmodel, temperature0.3, messages[ {role: system, content: ADVICE_SYSTEM_PROMPT}, {role: user, content: advice_prompt}, ], ) return resp.choices[0].message.content.strip()这里的关键是“每条理由必须引用数据”。否则模型容易给出空泛的理由例如“现在购买可能不太划算”用户无法判断这个结论是否可信。# prompts.py ADVICE_SYSTEM_PROMPT 你是一个理性、谨慎的购物顾问。 你的目标不是促成交易而是帮助用户做出长期最优决策。 你说话简洁不制造稀缺感不使用催促性语言。 当数据支持不购买时你会直接说明并给出后续行动建议。 4. 参数设计和提示词模板决定 agent 是“推销员”还是“顾问”4.1 决策阈值、权重表和温度参数同样的代码不同参数会得到完全不同的行为。默认阈值设置如下参数默认值说明buy_threshold0.6综合得分大于等于 0.6 时建议购买wait_threshold0.35得分在 0.35 到 0.6 之间建议等待低于 0.35-建议不购买调参时要理解每个因子的意义因子权重含义权重调大的影响urgency0.25需求紧急程度越容易倾向购买substitution0.2替代方案充分程度越容易倾向不买price_fairness0.2价格是否划算越高越倾向购买impulse-0.15冲动信号强度越高越倾向不买inventory-0.1已有同类商品越高越倾向不买budget_impact0.1超出预算压力越高越倾向不买注意budget_impact的权重是正数但它表达的是“预算压力大”所以这个因子越高时总分反而会升高最终触发的是not_buy的阈值判断。这里的设计逻辑是预算影响越大说明价格越超出用户承受能力越不适合购买。如果想要更直观可以把因子设计成“可负担性”并取反但原型里用“压力”语义也没有问题。temperature参数也要区分使用场景需求解析阶段用temperature0保证结构化输出稳定。解释生成阶段用temperature0.2到0.4让语言更自然同时避免离谱发挥。如果做 A/B 测试或探索性玩法可以调高到0.7但生产环境不建议。4.2 提示词模板示例需求解析和解释生成两处 Prompt 已经在前面给出。生产环境中Prompt 要集中管理避免散落在代码里。建议把prompts.py扩展成独立的prompts/目录每个 Prompt 写清版本号和变更说明。一个容易忽略的细节是解释生成阶段的 Prompt 不要直接粘贴 LLM 自己算出来的数字比如“综合得分 0.43其中价格公平度 0.6”。用户不关心评分卡细节。Prompt 里给出的数据应该已经被上游重新整理过例如“当前价格接近近 6 次记录中的最高价”这种人类可读描述。4.3 常见调参陷阱陷阱一权重不归一化。所有因子相加后分数可能超过 1 或低于 0导致阈值失效。解决方式是在evaluate中做 clamp把分数限制在 0 到 1。陷阱二只用 LLM 判断买不买。让模型直接输出“buy”或“not_buy”会出现同一需求反复横跳的问题。把评分交给确定性代码后同样的输入一定得到同样的分数可复现性好很多。陷阱三价格公平度只看折扣率。吊牌价虚高是常见操作只看折扣率会让 agent 在“假促销”面前失灵。必须引入历史价格序列。5. 运行验证看它怎么拒绝一次购买5.1 构建模拟场景在main.py中组装链路# main.py from requirements import parse_requirement from products import load_products, search_products from decision import DecisionEngine, build_factors from explain import generate_advice def main(text: str, llm_client, model: str): req parse_requirement(text, llm_client, model) products load_products() candidates search_products(req.category, products) if not candidates: print(没有找到相关商品) return engine DecisionEngine() best candidates[0] result engine.evaluate(build_factors(req, best)) advice generate_advice(req, best, result, llm_client, model) print( 需求解析 ) print(req) print( 决策结果 ) print(result) print( 给用户的建议 ) print(advice)运行命令python main.py 最近想买个平板家里有一个旧款就是看视频用看到打折了有点心动5.2 运行过程和中间结果第一个测试场景是“我有个旧平板只是看视频用看到打折有点心动”。这个输入包含三个关键信号已有同类商品、使用需求不紧急、冲动信号“有点心动”。合理输出应该是wait或not_buy。中间结果类似{ requirement: { category: tablet, budget: null, urgency: 0.2, usage_frequency: 0.4, existing_related: [旧款平板], impulse_indicators: [有点心动] }, factors: { urgency: 0.2, substitution: 0.6, price_fairness: 0.62, impulse: 0.3, inventory: 0.3, budget_impact: 0.0 }, score: 0.334, decision: not_buy }第二个测试场景是学生急需打印机交论文“我是个学生下周要交论文宿舍里没有打印机之前别人送的一台坏了预算 800 左右”。这个输入包含“下周”“坏了”“没有”等紧急信号合理输出是buy。{ requirement: { category: printer, budget: 800, urgency: 0.9, usage_frequency: 0.7, existing_related: [坏掉的旧打印机], impulse_indicators: [] }, factors: { urgency: 0.9, substitution: 0.6, price_fairness: 0.58, impulse: 0.0, inventory: 0.3, budget_impact: 0.0 }, score: 0.616, decision: buy }输出符合预期的关键点有三个decision是否与业务判断一致。factors中每个因子是否符合直觉。自然语言建议是否引用了数据。5.3 结果解释与输出检查需要检查 LLM 生成的建议是否遵守了 Prompt 中的规则。好的输出示例不建议现在买。 1. 你已有旧款平板且需求主要是看视频现有设备可以满足。 2. 当前价格虽然比原价低但接近近 6 次价格记录中的中高位。 3. 建议先使用现有平板两周如果仍然觉得体验不足再考虑新款。 可以做的替代方案清理旧平板缓存恢复出厂设置后继续使用。坏的输出示例是那种没有数据支撑、只有情绪判断的句子这个平板挺好的但是你觉得呢如果你喜欢就买吧。这种输出说明 Prompt 约束还不够强或者模型的输出解析逻辑太宽松。可以先从数据准备角度检查再决定是增强 Prompt 还是在代码层拦截。6. 常见问题排查6.1 排查顺序当 agent 的行为不符合预期时按下面顺序排查不要一上来就改 Prompt输入文本是否被正确解析urgency和impulse_indicators是否符合直觉。商品数据是否完整history是否覆盖了足够长的时间范围。评分卡因子计算是否符合预期必要时在代码里打印每个因子。阈值是否适合业务场景先跑一批标注数据再决定是否调。LLM 解析是否失败查看返回的 JSON 是否合法。日志是否完整能否反推出某个结论是由哪些因子驱动的。6.2 常见问题表问题现象可能原因检查方式处理建议agent 几乎从不推荐“不买”buy_threshold过低或urgency权重过高打印一段样本的 factor 分布调低buy_threshold或提高impulse负权重推荐理由前后矛盾LLM 解释生成没有引用结构化数据查看 Prompt 中的规则是否强制引用数据在 Advise Prompt 中增加“每条理由必须引用 score 或 price”约束价格总显示“划算”history只有最近几条且都是降价检查商品数据数据需要覆盖至少 6 到 12 次价格记录用户输入“就随便看看”却推荐买urgency被解析成 0.5 的默认值查看需求解析输出修正系统 Prompt让模型对无明显需求信号输入给出低 urgencyLLM 返回非法 JSON模型不支持response_format或 Prompt 中出现额外文本查看原始响应增加 JSON 解析兜底函数提取首个{}块生产环境价格抓取失败第三方接口限流、反爬、地区限制查看上游 API 错误码使用官方开放接口重试加退避失败时使用缓存数据6.3 一个容易忽略的坑数据时间范围价格分析依赖的历史价格列表如果只保存最近两三天的价格会被误判为“处在低位”。假设商品三天内从 5000 降到 4200history只有这三次记录价格位置计算会认为 4200 是历史最低点。但如果拉长到三十天可能 3800 出现过多次。没有足够的时间范围价格公平度就会失真。原型阶段可以用手工数据生产环境必须解决历史比价数据来源问题。常见方案是接入第三方比价服务或者自己每天定时采集价格快照到数据库。采集行为要遵守目标平台的服务条款优先使用官方开放接口。7. 从原型到生产工程化要点和后续方向7.1 数据来源与合规购物 agent 的价值上限取决于数据质量。商品价格、历史价格、评价信息、库存状态都需要稳定可靠的数据源。接入真实数据时要注意优先使用电商官方开放平台 API不要写爬虫去绕过反爬机制。价格数据要标记采集时间和数据来源方便追溯。用户输入的购物意图涉及个人消费偏好存储和调用要遵循隐私合规要求。7.2 决策链路日志生产环境必须记录决策链路日志。至少包含用户输入原文。需求解析后的结构化字段。候选商品 ID 和价格快照。每个评分因子的值。最终决策和得分。用户是否采纳了建议。日志的价值在后续调参时非常明显。没有日志你只知道某个建议被用户忽略了但不知道是价格判断错误、需求理解错误还是表达方式问题。日志可以用结构化格式写入独立索引或表每次决策一个记录。建议使用 JSON 行格式便于后续做离线分析。7.3 用户反馈闭环用户对“不购买”建议的反应是重要的训练信号。可以在建议卡片上增加两个按钮“有帮助”和“不合适”。用户点击后把反馈回写到决策日志。积攒一段时间后可以看到哪种类型的“不购买”建议被用户认可哪种被反感。例如用户可能接受“价格高于历史均值”的理由但不接受“你已经有同类产品”这种专家口吻。这时候可以调整解释生成阶段的 Prompt而不是调整评分卡。7.4 扩展方向原型跑通后可以从几个方向扩展多候选商品对比当前原型只对第一个商品评分可以改为对多个商品排序并输出最佳选择与放弃理由。延迟购买提醒当一个商品处于wait状态时记录用户关注并在价格到达历史低位时主动提醒。真实价格数据接入接入比价 API让价格分析更可靠。浏览器插件在用户浏览电商页面时唤起 agent在支付前增加一道决策缓冲。本地模型部署如果对数据隐私要求高可以使用部署在私有环境的模型避免把用户购物意图发送到外部服务。对于想从零实践这类项目的开发者建议按这个顺序学习先手工构造 20 条用户购物输入标注自己期望的决策结果然后跑评分卡看准确率再逐步接入真实商品数据。评分卡模型虽然简单但它是整个 agent 可解释性的基础。没有这个基础直接让大模型决定买不买后续很难评估和优化。