# 性能优化建议
在此模式下,每一次原子接口调用都对应一段完整的「模型决策 → 端侧执行 → 结果回流 → 再次推理」往返,通常带来 1~2 秒的额外耗时。在不调整模型与提示词的前提下,合理设计原子接口即可显著降低响应时延。
本文给出四项优化手段,按改造成本由低到高排列:
| 优化手段 | 优化对象 | 前置条件 |
|---|---|---|
| 输出精简 | 每段往返的输入规模 | 无,建议所有 SKILL 均执行 |
apiCalls 链式调用 | 往返次数 | 下游接口的触发与入参可由上游返回结果确定 |
| 公共逻辑下沉为中间件 | 往返次数 | 逻辑为多接口公共前置或后置,且幂等 |
| 绑定接口合并 | 往返次数 | 两个接口必然成对且有序出现 |
其中,输出精简降低的是每段往返的输入量,后三项减少的是往返段数。建议按上述顺序逐项评估。
# 一、输出精简
# 1.1 适用场景
structuredContent 同时面向两类消费者:小程序 AI(理解与推理)与端侧(原子组件渲染、接力)。仅供端侧使用、模型决策用不到的字段——如页面跳转路径、样式与埋点参数、大段枚举码表等——应从 structuredContent 中移出。
原子接口的返回结果会作为历史消息进入后续所有推理,因此该项优化的收益不限于单次调用:它缩减的是当前会话后续每一次调用的输入规模;同时上下文增长变慢,可降低上下文压缩的触发频率,使模型更长时间保有原始对话细节。
# 1.2 字段分工
| 字段 | 是否进入模型上下文 | 用途 |
|---|---|---|
content / structuredContent / isError | 是 | 供模型理解与推理 |
handoff | 否 | 接力数据,由小程序页面通过 wx.onAgentHandoff 获取 |
字段语义详见接入方式「原子接口实现」。
# 1.3 注意事项
- 标记为
format: "page-link"的混排链接字段必须保留在structuredContent,平台需从中抽取页面路径,详见接入方式「文本混排链接」。 - 不建议将后端接口返回的数据不加筛选直接写入
structuredContent。
# 二、apiCalls 链式调用
# 2.1 适用场景
当原子接口 A 执行后大概率紧接着调用原子接口 B(例如过半的 A 调用后紧跟 B),且 B 是否触发、入参取值均可由 A 的返回结果确定时,应在 A 的返回值中携带 apiCalls,由框架直接发起 B 的调用,跳过其间的一段模型推理。
# 2.2 声明方式
// 接口 A 的返回值
{
isError: false,
content: [{ type: 'text', text: '查询完成' }],
apiCalls: [{ name: 'B', arguments: { id: result.id } }]
}
# 2.3 示例
仅当天气查询结果中包含台风信息时才需续调台风接口,触发条件完全由返回结果决定:
// getDailyWeatherForecast 的返回处理
const result = await fetchDailyForecast(location, days)
const resp = {
isError: false,
content: [{ type: 'text', text: summarize(result) }],
structuredContent: result,
}
// 仅在返回中包含台风信息时下发 apiCalls
if (result.typhoonWarning != null) {
resp.apiCalls = [{
name: 'getTyphoonWarning',
arguments: { location, typhoonName: result.typhoonWarning.name },
}]
}
return resp
# 2.4 注意事项
- 按条件下发。仅在返回结果满足条件时携带
apiCalls,不应无条件下发。 - 依赖用户自由选择的下游接口不适用。B 的触发必须能由 A 的返回结果确定性推出。
apiCalls不等同于接口合并。A、B 仍是两个独立接口,其他链路仍可单独调用 B。
# 三、公共逻辑下沉为中间件
# 3.1 适用场景
同时满足以下全部条件的接口,应从 mcp.json 的原子接口列表中移除,改为通过 skill.use() 注册为中间件:
- 不构成独立的业务目标,用户不会专门为其发起请求;
- 其返回值仅作为后续调用的门禁或上下文,如登录态、定位、隐私授权、统一展示卡片;
- 是 SKILL 内多数接口的公共前置或公共后置逻辑。
保留为原子接口时,模型每轮都需先决策并调用一次,额外引入一段完整往返。
# 3.2 示例
const skill = wx.modelContext.createSkill(skillPath)
skill.use(async (ctx, next) => {
// 前置逻辑:每次原子接口调用前自动执行
const token = wx.getStorageSync('token')
if (!token) {
const { code } = await wx.login()
const res = await wx.request({ url: '.../login', data: { code } })
wx.setStorageSync('token', res.data.token)
}
try {
await next() // 执行实际的原子接口
} finally {
// 后置逻辑:日志上报、统一展示等
}
})
中间件的完整语义详见接入方式「中间件机制」。
# 3.3 注意事项
- 中间件须保持幂等、无副作用,其在每次原子接口调用前后均会执行。
- 整个中间件链与原子接口执行时间共享同一超时上限,中间件内不应引入长耗时逻辑。
- 登录与授权场景的处理方式,另见用户登录与授权场景处理建议。
# 四、绑定接口合并
# 4.1 适用场景
两个原子接口必然成对出现且调用顺序固定时,应合并为一个接口。
# 4.2 示例
以一段输入前置处理为例:原设计包含 checkContent(内容安全校验与登录检查)与 intentRecognition(意图识别)两个原子接口,两者入参均为用户原始输入,且逻辑上中始终成对出现且不会单独被调用。此类绑定关系应合并为单一接口:
// 合并前:每轮用户输入,模型需连续调用两次
// checkContent({ content, business }) -> intentRecognition({ query })
// 合并后:一次调用同时返回校验结果与识别意图
skill.registerAPI('preprocess', async ({ content, business }) => {
const check = await checkContentSafety(content) // 原 checkContent 的逻辑
if (!check.passed) {
return {
isError: false,
content: [{ type: 'text', text: check.reason }],
structuredContent: { passed: false },
}
}
if (check.needLogin) {
// 未登录分支保持原有处理:由 apiCalls 直连登录卡片,不经模型推理
return {
isError: false,
content: [{ type: 'text', text: '请先登录' }],
structuredContent: { passed: false, needLogin: true },
apiCalls: [{ name: 'authLogin', arguments: { originQuery: content } }],
}
}
const intent = await recognizeIntent(content) // 原 intentRecognition 的逻辑
return {
isError: false,
content: [{ type: 'text', text: '前置校验通过' }],
structuredContent: { passed: true, needLogin: false, intent },
}
})
合并后,每轮用户输入由 2 次接口调用(2 段模型推理)减少为 1 次。
# 4.3 不适用的情形
- 链路中间存在用户决策点,如选择门店、选择商品、确认订单。合并将剥夺用户的选择权。
- 仅为高频共现而非绑定关系。此类场景应使用
apiCalls,不应强行合并。