You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Microsoft Bot Builder中TurnContext在WebSocket监听内触发代理撤销错误

问题:WebSocket响应无法在Bot Builder模拟器中展示

我想把WebSocket服务器返回的响应展示在Microsoft Bot Builder模拟器里,但在ws.addEventListener监听函数里遇到了问题。调试发现调用await turnContext.sendActivity(responseMessage)时,TurnContext丢失了作用域,没法向模拟器返回响应。控制台能收到WebSocket的响应,但触发了如下错误:

responseMessage :- BOT : To speak to an agent, press or say #
Insise Error
TypeError: Cannot perform 'get' on a proxy that has been revoked
at WebSocket. (D:\azure-bot\echo-bot\bots\stateManagementBot.js:155:59)

相关代码片段

ws.addEventListener('message',async function (event) {
    const msg = JSON.parse(event.data);
    if(msg.topic === 'aws/chat' && msg.contentType === 'application/json'){
        const content = JSON.parse(msg.content);
        switch(content.Type){
            case 'MESSAGE':
                try{
                      if(content.ParticipantRole !== 'CUSTOMER'){
                        responseMessage = `${content.DisplayName} : ${content.Content}`;
                        console.log(`responseMessage :- ${responseMessage}`);
                        await turnContext.sendActivity(responseMessage);
                    }
                }catch(error){
                    console.log("Insise Error");
                    console.log(error);
                }
                break;
            case 'Event':
                if(content.ContentType.includes('ended')){
                    responseMessage = `Chat ended`;
                }
        }
    }
});

完整代码片段

// The accessor names for the conversation data and user profile state property accessors.
const CONVERSATION_DATA_PROPERTY = 'conversationData';
const USER_PROFILE_PROPERTY = 'userProfile';

let responseMessage;
let connectionToken;
let ws;

class StateManagementBot extends ActivityHandler {
    constructor(conversationState, userState) {
        super();
    // Create the state property accessors for the conversation data and user profile.
    this.conversationDataAccessor = conversationState.createProperty(CONVERSATION_DATA_PROPERTY);
    this.userProfileAccessor = userState.createProperty(USER_PROFILE_PROPERTY);

    // The state management objects for the conversation and user state.
    this.conversationState = conversationState;
    this.userState = userState;

    this.onMessage(async (turnContext, next) => {
        // Get the state properties from the turn context.
        // By calling next() you ensure that the next BotHandler is run.await next()
        
        if(turnContext.activity.text === '#'){
            const url = await this.connectToAgent(turnContext.activity.text);
            const initialMessage = {
                topic: "aws/subscribe",
                content: {
                topics: ["aws/chat"]
                }
            };
            console.log(url[1]);
            ws = new WebSocket(url[1]);
            console.log('Before Websocket....');
            ws.addEventListener("open", () => {
                console.log('Websocket....');
                ws.send(JSON.stringify(initialMessage));
            });
            console.log("Websocket Conn...");
            const param = {
                ConnectionToken: connectionToken, /* required */
                Content: turnContext.activity.text, /* required */
                ContentType: 'text/plain', /* required */
            };
            connectparticipant.sendMessage(param, function(err, data) {
                console.log('Sending....');
                if (err) console.log(err, err.stack); // an error occurred
                else     console.log(data);           // successful response
            });

            ws.addEventListener('message',async function (event) {
                const msg = JSON.parse(event.data);
                if(msg.topic === 'aws/chat' && msg.contentType === 'application/json'){
                    const content = JSON.parse(msg.content);
                    switch(content.Type){
                        case 'MESSAGE':
                            try{
                                  if(content.ParticipantRole !== 'CUSTOMER'){
                                    responseMessage = `${content.DisplayName} : ${content.Content}`;
                                    console.log(`responseMessage :- ${responseMessage}`);
                                    await turnContext.sendActivity(responseMessage);
                                }
                            }catch(error){
                                console.log("Insise Error");
                                console.log(error);
                            }
                            break;
                        case 'Event':
                            if(content.ContentType.includes('ended')){
                                responseMessage = `Chat ended`;
                            }
                    }
                }
            });
            
        }
        // By calling next() you ensure that the next BotHandler is run.
        await next();
    });

解决方案

问题根源在于:TurnContext是当前消息处理周期的临时对象,周期结束后会被系统回收(代理被撤销)。而WebSocket的message事件是异步触发的,此时原TurnContext已经失效,无法再用来发送消息。需要通过保存对话引用,发起新的对话轮次来发送消息。

具体修改步骤:

  1. 在Bot类中添加必要属性
    修改构造函数,传入adapter引用,并创建Map存储WebSocket与对话引用的映射,避免全局变量污染和多用户冲突:

    class StateManagementBot extends ActivityHandler {
        constructor(conversationState, userState, adapter) {
            super();
            this.adapter = adapter; // 保存adapter实例
            this.activeConversations = new Map(); // 存储活跃对话的引用
            
            // 原构造函数中的其他代码
            this.conversationDataAccessor = conversationState.createProperty(CONVERSATION_DATA_PROPERTY);
            this.userProfileAccessor = userState.createProperty(USER_PROFILE_PROPERTY);
            this.conversationState = conversationState;
            this.userState = userState;
        }
    }
    

    注意:创建Bot实例时要传入adapter参数。

  2. 保存对话引用
    在onMessage处理函数中,当用户发送#时,提取并保存对话引用:

    this.onMessage(async (turnContext, next) => {
        if(turnContext.activity.text === '#'){
            // ... 原有的WebSocket连接代码
            
            // 保存对话引用
            const conversationReference = TurnContext.getConversationReference(turnContext.activity);
            this.activeConversations.set(ws, conversationReference);
            
            // ... 其他代码
        }
        await next();
    });
    
  3. 修改WebSocket消息回调
    在回调中使用保存的对话引用,通过adapter.continueConversation发起新的对话轮次来发送消息:

    ws.addEventListener('message', async (event) => {
        const msg = JSON.parse(event.data);
        if (msg.topic === 'aws/chat' && msg.contentType === 'application/json') {
            const content = JSON.parse(msg.content);
            switch (content.Type) {
                case 'MESSAGE':
                    try {
                        if (content.ParticipantRole !== 'CUSTOMER') {
                            const responseMessage = `${content.DisplayName} : ${content.Content}`;
                            console.log(`responseMessage :- ${responseMessage}`);
                            
                            // 获取保存的对话引用
                            const conversationReference = this.activeConversations.get(ws);
                            // 发起新的对话轮次发送消息
                            await this.adapter.continueConversation(conversationReference, async (newTurnContext) => {
                                await newTurnContext.sendActivity(responseMessage);
                            });
                        }
                    } catch (error) {
                        console.log("Inside Error");
                        console.log(error);
                    }
                    break;
                case 'Event':
                    if (content.ContentType.includes('ended')) {
                        const responseMessage = `Chat ended`;
                        const conversationReference = this.activeConversations.get(ws);
                        await this.adapter.continueConversation(conversationReference, async (newTurnContext) => {
                            await newTurnContext.sendActivity(responseMessage);
                        });
                        // 清理资源
                        this.activeConversations.delete(ws);
                        ws.close();
                    }
                    break;
            }
        }
    });
    
  4. 清理全局变量
    将原代码中的全局变量ws、responseMessage、connectionToken改为类的属性,避免多用户场景下的冲突:

    class StateManagementBot extends ActivityHandler {
        constructor(conversationState, userState, adapter) {
            super();
            // ... 其他属性
            this.connectionToken = null;
        }
    }
    

内容的提问来源于stack exchange,提问作者Mohammed Tariq

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.25 14:45:42