乐于分享
好东西不私藏

微信AI官方文档里的MCP和Skill:7个容易踩的坑

微信AI官方文档里的MCP和Skill:7个容易踩的坑

昨天微信小程序AI发布了官方最佳实践文档,讲的是一件事:微信AI怎么调用你的小程序,你的小程序应该怎么写能力声明和Skill封装,才能被正确调用。

读完之后发现,里面踩过的坑远不止适用于微信。我按“腾讯原文 → 微信AI场景 → 通用场景”三层拆了一遍,所有在搓Skill、做Agent的人都可以对照看看。


01先搞清楚架构:微信是两层的,大多数平台是一层的

微信的架构比较特殊,它把“能力声明”和“流程编排”拆成了两个文件:

微信AI场景:

小程序通过mcp.json向微信AI声明自己有哪些能力——接口名称、功能描述、参数定义、参数取值来源。微信AI读到mcp.json后,决定“选不选这个接口、怎么填参数”。选中之后,微信AI用SKILL.md来理解“多个接口之间怎么串起来”——调用顺序、依赖关系、跨接口的约束规则。调用完成后,小程序接口返回content,告诉微信AI“刚才调用的结果是什么”和“下一步该做什么”。

简单说:mcp.json管单个接口怎么用,SKILL.md管多个接口怎么编排,content管当前进度。

通用Skill架构(主流):

大多数Agent平台不是mcp.json + SKILL.md两文件分开。而是一个Skill文件,内部有明确的分工——Skill的触发描述(description/触发词/元数据)告诉Agent“这个Skill是干什么的、什么时候该调用它”;Skill的正文(body/指令)告诉Agent“具体怎么做、多个步骤怎么串起来”。

微信把这两件事拆成了两个文件,但本质上的分工是一样的:声明层负责“选不选你”,编排层负责“怎么执行”。

理解了这个分工,后面的坑就好懂了。


02坑1:「这个能力做什么」写在了编排层,而不是声明层

腾讯原文:

把“接口本身的功能”写到SKILL.md:长文膨胀、与mcp.json易不一致。SKILL.md中的接口清单只写“前置条件+上下游关系”。

微信AI场景:

mcp.json里接口描述(description)的注意力权重是★★★★,SKILL.md只有★★★。你把接口功能写到SKILL.md里,微信AI在做“选不选这个接口”的决策时读的是mcp.json,根本看不到这些信息。你以为写清楚了,但AI还是选错接口或填错参数。

通用场景:

这个坑在Agent→Skill架构中同样成立,只是表现形式不同。

大多数Skill的正文可以写得非常长——几千行也不稀奇。但Agent在决定“要不要调你这个Skill”时,优先读取的是你Skill的触发描述(description/触发词/frontmatter),不是正文第300行的某句话。

很多Skill编写者把“我到底是干什么的”只写在正文里,触发描述只写了一个很模糊的标签。结果就是Agent在路由决策时看到“这是一个处理数据的Skill”——它不会往下翻几千行去找你具体是处理什么数据的。它直接选了别的Skill。

正确做法: 不管你的平台是mcp.json还是Skill触发描述,Agent做“选不选你”决策时读的那块地方,必须写清楚你具体是干什么的。正文/编排层只写“多个步骤怎么串起来”,不重复声明层已有的信息。


03坑2:跨步骤的规则写在了单个步骤里

腾讯原文:

把“跨多接口的业务规则”写到单个接口的description:仅在调用该接口时生效,其他接口决策时模型读不到。应写在SKILL.md。

微信AI场景:

你在接口A的description里写了“订单支付前必须先确认收货地址”。微信AI调用接口B时读的是接口B的description,这条规则根本不在它的视野里。

通用场景:

在Agent→Skill架构中,很多Skill内部会调用多个子Skill或工具。你在子Skill A的触发描述里写了一条“必须先检查用户权限”,Agent在执行子Skill B时,读的是子Skill B的触发描述,不会回头去翻子Skill A写了什么。

规则的生效范围等于它所在的那个信息块。放在子步骤/子Skill的描述里,只在那一步生效。所有需要跨步骤、跨子Skill生效的约束——调用顺序、互斥关系、前置条件——必须放在编排层(微信的SKILL.md,通用Skill的正文流程部分)。

正确做法: 单步骤的规则写在该步骤的声明里。跨步骤的规则写在编排层。判断标准很简单——“这条规则,在执行其他步骤时Agent需要知道吗?”如果需要,就往上提一层。


04坑3:返回内容里写了「我的功能是…」

腾讯原文:

把“接口功能描述”写到content:content只承载本次调用结果与下一步动作,功能描述属于description。

微信AI场景:

content注意力权重是★★★★★,微信AI会把它当作“事实”读取。你在content里写“本接口用于搜索饮品”,微信AI可能理解为“刚才搜索的结果是饮品”,而不是“这个接口的功能是搜索饮品”,引发误判。

通用场景:

这个坑是跨平台通用的。Agent调用Skill/工具后的返回内容,在所有信息源里权重最高,Agent会把它当事实直接采纳。

你在返回里混入了不该在那里的信息——功能说明、免责声明、历史背景——Agent就可能误解当前状态。比如你在一个查询返回里写“本接口每分钟更新一次”,Agent可能理解为“当前数据是一分钟前的旧数据”,而不是“这是最新数据,只是界面每分钟刷新一次”。

正确做法: 返回内容只写两件事——本次调用的客观结果 + 下一步建议动作。功能说明、使用边界、免责声明,放在声明层。不要让Agent在“读结果”的时候被迫做“理解功能”的判断。


05坑4:返回内容只说「下一步做什么」没说「刚才发生了什么」

腾讯原文:

content应先陈述本次返回的客观状态,再给出下一步动作。仅有动作没有事实时,模型可能把“展示卡片”理解为“准备调下一步接口”而跳过等待用户确认。

腾讯反例:「接下来请务必为用户展示订单确认卡片。」

腾讯推荐:「已根据所选规格生成订单。请展示订单确认卡片,并用一句话引导用户核对后下单。」

微信AI场景:

微信AI读到“请展示订单确认卡片”,理解为“展示完就可以继续了”,直接往下走,不等用户确认。

通用场景:

所有Agent开发者的经典问题:Agent不知道“当前停在哪一步”。

很多工具返回只写了“下一步做什么”,没写“刚才发生了什么”。Agent缺少状态锚点,会一直往前跑。只有当你明确告诉它“当前状态是等待用户确认”,它才会停下来。而“当前状态”需要用一个客观事实来锚定——“订单已生成”。

在Cursor、Claude Code这类场景中,这个问题表现为Agent执行完一个操作后直接跳到下一个,中间没有给开发者检查的机会。

正确做法: 返回内容永远先说事实——“已经完成了什么”——再说动作——“下一步做什么”。先给状态锚点,再给导航指令。


06坑5:说了「不许做什么」但没说「应该改做什么」

腾讯原文:

只写“不要做X”而不写“应做什么”,模型会缺少出口。每条禁令都应配一个明确的替代动作。

腾讯案例(搜索结果为空时,返回内容应包含三件事):

  1. 陈述事实:未匹配到「圣诞限定款」
  2. 给出出口:告诉用户“未找到”,引导在热销饮品中挑选
  3. 指出禁令:不要再以相同关键词重复调用此接口

微信AI场景:

你只写了“不要再重复调用”,微信AI不知道该做什么,可能卡住或乱试别的接口。

通用场景:

跨平台通用原则:Agent不能只被告诉“路是死的”,必须同时被告诉“活路在哪”。

很多Skill编写者在写错误处理时,花了大量篇幅描述“什么不能做”,但对“应该改做什么”一笔带过。Agent不是人类,不会在被拒绝后自己想办法。它只会在“可做的选项”里选一个。你没给替代选项,它就停在原地。

正确做法: 每一条“不要做X”必须紧跟一条“应该做Y”。适用于返回内容的错误处理,也适用于Skill正文里的约束描述。


07坑6:参数没有说明「值从哪里来」

腾讯原文:

业务ID容易被按格式凑出,需在description中显式声明来源。

腾讯反例: "drinkId": { "description": "饮品 ID" }

腾讯推荐: "drinkId": { "description": "饮品唯一标识,取自上游接口searchDrinks或getRecommendedDrinks返回的drinkId原值。不要从用户自然语言推断,也不要使用示例值。上下文无可用drinkId时,应先调searchDrinks。" }

微信AI场景:

用户说“那个拿铁”,微信AI不知道drinkId,自己编一个字符串填进去,接口收到不存在的ID直接报错。

通用场景:

跨平台通用原理:Agent会对所有“看起来像必须参数”的字段产生编造冲动。

在Skill架构中,当一个Skill内部编排了多个子Skill/工具时,上游返回的结果里有一个id字段,下游需要传这个id。如果你在下游的参数说明里只写了“用户ID”,没有声明“这个值必须来自上一步X接口返回的userId字段”,Agent在上下文里找不到值的时候就会自己编一个。

这个问题极其隐蔽——编出来的值往往格式正确,不会立刻暴露,而是在后续环节炸掉。

正确做法: 在下游步骤的参数说明里,显式声明三件事——值从哪个上游步骤/接口的哪个字段来、不能从用户自然语言推断、上下文里没有可用值的时候应该先执行哪个步骤来获取。这些约束必须写在该参数的声明位置(微信的字段级description,通用Skill的步骤参数说明),写在编排层正文里权重不够。


08坑7:参数举例只给了一个样本

腾讯原文:

举例时给多个不同样本(避免被当默认值),并配明确的缺省处理。单一举例容易被模型当作“标准答案”——用户只说“想喝点什么”时,可能直接照搬该例子填入。

腾讯反例: "keyword": { "description": "饮品关键词,如『拿铁』" }

腾讯推荐: "keyword": { "description": "饮品关键词,例如『拿铁』『美式』『奶茶』。用户未说出具体饮品时,不要填写本字段,应改走饮品推荐接口。" }

微信AI场景:

只举了“拿铁”一个例子,用户说“想喝点什么”,微信AI直接把“拿铁”填进去搜了。

通用场景:

跨平台通用原理:Agent把示例当成“默认值”而不是“参考样本”。

很多Skill编写者在触发描述、参数说明、步骤示例里只给一个例子。Agent在没有明确输入时,直接复用这个例子。这在所有Agent平台上都成立——Prompt里的单一样本天然具有“首选项”效应,Agent在不确定时倾向于选第一个看到的选项。

正确做法: 给至少2-3个不同方向的样本,并且明确写“无明确输入时不要填写本参数,应改走其他路径”。用多样性稀释首选项效应。


09附:三层信息源的注意力权重

微信文档给出的三层信息源注意力权重分级,本质是一套“信息放在哪里才有效”的判断标准。不只是微信AI,所有Agent都可以套用这个思路:

★★★★★ 返回内容——离当前决策点最近,Agent当作“事实”直接采纳。只能放执行结果和下一步动作,不能放功能说明。

★★★★ 声明层(微信的mcp.json,通用Skill的触发描述/参数说明)——Agent用来判断“选不选你”和“参数怎么填”。只放你自己这个步骤/接口的信息,跨步骤的规则在这里不生效。

★★★ 编排层(微信的SKILL.md,通用Skill的正文/流程部分)——离决策点最远,适合放跨步骤的流程规则。单步骤的功能描述不该放在这里,Agent选步骤时优先读的是声明层。

普适原则:离Agent当前决策点越近的信息,权重越高。把每个约束写在Agent在该决策点会实际读取的位置,而不是你觉得“最方便”的位置。


参考来源

微信小程序AI最佳实践文档 → developers.weixin.qq.com/miniprogram/dev/ai/best-practices.html