微信小程序接入微信 AI:让用户"说一句话"就能下单

用一杯「WeStore 拿铁」的下单流程,把小程序 AI 开发模式的原子接口、原子组件、SKILL 封装和联动闭环讲清楚。文中代码均来自官方示例,可直接对照复制。

一、这是个什么模式

微信悄悄上线了小程序 AI 开发模式(beta,内测中) 。它和我们熟悉的"在小程序里调个 AI 模型生成文本"完全不是一回事------它提供的是一整套智能化的运行环境和开发框架 :开发者把小程序的功能抽象成「原子接口」和「原子组件」,封装成 SKILL,然后小程序 AI 就能在用户对话时,自己推理该调哪个接口、渲染哪个组件,最终把任务执行完。

一句话区别:过去是"用户点按钮 → 小程序执行",现在是"用户说一句话 → AI 调接口、渲卡片、推进流程"。

官方放了一个完整示例 wechat-miniprogram/ai-mode-demo,做的就是一个虚构咖啡品牌 WeStoreCafe 的点单场景。这篇就以它为样本,把最核心的 SKILL 联动机制拆开讲。

注意:当前处于内测阶段,相关代码暂不开放提审,切勿合入正式版本提交审核。需先在「微信公众平台 - 基础功能 - AI 能力」申请「开发模式」,并用申请过的 AppID 跑 demo。

二、四个核心概念

官方框架建立在四个概念上,理解它们是看懂后面一切的前提。

小程序 MCP :向小程序 AI 暴露"可调用能力"的一套协议。它和标准 MCP 不同,针对小程序特点做了适配------开发者只要按规范提供完整的 SKILL,AI 就能正确推理并执行对应接口,无需关心协议细节。

原子接口:最小执行单元,封装单一业务功能,有标准化的输入参数和输出结构,运行在微信客户端的独立 JS 环境里。

原子组件:原子接口的可视化单元,把接口返回的结构化数据渲染成对话流里的 GUI 卡片。

SKILL :一个完整场景任务的能力封装。一个小程序可以有多个 SKILL,每个 SKILL 包含三样东西------业务说明 SKILL.md、能力声明 mcp.json、以及原子接口与原子组件的实现。

三、三方运行架构

运行时涉及三个角色,职责分得很清楚:

角色 职责
客户端运行时 微信客户端提供的独立运行环境,执行原子接口、渲染原子组件
小程序 AI 后台 加载 SKILL,结合用户请求做 LLM 推理,下发接口调用或渲染指令
第三方服务 开发者自己的服务,在原子接口实现里被调用,完成真实数据交互

客户端和 AI 后台之间通过小程序 MCP 通信,协议细节开发者不用管,只要把 SKILL 写完整。

这里有个必须警惕的运行环境细节 :原子接口、原子组件、实时动态原子组件分别运行在三个不同的执行上下文 里,权限集不同,且不共享全局变量。另外原子组件用的是自研卡片渲染引擎(glass-easel 框架),WXSS 支持范围和小程序的 WebView/Skyline 有差异------别拿小程序那套 CSS 习惯直接套。

四、SKILL 的目录结构

来看 demo 里 skills/drink-skill/ 的组织方式:

bash 复制代码
skills/
└── drink-skill/
    ├── SKILL.md          # 业务说明(给 AI 看的"说明书")
    ├── mcp.json          # 原子接口能力声明(类似 tool schema)
    ├── index.js          # 接口注册入口
    ├── apis/             # 原子接口实现
    │   ├── getRecommendedDrinks.js
    │   ├── searchDrinks.js
    │   ├── selectDrink.js
    │   ├── confirmSku.js
    │   ├── saveAddress.js
    │   ├── payOrder.js
    │   └── ...
    └── components/       # 原子组件(卡片)
        ├── recommended-drinks/
        ├── drink-detail-card/
        ├── order-confirm-card/
        ├── pay-success-card/
        └── store-status-card/

它在 app.json 里通过 agent.skills 注册,并作为独立分包存在:

json 复制代码
{
  "subPackages": [
    { "root": "skills", "pages": [], "independent": true }
  ],
  "agent": {
    "skills": [
      {
        "name": "drink",
        "description": "WeStoreCafe 点单场景:查询推荐饮品、选择规格、填写收货地址、下单支付",
        "path": "skills/drink-skill"
      }
    ],
    "pageMetadata": "page-meta.json"
  }
}

接口注册用 wx.modelContext.createSkill,注意 path 要和 app.json 里的 agent.skills[].path 一致:

javascript 复制代码
// skills/drink-skill/index.js
const getRecommendedDrinks = require('./apis/getRecommendedDrinks.js')
const selectDrink = require('./apis/selectDrink.js')
// ...

const skill = wx.modelContext.createSkill('skills/drink-skill')

skill.registerAPI('getRecommendedDrinks', getRecommendedDrinks)
skill.registerAPI('selectDrink', selectDrink)
skill.registerAPI('confirmSku', confirmSku)
skill.registerAPI('payOrder', payOrder)
// ... 注册所有原子接口,name 需与 mcp.json 声明一致

五、重点:SKILL 联动是怎么实现的

这是整个模式最有价值的部分。所谓"联动",本质是一个三层闭环:Agent 调接口 → 接口返回数据 → 卡片渲染 → 用户点击回灌 Agent → Agent 调下一个接口。下面逐层拆。

第一层:用 SKILL.md + mcp.json 双重约束 AI

AI 不能随便调接口。mcp.json 用 schema 约束单个接口怎么调 ,SKILL.md 用自然语言约束接口之间的顺序和铁律

mcp.json 里每个接口的 description 都写死了前置条件和严禁场景。比如推荐接口:

json 复制代码
{
  "name": "getRecommendedDrinks",
  "description": "获取推荐饮品列表。调用前置条件:用户表达想喝饮品但未指定具体商品名(如「想喝点什么」)。【严禁场景】用户已说出具体商品名(如「来杯拿铁」)时,禁止调用本接口,应调用 searchDrinks。",
  "inputSchema": { ... },
  "outputSchema": { ... },
  "_meta": { "ui": { "componentPath": "components/recommended-drinks/index" } }
}

SKILL.md 则用一张流程图 + "跨接口铁律"约束顺序,核心几条:

  • AI 不能跳过 selectDrink 直接调 confirmSku------必须先有有效的 drinkId;
  • AI 不能跳过 confirmSku 直接调 payOrder------必须先有 confirmed 状态的订单;
  • payOrder 未返回成功前,禁止向用户宣布"已支付成功" ;
  • drinkId/orderId 必须来自上游接口返回的原值,禁止从"那个 3 号"这类自然语言推断

这套约束让接口调用有了顺序性和状态依赖,AI 不会乱调、不会跳步、不会编造关键 ID。

第二层:接口返回数据分三层(精髓)

每个原子接口返回的数据被刻意分成三层,这是省 token + 防上下文污染的关键设计。看 selectDrink 的实现:

javascript 复制代码
// skills/drink-skill/apis/selectDrink.js
async function selectDrink({ drinkId } = {}) {
  const drink = findDrink(drinkId)
  if (!drink) {
    // 失败分支:堵死错误退路 + 给出正确出口
    return {
      isError: true,
      content: [{
        type: 'text',
        text: `未找到 drinkId=${drinkId}。禁止编造 ID 再次调用,禁止从用户语言推断。正确出口:调用 getRecommendedDrinks 或 searchDrinks 获取有效 drinkId。`
      }]
    }
  }

  return {
    isError: false,
    // ① content:事实陈述 + 给 AI 的下一步指令(AI 可见)
    content: [{
      type: 'text',
      text: `已加载饮品「${drink.name}」详情。接下来展示详情卡片,引导用户点击"直接下单"。禁止 AI 主动调用 confirmSku 跳过卡片展示。`
    }],
    // ② structuredContent:供 AI 理解屏幕内容的结构化数据(AI 可见,不含图片)
    structuredContent: {
      drinkId: drink.id,
      name: drink.name,
      price: drink.price,
      specOptions  // 精简版规格
    },
    // ③ _meta:纯渲染数据(AI 不可见,只给组件用)
    _meta: {
      imageUrl: drink.imageUrl,
      skuSchema: drink.skuSchema  // 完整 schema
    }
  }
}

三层各司其职:

  • content:事实陈述 + 给 AI 的下一步行动指令,AI 可见。
  • structuredContent :让 AI 理解"屏幕上现在是什么"的结构化语义数据,AI 可见,但不含图片 URL、完整 schema 等渲染细节
  • _meta :图片、完整 SKU schema 等纯渲染数据,AI 完全看不到,只给卡片组件用。

好处:AI 的上下文不会被一堆图片 URL 污染,既省 token 又让推理更聚焦。

第三层:sendFollowUpMessage 回灌闭环(最巧妙的一跳)

用户在卡片上点击,组件不在前端自己消化,而是把这个动作"回灌"给 AI------模拟成一条新的用户消息 + 一个接口调用指令。看推荐卡片组件:

kotlin 复制代码
// skills/drink-skill/components/recommended-drinks/index.js
Component({
  lifetimes: {
    created() {
      this._modelCtx = wx.modelContext.getContext(this)
      this._viewCtx = wx.modelContext.getViewContext(this)
      const { NotificationType } = wx.modelContext
      // 监听接口返回,拿数据渲染
      this._modelCtx.on(NotificationType.Result, (data) => {
        const result = data?.result || {}
        const sc = result.structuredContent || {}
        const meta = result._meta || {}
        // 渲染用 structuredContent + _meta(图片从 _meta 补)
        const viewItems = meta.viewItems || sc.items || []
        this.setData({ items: viewItems.slice(0, 3), total: sc.total })
      })
    }
  },
  methods: {
    onTapItem(e) {
      const item = e.currentTarget.dataset.item
      // 关键:点击 → 回灌为新一轮输入,并精确带上 drinkId
      this._modelCtx.sendFollowUpMessage({
        content: [
          { type: 'text', text: `选择${item.name}` },
          { type: 'api/call', data: { name: 'selectDrink', arguments: { drinkId: item.drinkId } } }
        ]
      })
    }
  }
})

这一跳解决了第一层埋下的难题------"禁止 AI 编造 drinkId"。用户点了"拿铁"卡片,等价于对 AI 说"选择拿铁",并直接携带了正确的 drinkId 。ID 不是 AI 猜的,是点击时精确带上的。组件初始化时通过 wx.modelContext.getContext(this) 拿上下文,监听 NotificationType.Result 接收数据渲染,点击时用 sendFollowUpMessage 把下一步发回去------一来一回,闭环成立。

联动闭环全景

把三层串起来,一次完整点单的数据流:

scss 复制代码
用户说"想喝点什么"
  → AI 按 mcp.json 约束选中 getRecommendedDrinks
  → 接口返回 content(指令) + structuredContent(数据) + _meta(图片)
  → 推荐卡片渲染(读 structuredContent + _meta)
  → 用户点击某款 → sendFollowUpMessage 回灌「选择X + 调 selectDrink(drinkId)」
  → AI 调 selectDrink → 详情卡片
  → 用户点"直接下单" → 组件回灌 confirmSku
  → 订单确认卡(无地址则展示地址空态)
  → 用户点地址区 → wx.chooseAddress / 半屏填写 → saveAddress 自动续跑订单
  → 用户点"确认下单" → payOrder → 支付成功卡片

六、完整交互时序(官方运行机制)

官方文档给了一个简化的"点拿铁少糖"时序,串起三方角色,能帮你建立整体认知:

css 复制代码
用户「点一杯 WeStore 拿铁,少糖」
  │ 上行消息
  ▼
[AI 后台] 加载 SKILL → LLM 推理:调用 confirmOrder
  │ 下发接口调用
  ▼
[客户端] 启动运行环境、下载分包 → 执行原子接口(wx.login 取身份)
  │
  ▼
[第三方] 创建订单 → 回传订单信息
  │ 回传结果
  ▼
[AI 后台] LLM 推理:下发渲染指令
  │
  ▼
[客户端] 渲染原子组件,展示订单 → 用户点击「确认支付」
  │ 上行消息:确认支付
  ▼
[AI 后台] LLM 推理:调用 payOrder
  │ 下发接口调用
  ▼
[客户端] wx.requestPayment 拉起收银台
  │
  ▼
[第三方] 创建预支付 → 查询支付状态 → 回传
  │
  ▼
[AI 后台] 下发渲染指令 → [客户端] 渲染支付结果卡片

可以看到:LLM 推理在 AI 后台,接口执行和卡片渲染在客户端,真实业务数据在第三方服务。三方通过小程序 MCP 协同。

七、接入步骤

  1. 在「微信公众平台 - 基础功能 - AI 能力」或「微信开发者助手 - 微信AI管理」中,接入模式选「开发模式」申请开通。
  2. 下载安装微信开发者工具 Nightly(Electron Build 最新版) ------普通正式版不支持调试本模式。
  3. 把功能抽象成原子接口 + 原子组件,封装成 SKILL,在 app.jsonagent.skills 注册。
  4. 用申请了「开发模式」的 AppID 导入 demo,编译运行,通过 AI 对话界面体验。

八、踩坑与最佳实践

结合官方文档和 demo 代码,几条值得记住的要点:

接口要给"错误出口" :接口失败时别只报错,要明确告诉 AI "正确的下一步该调谁"(如上面 selectDrink 的失败分支),引导 AI 自我纠正,而不是让它瞎试。

枚举值用英文、禁止中文 label :demo 里规格值统一用 ice/hot/normal 等英文枚举,避免 AI 在中文 label 和实际值之间混淆。

绑定卡片必须出卡片 :成功返回且绑定了组件的接口,必须展示卡片,禁止把商品名、价格等以 markdown 列表纯文本展开------这是 demo 明确写进 SKILL.md 的输出形态铁律。

三个执行上下文不共享全局变量:原子接口、原子组件、实时动态组件互相隔离,需要传状态用 storage 或接口返回,别指望全局变量。

WXSS 有差异 :卡片渲染引擎只支持部分 CSS 选择器(推荐用类选择器),尺寸单位支持 rpx/px/vw 等,写卡片样式前先查官方附录的支持范围。

登录态可复用 :本模式下用户登录身份与原小程序一致,可通过 storage 共享登录凭证,也能用 wx.login/wx.getPhoneNumber 走登录流程。

写在最后

这套模式真正可复用的,不是点单业务本身,而是它的四条设计约定:

  1. 用 schema + 自然语言双重约束 AI ------mcp.json 管参数和前置条件,SKILL.md 管顺序和铁律;
  2. 返回数据分三层 ------content(指令)/structuredContent(语义)/_meta(渲染),控制 AI 能看到什么;
  3. sendFollowUpMessage 回灌打通"点击 → 下一步" ,让精确参数随点击传递;
  4. 错误返回给出口,引导 AI 自我纠正。

任何想接入这套模式的业务------预约、查询、表单、交易------都可以照着这四条 + demo 的目录结构去套。剩下的,就是把你自己的业务拆成一个个"原子"。


参考资料:

相关推荐
leeyi2 小时前
Hook 系统:插件化安全护栏怎么设计
llm·agent
Nicander2 小时前
去除中文写作AI味的Skill:write-like-human-zh
agent
leeyi2 小时前
ReAct 循环的 50 行 Go 实现,逐行拆解
后端·agent
leeyi2 小时前
HITL:让人类随时叫停 AI,并且能优雅地继续
后端·agent
AKAMAI2 小时前
当OpenClaw遇见Linode:一键部署7×24h云端AI助理
云计算·agent
腾讯云开发者3 小时前
腾讯云TVP走进招商局,共探具身智能与Agent协同演进新路径
agent
小七-七牛开发者4 小时前
专访 Mainline 作者们:聊聊从代码协作到意图协作
ai·agent·mainline·ai coding
要开心吖ZSH5 小时前
AI医疗分诊与健康咨询助手agent开发——(0)项目背景与概要
java·ai·agent·健康医疗·rag
Python私教6 小时前
我把AI写作压成一条流水线:从写一篇到搭一条稳定产线
aigc·agent·claude