ARTICLE DETAIL

建站实战干货

来自一线的建站与推广经验沉淀,每一条都经过真实交付验证。

上下文优先:购物Agent的架构设计与工程实践

2026/9/8 5:25:41 拓冰建站 浏览量
上下文优先:购物Agent的架构设计与工程实践 1. 为什么标题把“上下文”放在“查询”前面先说一个我自己的亲历。去年我做一个购物推荐类的 AI Agent 原型当时的思路很简单用户输入一句话我把它翻译成几个关键词然后去商品库做一次搜索把 Top 10 结果扔给大模型排序。听起来顺理成章对不对结果第一轮测试就翻车了。用户说“我想要一台适合办公室用的、噪音小一点的咖啡机预算两千以内”我的 Agent 把关键词拆成了“咖啡机 静音 办公”查出来的结果里一半是商用大机器一半是胶囊机而且价格从三百到八千都有。用户看到结果直接说“你是不是没听懂我说什么”这个例子特别典型它暴露了单靠“查询”做购物 Agent 的根本问题用户给出的从来不是结构化查询而是带着大量未说出口的上下文约束的自然语言。关键词抽取只是把这句话压扁了丢失了“办公场景”“预算区间”“噪音偏好”这些真正的决策条件。所以“AI 购物代理为何上下文优先于查询”这个标题我认为把核心矛盾点得很透。购物 Agent 能不能真正帮上忙关键不在于搜索引擎式的召回做得多好而在于你能否把用户所处的完整语境——包括对话历史、实时交互、商品数据、工具能力边界——在发起查询之前先构建成一份可用的上下文。查询只是上下文驱动下的最后一个动作而不是出发点。这篇文章的目标读者是想自己动手做 Agent 的开发者、产品经理或者纯粹对 AI 应用感兴趣的人。我会结合自己踩过的坑讲清楚上下文为什么优先、优先到什么程度、以及具体要怎么落地。2. 传统“查询”思维在购物场景里为什么会失灵2.1 用户的真实语言不是结构化查询传统搜索系统的假设是用户知道自己要什么并且能用关键词表达出来。但在购物场景里这个假设几乎从来都不成立。用户说“我想买个礼物送女朋友预算一千左右不要太俗的。”这句话里真正可以直接用来查询的词只有“礼物”和“一千”。可“女朋友”意味着什么意味着用户可能需要考虑性别偏好、节日氛围、甚至对方之前提到过的东西。“不要太俗”就更模糊了——每个人对“俗”的理解完全不一样。如果把这个句子丢给搜索引擎得到的结果大概率是“送女友礼物排行榜”“创意礼物推荐清单”这类泛内容页。但一个购物 Agent 如果只能做到这种程度那它跟一个普通搜索框没有本质区别用户凭什么要用它真正有用的购物 Agent需要把这句话分解成多个维度的上下文约束人群女性、关系恋人、价格带800-1200元、风格诉求独特感、非大众款、使用场景送礼。这些约束全部要到上下文里去找而不是靠查询关键词能解决的。2.2 搜索式 Agent 的三宗罪我把只做“查询”不做“上下文”的购物 Agent 的问题总结成三个硬伤这三个硬伤是结构性的加再多的搜索技巧也救不回来。第一单轮表达的信息量严重不足。用户在电商平台搜索“手机”两个字系统知道要给手机分类页但用户对 Agent 说“我想换个手机”他真正想表达的可能包括旧手机用了几年、目前预算、对拍照还是续航更在意、有没有指定品牌偏好。这些话他不可能一次性说完Agent 需要通过多轮对话去主动问、去收集。没有上下文记忆每一轮都是重新开始用户就得不停重复体验会非常差。第二没有状态追踪就无法履行“承诺”。购物决策不是一次查询就结束的。用户可能在第一轮说“预算五千以内”在第三轮看中一款 4999 的手机后又问“这个有 512G 版本吗”加了 500 元预算变 5499。此时如果 Agent 还记得“用户预算是 5 千以内”这条初始约束就需要提醒用户超预算了或者主动去找同价位 512G 的其他机型。但一个无状态的查询 Agent 根本不知道自己在第几轮它只会把 5499 的结果直接推荐出来也不会记得自己推荐过什么。第三缺少跨商品推理能力。用户问“这个咖啡机和那个相比哪个更值得买”搜索式 Agent 会返回两个商品页但不会告诉你该怎么比。比较需要上下文里有用户的具体偏好权重你说“静音重要”那两台机器的噪音分贝数就是核心指标你说“家里人多”那水箱容量和连续出杯能力就是关键参数。这些偏好查询结果是不会告诉你的。所以说购物 Agent 的战场根本不在数据库查询层而是在上下文的理解与维护层。把上下文做扎实了查询才有意义。3. 上下文到底包含哪些东西一个都不能少要理解“上下文优先”先要把上下文的范畴说清楚。我做了几个版本的 Agent 之后把购物场景里的上下文归纳为五大类缺了任何一类最后的表现都会出问题。3.1 购物上下文的五层结构第一层用户画像。这不是让你去采集用户隐私而是通过对话逐渐积累的偏好信息。比如用户提到过“家里有猫”“平时喝美式”“对乳糖不耐受”这些信息会在未来的很多商品推荐中起作用。画像的获取应该是自然的、在对话中沉淀的而不是一上来就甩给用户一个调查问卷。第二层会话状态。这是最容易理解也最容易被忽略的一层。用户刚才看过什么商品点过哪个链接把哪个加进了购物车对某个商品的哪条参数提出了疑问——这些是当前会话内最鲜活的上下文直接影响下一步推荐。第三层外部数据。商品目录、实时库存、价格波动、优惠活动、物流时效这些数据通常需要通过接口拿到。它们本身不是“上下文”但查询结果能不能被正确解读全靠这些数据撑底。比如用户说“明天就要用”如果系统不知道快递要三天才能到推荐再好的商品也白搭。第四层系统能力边界。上下文不仅包含用户和商品还要包含“我这个 Agent 现在能做什么”。能直接下单、只能加购物车、能比价、能查物流这些能力边界要写进上下文避免 Agent 做出超出能力的承诺。第五层历史记忆。跨会话的记忆比如用户上次购买过什么品类、对某个品牌评价如何。这层最难做因为它涉及隐私和数据保留策略但对复购型购物场景价值极高。3.2 上下文优先的三个设计原则理解了上下文包含什么之后更重要的是知道“优先”两个字该怎么落地。我总结了三句话算是核心原则。原则一约束先行查询后置。不要一上来就拆关键词去搜先把用户这句话里所有能提取的约束条件全部列出来形成结构化的条件集合再去设计查询。宁可查询条件多到搜不出结果也不能条件太少搜出一堆没用的。原则二让状态机说话别让模型全猜。购物流程是相对固定的表达需求、推荐候选、对比参数、决策下单。这个流程应该用一个显式的状态机去管理每一步需要哪些上下文、下一步可能触及哪些分支都在状态机里有定义。大模型负责理解和生成自然语言但流程控制必须用代码锁定不能全靠模型临场发挥。否则模型一念之差把“加入购物车”理解成“直接下单”后果很严重。原则三查询参数里要能看见上下文。做推荐之后把系统理解到的用户约束原样展示出来比如“根据您的偏好预算 2000 以内、静音优先、适合办公室”。这样做有两个好处一是用户能感知到 Agent 确实听懂了自己二是用户有机会纠正误解。人机共融的信任感就是这么建立起来的。4. 实操落地一个上下文优先的购物 Agent 怎么搭4.1 整体架构五个模块各司其职我建议的架构分为五个模块直接面向购物场景来设计而不是套通用的 ChatBot 框架。意图识别模块判断用户当前是初次提需求、追问细节、修改约束、还是进入比价/下单阶段。这一步决定了要触发的状态分支。上下文管理模块维护五层上下文数据支持实时读写提供约束提取与更新接口。这是整个架构的核心。条件构造模块把上下文中的约束翻译成结构化的查询条件类似 SQL 的 WHERE 子句但更灵活。商品检索模块对接商品库或上游电商接口执行带条件的查询返回候选商品池。结果重排与生成模块把候选商品结合上下文偏好重新打分排序再生成给用户看的自然语言回复。模块之间的数据流转是这样的用户输入先进意图识别识别结果更新上下文管理模块接着条件构造模块根据上下文生成查询条件商品检索模块执行查询得到候选集最后结果重排模块结合上下文做精排并生成自然语言回复。4.2 上下文优先的推荐实现思路我先写一段思路性的 Python 代码展示从用户输入到查询条件构造的关键路径。基于常见实践补充为了方便说明做了简化但核心逻辑是能迁移到生产环境的。from dataclasses import dataclass, field from typing import Optional dataclass class UserContext: 购物场景下的用户上下文结构 budget: Optional[tuple[float, float]] None # 预算区间 categories: list[str] field(default_factorylist) # 类目偏好 must_have: list[str] field(default_factorylist) # 必须具备的特性 nice_to_have: list[str] field(default_factorylist) # 加分特性 exclude: list[str] field(default_factorylist) # 排除项 scenario: str # 使用场景 history_items: list[str] field(default_factorylist) # 已浏览商品ID rejected_items: list[str] field(default_factorylist) # 用户已排除的 def parse_user_input(raw: str, current: UserContext) - UserContext: 用大模型从用户输入中提取约束更新上下文。 这里不展开具体 prompt但核心是让模型输出结构化的 JSON 字段变更 而不是自由文本。示例 {budget: {max: 2000}, must_have: [静音], scenario: 办公室} # dirty hack真实项目里这里调用 LLM 接口做结构化提取 extracted { budget: {max: 2000}, must_have: [静音], scenario: 办公室 } if extracted.get(budget): current.budget (0, extracted[budget].get(max, 10_0000)) current.must_have.extend(extracted.get(must_have, [])) if extracted.get(scenario): current.scenario extracted[scenario] return current def build_query_conditions(ctx: UserContext) - dict: 把上下文转换成查询条件。注意这里的核心是条件 关键词。 conditions { category: ctx.categories, price_range: ctx.budget, filters: { must_have: ctx.must_have, exclude: ctx.exclude }, sort: default, page_size: 50, # 多取一些候选留给后续重排 } # 场景约束办公室场景优先筛选噪音参数 if ctx.scenario 办公室: conditions[filters][must_have].append(低噪音) return conditions这段代码的关键是parse_user_input负责把每个轮次的用户语言转换成结构化的上下文更新而不是转成搜索词。build_query_conditions始终以完整的 UserContext 为输入而不是以原始文本为输入。这样来十个用户说同一句话因为上下文不同构造出的查询条件也可能是完全不同的。4.3 查询如何做从关键词匹配到条件化召回查询环节的设计同样体现了上下文优先。我不建议直接拿 must_have 列表做全文匹配因为商品数据通常不是统一的自然语言文档而是结构化的属性表。正确的做法是把 must_have 映射到具体的商品属性字段上。比如“静音”这个特性在商品数据库里对应的是“噪音分贝”字段。你需要把用户语言里的“静音”翻译成查询参数noise_level 45dB而不是在名称字段里模糊搜“静音”。这一步翻译的实现思路是为每个常见的用户约束维护一个属性映射表或者让大模型先输出约束类别噪音、容量、功率等再用代码映射到具体的查询字段上。另外我强烈建议在查询阶段放宽条件把候选商品多捞一些出来然后在重排阶段用更严格的上下文约束去打分。原因很简单商品数据的标注质量参差不齐严格过滤容易把实际上符合预期但标注不全的商品误杀。召回多、精排狠是信息检索的经典思路放到这里同样成立。4.4 精排怎么做上下文是打分器的锚点精排阶段是上下文优先原则最直观的体现。同一个商品在不同用户的上下文里得分应该完全不同。def rerank_items(items: list[dict], ctx: UserContext) - list[dict]: 根据上下文为候选商品重新打分排序 scored [] for item in items: score 0.0 # 价格区间命中加分 if ctx.budget and ctx.budget[0] item[price] ctx.budget[1]: score 3.0 elif ctx.budget and item[price] ctx.budget[1]: score - 1.5 # 超预算但要保留待会儿解释原因 # 必备特性命中 for feature in ctx.must_have: if feature in item.get(feature_tags, []): score 2.0 # 场景匹配 if ctx.scenario 办公室 and item.get(noise_level, 99) 45: score 2.5 if ctx.scenario 办公室 and item.get(noise_level, 99) 60: score - 2.0 # 用户明确排除的商品直接沉底 if item[id] in ctx.rejected_items: score - 10.0 # 会话内的浏览记录加权看过的品牌、同类目给予小幅加分 if item[id] in ctx.history_items: score 0.3 item[rerank_score] round(score, 2) scored.append(item) scored.sort(keylambda x: x[rerank_score], reverseTrue) return scored这段代码里有几个细节我特意做了处理超预算的商品没有被直接删除而是减分保留。因为实际购物中用户嘴上说预算两千但看到一款 2399 但评价极好的商品时往往会愿意加预算。保留超预算候选并由大模型在回复中提示“这款略超预算但功能上多了一个 X”反而可能带来惊喜。对用户已排除的商品一定要重罚而不是直接过滤。过滤会导致同款换色的商品重新冒出来重罚降权则保留了一定的弹性。会话内的浏览历史加权很轻只有 0.3。原因是浏览过不代表喜欢只代表“有潜在兴趣”。权重太高会让推荐结果过度收敛用户浏览了两三个同类商品后就再也推荐不出其他类型的东西。精排完成之后再把 Top N 的结果连同精排理由交给大模型生成自然语言。注意一定要把打分依据一并传给大模型让它用原因 结果的方式回复用户而不是干巴巴地甩链接。5. 踩坑记录上下文优先的买卖坑都在细节里5.1 上下文膨胀塞得越多模型越糊涂第一个坑是过度用力。刚开始做上下文优先时我的想法是“反正上下文重要那我把所有历史都塞进去”。结果一次会话进行了 30 轮之后模型开始把早期轮次里用户随口提的一句“其实我也看过 A 品牌”当成核心约束推荐结果被带偏了。后来我换成了分层记忆的策略整个对话历史保留最近 5 轮完整原始文本再往前只保留大模型定期生成的摘要。摘要的粒度是“用户在 5 轮之前表达过对某品牌的偏好当前是否仍然有效由意图识别模块判断”。这个方案把上下文从“越长越好”变成了“越精越好”。5.2 上下文数据不是越多越好而是要带时间戳这里涉及到一个很容易被忽略的问题上下文的时效性。用户在第 2 轮说过“预算五千以内”在第 15 轮问“有没有一万左右的旗舰机”这两个信息是矛盾的。如果不做时效性处理Agent 会把两个条件同时应用到查询里结果就是什么都查不出来。我给每条上下文约束都加了时间戳和置信度当新约束与旧约束冲突时新约束覆盖旧约束但旧约束会被记录在“已覆盖约束”列表里方便回溯用户决策路径。让用户明确知道“你一开始预算五千现在看一万的是预算调整了对不对”这样既解决了冲突也提升了交互质感。5.3 查询超时和幂等上下文越多查询压力越大商品检索模块如果直接对接外部电商 API查询超时是家常便饭。上下文条件多了之后一个请求可能要在上游拆成好几个子查询。我遇到过的情况是外部接口超过 3 秒没响应用户已经在对话里又追加了两个新条件两个请求并发修改同一个上下文对象导致条件互相覆盖。解决方案是上下文管理模块加锁同一会话的写入串行化查询模块增加超时熔断子查询失败时先降级返回缓存结果而不是一直傻等接口重试要做好幂等设计——同一个查询条件重试多次返回结果不能有重复副作用比如重复加入购物车这种操作是绝对不能出现的。5.4 常见问题速查表我把实际运维中遇到的高频问题整理成了表格方便遇到问题时快速定位。现象可能原因排查思路推荐结果和用户说的大相径庭约束提取遗漏上下文没更新完整查看上下文对象的 must_have / exclude 是否有值用户说“我刚刚不是说了不要这个”已排除项没写入上下文的 rejected_items检查对话状态机是否覆盖了“排除商品”分支同一个商品反复推荐精排阶段没有排除历史已推荐项在重排前先把历史推荐 ID 加入降权列表多轮对话后推荐越来越偏上下文膨胀早期无用约束干扰检查摘要策略确认旧约束是否被覆盖用户改条件后结果无变化查询缓存键没有包含更新后的上下文确认缓存 key 是否绑定了上下文的版本号外部接口偶发超时导致回复很慢查询模块没有熔断降级增加超时阈值和本地缓存兜底5.5 上下文数据安全的一个提醒购物场景的上下文必然涉及用户个人信息比如收货地址、购物偏好、历史订单。在做上下文存储时强烈建议遵循数据最小化原则只保留当前决策所必需的信息历史数据做脱敏和定期清理。不要为了“以后可能用到”就把所有原始数据一直存着。这既是合规要求也是降低数据泄露风险的底线操作。我在生产环境是这么做的用户画像和会话状态分开存储会话状态 24 小时后自动清理只把结构化后的偏好摘要写入画像库原始对话文本不落盘。这样做既保住了跨会话的上下文连续性又把隐私暴露窗口控制在最小。6. 从购物 Agent 延伸到上下文工程一个新方向做购物 Agent 做到后面我有一个越来越强的感受上下文工程正在变成一个独立于提示工程的重要方向。提示工程解决的是“怎么对一个模型说话”上下文工程解决的是“把哪些信息以什么结构在什么时候喂给模型”。后者远比前者复杂因为它涉及状态管理、数据流、缓存策略、时效性决策、隐私约束而购物场景恰恰是练习上下文工程的最佳试验田——约束维度多、交互轮次长、状态迁移明确、结果用户可即时感知。如果你也想从这个方向切入我建议从一个小而完整的场景做起比如“让 Agent 帮你选一台咖啡机”。不需要一开始就接真实电商平台数据可以用本地 JSON 模拟。先把五层上下文建起来再把意图识别、约束提取、查询构造、结果重排这四个环节跑通。整个过程做完你对“上下文优先于查询”这句话的感受会从理解变成肌肉记忆。我自己做这个项目的体会是真正难的地方并不是大模型的能力而是软件工程层面的上下文治理能力。谁能把用户的上下文管理得干净、准确、及时谁做出的 Agent 体验就会显著优于同赛道对手。这可能是未来几年 AI 应用开发的核心竞争力之一值得在这个方向上长期投入。