Workerman 无法靠连接生命周期管理对话状态,因为 $connection 仅对应单次 TCP/WebSocket 连接,而用户刷新、重连或多端登录会创建新连接,导致依赖连接的状态丢失;必须将状态存于 Redis 等共享存储,以 session_id 为 key,并每次交互动态更新 TTL,确保跨连接延续与及时清理。

Workerman 本身不提供对话状态管理能力,多轮对话必须自己实现状态机;直接在 onMessage 里写 if-else 处理上下文,很快就会失控。
为什么不能靠 Workerman 自带连接生命周期管理对话状态?
Workerman 的每个 $connection 只代表一次 TCP/WebSocket 连接,不是一次会话。用户刷新页面、网络重连、多端登录都会触发新连接,但对话状态(比如“正在填发票抬头”)必须跨连接延续。如果把状态存在 $connection 上,一断线就丢,用户回来就得从头问起。
- 状态必须脱离连接实例,存到共享存储(如 Redis)中,key 建议用
"dialogue:{$session_id}" -
$session_id不能依赖uniqid(),得由业务层生成并透传(例如 URL 参数或首次 handshake 携带) - 每次
onMessage都要先查 Redis 加载当前状态,再决定走哪个状态节点
怎么设计轻量级状态机结构?
推荐用数组定义状态转移表,而不是引入 FSM 类库——Workerman 场景下追求低开销和可调试性。核心字段只需 state、next_state、action 和 timeout。
- 每个状态节点对应一个 PHP 函数(如
handle_invoice_company()),负责解析语义、校验槽位、调用 NLP 模型 - 转移条件写在
if里,不依赖正则匹配全句,而是提取关键词+意图置信度(例如:检测到“发票”且意图分类模型返回invoice_request置信度 > 0.85) - 必须设
timeout字段,比如 5 分钟无操作自动退回到idle状态,避免 Redis key 永久堆积
如何让状态机与 NLP 模型协同工作?
不要把 NLP 模型塞进 Workerman 进程里做同步推理——会阻塞事件循环。正确做法是把意图识别、槽位填充等耗时任务扔给独立的 HTTP API 或消息队列。
- 收到消息后,Workerman 只做轻量预处理(去噪、截断、基础关键词提取),然后发到
http://nlp-api/parse或推入 Redis List - 响应必须带原始
$session_id和$message_id,回调时才能精准更新对应会话状态 - 若 NLP 服务超时或失败,状态机要 fallback 到兜底逻辑(如回复“请再说一遍”,并保持原 state 不变)
容易被忽略的边界问题
真实客服场景里,最常崩掉的不是主流程,而是这些细节:
- 用户中途发一句“算了”,状态机没定义
cancel事件,导致后续消息还按旧 state 处理 - Redis 中的对话状态 TTL 设为固定值,但不同会话活跃度差异大,应动态更新 TTL(每次交互后
EXPIRE dialogue:xxx 1800) - 没做
onClose清理,用户关页后状态残留,下次同 session_id 进来加载到过期数据

















