Bot Framework恢复对话时抛出TypeError:无法对已撤销代理执行'set'
问题描述
我正在调试Microsoft Teams Bot Framework的bot-conversation示例,需求是通过WebSocket将用户消息发送至后端,收到后端响应后向聊天发送消息。
当前机器人核心实现代码:
class TeamsConversationBot extends TeamsActivityHandler { ws = null; constructor(adapter) { super(); this.adapter = adapter; this.onMessage(async (context, next) => { // 用户消息发往后端,这部分正常工作 TurnContext.removeRecipientMention(context.activity); await context.sendActivity({ type: 'typing' }); const text = context.activity.text.trim(); await this.handleUserPrompt(context, text); await next(); }); this.ws = new WebSocket('wss://.../dev'); this.ws.on('message', async (data) => { // 后端消息发给用户 console.log('\nKhydra->Teams: ', data.toString('utf8')); const messageData = JSON.parse(data); await this.handleKhydraFragment(messageData); }); }
当收到后端包含textBlock的响应时,我尝试通过以下代码恢复对话并发送消息:
async handleKhydraFragment(messageData) { const fragment = messageData.fragment; if ('textBlock' in fragment) { const claimsIdentity = await this.adapter.createClaimsIdentity(process.env.MicrosoftAppId); await this.adapter.continueConversationAsync(claimsIdentity, messageData.conversationReference, async (context) => { context.sendActivity(fragment.textBlock); }); } else { console.log(fragment); } }
奇怪的是,textBlock内容能成功发送到聊天,但随即抛出错误:
E:\Source\Microsoft-Teams-Samples\samples\bot-conversation\nodejs\node_modules\botbuilder-core\lib\turnContext.js:392 this.responded = true; ^ TypeError: Cannot perform 'set' on a proxy that has been revoked at Proxy.<anonymous> (E:\Robora\Source\Microsoft-Teams-Samples\samples\bot-conversation\nodejs\node_modules\botbuilder-core\lib\turnContext.js:392:36) at Generator.next (<anonymous>) at fulfilled (E:\Robora\Source\Microsoft-Teams-Samples\samples\bot-conversation\nodejs\node_modules\botbuilder-core\lib\turnContext.js:9:58) at process.processTicksAndRejections (node:internal/process/task_queues:95:5) Node.js v18.15.0
请问我遗漏了什么?
解决方案
这个错误的核心原因是:context.sendActivity是异步操作,但你没有用await等待它执行完成,导致continueConversationAsync创建的上下文代理在异步操作结束前就被系统撤销,后续代理对象的属性赋值(比如this.responded = true)就会失败。
第一步:修复异步操作等待问题
修改handleKhydraFragment中的回调函数,给context.sendActivity添加await关键字,确保异步操作完成后再结束上下文:
async handleKhydraFragment(messageData) { const fragment = messageData.fragment; if ('textBlock' in fragment) { const claimsIdentity = await this.adapter.createClaimsIdentity(process.env.MicrosoftAppId); await this.adapter.continueConversationAsync(claimsIdentity, messageData.conversationReference, async (context) => { await context.sendActivity(fragment.textBlock); // 添加await等待发送完成 }); } else { console.log(fragment); } }
第二步:优化对话引用与身份验证(可选但推荐)
你当前手动创建claimsIdentity的方式存在潜在风险,正确的做法是在用户首次发送消息时,把完整的对话引用(包含身份信息)发送给后端,后续直接使用后端返回的对话引用即可:
- 在处理用户消息的
handleUserPrompt中,获取完整对话引用并发送给后端:
async handleUserPrompt(context, text) { const conversationRef = TurnContext.getConversationReference(context.activity); // 将conversationRef和text一起通过WebSocket发送给后端 this.ws.send(JSON.stringify({ text, conversationReference: conversationRef })); }
- 在
handleKhydraFragment中,直接使用后端返回的对话引用,无需手动创建身份:
async handleKhydraFragment(messageData) { const fragment = messageData.fragment; if ('textBlock' in fragment) { // 使用微软应用ID和后端返回的对话引用恢复对话 await this.adapter.continueConversationAsync(process.env.MicrosoftAppId, messageData.conversationReference, async (context) => { await context.sendActivity(fragment.textBlock); }); } else { console.log(fragment); } }
这样既解决了代理被撤销的错误,也保证了身份验证的正确性,避免了手动创建身份可能带来的权限问题。
内容的提问来源于stack exchange,提问作者Basic
相关产品推荐
相关产品推荐

