先说结论
Odoo Live Chat 并不是“访客一进来,系统随便挑个在线客服”。
源码里的真实设计更像三段式:
- 先判断这条渠道能不能接待:有没有 chatbot,或者有没有可用坐席;
- 如果配置了 chatbot,就优先让脚本充当第一接触层;
- 只有在需要人工介入时,才进入 operator 选择逻辑,并且这个选择不是简单随机,而是尽量避开忙碌坐席、通话中的坐席,以及不匹配语言/国家/专长的人员。
所以你在现场看到的“同一个 Live Chat 渠道,有时先机器人,有时先人工,有时重开对话后流程又不一样”,基本都不是玄学,而是源码设计使然。
这篇主要看哪里
核心源码在:
addons/im_livechat/models/im_livechat_channel.pyaddons/im_livechat/models/discuss_channel.py
重点方法:
im_livechat.channel._get_operator_info()im_livechat.channel._get_operator()im_livechat.channel._get_less_active_operator()discuss.channel._chatbot_find_customer_values_in_messages()discuss.channel._post_current_chatbot_step_message()discuss.channel._chatbot_restart()discuss.channel.livechat_join_channel_needing_help()
这几段组合起来,基本就是“访客进入 → 机器人提问 → 记录答案 → 需要人工时挑选坐席 → 会话重开或转接”的主链路。
第一层:渠道是否可用,先看的是“机器人或人工”二选一
im_livechat.channel 里有一个很关键但容易被忽视的判断:
_is_livechat_available()返回的是:有 chatbot,或者有可用 operator,就算渠道可用。
这意味着:
在 Odoo 的产品设计里,chatbot 不是人工客服的附属物,而是可以单独构成“渠道可接待能力”的第一层。
这也是为什么某些站点明明没客服在线,Live Chat 按钮仍然能亮:
- 不是系统误判;
- 而是该渠道还有 chatbot 可以先接住访客。
如果你把“在线可接待”理解成“必须有人类坐席在线”,你会误判很多现场问题。
第二层:谁先接待,取决于 _get_operator_info() 的优先级
_get_operator_info() 的作用不是单纯“找个 operator”,而是先决定本次会话由哪种 operator model 接管。
它大致做了两步:
情况 A:规则里指定了 chatbot script
如果传入的 chatbot_script_id 在该渠道规则允许的脚本列表里,源码会:
- sudo 读取 chatbot script;
- 切到 chatbot 对应语言上下文;
- 把
operator_partner指向 chatbot 的 operator partner; - 并把
operator_model标为chatbot.script。
这说明 Odoo 的想法很明确:
机器人并不是“假装成系统消息”的特殊分支,而是一个真正进入 operator 选择层的接待主体。
情况 B:没有 chatbot,或者当前流程不再由 chatbot 接管
这时才调用 _get_operator() 去找真人坐席,并把 operator_model 标成 res.users。
所以“机器人优先还是人工优先”,不是前端按钮逻辑,而是这里的 operator model 决定的。
第三层:人工路由并不随机,而是尽量选“当前最空的人”
很多人把 Live Chat 分配想成 round-robin,但 _get_less_active_operator() 暴露的逻辑更细。
它的思路大概是:
1. 先只看候选名单里的 operator
也就是:
- 语言匹配后剩下的人;
- 国家规则匹配后剩下的人;
- 专长过滤后剩下的人;
- 或者当前渠道允许的 available operators。
2. 优先挑“没有活跃状态”的坐席
源码会先看 operator status 里哪些人最近没有活跃聊天。
如果有这种人,直接在这些“当前最空”的人里随机一个。
3. 如果所有人都忙,再比较负载和通话状态
这里很关键:
- 不只比较 active chat 数量;
- 还比较
in_call; - 并显式避免“正在打电话且已有多条会话的人”拿到不合理优先级。
这意味着:
Odoo Live Chat 的分配核心不是绝对公平,而是尽量避免把新访客继续塞给已经最忙、而且可能正在通话的那个人。
这是一种很实用的协同优化,而不是数学上最平均的调度器。
第四层:chatbot 不是只会发话术,它还会从消息里抽取客户信息
_chatbot_find_customer_values_in_messages() 很值得看,因为它暴露了 Odoo 对 chatbot 的定位。
它会遍历当前频道里和脚本步骤相关的消息,把用户输入回填成结构化字段,例如:
- 邮箱;
- 手机号;
- 其他脚本提问对应的客户信息。
也就是说,chatbot 在 Odoo 里不是单纯“自动回复器”,而是一个访客预采集层:
- 先问;
- 再从历史消息里抽值;
- 然后把这些值用于后续客户创建、更新或转人工时的上下文补全。
这就是为什么很多企业会觉得 Odoo 的 Live Chat 看起来像“客服”和“表单”混合在一起:源码上它本来就是这么设计的。
第五层:机器人发消息,本质上仍然是往 discuss channel 里 message_post
_post_current_chatbot_step_message() 并没有造一套独立的机器人消息系统。
它最终还是调用 _chatbot_post_message(),而后者会以 chatbot operator partner 的身份,在 channel 上执行:
message_post()message_type='comment'subtype_xmlid='mail.mt_comment'
这意味着两件事:
1. 机器人消息和人工消息共用 discuss / mail 基础设施
好处是:
- UI 渲染统一;
- 已读、未读、时间线、bus 推送机制统一;
- 后续转人工不用迁移消息结构。
2. 机器人并不是“系统提示条”
它在协作语义上是一位特殊操作员。
所以你在调试时如果只盯着前端 widget,不去看 message_post() 落出来的 mail.message,会漏掉很多核心问题。
第六层:重启会话不是清前端状态,而是清频道里的 chatbot 轨迹
_chatbot_restart() 非常能体现 Odoo 的设计边界。
它做的不是简单重载页面,而是:
- 清掉
chatbot_current_step_id; - 清空
livechat_end_dt; - 删除
chatbot_message_ids; - 再重新发一条“Restarting conversation...”消息。
也就是说:
在 Odoo 看来,重启 Live Chat 是一件后端状态重置动作,而不是前端重新打开窗口。
这解释了一个很常见的现象:
- 用户刷新浏览器,有时流程不会重置;
- 但调用正式的 restart 逻辑后,脚本会真正从头开始。
因为决定流程位置的是频道状态和 chatbot message 记录,而不是浏览器页面本身。
第七层:人工接管本质上是“有人加入需要帮助的 channel”
livechat_join_channel_needing_help() 的命名已经把这件事说透了:
- 不是新建另一个会话;
- 也不是把访客拷贝给某个客服;
- 而是让真人加入一个已经存在、而且“正在请求帮助”的 channel。
这很关键,因为它决定了转人工时哪些东西会被保留:
- 机器人已经发过的话;
- 访客已经回答过的信息;
- 当前 livechat 状态;
- channel 本身的上下文。
所以 Odoo 的人工接管是在同一条协作线程里续接,而不是“机器人阶段”和“人工阶段”两张皮。
实战里最容易误解的 3 个点
误解 1:有 chatbot 就一定不会走人工分配
不对。
正确理解是:
- chatbot 可以先成为 operator model;
- 但在脚本结束、访客请求人工、或规则需要人工介入时,仍会进入
_get_operator()路由真人。
误解 2:人工分配就是随机
不对。
随机只是最后一层平局裁决。
在那之前,源码已经做了:
- 候选过滤;
- 活跃度比较;
- 通话状态规避;
- 前 operator 复用等考虑。
误解 3:重启对话只和前端有关
不对。
真正控制流程的是:
chatbot_current_step_idchatbot_message_idslivechat_end_dt
只刷新前端,不一定等于重启对话。
二开时最值得注意的边界
1. 不要绕开 _get_operator_info() 直接硬塞某个 user
你会跳过:
- chatbot 优先级;
- 语言/国家/专长过滤;
- 负载均衡;
- 前 operator 延续逻辑。
最后看起来只是“指定了一个客服”,实际上会破坏整条接待链路。
2. 不要把 chatbot 当独立消息系统改
它本质上依赖 discuss channel 的 message_post()。
如果你单独做一套消息表或只在前端追加假消息,后续人工接管、bus 同步、消息追踪很容易出问题。
3. 排查“为什么一直不转人工”时,不要只看前端按钮
优先检查:
- 当前渠道规则里有没有 chatbot;
- operator 是否真的 available;
_get_operator()的候选是否被语言、国家或专长过滤空了;- channel 是否还停在 chatbot 当前步骤。
结论
Odoo Live Chat 的核心,不是“聊天窗口长什么样”,而是它把一次在线接待拆成了三层职责:
- chatbot 负责前置采集与分流;
- operator 路由负责把人分给合适且不太忙的坐席;
- discuss channel 负责承载整条会话线程。
理解这三层以后,你就能解释很多现场现象:
- 为什么没客服在线按钮还能亮;
- 为什么机器人结束后还能无缝接给人工;
- 为什么重启脚本要清后端状态;
- 为什么“随机分配客服”其实并不随机。
一句话总结:
Odoo Live Chat 不是把机器人和客服拼在一起,而是用同一条会话线程,把“预接待、分流、人工协作”串成了一条连续链路。
DISCUSSION
评论区