使用微信云开发构建小程序 Skills,您无需编写
wx.request、无需自行搭建服务器、也无需维护登录态——通过wx.cloud.callFunction即可直连云函数,用户身份OPENID由云开发的免鉴权能力自动注入。
写在开头
目前很多开发者都在关注最新推出的小程序 Skills 如何开发,所以我们快速搭好了三个开箱即用的小程序 SKILL 模板。
-
todolist-skill(待办清单):查询、新增、完成切换、删除,基于云开发数据库直读,入门首选 https://github.com/TencentCloudBase/awesome-miniprogram-skills/tree/main/skills/todolist-skill -
queue-skill(门店排队取号):门店搜索、排队状态查看、线上取号、排队进度查询 https://github.com/TencentCloudBase/awesome-miniprogram-skills/tree/main/skills/queue-skill -
drink-skill(咖啡饮品点单):推荐 / 搜索饮品 → 选规格 → 填地址 → 下单支付,完整点单链路 https://github.com/TencentCloudBase/awesome-miniprogram-skills/tree/main/skills/drink-skill
每个模板均基于微信云开发实现前后端逻辑,包含了完整的 SKILL.md 业务说明、接口链路图、约束规则和意图分流示例,覆盖了最常见的小程序场景。
下面以 todolist-skill 为例,走一遍完整的开发流程。
一、小程序 Skills 是什么
微信小程序今天推出了 AI 开发模式:你可以把现有小程序的功能,封装成能被小程序 AI 在对话里调用的能力。这个能力单元就叫 SKILL。
文档:https://developers.weixin.qq.com/miniprogram/dev/ai/guide.html
跟过去「用户点按钮 → 跳页面 → 填表单」的流程不一样,小程序 Skills 把交互提前到了对话里:
[用户] 在小程序 AI 对话框说:"帮我记个待办:买牛奶"
↓
[小程序 AI] 理解意图,自动选中你的 addTodo 能力,填好参数 title="买牛奶"
↓
[你的代码] 执行写入,返回结构化数据
↓
[小程序 AI] 在聊天流里弹出一张你设计的「待办清单卡片」
你要做的事情就两件,剩下的——这句话该调哪个函数、参数怎么填、什么时候弹卡片——全交给 AI:
-
把功能写成函数(官方叫原子接口),就是真正做事的那段代码; -
给函数配一张卡片(官方叫原子组件),把结果渲染成对话流里的 GUI 卡片。
小程序 Skill 的四件套
一个小程序 SKILL 放在独立分包里,包含四个核心文件:
|
|
|
|---|---|
mcp.json |
|
apis/*.js |
|
index.js |
|
components/ |
|
SKILL.md
|
|
底层是小程序 MCP 协议:微信客户端运行时与小程序 AI 后台之间基于该协议交互,你不用理解协议细节,按规范把 SKILL 实现完整就行。
注意:这个模式目前还在内测,暂时没开放代码提审。别把相关代码合进正式版本提交审核。
二、绕不开的问题:登录态怎么办
原子接口跑在微信客户端一个独立的 JS 环境里,跟小程序主环境是隔离的。AI 调你函数的时候,你得自己在函数里搞清楚「这个用户是谁」。
传统做法:自建后端 + 鉴权中间件
如果你的后端是自己搭的服务器,标准流程是这样的:
-
调 wx.login()拿临时code; -
把 code发给你的后端; -
后端用 code+ AppSecret 调微信的code2session接口换openid和session_key; -
后端生成自定义登录态 token返回; -
把 token存进storage,后续请求都带上; -
多个原子接口都要走这套,于是你还得写一个中间件统一处理。
落到代码上就是下面这样——每个 Skill 都得维护这么一段:
// index.js —— 传统自建后端方案:必须写中间件维护登录态
const skill = wx.modelContext.createSkill('skills/todo-skill')
skill.registerAPI('addTodo', addTodo)
skill.use(async (ctx, next) => {
// 统一登录态:没 token 就走一遍换登录态流程
let token = wx.getStorageSync('token')
if (!token) {
const { code } = await wx.login()
const res = await wx.request({
url: 'https://your-server.com/login', // 你自己的后端
data: { code }
})
token = res.data.token
wx.setStorageSync('token', token)
}
await next()
})
然后后端还得写一套 code2session 的换取逻辑、session_key 的存储、token 的签发与校验。这部分代码跟业务没任何关系,纯粹是为了「知道用户是谁」而存在的样板。
三、用微信云开发:不用管登录态,直接拿 openid
用微信云开发做后端,上面那一整套样板代码可以全删掉。
原子接口环境原生支持云开发接口(wx.cloud.init、wx.cloud.callFunction、wx.cloud.database 都能直接用)。云函数有个特性:调用方身份由微信底层链路天然带过来,你在云函数里一行代码就能拿到当前用户的 openid,不需要 wx.login、不需要 code2session、不需要自建鉴权服务、不需要中间件维护 token。
|
|
|
|
|---|---|---|
wx.login()
|
|
|
code2session 换 openid |
|
|
|
|
|
|
|
|
|
|
|
|
|
cloud.getWXContext().OPENID
|
下面用一个完整的 todo 待办清单 SKILL 来演示,看看代码能精简到什么程度。
四、动手做一个待办清单的小程序 Skill
目标很简单:用户用大白话说「记一下」就新增、「看看我的待办」就查看、点卡片就标记完成。
第 1 步:声明分包与 SKILL(app.json)
SKILL 必须放在独立分包里,按需注入和云开发都要开:
{
"lazyCodeLoading": "requiredComponents",
"cloud": true,
"subPackages": [
{
"root": "skills/todo-skill",
"independent": true,
"pages": []
}
],
"agent": {
"skills": [
{
"name": "todo",
"description": "待办清单业务",
"path": "skills/todo-skill"
}
]
}
}
第 2 步:向 AI 声明能力(mcp.json)
待办场景有三个动作:新增、查看、完成。参数说明里要写清楚「取值来源」和「缺失时怎么办」,并且明确告诉 AI 别自己编——这直接决定了 AI 能不能调对接口、填对参数:
{
"apis": [
{
"name": "addTodo",
"description": "新增一条待办事项。当用户说『记一下/帮我记/加个待办』并给出具体内容时调用。",
"_meta": { "ui": { "componentPath": "components/todo-card/index" } },
"inputSchema": {
"type": "object",
"properties": {
"title": {
"type": "string",
"description": "待办内容,取自用户原话(如『买牛奶』)。用户未说出具体内容时禁止填写,应反问『您要记什么待办?』。"
}
},
"required": ["title"]
}
},
{
"name": "listTodos",
"description": "查看当前用户的全部待办清单。当用户说『看看我的待办/还有什么没做』时调用,无需任何参数。",
"_meta": { "ui": { "componentPath": "components/todo-card/index" } },
"inputSchema": { "type": "object", "properties": {} }
},
{
"name": "completeTodo",
"description": "把一条待办标记为已完成。",
"_meta": { "ui": { "componentPath": "components/todo-card/index" } },
"inputSchema": {
"type": "object",
"properties": {
"todoId": {
"type": "string",
"description": "待办唯一标识,必须来自上游 listTodos 返回的 todoId 原值。禁止编造,也禁止从用户自然语言推断;上下文无可用 todoId 时应先调 listTodos。"
}
},
"required": ["todoId"]
}
}
],
"components": [
{ "path": "components/todo-card/index", "relatedPage": "/pages/index/index" }
]
}
三个接口共用同一张
todo-card卡片渲染。relatedPage是卡片右上角「进入小程序」入口关联的页面,填你小程序里真实的待办页路径就行。
第 3 步:原子接口直接调微信云开发(index.js)
index.js 跑在原子接口环境里。初始化 wx.cloud 后就地实现并注册接口。注意:写操作走云函数,只读操作直连云数据库,用户身份全程由微信底层自动带上:
// skills/todo-skill/index.js
wx.cloud.init({ env: 'your-env-xxxxxx' }) // 换成你的云开发环境 ID
const skill = wx.modelContext.createSkill('skills/todo-skill')
// 添加待办:写操作走云函数(OPENID 由云开发自动注入,无需登录)
skill.registerAPI('addTodo', async ({ title }) => {
if (!title) return { isError: true, content: [{ type: 'text', text: '缺少待办内容,请反问用户要记什么,禁止编造。' }] }
const { result } = await wx.cloud.callFunction({ name: 'addTodo', data: { title } })
return {
content: [{ type: 'text', text: `已添加「${title}」,请展示最新待办清单卡片。` }],
structuredContent: { todos: result.todos }
}
})
// 查看待办:只读,直接连云数据库
skill.registerAPI('listTodos', async () => {
const { data } = await wx.cloud.database().collection('todos').orderBy('createTime', 'desc').get()
return {
content: [{ type: 'text', text: `共 ${data.length} 条待办,请展示清单卡片,禁止纯文本罗列。` }],
structuredContent: { todos: data.map(t => ({ todoId: t._id, title: t.title, done: t.done })) }
}
})
// 完成待办:写操作走云函数
skill.registerAPI('completeTodo', async ({ todoId }) => {
if (!todoId) return { isError: true, content: [{ type: 'text', text: 'todoId 须来自 listTodos 返回值,禁止编造。' }] }
await wx.cloud.callFunction({ name: 'completeTodo', data: { todoId } })
return { content: [{ type: 'text', text: '已标记完成,请刷新待办清单卡片。' }] }
})
module.exports = skill
content给 AI 看,建议用「陈述事实 + 指示下一步」两段式;structuredContent既给 AI 理解,也传给组件渲染。整段代码没有skill.use(...)维护登录态的样板。
第 4 步:云函数里一行拿到 openid(cloudfunctions/)
云函数天然知道用户是谁。写操作在云函数里落库,顺手做好所有权校验:
// cloudfunctions/addTodo/index.js
const cloud = require('wx-server-sdk')
cloud.init({ env: cloud.DYNAMIC_CURRENT_ENV })
const db = cloud.database()
exports.main = async (event) => {
// ✨ 免鉴权:直接拿到当前用户 openid,无需 wx.login,无需 code2session
const { OPENID } = cloud.getWXContext()
const title = String(event.title || '').slice(0, 100) // 服务端二次校验
if (!title) return { code: 1, msg: '内容为空' }
await db.collection('todos').add({ data: { _openid: OPENID, title, done: false, createTime: db.serverDate() } })
const { data } = await db.collection('todos').where({ _openid: OPENID }).orderBy('createTime', 'desc').get()
return { code: 0, todos: data.map(t => ({ todoId: t._id, title: t.title, done: t.done })) }
}
// cloudfunctions/completeTodo/index.js
const cloud = require('wx-server-sdk')
cloud.init({ env: cloud.DYNAMIC_CURRENT_ENV })
const db = cloud.database()
exports.main = async (event) => {
const { OPENID } = cloud.getWXContext()
// where 同时带上 _openid,确保只能改自己的待办(越权校验)
await db.collection('todos').where({ _id: event.todoId, _openid: OPENID }).update({ data: { done: true } })
return { code: 0 }
}
AI 生成参数不可信任。免鉴权解决的是「用户是谁」,而「这个 todoId 是不是这个用户的」还是要靠
where里带上_openid来兜底。
第 5 步:做一张卡片(components/todo-card/index.js)
组件环境不支持 wx.cloud,数据全部来自接口返回的 structuredContent。用户点某条待办时,通过代用户上行消息触发 completeTodo:
Component({
data: { todos: [] },
lifetimes: {
created() {
this.ctx = wx.modelContext.getContext(this)
// 监听原子接口返回结果,渲染卡片
this.ctx.on(wx.modelContext.NotificationType.Result, ({ result }) => {
this.setData({ todos: (result.structuredContent || {}).todos || [] })
})
}
},
methods: {
onTapTodo(e) {
const { todoId, title } = e.currentTarget.dataset.todo
this.ctx.sendFollowUpMessage({
content: [
{ type: 'text', text: `完成「${title}」` },
{ type: 'api/call', data: { name: 'completeTodo', arguments: { todoId } } }
]
})
}
}
})
对应的 index.wxml:
<view class="card">
<view wx:for="{{todos}}" wx:key="todoId" class="item {{item.done ? 'done' : ''}}"
data-todo="{{item}}" bindtap="onTapTodo">
{{item.done ? '✓' : '○'}} {{item.title}}
</view>
</view>
在开发者工具里把编译模式切到「小程序 AI 编译」,在对话里输入「帮我记个待办:买牛奶」,就能看到 AI 调用 addTodo 并弹出待办清单卡片。点一下某条待办,会自动触发 completeTodo 刷新卡片。
五、为什么微信云开发和小程序 Skills 特别搭
1. 省掉一整层登录样板
原子接口跑在独立 JS 环境里,传统方案要靠 wx.login + 自建后端 code2session + token 中间件来识别用户。云函数里 cloud.getWXContext().OPENID 一行就拿到了。你写的每一行代码都是业务,没有一行是为了搞清楚用户是谁。
2. 不存在登录态过期这回事
自定义 token 会过期,过期了要刷新,刷新逻辑还得在中间件里兜底。云开发的身份是请求级别天然携带的,根本没有「登录态失效弹窗重登」这条链路,对话体验也顺很多。
3. 后端能力开箱即用
数据库、存储、定时触发器在云函数里随手就用:
const { OPENID } = cloud.getWXContext()
// 读用户专属数据,天然带权限隔离
const todos = await db.collection('todos')
.where({ _openid: OPENID })
.get()
云数据库默认按 _openid 做数据归属,放到 Skills 场景里,用户只能看到自己的数据——这种权限隔离几乎是免费的,不用额外写一层 AuthZ 校验。
六、小结
把小程序接入 AI 对话,最枯燥也最容易出错的部分,往往不是业务逻辑,而是在隔离环境里搞清楚用户是谁。
-
小程序 Skills 让你的功能能被 AI 在对话里直接调用,你只要写好「函数 + 卡片」; -
微信云开发让「识别用户」这件事不用你操心——不写中间件、不换登录态、一行 getWXContext().OPENID直达。
两者一搭,就能把精力全放在「这个 Skill 到底要帮用户做成什么事」上。
想跑通第一个例子,参考微信官方的小程序 AI 开发模式文档和示例项目 ai-mode-demo(需要用申请了开发模式的 AppID)。相关资源链接见文末附录。
附录:参考链接
-
小程序 AI 开发模式官方文档与示例项目:ai-mode-demo — https://github.com/wechat-miniprogram/ai-mode-demo -
待办清单 SKILL 模板(todolist-skill) — https://github.com/TencentCloudBase/awesome-miniprogram-skills/tree/main/skills/todolist-skill -
门店排队 SKILL 模板(queue-skill) — https://github.com/TencentCloudBase/awesome-miniprogram-skills/tree/main/skills/queue-skill -
咖啡点单 SKILL 模板(drink-skill) — https://github.com/TencentCloudBase/awesome-miniprogram-skills/tree/main/skills/drink-skill

