You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

Azure Bot Framework SDK v4 Node.js接入人工客服REST API故障求助

问题分析与解决方案

首先得明确:Waterfall 对话流程并不适合这种需要持续双向通信+定时轮询的人工客服集成场景。Waterfall 是线性、步骤式的流程,每次 replaceDialogendDialog 都会重置或终止流程,没办法实现持续监听人工客服消息、同时接收用户输入的需求。

下面是针对你的场景重新设计的实现方案,核心思路是用对话状态维护会话生命周期+定时轮询拉取客服消息+持续接收用户输入


第一步:定义会话状态(维护人工聊天的核心状态)

首先需要在对话中保存人工会话的激活状态、轮询定时器ID等关键信息,避免会话中断后丢失状态:

const { ComponentDialog, TextPrompt, DialogSet, DialogTurnStatus } = require('botbuilder-dialogs');
const { ConversationState, MemoryStorage } = require('botbuilder');

class LiveAgentDialog extends ComponentDialog {
    constructor(dialogId, conversationState) {
        super(dialogId);
        this.conversationState = conversationState;
        // 创建会话状态属性,用来保存人工聊天的状态
        this.liveChatState = this.conversationState.createProperty('LiveChatState');
        
        // 添加文本输入提示器
        this.addDialog(new TextPrompt('textPrompt'));
        
        // 设置对话的起始逻辑
        this.initialDialogId = dialogId;
    }

    // 对话启动时的逻辑
    async beginDialog(turnContext, options) {
        const state = await this.liveChatState.get(turnContext, {
            isActive: false,
            pollIntervalId: null
        });

        // 启动人工客服轮询(仅在会话未激活时)
        if (!state.isActive) {
            state.isActive = true;
            // 每3秒轮询一次客服消息(可根据API限制调整间隔)
            state.pollIntervalId = setInterval(async () => {
                await this.pullAgentMessages(turnContext, state);
            }, 3000);
            await turnContext.sendActivity('正在为您连接人工客服,请稍候...');
        }

        // 等待用户输入消息
        return await this.prompt('textPrompt', '请输入您的问题:');
    }

    // 处理用户后续输入的逻辑
    async continueDialog(turnContext) {
        const state = await this.liveChatState.get(turnContext);
        if (!state.isActive) {
            return await this.endDialog(turnContext);
        }

        // 发送用户消息给人工客服API
        const userMessage = turnContext.activity.text;
        const sendResult = await sendChatMessage(userMessage);
        if (sendResult !== 'success') {
            await turnContext.sendActivity(`消息发送失败:${sendResult}`);
        }

        // 继续等待用户输入
        return await this.prompt('textPrompt', '');
    }

    // 定时拉取人工客服消息的核心函数
    async pullAgentMessages(turnContext, state) {
        try {
            const agentMessages = await getLiveAgentMessages();
            if (agentMessages.length > 0) {
                for (const msg of agentMessages) {
                    switch(msg.type) {
                        case 'ChatRequestSuccess':
                            await turnContext.sendActivity("人工客服请求已受理。");
                            break;
                        case 'ChatEstablished':
                            await turnContext.sendActivity("已成功连接人工客服,您可以开始对话了!");
                            break;
                        case 'ChatMessage':
                            await turnContext.sendActivity(`客服:${msg.message.text}`);
                            break;
                        case 'ChatEnded':
                            await turnContext.sendActivity("人工会话已结束,感谢您的咨询!");
                            // 清理定时器,终止会话
                            clearInterval(state.pollIntervalId);
                            state.isActive = false;
                            await this.endDialog(turnContext);
                            break;
                        default:
                            await turnContext.sendActivity(`收到系统消息:${msg.type}`);
                            break;
                    }
                }
                // 保存更新后的会话状态
                await this.liveChatState.set(turnContext, state);
                await this.conversationState.saveChanges(turnContext);
            }
        } catch (err) {
            console.error('拉取客服消息出错:', err);
            await turnContext.sendActivity("暂时无法获取客服消息,请稍后再试。");
        }
    }

    // 对话结束时清理资源
    async endDialog(turnContext) {
        const state = await this.liveChatState.get(turnContext);
        if (state.pollIntervalId) {
            clearInterval(state.pollIntervalId);
        }
        state.isActive = false;
        await this.liveChatState.set(turnContext, state);
        await this.conversationState.saveChanges(turnContext);
        return super.endDialog(turnContext);
    }
}

为什么你的Waterfall方案会失败?

  1. 线性流程无法适配持续通信:Waterfall是按步骤执行的,每次replaceDialog都会重启流程,没办法同时保持轮询和接收用户输入的状态。
  2. 状态丢失风险:没有用对话状态保存人工会话的激活状态,流程跳转后无法恢复之前的轮询任务。
  3. 轮询逻辑无法持续运行:原代码中的轮询只在liveAgentMsg步骤执行一次,流程跳转后就会停止,无法实现定时拉取。

关键注意事项

  • 轮询间隔设置:根据人工客服API的限流规则调整轮询间隔,避免因频繁调用被封禁。
  • 资源清理:一定要在会话结束(比如ChatEnded、用户主动退出)时清理定时器,防止内存泄漏。
  • 错误处理:给API调用添加try-catch,避免因网络问题或API异常导致整个对话崩溃。
  • 主动消息兼容性:如果需要在非对话轮次(比如定时器触发时)发送消息,确保turnContext是有效的,或者使用Bot Adapter的continueConversation方法创建上下文。

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

火山引擎 最新活动