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

同步行为与时序

精确说明上下文更新何时到达角色、对话开始前会发生什么,以及 SDK 如何决定发送什么内容。

动态上下文更新如何传输以及何时传输

理解 Dynamic Context 的更新何时被传输,以及以何种形式被传输,可以避免最常见的集成意外。SDK 并不会把每次更新都作为单个 Append 消息发送。根据 tracker 中已经存在的状态,它可能会发送两条消息,也可能会将更新排队,直到对话开始。本页将详细说明全部四种同步场景、对话前队列,以及规范上下文格式。

本页面面向正在调试集成行为、优化更新频率,或将 Dynamic Context 与外部系统集成的开发者。初学者可以先跳过,直到遇到意外的字符行为再回来查看。

四种同步场景

同步行为取决于被更新的状态是否已经存在于 tracker 中。这些场景适用于活动对话期间。对话前行为将在下一节中说明。

场景 1 — 新状态已添加

SetState("Name", "Value") 被调用,且 "Name" 在 tracker 中尚不存在:

会发送一条 Append 消息。角色接收到新事实,并将其纳入自身感知。

场景 2 — 已有状态发生变化

SetState("Name", "NewValue") 被调用,且 "Name" 已经存在,但其值 不同

会发送两条消息: 先发送一个携带完整规范上下文的 Replace(其中已更新的值就地替换),再发送一个携带人类可读差异说明的 Append。Replace 为角色提供权威、完整的视图;Append 则用清晰可读的方式说明具体发生了什么变化,从而让它在对话中能够自然地提及这一过渡。

场景 3 — 状态被移除

RemoveState("Name") 被调用:

会发送一条 Replace 消息,携带完整的规范上下文,但已移除的状态会被排除在外。不会有差异 Append——该状态的缺失从 Replace 载荷中即可一目了然。

场景 4 — 批量 SetStates,且包含新旧混合状态

SetStates(dict) 被调用,且字典中至少包含一个已经存在于 tracker 中的状态(无论其值为何):

Replace + Append,其结构与场景 2 相同,但 Append 会将批处理中的所有变更汇总到一条消息里。如果字典中的所有状态都是新的(在 tracker 中都尚不存在),则只会发送一条 Append。

对话前队列

所有受跟踪的方法—— SetState, SetStates, AddEvent, RemoveState,以及 Reset ——在角色不处于活动对话时,会自动将其效果排队。对话开始时,队列会被刷新。

队列机制

每当在对话开始前对受跟踪的状态或事件进行更新时,SDK 都会记录一个待同步项。对话开始时,会发送一条 Replace 消息,其中包含当时完整的规范上下文——而不是一系列单独的更新。

Reset() 如果在对话开始前被调用,则会记录一个待重置项,并覆盖任何待同步项。对话开始时,角色接收到的是 Reset 消息,而不是 Replace。

优先级规则:

  • 调用 Reset() 会取消任何待同步项。随后 SetState()Reset() 会将队列恢复为待同步状态。

  • 只会发送在对话开始时 tracker 的最终状态。多次 SetState 调用在对话开始前只会产生一条包含最终值的 Replace,而不会是一系列递增消息。

Apply() 不会排队。 如果 Apply() 如果在对话开始前被调用,更新会被静默丢弃。如果你需要让对话开始前的设置保留到会话启动,请使用受跟踪的方法。

规范上下文格式

规范上下文字符串是在某一时刻对所有受跟踪状态和事件的完整、权威的文本表示。它用于 Replace 消息以及对话前的刷新。

格式:

规则:

  • 状态最先出现,按 插入顺序 排列——也就是每个状态名称首次引入的顺序。就地更新已有状态会保持其位置不变。

  • 事件在所有状态之后出现,按 时间顺序 排列——也就是 AddEvent 被调用的顺序。

  • 每个状态都按 "{Name} 是 {Value}"的形式格式化,每行一个。

  • 每个事件都是单独一行,没有前缀。

示例:

在以下调用序列之后:

规范上下文是:

"Station" 即使 "Hazard Level" 在其之后被更新,其插入位置仍保持不变。事件则会按添加顺序保留。

Apply() 与 Tracker 边界

Apply(ConvaiDynamicContextUpdate update) 是唯一一个完全绕过 tracker 的方法。它会将 update 中的内容直接发送到 transport,而不会重建规范上下文,也不会记录本地状态。

后果:

  • TryGetStateValue 不会被 Apply() 调用所更新。如果你发送 Apply(new ConvaiDynamicContextUpdate("Score is 95", ConvaiContextUpdateMode.Append)),查询 TryGetStateValue("Score", out _) 返回 false.

  • 随后由 SetState 调用触发的规范同步不会包含任何通过 Apply().

  • Apply() 发送的内容。如果角色不在对话中,它是一个静默的无操作——不会排队。

使用 Apply() 仅在你确实需要绕过 tracker 时才使用,例如从外部评分系统发送后端格式化的文本块,或强制进行一种与受跟踪状态相互独立的模式-Reset 更新。

传输层参考

Dynamic Context 更新通过 WebRTC 数据通道以 RTVI 消息的形式传输。

JSON 字段
说明

type

"context-update"

固定的消息类型标识符。

data.text

任意字符串,或省略

上下文文本载荷。当 mode 为 重置.

data.mode

"append", "replace", "reset"

对应 ConvaiContextUpdateMode.

data.run_llm

"auto", "true", "false"

对应 ConvaiContextReactionMode. Auto"auto", 立即响应"true", 仅同步"false".

此信息提供给正在检查网络流量或构建自定义传输集成的开发者。在正常使用 SDK 时无需执行任何操作——从类型化 C# 值到 JSON 的映射由 SDK 自动处理。

接下来做什么

  • 故障排除与诊断 ——用于诊断当更新未产生预期角色响应时的异常行为。

结论

SDK 的同步逻辑——新状态使用 Append,已更改状态使用 Replace 然后 Append,移除状态使用 Replace——确保角色始终接收到对世界的权威视图。理解这些模式可以让集成行为变得可预测,调试也更直接。如果你遇到意外行为,请参阅 故障排除与诊断.

最后更新于

这有帮助吗?