AI 回复
用户和 AI 机器人单聊时,机器人的回复是一条 type 为 ai 的消息,内容边生成边推送过来。小程序 SDK 的 im.ai 与 Web SDK 相同:按顺序拼接实时内容、断线后续传、重新上线时补发,回复结束后以消息正文为准。接口、字段和事件的完整说明见 Web SDK 的 AI 回复,本页说明在小程序页面中如何显示。机器人和回复由你的服务端创建,见 AI 对话。
版本
AI 回复从 @deeprespond/im-miniprogram@1.0.6 起提供,安装见概述与安装。
整理成视图
小程序不能直接渲染 Markdown。SDK 的 render.aiView(message, live) 把一条 AI 消息整理成模板可以直接使用的视图,正文转成 rich-text 的节点数组(只生成节点,不生成 HTML 字符串;链接只保留文字):
const { render } = require('@deeprespond/im-miniprogram');
const view = render.aiView(message); // 已结束:取消息正文
const live = render.aiView(message, im.ai.state(message.body.stream_id)); // 生成中:取实时内容| 字段 | 说明 |
|---|---|
stream_id、status | 流 ID;streaming、completed、stopped、failed |
streaming、thinking、waiting | 生成中;只有思考过程、还没有正文(显示“思考中”);还没有任何内容(显示“正在回复”) |
reasoning | 思考过程的文字 |
tools | 工具调用:id、title、state(running、done、failed,结束时仍在进行的为 canceled) |
blocks | 正文中的块:kind 为 text(nodes 为 rich-text 节点,last 表示最后一块,用于显示光标)、image、file(name、size、ext) |
error | 生成中断时要显示的说明;其他状态为空字符串 |
text | 复制用的纯文本 |
不是 AI 消息时返回 null。
跟随生成中的内容
消息列表中的 AI 消息是正文的快照,生成中的内容随 ai.streamUpdated 变化。用页面绑定按 stream_id 维护一份实时视图,事件触发时重新计算:
Page({
behaviors: [app.behavior],
onLoad({ key }) {
// 整理消息列表时附上正文的视图(结束后以它为准)
this.drMessageList('chat', key, { mapItem: (item) => ({ ...item, ai: render.aiView(item) }) });
this.drState('aiLive', (im) => {
const items = this.drListOf('chat')?.getSnapshot().items || [];
const live = {};
for (const m of items) {
if (m.type !== 'ai' || m.body?.status !== 'streaming') continue;
const state = im.ai.state(m.body.stream_id);
// 键加前缀:setData 的路径中纯数字的键会被当作数组下标
if (state) live['s' + m.body.stream_id] = render.aiView(m, state);
}
return live;
}, ['ai.streamStarted', 'ai.streamUpdated', 'ai.streamEnded'], { lists: ['chat'] });
},
stopAI(e) {
app.im.ai.stop(e.currentTarget.dataset.stream).catch((error) => app.report(error));
},
});只需要一条回复时,也可以用 /bind 入口的 DR_STATES.aiStream(streamId) 绑定它的实时状态(im.ai.state() 的结果,结束后为 null)。
消息循环中优先使用实时视图,结束后回到 mapItem 中由消息正文整理出的视图:
<template name="ai">
<view class="ai-msg" data-status="{{ai.status}}">
<view wx:if="{{ai.reasoning}}" class="ai-reasoning">{{ai.thinking ? '思考中…' : '已深度思考'}}</view>
<view wx:for="{{ai.tools}}" wx:for-item="tool" wx:key="id" class="ai-tool ai-tool-{{tool.state}}">{{tool.title}}</view>
<block wx:for="{{ai.blocks}}" wx:for-item="block" wx:key="index">
<view wx:if="{{block.kind === 'text'}}">
<rich-text nodes="{{block.nodes}}" user-select/><text wx:if="{{ai.streaming && block.last}}">▍</text>
</view>
<image wx:elif="{{block.kind === 'image'}}" src="{{block.url}}" mode="widthFix"/>
<view wx:elif="{{block.kind === 'file'}}">{{block.name}} {{block.size}}</view>
</block>
<view wx:if="{{ai.status === 'stopped'}}">已停止生成</view>
<view wx:if="{{ai.status === 'failed'}}">生成中断:{{ai.error}}</view>
<button wx:if="{{ai.streaming}}" size="mini" data-stream="{{ai.stream_id}}" catchtap="stopAI">停止生成</button>
</view>
</template>
<view wx:for="{{chat.items}}" wx:key="_key">
<template wx:if="{{item.type === 'ai'}}" is="ai" data="{{ai: aiLive['s' + item.body.stream_id] || item.ai}}"/>
</view>rich-text 节点带有 ai-p、ai-h、ai-list、ai-li、ai-quote、ai-code、ai-inline-code、ai-strong、ai-em、ai-link、ai-table、ai-tr、ai-th、ai-td 等 class,可以在页面样式中设置。
- 停止生成:只有接收回复的用户可以停止,已结束的回复返回当前状态,不报错。
- 重新生成:再发一次被回复的问题,机器人的服务端会开始一条新的回复。
- 断线、切到后台再回来时,SDK 自动补齐正在生成的内容。
示例
示例小程序 的聊天页已显示 AI 回复(pages/chat),包括思考过程、工具调用、Markdown 正文、文件、停止生成、复制和生成中断的提示,可以作为参考。
