最近有一个新闻值得所有做 AI Agent 的开发者停下来想一想:Perplexity 试图让 AI 帮你直接下单购物,结果遭到亚马逊平台方面的限制,随后又出现“反转”传闻。乍一看这是商业新闻,但往深一层看,它其实是 AI Agent 发展中绕不开的“合规分岔口”——你的 Agent 到底应该以什么身份、什么方式去调用外部系统?
过去一年,我见过太多团队兴奋地做出“AI 自动购物”“AI 自动抢课”“AI 自动填报”这类 Demo,但很少有人认真问过:这个自动化动作,真的被平台允许吗?如果平台不允许,Agent 的行为边界在哪里?如果用户授权了,Agent 就能替用户执行一切操作吗?
这篇文章不打算停留在新闻评论层面。我想从工程角度拆解 AI 下单 Agent 的技术链路,分析平台限制背后的原因,再给出一个合规的最小可运行系统设计。最终你会理解:AI 帮你下单本身不是问题,问题在于你是否用对了通道。
1. AI 购物 Agent 到底动了谁的蛋糕
先还原一下场景。传统购物流程是:用户打开购物网站,搜索商品,浏览详情,比较价格,加入购物车,结算支付。每一步都是用户亲自完成,平台也能根据用户行为做推荐和转化统计。
AI 购物 Agent 改变的是这个流程的入口和决策环节。用户只需要输入一句话,比如“帮我买一箱 24 瓶装的农夫山泉,价格在 30 元以内”,Agent 就会自动检索商品、比价、选择店铺、下单。如果这套链路跑通,用户的购物入口就从“搜索框”变成了“对话框”,平台对流量的掌控力会被显著削弱。
但问题远不止流量入口这么简单。真正让平台警觉的是三件事:
第一,自动化访问带来的技术压力。Agent 如果通过网页自动化操作(模拟点击、填写表单)来完成下单,本质上就是高频脚本访问,这会触发平台的反爬机制,也可能影响平台正常用户的体验。
第二,交易责任的归属模糊。Agent 替用户下单,如果买错了商品、下错了颜色、买贵了,责任算谁的?用户说“我没看清”,平台说“这是你授权的 Agent 操作的”,Agent 开发商说“我只是执行工具”——最终纠纷还是会落到平台上。
第三,消费者权益保护的合规缺口。电商交易涉及支付、退款、售后、知情权、后悔权,这些流程在设计时都假设“操作者是本人”。Agent 介入后,“本人授权”和“本人操作”之间的边界被打破了,平台需要重新定义规则。
所以,平台对 AI 购物 Agent 保持警惕,不是简单的“保守”,而是因为这套技术确实触碰了电商系统底层的信任模型。
2. 一个购物 Agent 的典型技术链路
要理解合规问题,必须先理解技术实现。一个完整的 AI 购物 Agent,内部其实是一套多模块协作系统:
用户输入 ↓ 意图理解模块(LLM) ↓ 商品检索模块(搜索 API / 网页抓取) ↓ 决策与推荐模块(比价、筛选、排序) ↓ 执行下单模块(购物车 → 结算 → 支付) ↓ 订单状态跟踪模块每个模块都有不同的技术选型,也对应不同的合规风险。
2.1 意图理解模块
这是 Agent 的“大脑”。用户输入“帮我买一箱水”之后,模型需要从中抽取结构化参数:商品品类(水)、数量(一箱)、价格上限(可选)、品牌偏好(可选)。这一步的技术方案通常是 LLM + 函数调用,让模型输出一段 JSON,再经过 schema 校验。
这里最容易出现的问题是AI 幻觉。模型可能把“24 瓶装”理解成“24 箱”,也可能在用户没说品牌时“默认”推荐了一个品牌。如果这个错误的参数直接进入下单流程,会造成真实的经济损失。
2.2 商品检索模块
这一步有两种实现路线:
- API 路线:调用平台官方开放接口,按照类目、关键词、价格区间检索商品。技术上最干净,返回结构化数据,稳定且合规。
- 网页抓取路线:模拟浏览器请求或直接解析 HTML,绕过官方接口获取商品信息。技术上有更多不确定性,而且大概率违反平台服务条款。
很多开发者在 Demo 阶段觉得“抓个网页太简单了”,但一旦进入生产环境,网页结构变动、反爬验证、请求频率限制会迅速反噬。
2.3 决策与推荐模块
Agent 拿到候选商品列表后,需要根据用户的约束条件筛选。比如价格在预算内、评分高于 4.5、发货地在国内。这一步可以做成规则引擎,也可以用 LLM 做自然语言条件的解析。
2.4 执行下单模块
这是合规风险最高的模块。走 API 路线时,开发者需要申请平台的应用凭证,明确授权范围;走浏览器自动化路线时,Agent 往往需要“借用”用户的登录态(比如通过浏览器插件或 Cookie),这等于让第三方系统操作个人账号。
2.5 订单跟踪模块
下单之后,Agent 还需要查询订单状态、处理退款、发起售后。这些操作同样面临“API 可用性”和“账号操作授权”的问题。
从整体来看,AI 购物 Agent 的技术难度并不在于“调用一下 Claude/GPT 的接口”,而在于把这些模块稳定地串起来,并且每一环都经得起合规审查。
3. 平台为什么警觉:禁令背后的真实原因
回到 Amazon 对 Perplexity Shopping Agent 的态度问题。从公开报道看,Perplexity 在购物场景中尝试直接帮助用户完成购买,这引发了平台方面的限制。虽然业内把这件事称为“禁令”,但更准确的说法是:平台对未经授权的自动化购物行为亮明了态度。
从平台视角看,至少有四个层面的考虑:
3.1 服务条款层面的约束
几乎所有主流电商平台的服务条款里都有类似“禁止未经授权的自动化访问”条款。用户注册使用平台时说“本人操作”,但 Agent 介入后,实际操作者变成了程序。这不是用户本人的直接操作,所以即使有用户授权,第三方 Agent 的自动化访问仍然可能违反 ToS(Terms of Service)。
3.2 反爬虫机制的正常反应
电商平台普遍有风控系统。Agent 如果通过网页自动化访问,请求频率、点击轨迹、操作速度都和真人差异明显。风控系统识别出异常后,会触发验证码、账号限制甚至封禁。别把平台的检测能力想得太弱——头部电商平台的风控系统能基于上千个特征判断“这不是人在操作”。
3.3 交易安全与支付风险
自动下单意味着自动支付。如果 Agent 在用户不知情的情况下完成了支付,或者因为价格解析错误导致支付金额超出预期,平台需要花大量人力处理纠纷。更严重的是,如果 Agent 被恶意控制,可能变成“薅羊毛工具”或“刷单工具”,直接冲击平台的交易秩序。
3.4 消费者权益保护的合规压力
在中国和欧美市场,消费者都有“后悔权”。用户通过 Agent 下单后,如果要退款,责任链路怎么走?平台客服无法判断“这是用户真实意图,还是 Agent 的误解”。如果平台无法确认交易真实性,就不敢轻易放行这种第三方自动化下单能力。
所以,与其说平台在“反 AI”,不如说平台是在保护现有交易体系的确定性。只要 Agent 带来的不确定性高于收益,平台就会倾向于限制。
4. “反转”传闻给我们什么信号
关于亚马逊对 Perplexity 禁令出现“反转”的传闻,我没有内部信息可以验证,但可以给出一个更稳妥的判断:在商业博弈中,“禁令”和“反转”都只是阶段性的状态。真正决定最终走向的,是 Agent 产品能不能从“绕过平台”变成“与平台共建”。
Perplexity 这类搜索型 Agent 的天然优势在于:它已经掌握了用户意图,用户也愿意信任它的推荐。如果它只是把搜索结果导流到平台,平台反而欢迎;如果它试图代替用户完成所有交易动作,平台就会警惕。因此,“反转”大概率不是“平台放开限制”,而是 Agent 调整了产品形态——比如从“替你下单”转为“帮你选好,你确认后跳转下单”。
这个信号对开发者很重要。它意味着:AI Agent 的落地不能只考虑技术可行性,还要考虑平台生态的接受度。你可以在技术上实现全自动下单,但用户和平台是否接受全自动,是另一回事。
5. 从零搭建一个谨慎的 AI 下单最小系统
安全提示: 以下代码仅用于学习 AI Agent 的流程设计,接入真实电商平台时,必须以平台官方 API 文档为准,并获得用户明确授权。不要试图绕过平台的访问限制、登录验证或反爬机制。
为了让流程可感知,我设计一个最小化的、走“官方 API + 用户确认”路线的 AI 下单示例。代码不绑定任何真实平台,只演示流程结构。
5.1 环境准备
- Python 3.10+
- FastAPI + uvicorn
- pydantic
- 一个可用的 LLM API(支持 tool/function calling 的模型均可)
pip install fastapi uvicorn pydantic requests openai5.2 定义意图解析
先让 LLM 把用户输入转成结构化下单意图。这里的关键是使用 JSON Schema 约束输出,降低 AI 幻觉的影响。
# 文件路径:agent/intent.py import json from openai import OpenAI client = OpenAI() INTENT_SCHEMA = { "type": "object", "properties": { "product_name": {"type": "string"}, "quantity": {"type": "integer", "minimum": 1}, "max_price": {"type": ["number", "null"]}, "brand": {"type": ["string", "null"]} }, "required": ["product_name", "quantity"] } def parse_intent(user_input: str) -> dict: response = client.chat.completions.create( model="gpt-4o-mini", temperature=0, messages=[ {"role": "system", "content": "你是购物助手。请从用户输入中提取商品名、数量、品牌、价格上限。" "如果没有指定品牌或价格,返回 null。不要自行补充用户没说过的限制条件。"}, {"role": "user", "content": user_input} ], response_format={"type": "json_schema", "json_schema": INTENT_SCHEMA} ) return json.loads(response.choices[0].message.content)这段代码里最容易忽略的是 system prompt 里的最后一句:“不要自行补充用户没说过的限制条件”。没有这一个约束,模型很可能自作主张加上“默认买销量最高的”或者“默认选价格最低的”,从而偏离用户真实意图。
5.3 模拟商品检索(官方 API 路线)
真实平台接入时,这里应该请求平台的官方搜索 API。为了演示流程,我用一个本地 mock 代替。
# 文件路径:agent/search.py from typing import Optional # 模拟的商品数据,实际开发中替换为平台官方 API 返回 MOCK_PRODUCTS = [ {"id": "p001", "name": "农夫山泉 550ml*24瓶", "brand": "农夫山泉", "price": 35.9, "stock": 100}, {"id": "p002", "name": "怡宝 纯净水 555ml*24瓶", "brand": "怡宝", "price": 32.9, "stock": 50}, {"id": "p003", "name": "农夫山泉 5L*4桶", "brand": "农夫山泉", "price": 39.9, "stock": 0}, ] def search_products(product_name: str, max_price: Optional[float] = None, brand: Optional[str] = None) -> list[dict]: results = [p for p in MOCK_PRODUCTS if product_name.lower() in p["name"].lower()] if max_price is not None: results = [p for p in results if p["price"] <= max_price] if brand is not None: results = [p for p in results if p["brand"] == brand] return results在真实项目中,这一步建议优先看平台是否提供了开放搜索 API。即便 API 返回的数据字段没有网页上那么丰富,也远比抓取网页稳定和合规。
5.4 用户确认状态机
这里我认为是整个系统里最重要的一步。很多 AI 自动下单事故,都源于“跳过用户确认”。考虑到价格变动、库存变化、用户输入歧义等因素,高价值操作必须有人工确认环节。
我用一个简单的状态机来管理流程:
# 文件路径:agent/order_state.py from enum import Enum class OrderState(str, Enum): PENDING_CONFIRM = "pending_confirm" CONFIRMED = "confirmed" PLACED = "placed" CANCELLED = "cancelled" FAILED = "failed" class OrderFlow: def __init__(self, user_id: str, intent: dict): self.user_id = user_id self.intent = intent self.state = OrderState.PENDING_CONFIRM self.selected_product = None self.order_id = None def select_product(self, product: dict) -> None: if self.state != OrderState.PENDING_CONFIRM: raise ValueError("当前状态不允许选择商品") self.selected_product = product print(f"已选中商品: {product['name']}, 价格: {product['price']}") def confirm(self) -> None: if self.state != OrderState.PENDING_CONFIRM: raise ValueError("当前状态不允许确认") if self.selected_product is None: raise ValueError("请先选择商品") self.state = OrderState.CONFIRMED print("用户已确认,准备下单") def mark_placed(self, order_id: str) -> None: if self.state != OrderState.CONFIRMED: raise ValueError("只有确认后才能下单") self.order_id = order_id self.state = OrderState.PLACED print(f"下单成功,订单号: {order_id}") def cancel(self) -> None: if self.state in (OrderState.PLACED, OrderState.CANCELLED): raise ValueError("当前状态不允许取消") self.state = OrderState.CANCELLED print("订单已取消")状态机最重要的价值是:它把“用户意图 → 商品选择 → 用户确认 → 真正下单”每一步都变成了显式状态,避免异常流程下出现“商品没选就下单”或“重复下单”的问题。
5.5 模拟下单执行(带幂等键)
真实的下单接口必须支持幂等。否则用户点击两次“确认”,系统可能创建两个订单。
# 文件路径:agent/executor.py import uuid def create_order(user_id: str, product_id: str, quantity: int, idempotency_key: str) -> dict: # 在真实系统中: # 1. 调用平台官方下单 API,传递 idempotency_key # 2. 平台端通过该 key 判断是否已处理过相同请求 # 3. 记录返回的订单号 print(f"[模拟] 用户 {user_id} 下单,product_id={product_id}, quantity={quantity}") print(f"[模拟] 幂等键: {idempotency_key}") order_id = f"ORD-{uuid.uuid4().hex[:12].upper()}" return {"order_id": order_id, "status": "created"} def place_order(flow: OrderFlow, quantity: int) -> str: flow.confirm() idempotency_key = f"{flow.user_id}:{flow.selected_product['id']}:{uuid.uuid4()}" result = create_order( user_id=flow.user_id, product_id=flow.selected_product["id"], quantity=quantity, idempotency_key=idempotency_key ) flow.mark_placed(result["order_id"]) return result["order_id"]幂等键的设计原则是:同一个用户、同一个商品、同一次操作意图,必须生成相同的 key。如果每次重试都生成新的 key,那幂等就失去了意义。更稳妥的做法是,在用户点击“确认”时生成一个 confirm_token,下单请求带上这个 token,服务端依据 token 去重。
5.6 组装成 FastAPI 服务
# 文件路径:app.py from fastapi import FastAPI, HTTPException from pydantic import BaseModel from collections import defaultdict from agent.intent import parse_intent from agent.search import search_products from agent.order_state import OrderFlow from agent.executor import place_order app = FastAPI() # 用内存 dict 保存会话状态,真实项目使用 Redis 并设置过期时间 active_flows = defaultdict(dict) class ShoppingRequest(BaseModel): user_id: str query: str class ConfirmRequest(BaseModel): user_id: str product_id: str @app.post("/api/agent/search") def agent_search(req: ShoppingRequest): intent = parse_intent(req.query) products = search_products( product_name=intent["product_name"], max_price=intent.get("max_price"), brand=intent.get("brand") ) flow = OrderFlow(user_id=req.user_id, intent=intent) active_flows[req.user_id] = flow return {"intent": intent, "candidates": products} @app.post("/api/agent/confirm") def agent_confirm(req: ConfirmRequest): flow = active_flows.get(req.user_id) if flow is None: raise HTTPException(status_code=400, detail="请先执行搜索") products = search_products(flow.intent["product_name"]) target = next((p for p in products if p["id"] == req.product_id), None) if target is None: raise HTTPException(status_code=404, detail="商品不存在或已下架") flow.select_product(target) order_id = place_order(flow, quantity=flow.intent["quantity"]) return {"order_id": order_id, "state": flow.state}注意:这个接口里的 place_order 会直接下单。真实生产系统中,confirm 应该拆成两步:先向用户展示订单摘要(商品、价格、预计送达时间),用户再次确认后才真正调用下单接口。我在这里合并是为了让示例代码更短,但工程上不建议这么做。
6. 运行与验证
启动服务:
uvicorn app:app --reload --port 8000然后依次执行两个请求。第一个请求让 Agent 解析意图并返回候选商品:
curl -X POST http://localhost:8000/api/agent/search \ -H "Content-Type: application/json" \ -d '{"user_id": "u001", "query": "帮我买一箱农夫山泉,预算40元"}'预期的输出类似:
{ "intent": { "product_name": "农夫山泉", "quantity": 1, "max_price": 40, "brand": null }, "candidates": [ { "id": "p001", "name": "农夫山泉 550ml*24瓶", "brand": "农夫山泉", "price": 35.9, "stock": 100 } ] }第二个请求确认商品并下单:
curl -X POST http://localhost:8000/api/agent/confirm \ -H "Content-Type: application/json" \ -d '{"user_id": "u001", "product_id": "p001"}'预期输出:
{ "order_id": "ORD-XXXX", "state": "placed" }如果下单失败,先检查三个地方:LLM 返回的 JSON 是否通过 pydantic 校验;搜索接口是否返回空列表;状态机的当前状态是否允许执行 confirm。绝大多数问题都出在这三处。
7. 常见问题与排查思路
如果你在自己构建 AI 下单 Agent,大概率会遇到下面这些问题:
| 问题现象 | 可能原因 | 排查方式 | 解决方案 |
|---|---|---|---|
| LLM 解析出的商品名与用户表达不一致 | AI 幻觉或上下文丢失 | 打印 LLM 返回的完整 JSON,检查字段是否有额外补充 | 加强 system prompt 约束;使用更高的 temperature=0;增加 schema 校验 |
| 搜索接口返回空结果 | 关键词不匹配或平台接口参数错误 | 先人工用同样关键词调用平台搜索 API 验证 | 增加同义词扩展;改用类目 ID 检索;记录并展示“未找到商品” |
| 用户重复点击确认导致重复下单 | 缺少幂等键或幂等键生成规则错误 | 检查下单接口是否接收并校验幂等键 | 在用户会话中生成一次性的 confirm_token,请求携带该 token |
| 状态机报错“当前状态不允许确认” | 前端未按顺序调用接口 | 查看当前 flow.state 与请求操作是否匹配 | 前端严格按流程按钮置灰;后端返回当前状态辅助定位 |
| 支付回调丢失,用户已扣款但订单未创建 | 异步通知机制失效 | 查看支付回调日志与订单表记录 | 实现订单支付状态轮询;对账任务定期拉取未完成订单 |
| Agent 被平台风控限制 | 请求频率过高或访问行为异常 | 检查请求频率日志和平台返回码 | 降低请求频率;优先使用官方 API;在用户授权范围内运行 |
8. 合规与工程最佳实践
从这次 Perplexity 与亚马逊的摩擦中,值得总结的工程原则不止一条。
8.1 API 优先原则
任何 Agent 要操作外部系统,第一选择永远是官方 API。哪怕官方 API 的功能覆盖不全、返回字段不够丰富,也优先使用。原因很简单:API 是平台“同意”你访问的通道,服务条款、速率限制、数据边界都是明确写好的。浏览器自动化和非官方接口,只是在平台没有同意的情况下强行打开一扇门。
8.2 用户授权与最小权限
Agent 不应请求超过完成任务所需的权限。比如只做商品搜索,就不应该申请下单权限;只做比价,就不需要用户登录。对于高权限操作(下单、支付、修改账号信息),必须显式获得用户确认,并在服务端记录授权凭证、授权时间和授权范围。
8.3 高价值操作必须人工确认
即使技术上可以实现“用户说一句就全自动完成”,工程上也应该保留“人工确认”环节。下单、支付、退款都属于不可逆或高成本操作,这些操作之前必须有明确的确认交互。最好的做法是展示一个结构化摘要卡片,让用户核对商品、价格、数量、收货地址后再点击确认。
8.4 记录完整的决策链路
Agent 的决策过程要尽量可追溯。用户输入了什么,LLM 解析出了什么,候选商品有哪些,最终选了哪一个,为什么选它,用户的确认内容是什么——这些信息都应当写日志留存。一旦出现纠纷,完整的审计日志是保护用户、保护平台、保护你自己的关键证据。
8.5 失败时的默认行为
Agent 在不确定或失败时,应该默认“什么都不做”,而不是“尝试自己修复”。比如下单接口超时,不能盲目重试,而是要标记为“待确认”,向用户展示当前状态,等待用户指令。宁可让用户觉得 Agent“笨”,也不能让 Agent 在异常状态下贸然操作。
8.6 别把生产环境当实验场
AI 模型的输出有随机性,所以凡是涉及真实交易的 Agent 系统,都应该先做影子测试:不真实下单,只记录 Agent 会做什么决策,与人工决策对比。连续运行一段时间,确认决策准确率达到可接受水平之后,再逐步放开真实执行。
9. 总结与后续学习方向
Perplexity 与亚马逊这次的摩擦,本质上是一次“技术能力”与“平台规则”的碰撞。AI Agent 当然有能力替用户完成复杂的多步操作,但能力不等于许可。开发者在兴奋于“AI 能做什么”的同时,必须冷静回答“AI 被允许做什么”。
从这篇文章里,你至少应该带走几个结论:
- AI 下单的技术链路可以被拆解成意图解析、商品检索、决策推荐、执行下单、订单跟踪五个模块,每一环都有不同的合规风险。
- 平台限制 AI 自动下单,主要出于 ToS、反爬风控、交易安全和消费者权益保护四层考虑。
- 一个合规的 AI 下单系统,应该是“官方 API + 用户授权 + 状态机 + 确认机制 + 幂等键 + 审计日志”的组合,而不是“模拟浏览器 + 绕过验证”。
- 未来购物 Agent 的方向不是“绕过平台”,而是成为平台生态里更聪明的用户入口。谁能和平台建立规则内的合作,谁才能走得更远。
如果你想继续深入,可以从这几个方向入手:学习 function calling / tool use 的工程化写法;研究电商开放平台的 API 设计和权限模型;动手为你的 Agent 增加影子测试和审计日志;了解不同市场对 AI 代理交易的法律与合规要求。
建议把文中的最小示例跑一遍,然后用它作为骨架,尝试接入你熟悉的电商开放平台。跑通之后,你才会真正理解“AI 帮你下单”这句话的分量——它不只是技术上的一个 Demo,更是一系列产品、合规和商业决策的交汇点。