☰
多轮对话用户状态建模:OpenClaw疲劳度与兴趣度特征体系设计实战
2026/10/20 12:22:19 网站建设 项目流程

多轮对话做得久了,有一个特别扎心的体验:你的机器人明明能听懂每一句话,却经常在聊到第十轮的时候,把已经明显不耐烦的用户当成刚进门的客人来热情接待。单轮意图识别做得再漂亮,也解决不了“用户状态在变”这件事。OpenClaw 的对话框架里专门有一层用户状态建模(User State Modeling),它不回答“用户想要什么”,而是回答“用户现在处于什么状态”——疲劳度、兴趣度、情绪、认知负荷这些维度,都会被量化成特征喂给下游策略层。这篇文章基于我在实际项目里使用 OpenClaw 的经验,把这套特征体系怎么设计、怎么计算、怎么落库、怎么用起来,掰开揉碎讲清楚。

1. 为什么多轮对话必须建模“用户状态”,而不只是“用户意图”

1.1 单轮意图识别解决不了的三个问题

很多团队做对话机器人的第一步都是怼意图识别:用户说“我要退货”,识别出 return_intent,然后走退货流程。这套逻辑在单轮场景下完全够用,但一旦对话超过三轮,问题就来了。

第一个问题是用户目标会漂移。用户可能一开始在问价格,聊着聊着其实是想比价,然后又变成问售后政策。单轮意图识别每次都在猜“当前这句话是什么意思”,但不知道用户从头到尾在追什么,回复就容易断档。第二个问题是用户的交互状态在变。同样一句“你再说一遍”,可能是刚才没听清,也可能是已经烦了。意图一样,状态完全不一样,处理方式也必须不一样。第三个问题更隐蔽:用户的耐心是有限度的。如果系统感知不到用户已经疲劳,还在按标准流程一步一步确认信息,用户大概率会直接流失。

所以多轮对话系统必须有一个跨越单轮之上的“状态视角”。OpenClaw 的做法是在对话管理模块里单独维护一份用户状态,每一轮对话结束后都会更新一次,而不是每次从零推理。

1.2 用户状态层在 OpenClaw 架构里的位置

OpenClaw 的整体结构大致分成四层:输入理解层(ASR/NLU)、对话管理(DM)、用户状态层(User State Tracker)、策略与生成层(Policy/Response Generation)。很多框架会把状态跟踪放在 DM 内部,OpenClaw 把它拆出来单独做,原因很实际:状态特征不仅要给 DM 用,还要给推荐、风控、话术策略等多个模块共用。

举个例子,电商售后的多轮对话里,如果用户状态层判断“当前兴趣度下降”,推荐模块就不会再推送商品,而是转人工;如果判断“情绪强烈负面”,风控模块会提前介入,避免投诉升级。这些决策不是 DM 一家的需求,所以状态建模独立成层,方便复用。

同时用户状态层还负责维护一个叫“会话记忆”的东西——注意它不是简单的历史消息堆叠,而是经过结构化抽样的状态序列。每轮对话结束时,状态层会把“用户说了什么”抽象成“用户处于什么状态”,然后传给策略层去做加权决策。

1.3 规则打分与模型预测的混合路线

对用户状态建模,业界主流有两条路线:一条是纯规则加权,一条是端到端模型预测。OpenClaw 实际采用的是混合方案。核心状态(疲劳度、兴趣度、情绪)先用可解释的规则指标算出原始分,再用一个轻量模型做校准和融合。

纯规则的好处是可控、可解释、好排查。某个用户的疲劳度突然飙升,你打开日志就能看到是哪几个指标把它顶上去的,这对线上问题定位极其重要。纯模型的好处是能捕捉到规则想不到的高阶特征,比如用户语气里那种微妙的敷衍感。OpenClaw 的做法是让规则负责“稳定”,让模型负责“纠偏”,二者取长补短。

我自己的实践经验是,混合方案虽然在开发阶段多花一点时间,但上线后调优成本低很多。状态特征是强时序相关的东西,用户聊十轮和聊三轮,各项特征天然不同,规则部分天然适应这种差异,模型部分再用历史序列做修正,整体效果比单用任何一种都好。

2. OpenClaw 的状态特征体系:疲劳度、兴趣度是怎么定义的

2.1 疲劳度:最容易理解、也最容易做坏的特征

疲劳度是 OpenClaw 用户状态里最有代表性的一个特征。它想回答的问题是:用户当前还有多少耐心继续这场对话。这个特征设计得是否合理,直接决定对话会不会把用户聊跑。

OpenClaw 里的疲劳度采集信号包括:

  • 响应时延:用户回复越来越慢,或者突然变快(典型的不耐烦打字),都是信号。
  • 消息长度变化:从长句变成短句、从完整句子变成单字回复,都是疲劳上升的迹象。
  • 重复措辞:用户开始重复说过的需求,说明他没得到想要的答案,耐心在被消耗。
  • 纠错次数:用户频繁纠正系统,比如“不是这个”“我说的是另一个”,疲劳累积速度会非常快。
  • 标点与符号密度:感叹号变多、表情符号消失,在中文对话里也是常见信号。

这些信号在 OpenClaw 里并不是简单相加,而是带权重的。每个信号先归一到 0-1 区间,再做加权平均。我从项目里的默认配置抄过一组参考权重:响应时延 0.25、消息长度变化 0.2、重复措辞 0.25、纠错次数 0.2、符号表达 0.1。这个权重不是玄学,它的逻辑是“重复”和“纠错”直接表示用户已在对抗,权重给得最高;时延和长度的置信度相对低,容易受客观因素干扰,权重就给得保守一点。

疲劳度计算之后会映射到三个档位:正常(0-0.4)、偏高(0.4-0.7)、严重疲劳(0.7-1.0)。档位的意义在于给策略层一个明确的动作边界,而不是让策略层去跟连续值硬碰硬。

2.2 兴趣度:追踪用户“还想不想聊”

兴趣度是 OpenClaw 里另一个核心特征,它衡量的是用户对当前话题的投入程度。疲劳度偏“负向警戒”,兴趣度则偏“正向引导”,两个特征一起构成了对话节奏调节的核心依据。

OpenClaw 的兴趣度主要看以下几个信号:

  • 话题停留时长:用户在某个主题上连续追问的轮次越多,兴趣度越高。
  • 主动提问率:用户主动抛出“那这个呢”“还有别的吗”,说明兴趣高涨。
  • 追问深度:用户的问题越来越具体,比如从“多少钱”到“保修期内换新怎么收费”,这是强烈兴趣的信号。
  • 回复开放性:用户给出的是开放性回答还是封闭式回答,前者兴趣更高。
  • 情感极性:对话中正面情绪占比越高,兴趣度整体越高。

实际操作中,兴趣度是用“正向信号累加”和“负向信号折减”的方式计算的。比如用户连续追问三次,每追一次正向信号加 0.2;如果用户连续三次只回“嗯”“哦”“好”,负向信号把分数往下拉。OpenClaw 默认还会加入一个小小的随机扰动项,防止多个用户的状态值一模一样,这在做策略 A/B 测试时特别有用,不然分桶会非常尴尬。

2.3 情绪、满意度、认知负荷这些辅助维度

除了疲劳度和兴趣度,OpenClaw 的状态特征体系里还有几个辅助维度,虽然单个看起来不那么显眼,但组合起来价值很高。

情绪维度(Emotion)是直接基于情感分析结果做的七分类映射:高兴、平静、困惑、不满、焦虑、愤怒、中性。它和疲劳度的区别在于,情绪是短时波动,疲劳是长时间累积。用户可能上一轮生气,下一轮被安抚后平静下来,情绪特征每轮都会刷新。

满意度(Satisfaction)更像一个滑动窗口内的累积评分,它综合了问题是否解决、解决速度、交互轮次等外部结果信号。认知负荷(Cognitive Load)用信息密度来衡量,比如用户回复里的数字、专业术语、技术参数多了,说明用户正在处理高负荷信息,系统这时要主动简化表达。

还有一个容易被忽略的特征是“时间压力”,OpenClaw 会记录用户是否在短时间内反复查看时间相关的内容,或者是消息里出现“快”“赶时间”等词。时间压力高时,即使疲劳度不高,系统也应该缩短回复。

辅助维度在 OpenClaw 里统一走一个特征融合层。融合层做的事情是:每个辅助维度先独立打分,再按场景权重汇入一个总状态向量。场景权重是配置化的,比如客服场景里情绪权重高,教育场景里认知负荷权重高,闲聊场景里兴趣度权重最高。这套设计的弹性很好,不用改动特征本身,只调权重就能适配不同业务。

3. 特征的在线更新机制:快照、衰减与状态切换

3.1 状态快照的数据结构

有了特征定义,下一步就是怎么存。OpenClaw 每一轮对话结束,都会生成一份用户状态快照(State Snapshot),结构大概是这样的 JSON:

{ "session_id": "sess_20240516_001", "turn_id": 12, "fatigue": { "score": 0.63, "level": "high", "signals": { "latency": 0.42, "length_shrink": 0.78, "repeat": 0.8, "correction": 0.55, "punctuation": 0.12 } }, "interest": { "score": 0.35, "level": "low", "signals": { "topic_dwell": 0.3, "question_rate": 0.2, "depth": 0.4, "openness": 0.5, "sentiment": 0.0 } }, "emotion": "frustrated", "satisfaction": 0.4, "cognitive_load": 0.2, "time_pressure": false, "updated_at": "2024-05-16T12:33:21Z" }

每个快照里都带信号明细而不是只存最终分数,这个设计我很认同。排查线上问题的时候,只看总分根本不知道问题出在哪,必须能下钻到信号级别。比如疲劳度 0.63,你必须看到底是重复措辞爆了还是响应时延爆了,才能决定调策略还是调采集。

3.2 衰减因子:让旧状态逐步失效

多轮对话状态建模很容易犯一个错:把第一轮的老黄历一直背到最后。OpenClaw 在更新状态时给每个特征都配了时间衰减因子,旧状态对当前状态的影响随时间指数衰减。

衰减的公式很朴素:当前有效值 = 历史值 × e^(-λ × Δt)。其中 λ 是衰减速率,不同特征取值不同。疲劳度的衰减速率慢,因为疲劳是慢变量;情绪衰减快,因为情绪是快变量。在长会话里这个差异尤其重要,用户前一小时很兴奋,不代表现在还兴奋,但用户连续聊了一小时,疲劳度一定是累积的。

实际代码里我经常把衰减做成一个独立函数,方便在实验里对比不同 λ 的效果:

import math def decayed_value(old_value, elapsed_turns, lambda_): return old_value * math.exp(-lambda_ * elapsed_turns)

在 OpenClaw 的默认配置里,疲劳度 λ 取 0.05,情绪 λ 取 0.3,兴趣度 λ 取 0.15。这几个数值调起来非常敏感,后面我会专门讲怎么调。

3.3 阈值切换与状态机

如果每个特征都只是连续分数,策略层用起来会很麻烦。OpenClaw 在特征分数之上建了一层简单的状态机,把连续分数离散化成档位,同时允许状态之间有转移条件。

疲劳度的状态机是:正常 → 偏高 → 严重疲劳,只有“严重疲劳”能触发降级策略(比如主动建议转人工、压缩回复长度)。转移条件不光是看当前分数,还要看这个分数持续了几轮。比如疲劳度连续三轮超过 0.7,才会进入严重疲劳状态。这么做的目的很明确:防止单轮抖动造成误判。

3.4 状态如何影响下游策略

状态层的最终价值,体现在它对策略层的影响上。OpenClaw 的策略模块在生成回复前,会先读当前状态向量,然后做三件调整:调整回复长度、调整主动引导程度、调整交互模式。

疲劳度偏高时,策略层会强制压缩回复长度,把原本两段式回复合并成一句话,同时减少反问,直接给结论。兴趣度高时,策略层会增加追问和推荐,把对话往深处带。情绪低落时,策略层会切换话术模板,加入更多共情表达。这些调整通过一组权重配置来控制,状态分数只是输入,真正的业务规则都落在配置里,这使得非技术人员也能参与调优。

4. 实操:搭一个最小可用的用户状态模块

4.1 从对话日志里提取信号

原理讲得再多,不如亲手搭一遍。我建议你用 OpenClaw 的离线日志模式先跑通整个流程,日志里每一轮对话都会记录消息内容、时间戳、用户ID、解析结果。信号提取就是把这些原始字段换算成前面表格里的特征信号。

实际工作中,响应时延用当前轮和上一轮的时间差减去系统回复耗时;消息长度变化用的是当前轮消息长度除以会话平均长度的比值;重复措辞通过计算当前消息和近三轮消息的文本相似度来判断,相似度超过 0.7 就记为一次重复。这些信号提取代码最好做成与具体业务解耦的纯函数,输入原始对话数据,输出结构化信号,方便单测。

4.2 疲劳度的规则实现

我在项目里写过一个最小版本的疲劳度计算函数,核心逻辑就是用加权平均把信号合成总分:

WEIGHTS = { "latency": 0.25, "length_shrink": 0.2, "repeat": 0.25, "correction": 0.2, "punctuation": 0.1, } def compute_fatigue(signals: dict) -> float: score = 0.0 for k, w in WEIGHTS.items(): score += w * clamp_01(signals.get(k, 0.0)) return round(score, 2)

注意几个细节:信号进来之前必须先做归一化和异常值截断。比如用户响应时延可能因为网络问题飙到 10 分钟,这种异常值必须截断在合理上限内,否则一个离群点直接让疲劳度打满。我踩过这个坑,后来在信号层统一加了一层 winsorize 处理,超过 95 分位数的值全部压到 95 分位数。

挡位映射我习惯用分位数而不是固定阈值来做。因为不同业务的用户打字习惯差异很大,游戏用户回复普遍短,客服用户回复普遍长,固定阈值在 A 业务合理,在 B 业务就失灵。OpenClaw 本身也支持用历史数据动态计算分位数阈值,我强烈建议你在上线前用一周历史日志跑一遍,看看自己的用户分布落在哪。

4.3 兴趣度的情感加权实现

兴趣度的核心是扣住“主动”和“投入”。最小实现里,我用消息长度、问号数量、追问标记、情感极性四个信号做加权,其中情感极性来自一个小的情感分析模型,输出 -1 到 1 之间的值。

def compute_interest(msgs, sentiment_score): length_factor = min(1.0, sum(len(m) for m in msgs) / 80.0) question_factor = sum(1 for m in msgs if m.endswith("?")) / max(1, len(msgs)) initiative_factor = has_question_mark(msgs[-1]) and "还有" in msgs[-1] sentiment_factor = max(0.0, sentiment_score) interest = (0.3 * length_factor + 0.3 * question_factor + 0.2 * float(initiative_factor) + 0.2 * sentiment_factor) return round(interest, 2)

这个版本的参数比较糙,但作为基线足够用。你上线后会很快发现,光靠这几项是不够的,这时再往里面加“话题停留时长”“追问深度”这些信号就行。OpenClaw 的好处是特征接口是开放的,你可以在不碰其他模块的情况下,往里挂自己的特征计算器。

4.4 冷启动:新用户的状态从哪来

新用户没有历史对话,状态怎么初始化是很多人忽略的问题。OpenClaw 的默认做法是用类型化先验:按入口渠道、场景类型、时段来赋初值。比如通过客服入口进来的用户,初始疲劳度给 0.3,初始情绪给中性;通过活动页进来的用户,初始兴趣度给 0.5,疲劳度给 0.2。

冷启动初始化不可太激进。我的经验是,前两轮一律让状态层处于“观察模式”,只更新不决策,等到第三轮有了足够的信号,再让状态真正参与策略调整。这样避免了第一句话稍微长一点就把用户标记成疲劳这种乌龙。

5. 常见问题与排查实录

5.1 疲劳度被误判:用户只是打字慢

这是最常见的问题。有人天生打字慢,或者正在走路只用单手回复,响应时延和消息长度两个信号会同时走高,把疲劳度顶到高位。更麻烦的是,这类误判一旦发生,策略层会强制缩短回复,用户反而觉得系统变敷衍了。

排查方法是别只看时延,要结合上下文。OpenClaw 里有个好用的技巧是看“相对时延”而不是“绝对时延”:拿用户当前轮的响应时间,跟他自己前五轮的平均响应时间做对比,比值超过 1.5 才算时延信号。这样打字慢的用户只是基线高,不会被误伤。

另外,回复长度缩短不等于疲劳。如果你发现用户消息长度在降,同时保留着表情符号和问句,那大概率是进入“高效沟通模式”,不是疲劳。这种时候要把符号信号和重复信号结合起来看,避免单指标决策。

5.2 兴趣度分数震荡

兴趣度是几个特征里最容易震荡的。用户可能这一轮连续追问,兴趣度冲到 0.8,下一轮因为收到一条不满意的回答,立刻回个“哦”,兴趣度掉到 0.2。策略层如果在每个轮次都跟着这个分数剧烈调整行为,用户体验会非常分裂。

我的解法是给兴趣度做指数滑动平均,平滑系数取 0.6 到 0.7,让历史信息占一定权重。OpenClaw 里对应的配置项是 interest_smoothing_factor。还有一招是只在高兴趣或低兴趣状态持续两轮以上时才触发策略变化,单轮极值只更新分数不改变策略,实测下来能让状态系统稳定很多。

5.3 跨会话污染

用户昨天聊了一轮很糟糕的售后,今天再次进来,如果系统把昨天的低满意度直接带过来,用户会感觉到莫名其妙的“被安抚”——我们明明才刚说第一句话。OpenClaw 的做法是把跨会话状态分为“强保留”和“弱保留”两类。

强保留特征包括用户的长期偏好、知识背景、历史满意度基线,这些可以跨会话保留,但也要加衰减。弱保留特征包括疲劳度、情绪、时间压力,这些必须随会话结束立即清零或者大幅衰减。我在配置里把情绪跨会话保留系数设为 0.1,疲劳度设为 0,效果比较好。

5.4 阈值怎么调:先看分布而不是拍脑袋

最后说说阈值调整。很多人上来就问“疲劳度阈值应该设多少”,这个问题没法直接回答,因为答案藏在数据里。正确的姿势是:先跑一周历史日志,统计所有特征的分布,然后按分位数确定初始阈值。比如把 70 分位作为“偏高”的起点,85 分位作为“严重”的起点,再结合线上业务指标去微调。

我调阈值时看的核心指标是“误触发率”和“漏触发率”。误触发是用户明明状态正常,系统却降级了;漏触发是用户已经很不耐烦,系统还在装模作样地走流程。理想情况下,误触发率控制在 10% 以下,漏触发率控制在 5% 以下。如果两个指标都下不来,别急着调阈值,先回去看信号采集是不是漏了关键项。

6. 关于这套状态体系,我最后想说的几句

OpenClaw 这套用户状态建模思路,其实本质上是把人的直觉变成了可计算的特征。你别把它想得太玄,疲劳度就是在回答“用户还有多少耐心”,兴趣度就是在回答“用户还想不想聊”,情绪就是在回答“用户现在爽不爽”。特征体系本身并不复杂,复杂的是怎么让这些特征在真实业务里稳定可靠地工作。

我在实际项目里最大的体会是:状态建模的第一原则不是精确,而是稳定。一个偶尔不准但从不震荡的状态系统,远比一个时准时不准的系统更容易调优和上线。所以如果你要动手做类似的东西,我建议先跑通规则版本,把日志和排查工具做扎实,再考虑上模型。

最后再分享一个小技巧:把状态快照全部落到独立的日志表里,每天都跑一张“状态分布日报”,看看疲劳度偏高占比、情绪负面占比这些指标有没有异常波动。状态层出问题,往往不是线上报错,而是业务指标悄悄变差,有了这份日报,你就能在用户流失之前提前发现问题。这套用户状态建模的方法,后续还可以扩展去做用户分层、个性化话术生成、甚至预测用户流失概率,路径都是通的,关键是先把当前这套跑稳。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询