会话生命周期
说明角色连接如何在不同状态之间移动、如何在重新连接后保持会话,以及如何暂停、恢复或进入后台。
每个 ConvaiCharacter 您场景中的每个角色都会与 Convai 保持一个独立会话。该会话会跟踪角色是否已连接、当前状态是什么,以及——在启用持久化时——您上次连接时它所在的对话。理解会话如何创建、持久化和恢复,能够帮助您在训练模拟、交互体验和游戏中构建可靠、可续接的角色交互。
会话状态机
每个角色会话都会按以下状态流转。
已断开连接
0
没有活动会话。干净断开连接后的初始状态和最终状态。
Connecting
1
连接尝试进行中。正在从 Disconnected 转换到 Connected。
已连接
2
会话处于活动状态。音频流和对话都在运行。
重新连接中
3
连接已丢失。SDK 正在尝试自动重新建立连接。
断开连接中
4
正在进行平滑关闭。正在从 Connected 转换到 Disconnected。
错误
5
不可恢复的错误。需要手动干预才能重新连接。
您会通过 SessionStateChangedRelayData 事件接收到状态转换, ConvaiSessionEventRelay。请参见 事件系统 了解如何订阅。
按角色的会话
每个 ConvaiCharacter 每个角色都有自己的独立会话。会话不会在角色之间共享。在多角色场景中,每个角色都会独立连接和断开——会话 ID 以 Inspector 中为该角色设置的 ID 字符串为键(不是场景名或对象名),重连策略按角色分别应用,一个角色的会话错误不会影响其他角色。
这描述的是每个 ConvaiCharacter 在本地跟踪的连接。当一个场景注册了两个或更多角色时,SDK 会在连接时围绕它们构建一个共享的多角色房间,并在这些按角色会话之上叠加自己的一套成员列表和就绪状态模型。有关该层,请参见 多角色会话如何工作 。
ConvaiSessionData 是一个持久化会话存储,用于将每个角色映射到其当前会话标识。它会在启动时自动从磁盘加载,并在每次更改时写入 {Application.persistentDataPath}/Convai/sessions.json ——会话 ID 可在应用重启后保留,无需任何额外设置。
GetSessionId(characterId)
返回该角色当前的会话 ID,若不存在则返回 null 。
StoreSessionId(characterId, sessionId)
为角色存储一个会话 ID,并立即将其保存到磁盘。
ClearSessionId(characterId)
移除某个角色的会话 ID 并保存。
ClearAllSessionIds()
移除所有已存储的会话 ID 并保存。
GetAllSessionIds()
返回当前所有角色→sessionId 映射的只读快照。
会话持久化
当会话 ID 被持久化后,SDK 可以在下次连接时恢复之前的对话——角色会记住先前交互的上下文。
哪些会保留,哪些会重置
会话 ID
通过 ConvaiSessionData 持久化——支持恢复
对话历史
由 Convai 管理;当会话 ID 有效时会恢复
进行中的音频
重置——任意中途音频都会被丢弃
当前回合状态
重置——回合会干净地重新开始
模块状态(例如情绪)
重置——模块会在重新连接时重新初始化
默认持久化栈
SDK 通过 ISessionPersistence 暴露了一个可插拔的持久化层,适用于需要自定义后端存储的项目(加密存储、云存档、数据库)。默认栈如下:
会话 ID 存储在格式化为以下形式的键下: convai.session.<characterId>.
替换持久化存储
实现 IKeyValueStore 以使用任意后端存储——数据库、加密存储、云存档系统。 PlayerPrefsKeyValueStore 会在内部将所有读写都转发到 Unity 主线程;如果您的后端存储有线程限制,请应用相同的线程安全模式。
通过以下方式注册: ConvaiRuntimeBuilder:
重连策略
ReconnectPolicy 控制连接意外中断时 SDK 的行为。
RoomRejoinTtlSeconds
double
60
在断开后,SDK 可以重新加入现有房间的秒数窗口。超过该窗口后,将改为创建新房间。
ResumePolicy
ResumePolicy
ResumeIfPossible
控制 SDK 是否尝试通过 character_session_id.
MaxReconnectAttempts
整数
3
会话在进入之前允许的自动重连最大次数 错误 状态时为 True。
SpawnAgentOnRejoin
布尔值
是
重新加入现有房间时是否重新生成 AI 代理。
StartWaitTimeoutMs
整数
5000
连接 Start() 阶段的超时时间(毫秒),超过后该次尝试将被视为失败。
AutoMicStartDelaySeconds
float
0.5
连接后等待多少秒再启动麦克风。可防止在会话完全就绪前捕获音频。
ResumePolicy options
AlwaysFresh
始终开启新对话。角色不会记住上一个会话的内容。
ResumeIfPossible
尝试恢复上一个对话。如果会话已过期或恢复失败,则回退到一个全新会话。
AlwaysResume
始终恢复。如果恢复失败,则连接失败——不会回退到全新会话。
预设策略
ReconnectPolicy.Default
60 秒 TTL, ResumeIfPossible,3 次尝试,麦克风延迟 0.5 秒
ReconnectPolicy.AlwaysCreateNew
不尝试重新加入。始终创建新房间和全新会话。
AlwaysResume 会将会话置于 错误 状态,如果 Convai 无法恢复该会话(例如会话已过期)。除非您的训练模拟要求严格连续性,并且您已明确处理错误状态,否则请使用 ResumeIfPossible 。
显式会话控制
ConvaiManager 提供三个异步方法,用于在自动重连之外控制会话: PauseAsync(), ResumeAsync()以及 ReconnectAsync()当您的应用需要在不中断连接的情况下暂停展示,或按需强制进行一次全新连接时使用它们——例如,训练模拟中为讲师休息而暂停,或打断游戏流程的菜单界面。
PauseAsync()
在保持房间连接的同时,暂停 Convai 角色音频、已传输的转写展示以及运行时模块。
ResumeAsync()
仅移除由 PauseAsync()设置的手动暂停原因。与此同时的应用后台暂停(见下文)会保持激活,直到 Unity 报告前台状态。
ReconnectAsync()
执行一次显式断开/连接循环,使用上文所述的 ReconnectPolicy 和会话恢复行为。若在 SessionState 已经是 Connecting 或 断开连接中 期间调用它,会抛出一个 InvalidOperationException.
应用后台策略
RuntimeBackgroundPolicy 控制 Unity 应用进入后台时哪些内容继续运行——例如玩家切换到其他应用,或移动端应用切到后台。可在 Edit > Project Settings > Convai SDK > Runtime Defaults > Background Policy下设置项目默认值,或在运行时更改当前管理器:
ContinueAudibly
继续;请求后台执行
继续
继续
继续,按照实时音频时钟运行
PauseTimeline
Convai AudioSource 暂停;不影响无关的游戏音频
继续接收房间事件
暂停时隐藏,恢复后重新播放当前状态
展示节拍暂停;输入保持受限,恢复时会重新锚定到可用音频,因此可以跳过已过期的缓冲数据
MuteButCatchUp
在本地静音,同时播放继续推进
继续
继续
继续;返回前台后按实时恢复,而不是回放错过的展示
ContinueAudibly 和 MuteButCatchUp 设置 Application.runInBackground 在活动时有效,但操作系统或浏览器仍可能挂起或静音进程。WebGL 音频由浏览器路由,而不是由 Unity 直接拥有 AudioSource,因此 PauseTimeline 回退到 MuteButCatchUp 在该平台上的该值——状态变化事件会报告请求值和实际值。 PauseTimeline 只会暂停本地展示;不会阻止 Convai 生成响应,也不会阻止 SDK 读取转写。
订阅 OnRuntimeBackgroundStateChanged 来观察请求值和实际策略,因为当平台无法实现请求的行为时它们可能不同。管理器会在以下两种情况下应用所选策略: OnApplicationPause 以及失去焦点的转换上,并且当 Unity 同时报告这两个回调时不会重复暂停。
空闲警告和超时
Convai 会发送一条 user-idle-warning 消息,带有 remaining_seconds ,然后才断开一个空闲会话。SDK 将其暴露为 ConvaiManager.Events.OnUserIdleWarningReceived 以及,对于基于 Inspector 的监听器, ConvaiSessionEventRelay.OnUserIdleWarning.
管理器还会从该倒计时派生一个一次性的本地截止时间,并将其暴露为 ConvaiManager.Events.OnUserIdleTimeoutElapsed 和 ConvaiSessionEventRelay.OnUserIdleTimeout. OnUserIdleTimeoutElapsed 是一个客户端侧的截止时间信号,用于超时 UI 和恢复工作流——它并不能确认 Convai 已关闭房间,因为 Convai 目前不会发送单独的超时数据包。请使用 OnSessionStateChanged 或 OnDisconnected 作为权威的传输状态。
语音、文本、触发器和动态上下文活动都会重置 Convai 的空闲追踪。对于警告后的仅 UI 活动,请调用 ResetIdleTimer() (或其面向 UI 的别名 ExtendIdleTimeout())来延后截止时间;当没有可接受重置的已连接房间时,两者都会返回 否 。
使用示例
示例 1:医疗培训模拟——网络中断后恢复
学习者正在进行评估时网络中断。当连接恢复后,患者角色会继续同一段对话——不会丢失上下文。
预期结果: SDK 会在 2 分钟窗口内自动重试最多 5 次。如果在 Convai 侧会话仍然有效,对话会从中断处继续。如果会话已过期,角色会开启全新对话,而不是卡住。
示例 2:企业入职 kiosk——始终全新的对话
每位来到 kiosk 的新员工都应该从头开始,并且不记得之前的用户。 AlwaysFresh 和 AlwaysCreateNew 确保每次都是一个干净的开始。
为了保证在下一次会话开始前移除之前的用户数据:
预期结果: 每位新用户都会开启一个完全全新的对话。角色不会记得之前的交互,这对于共享 kiosk 部署是正确的。
示例 3:在训练模拟中处理错误状态
当所有重连尝试都耗尽后,会话进入 错误 状态。请将其呈现给讲师并允许手动重试,而不是静默卡住。
预期结果: 当会话进入 错误 状态时,错误面板会出现。讲师点击“重试”即可尝试一次全新连接,而无需重启模拟。
故障排查
会话在 错误 状态下停留在中断后
AlwaysResume 因为它已经过期,无法恢复该会话
切换到 ResumeIfPossible;调用 ClearSessionId(characterId) 以移除失效的会话 ID,然后重新连接
尽管如此,角色在每次启动时仍会开启全新的对话 ResumeIfPossible
之前的 ClearAllSessionIds() 调用清空了会话文件,或者两次运行之间角色 ID 发生了变化
请检查 characterId 字符串在各次运行之间是否保持稳定;检查 {persistentDataPath}/Convai/sessions.json
会话卡在 Connecting 始终
StartWaitTimeoutMs 未为慢速网络配置;或者防火墙阻止了传输
提高 StartWaitTimeoutMs 中的 ReconnectPolicy;请验证对 Convai 端点的网络访问
重连循环始终不成功;会话最终达到 错误
MaxReconnectAttempts 耗尽
订阅 ConvaiSessionEventRelay.OnSessionStateChanged 并将错误呈现给用户;在用户确认后手动调用重连
两个角色共享了一个会话 ID
Inspector 中的角色 ID 字符串相同
为每个角色分配唯一的角色 ID ConvaiCharacter 在场景中
ResumeAsync() 不会恢复音频
应用仍处于后台,因此后台策略会保持手动暂停原因处于激活状态
等待应用返回前台,或检查 IsBackgrounded 在最新的 OnRuntimeBackgroundStateChanged 有效载荷
ReconnectAsync() 会抛出 InvalidOperationException
在 SessionState 期间调用 Connecting 或 断开连接中
检查 SessionState 之前已是
空闲警告从未触发
没有监听器订阅,或者活动一直在重置 Convai 的空闲追踪
订阅 OnUserIdleWarningReceived 或 OnUserIdleWarning;请注意,语音、文本、触发器和动态上下文活动都会重置空闲追踪
下一步
您现在已经了解角色会话如何创建、状态转换如何工作、会话 ID 如何在重启之间持久化,以及如何配置重连行为。接下来请阅读轮流发言模式,以配置 SDK 如何检测语音输入,然后阅读事件系统,了解如何从场景脚本中订阅会话和角色事件。
最后更新于
这有帮助吗?