For the complete documentation index, see llms.txt. This page is also available as Markdown.

角色身份与称呼

了解 Convai 角色在共享 Unity 房间中携带的六个标识符,以及在称呼、音频和对话历史中应使用哪一个。

在多角色房间中,角色不会用一个值来标识,而是用多个值来标识;每个值回答不同的问题:这是哪个角色定义、这是它的哪个实例、哪个音频轨道属于它,以及它正在继续哪段对话。选错值是多角色场景中最常见的 bug 来源,因为错误的选择在场景里出现两个同一角色的副本之前都会一直“正常工作”。


成员项上的标识符

CharacterRoomMembership 包含某个角色实例的所有标识符。下表列出了每个标识符的用途。

字段
用于

CharacterId

标识 Convai 角色定义。同一个值可以出现在一个房间中的多个成员项上。

MembershipId

用于指向一个具体实例——设置交互目标、将其从名单中移除,或使用 FindByMembershipId.

ParticipantIdentity

将该实例与其传输参与者和音频轨道匹配。将该值视为不透明值。

CharacterSessionId

在后续会话中继续该实例的对话,并区分重复的角色 ID。

SessionId

Convai 在连接响应中为该成员项返回的会话标识符。

ParticipantId

当其媒体出现时,SDK 为此成员身份绑定的传输分配的参与者。是在绑定之后填充,而非连接时。

IsInitial 将打开房间的那个成员项标记为交互目标。 角色 保存本地 ConvaiCharacter SDK 绑定到该成员项的,或 null 当名单中包含某个成员项而场景没有对应组件时。


为什么角色 ID 不是地址

角色 ID 指代的是 Convai 中的一个角色定义,而不是它在房间中的某个实例。名单中可能多次出现同一个角色 ID,每一项都会成为一个独立的成员项,拥有自己的对话、自己的音频轨道以及自己的就绪状态。就房间而言,两个具有相同角色 ID 的条目就是两个不同的角色。

因此,任何以角色 ID 为键的查找,一旦出现克隆体就会变得歧义。SDK 会把这种歧义视为错误,而不是猜测:当多个成员项带有请求的角色 ID 时,内部解析会拒绝该查找,所以只能通过角色 ID 识别的一条消息会被丢弃,而不会被发送给任意一个实例。通过 MembershipId 来寻址实例,


SDK 如何解析传入消息

在多角色会话处于活动状态时,解析会按固定顺序经过成员索引:先成员 ID,再参与者标识,最后参与者 ID。第一个匹配成功的索引获胜,而与这三者都不匹配的消息不会被投递。

这种顺序也解释了为什么在多角色房间中,按角色的事件匹配会改变形态。当一个 ConvaiCharacter 决定某条传入消息是否属于自己时,它会通过房间成员项解析这条消息,并将解析出的成员项绑定实例与自身比较。旧路径——先比较角色 ID,再查询参与者映射——只在没有多角色会话活动时适用。

音频轨道解析遵循同样的规则,没有旧版回退。在多角色会话处于活动状态时,传入轨道会通过成员索引与某个成员项匹配,而解析不到任何成员项的轨道不会附加到任何角色上。


按参与者标识映射音频

使用以下方式按参与者标识绑定音频输出, BindParticipantAudioOutput(participantIdentity, audioSource) 时自行注册 IConvaiRoomAudioService,并使用以下方式静音或重新启用某个参与者, SetParticipantAudioEnabled(participantIdentity, enabled)。二者都使用来自成员项的标识值,因此克隆体及其原始体会保持在彼此独立的 AudioSource 组件中。

同样的接口也适用于房间中的其他人类参与者。人类参与者使用如下标识形式 human:{speaker_id},因此,一个将另一位学习者的声音通过空间化 AudioSource 呈现的场景,会像绑定角色一样精确地绑定它。


为转录内容分配发言轮次

转录时间线是整个房间共享的。 ConvaiManager.Transcripts 维护一条覆盖玩家和房间中每个角色的时间线,而不是每个成员项一条时间线,因此多角色转录 UI 读取的是与单角色 UI 相同的数据源,并且自行标记每一轮发言的归属。

每个 TranscriptTurn 用一个 TranscriptSpeaker来标识其发言者,而不是用成员项。 类型 将玩家发言与角色发言区分开来, Id 保存的是发言角色的 CharacterId, DisplayName 保存要渲染的名称,而 ParticipantId 保存该轮发言到达时所使用的传输参与者。完整类型见 转录 API

该列表中没有成员 ID,这正是克隆体变得有意思的地方。共享同一个 CharacterId 的两个成员项会产生具有相同 Speaker.Id 值的发言轮次,唯一能区分它们的字段是 ParticipantId。将其与名单匹配即可恢复成员项,或者使用 TranscriptQuery.ParticipantId.


将玩家发言归属到某个成员项

玩家发言不会记录玩家在说这句话时所针对的是哪个成员项。 TranscriptSpeaker.TypeTranscriptSpeakerType.Player 在那一轮发言中,而且它的字段都不会—— Id, DisplayName, ParticipantId ——标识某个角色。 MultiCharacterRoomSession 也不会保留过去交互目标的历史;它只暴露当前的 ActiveMembershipId,而不是先前几轮发言的日志。

如果应用需要知道玩家在某一轮发言中是在对谁说话,就必须在发言开始的那一刻自行捕获 ActiveMembershipId 。订阅 ConvaiManager.Transcripts ,并在玩家发言被添加到时间线时立即记录其目标:

稍后查找 _playerTurnTargets[turn.Id] ,即可恢复某个特定发言轮次所针对的是哪个成员项。SDK 不会事后回填这些信息——要实时捕获,否则一旦房间继续推进,这些信息就会丢失。


角色事件中的标识

当前交互目标的 CharacterReady 事件会把成员标识符与它原本就有的角色 ID 一起携带: MembershipId, CharacterSessionId,以及 ParticipantIdentity。在单角色房间中触发的事件里,这三者都会是空字符串,这也正是一个处理器能同时服务这两种房间形态的原因——在 MembershipId 不为空时读取它, CharacterId 为空时则回退到


协议层面的相同规则

Live API 页面说明了 SDK 所使用的线协议中的这些规则:用成员 ID 寻址实例,用参与者标识映射媒体,且绝不要用角色 ID 映射媒体。本页上的 Unity 类型正是这些字段在客户端侧的投影,因此在一处正确的映射,在另一处也同样正确。

使用多角色会话

下一步

名册就绪与部分调度构建你的第一个多角色会话

最后更新于

这有帮助吗?