对话机器人 Rasa(四十二):SocketIO Channel 的 Room 问题

更新日期: 2025-06-25 阅读次数: 35 字数: 1147 分类: AI

之前基于 SocketIO 实现了一个 Rasa 的自定义 Channel,参考 对话机器人 Rasa(三十二):新建一个基于 socket.io 的自定义 channel

但是,实现的第一版有一个问题,就是当客户端连接时,默认会收到所有在线设备的消息。

从现象上看,应该是 socketio emit 时没有指定 room,导致所有在线的客户端都能收到消息。

参考 socketio 的文档,emit 时可以指定 to/room 参数来限制消息的接收者。

https://python-socketio.readthedocs.io/en/v4/api.html

async emit(event, data=None, to=None, room=None, skip_sid=None, namespace=None, callback=None, **kwargs)

Emit a custom event to one or more connected clients.

参数说明:

  • event – The event name. It can be any string. The event names 'connect', 'message' and 'disconnect' are reserved and should not be used.
  • data – The data to send to the client or clients. Data can be of type str, bytes, list or dict. To send multiple arguments, use a tuple where each element is of one of the types indicated above.
  • to – The recipient of the message. This can be set to the session ID of a client to address only that client, or to to any custom room created by the application to address all the clients in that room, If this argument is omitted the event is broadcasted to all connected clients. 注意,不传 to 参数时,默认会广播到所有连接的客户端。
  • room – Alias for the to parameter.

rasa 的默认 socketio 实现

rasa 自带的 socketio 的实现,emit 时,指定了 room,所以只会收到指定房间的消息。

~/miniforge3/envs/rasa/lib/python3.9/site-packages/rasa/core/channels/socketio.py

原代码:

async def _send_message(self, socket_id: Text, response: Any) -> None:
    """Sends a message to the recipient using the bot event."""

    await self.sio.emit(self.bot_message_evt, response, room=socket_id)

而我为了方便测试,把 room 参数去掉了。

enter_room 的问题

但根本的问题在于,我的实现,没有将每个连接的用户加入到对应的房间。 也就是没有调用 enter_room 方法。

所以,只需要在 handle_message 方法中,加入以下代码即可:

await self.sio.enter_room(sid, data["sender"])

socketio server 端,是否可以将一个 sid 反复多次进入同一个 room

在 Socket.IO 服务器端,同一个 sid(客户端连接 ID)可以多次加入同一个房间(room),但实际效果等同于单次加入,不会产生重复成员关系

代码中的 emit session_confirm 有什么用

emit("session_confirm", data["session_id"], room=sid)

作用是:

当客户端通过 session_request 事件请求会话时,服务端会生成或确认一个 session_id,然后通过 session_confirm 事件把这个 session_id 发送回给当前连接的客户端(即 sid 对应的 socket 连接)。这样客户端就能拿到自己的 session_id,用于后续通信(如加入房间、消息同步等)。

流程:

  • 客户端发起 session_request。
  • 服务端生成/确认 session_id,让 sid 加入该 room。
  • 服务端通过 session_confirm 把 session_id 发给客户端。
  • 客户端收到 session_id,后续通信可带上该 id,实现多端同步。

这样可以保证每个连接都能拿到唯一的会话标识,实现消息的定向推送和隔离。

额外发现

其实,把用户的输入,直接 emit 也是可以的。 这样就能实时看到对方的输入,而不需要等待从回复信息里解析输入。

线上日志报错

docker compose logs -f --tail=100 rasa_core

具体错误信息:

await self.sio.enter_room(sid, data["sender"])
TypeError: object NoneType can't be used in 'await' expression

https://github.com/miguelgrinberg/python-socketio/issues/1256

应该是 socketio 这个库的版本不同,enter_room 在旧版本中是 async 的,但是在新版本中不是 async 的。

所以,文档里是对的。

因为低版本的,如果不加 await 不会报错,但是会导致并没有加入房间,于是收不到消息。 而高版本,加了 await 就会报错。

所以,在使用 await 之前,判断一下 enter_room 是否返回 None。

查看合集

📖 对话机器人 Rasa 中文系列教程

微信关注我哦 👍

大象工具微信公众号

我是来自山东烟台的一名开发者,有感兴趣的话题,或者软件开发需求,欢迎加微信 zhongwei 聊聊, 查看更多联系方式