本页内容

本页内容

主流消息传递

使用 Discord 语音关注用户

跟随模式使机器人在 Discord 语音通道之间移动时跟踪选定的用户。

语音关注用户

当您希望 Discord 语音机器人与一个或多个已知的 Discord 用户在一起,而不是在启动时加入固定频道或等待 /vc join 时,请使用 voice.followUsers。

json5
{  channels: {    discord: {      voice: {        enabled: true,        followUsersEnabled: true,        followUsers: ["discord:123456789012345678"],        allowedChannels: [          {            guildId: "123456789012345678",            channelId: "234567890123456789",          },        ],      },    },  },}

行为:

  • followUsers 接受原始 Discord 用户 ID 和 discord:<id> 值。 OpenClaw 在匹配语音状态事件之前对两种形式进行标准化。
  • 配置 followUsers 时,followUsersEnabled 默认为 true。设置为false,保留已保存的列表,但停止自动语音跟随。
  • followUsers 只控制语音驻留。它不会授予发言者权限或所有者权限;请分别配置 commands.ownerAllowFrom 以及公会或频道的用户和角色。
  • 当被跟随用户加入一个允许的语音频道时,OpenClaw 会加入该频道。当用户移动时,OpenClaw 也会随之移动。当当前被跟随用户断开连接时,OpenClaw 会离开。
  • 如果同一公会中有多个被跟随用户,而当前活跃的被跟随用户离开,OpenClaw 会在离开公会前先移动到另一个被跟踪用户所在的频道。如果多个被跟随用户同时移动,则以最新观察到的语音状态事件为准。
  • allowedChannels 仍然生效。位于不允许频道中的被跟随用户会被忽略,而一个跟随拥有的会话会移动到另一个被跟随用户那里,或者离开。
  • OpenClaw 会在启动时以及以受限间隔校正遗漏的语音状态事件。校正会采样已配置的公会,并对每次运行的 REST 查询数量设上限,因此非常大的 followUsers 列表可能需要超过一个间隔才能收敛。
  • 如果 Discord 或管理员在机器人跟随用户时移动了机器人,OpenClaw 会重建语音会话,并在目标位置允许时保留跟随所有权。如果机器人被移动到 allowedChannels 之外,OpenClaw 会离开,并在存在已配置目标时重新加入该目标。
  • DAVE 接收恢复在连续解密失败后可能会离开并重新加入同一频道。跟随拥有的会话会在该恢复路径中保留其跟随所有权,因此稍后被跟随用户断开时仍会离开频道。

在加入模式之间选择:

  • 对于个人或 operator 设置,如果希望机器人在你进入语音时自动跟随,就使用 followUsers。
  • autoJoin用于固定房间。当机器人仅在人类位于该房间时才应出现时,请添加 whenOccupied: true;省略它以获得始终在线的语音状态。
  • 对于一次性加入,或自动语音存在会让人意外的房间,就使用 /vc join。

Discord 语音编解码器:

  • 语音接收日志会显示 discord voice: opus decoder: libopus-wasm。
  • 实时播放使用相同的捆绑 libopus-wasm 包将原始 48 kHz 立体声 PCM 编码为 Opus,然后将数据包传递给 @discordjs/voice。
  • File 和提供程序流播放使用 ffmpeg 转码为原始 48 kHz 立体声 PCM,然后使用 libopus-wasm 将 Opus 数据包流发送到 Discord。

STT 加 TTS 流水线:

  • Discord PCM 捕获会转换为一个 WAV 临时文件。
  • tools.media.audio 负责 STT,例如 openai/gpt-4o-mini-transcribe。
  • 批量捕获遵循最大的适用音频输入限制,包括配置的模型和CLI回退。过大的捕获会停止并警告说较短的片段;部分音频未转录。此限制不会缓冲或截断直接实时流。
  • 转写会通过 Discord ingress 和 routing 发送,而响应 LLM 会使用一个语音输出策略:隐藏 agent 的 tts 工具并要求返回文本,因为 Discord 语音负责最终的 TTS 播放。
  • voice.model 在设置时,只会覆盖此语音频道轮次的响应 LLM。
  • voice.tts 会覆盖合并到 tts 之上;支持流式的 provider 会直接把音频送给播放器,否则生成的音频文件会在已加入的频道中播放。

默认 agent-proxy 语音频道会话示例:

json5
{  channels: {    discord: {      voice: {        enabled: true,        model: "openai/gpt-6-astra",        followUsersEnabled: true,        followUsers: ["123456789012345678"],        realtime: {          provider: "openai",          model: "gpt-realtime-2.1",          speakerVoice: "cedar",        },      },    },  },}

在没有 voice.agentSession 块时,每个语音频道都有自己的路由后 OpenClaw 会话。例如,/vc join channel:234567890123456789 会与该 Discord 语音频道对应的会话通信。实时模型只是语音前端;实质请求会交给配置好的 OpenClaw agent。如果实时模型在没有调用 consult 工具的情况下生成了最终转写,OpenClaw 会强制 consult 作为回退,因此默认行为仍然像是在与 agent 对话。

旧版 STT 加 TTS 示例:

json5
{  channels: {    discord: {      voice: {        enabled: true,        mode: "stt-tts",        model: "openai/gpt-5.4-mini",        tts: {          provider: "openai",          providers: {            openai: {              model: "gpt-4o-mini-tts",              speakerVoice: "cedar",            },          },        },      },    },  },}

实时 bidi 示例:

json5
{  channels: {    discord: {      voice: {        enabled: true,        mode: "bidi",        model: "openai/gpt-6-astra",        realtime: {          provider: "openai",          model: "gpt-realtime-2.1",          speakerVoice: "cedar",          toolPolicy: "safe-read-only",          consultPolicy: "always",        },      },    },  },}

将语音作为现有 Discord 频道会话的扩展:

json5
{  channels: {    discord: {      voice: {        enabled: true,        mode: "agent-proxy",        model: "openai/gpt-6-astra",        agentSession: {          mode: "target",          target: "channel:123456789012345678",        },        realtime: {          provider: "openai",          model: "gpt-realtime-2.1",          speakerVoice: "cedar",        },      },    },  },}

在 agent-proxy 模式下,机器人会加入配置的语音频道,但 OpenClaw agent 的轮次会使用目标频道的正常路由会话和 agent。实时语音会话会把返回结果重新播回语音频道。监督 agent 仍可根据其 tool policy 使用正常消息工具,包括在合适时发送单独的 Discord 消息。

在实时语音通话期间,要求客服人员列出可用的语音或切换到一种语音。 talk_voice 工具更改当前房间的语音并重新连接其实时提供商连接,同时保留 Discord 连接、对话和正在进行的座席工作。更换准备就绪后,代理确认变更。如果切换失败,OpenClaw 保留或恢复之前的语音。如果恢复也失败,会要求您重新加入语音;已接受的代理工作仍可完成。该选择适用于该房间中的后续扬声器,并持续到房间断开连接为止; voice.realtime.speakerVoice 仍然是未来调用的默认值。这需要所有者授权的语音转向、代理委派以及对 talk_voice 工具的访问。

通话中的更改需要提供商语音目录。 Google Live 支持相同的 talk_voice 流程:OpenClaw 打开与所选语音的新连接,并通过 Discord 呼叫保持未完成的座席工作。替换后等待发送的答案。已经提交到先前连接的响应可能会被交换机中断并且不会重播,从而避免重复语音。

当委派的 OpenClaw 运行处于活动状态时,命令授权的 Discord 对话记录在开始另一个座席轮流之前将被视为实时运行控制。诸如“状态”、“取消该”、“使用较小的修复”或“完成后还检查测试”之类的短语被分类为活动会话的状态、取消、转向或后续输入。状态、取消、接受的转向和后续结果都会通过语音通道讲回,以便呼叫者知道 OpenClaw 是否处理了请求。

当 OpenClaw 取消委托咨询时,Discord 记录取消而不是失败,并且不播放通用错误回退。匹配较晚的提供程序工具调用会收到相同的终端取消,而不是重新启动工作。语音会话仍可用于下一个请求;超时和真正的故障保持正常的错误处理。

有用的 target 形式:

  • target: "channel:123456789012345678" 通过 Discord 文本频道会话路由。
  • target: "123456789012345678" 会被视为频道目标。
  • target: "dm:123456789012345678" 或 target: "user:123456789012345678" 通过该私信会话路由。

回声较重的 OpenAI Realtime 示例:

json5
{  channels: {    discord: {      voice: {        enabled: true,        mode: "bidi",        model: "openai/gpt-6-astra",        realtime: {          provider: "openai",          model: "gpt-realtime-2.1",          speakerVoice: "cedar",          bargeIn: true,          minBargeInAudioEndMs: 500,          consultPolicy: "always",          providers: {            openai: {              interruptResponseOnInputAudio: false,            },          },        },      },    },  },}

当模型通过打开的麦克风听到自己的 Discord 播放,但您仍然想通过说话来打断它时,请使用此功能。 OpenClaw 防止 OpenAI 在原始输入音频上自动中断,而 bargeIn: true 允许 Discord 扬声器启动事件和已经活动的扬声器音频在下一个捕获的回合到达 OpenAI 之前取消活动的实时响应。 audioEndMs 低于 minBargeInAudioEndMs 的非常早期的插入信号被视为可能的 echo/noise 并被忽略,因此模型不会在第一个播放帧处中断。

预期的语音日志:

  • 加入时:discord voice: joining ... voiceSession=... supervisorSession=... agentSessionMode=... voiceModel=... realtimeModel=...
  • realtime 开始时:discord voice: realtime bridge starting ... autoRespond=false interruptResponse=false bargeIn=false minBargeInAudioEndMs=...
  • 有说话者音频时:discord voice: realtime speaker turn opened ...、discord voice: realtime input audio started ... outputAudioMs=... outputActive=...,以及 discord voice: realtime speaker turn closed ... chunks=... discordBytes=... realtimeBytes=... interruptedPlayback=...
  • 跳过陈旧语音时:discord voice: realtime forced agent consult skipped reason=incomplete-transcript ... 或 reason=non-actionable-closing ...
  • 实时响应完成时:discord voice: realtime audio playback finishing reason=completed ... audioMs=... chunks=...;在此行之后仍然可以播放缓冲的音频。
  • 普通播放反压上:discord voice: realtime audio playback buffering ... bufferedBytes=...;当 Discord 耗尽缓冲音频时,播放继续。
  • Discord 在使用 PCM 后确认范围内的提供者播放标记。 xAI 在开始以下响应之前等待这些确认;清除丢弃的音频不会将其报告为已播放。
  • 播放 stop/reset: discord voice: realtime audio playback stopped reason=... audioMs=... elapsedMs=... chunks=...
  • realtime consult 时:discord voice: realtime consult requested ... voiceSession=... supervisorSession=... question=...
  • agent 答复时:discord voice: agent turn answer ...
  • 排队的精确语音时:discord voice: realtime exact speech queued ... queued=... outputAudioMs=... outputActive=...,随后是 discord voice: realtime exact speech dequeued reason=player-idle ...
  • 检测到 barge-in 时:discord voice: realtime barge-in detected source=speaker-start ... 或 discord voice: realtime barge-in detected source=active-speaker-audio ...,随后是 discord voice: realtime barge-in requested reason=... outputAudioMs=... outputActive=...
  • realtime 中断时:discord voice: realtime model interrupt requested client:response.cancel reason=barge-in,随后会出现 discord voice: realtime model audio truncated client:conversation.item.truncate reason=barge-in audioEndMs=... 或 discord voice: realtime model interrupt confirmed server:response.done status=cancelled ...
  • On 忽略 echo/noise: discord voice: realtime model interrupt ignored client:conversation.item.truncate.skipped reason=barge-in audioEndMs=0 minAudioEndMs=250
  • 禁用打断:discord voice: capture ignored: ... reason=protected playback
  • 空闲播放时:discord voice: realtime barge-in ignored reason=... outputActive=false ... playbackChunks=0

若要调试音频被截断,请按时间线阅读 realtime 语音日志:

  1. realtime audio playback started 表示 Discord 已开始播放 assistant 音频。从此时起,bridge 开始统计 assistant 输出 chunk、Discord PCM 字节、provider realtime 字节以及合成音频时长。
  2. realtime speaker turn opened 标记 Discord 中某个说话者变为活跃状态。如果播放已在进行且启用了 bargeIn,这之后可能会出现 barge-in detected source=speaker-start。
  3. realtime input audio started 标记该说话者轮次收到的第一帧实际音频。这里若 outputActive=true 或 outputAudioMs 非零,表示麦克风在 assistant 播放仍然活跃时正在输入。
  4. barge-in detected source=active-speaker-audio 表示 OpenClaw 在 assistant 播放活跃时看到实时说话者音频。这有助于区分真正的中断和只是 Discord 说话开始事件但没有有效音频的情况。
  5. barge-in requested reason=... 表示 OpenClaw 要求实时提供者取消或截断主动响应。 outputAudioMs和playbackChunks描述生成的音频;随后的 audioEndMs 截断字段记录每个本机项目消耗的音频。 Discord 测量 AudioPlayer 准备的 Opus 数据包的本地消耗,粒度为 20 ms;它无法测量远程监听器的播放。
  6. realtime audio playback stopped reason=... 是本地 Discord 播放复位点。 player-idle表示Discord已完成音频消费;仅提供者 response.done 和编码器完成并不意味着播放已完成。其他原因包括 barge-in、provider-clear-audio、forced-agent-consult、stream-close、output-audio-overflow 和 session-close。
  7. realtime speaker turn closed 会总结捕获到的输入轮次。chunks=0 或 hasAudio=false 表示说话者轮次已打开,但没有可用音频到达 realtime bridge。interruptedPlayback=true 表示该输入轮次与 assistant 输出重叠并触发了 barge-in 逻辑。

有用字段:

  • outputAudioMs:realtime provider 在该日志行之前生成的 assistant 音频时长。
  • audioMs:OpenClaw 在播放停止前统计的 assistant 音频时长。
  • elapsedMs:打开和关闭播放流或说话者轮次之间的墙钟时间。
  • discordBytes:发送到或从 Discord voice 接收的 48 kHz 立体声 PCM 字节数。
  • realtimeBytes:发送到或从 realtime provider 接收的 provider 格式 PCM 字节数。
  • playbackChunks:当前响应转发到 Discord 的 assistant 音频 chunk 数。
  • sinceLastAudioMs:最后一帧捕获到的说话者音频与说话者轮次关闭之间的间隔。

常见模式:

  • 立即截断,且 source=active-speaker-audio、outputAudioMs 很小,并且附近是同一用户,通常说明扬声器回声进入了麦克风。可提高 voice.realtime.minBargeInAudioEndMs、降低扬声器音量、使用耳机,或设置 voice.realtime.providers.openai.interruptResponseOnInputAudio: false。
  • source=speaker-start 后接 speaker turn closed ... hasAudio=false,表示 Discord 报告了说话开始,但没有音频到达 OpenClaw。这可能是临时的 Discord voice 事件、噪声门行为,或客户端短暂触发麦克风。
  • audio playback stopped reason=output-audio-overflow 表示持续交付问题超出了有界的待处理音频队列。检查关联的 Discord realtime audio playback overflow 错误和前面的提供程序或 Discord 连接诊断;普通播放背压不应产生此错误。
  • Discord voice receive backlog exceeded 表示身份查找、解码或本地磁盘无法跟上传入的音频。 OpenClaw 将每个接收流限制为 1,000 个待处理数据包和 1 MiB 编码音频;等瓶颈消除后再说话。其他发言者和任何已注册的捕获保持活动状态,不完整的语音不能成为新的对话命令。
  • conversation audio backlog exceeded、conversation is busy 或 conversation transcript limit exceeded 表示已达到会话限制。录音继续;等待待处理的请求完成或重复较短的完整请求。
  • Discord voice recording backlog exceeded 表示待处理的 WAV 文件已达到记录队列的块或字节限制。受影响的话语停止,并且在待处理的音频处理赶上后,注册的捕获仍然可用。
  • audio playback stopped reason=stream-close 且附近没有 barge-in 或 provider-clear-audio,表示本地 Discord 播放流意外结束。请检查前面的 provider 和 Discord 播放器日志。
  • capture ignored: ... reason=protected playback 表示 OpenClaw 在助理音频处于活动状态时故意保留对话输入。明确开始的捕获仍然记录该演讲。如果您希望语音中断播放,请启用 voice.realtime.bargeIn。
  • barge-in ignored ... outputActive=false 表示 Discord 或 provider VAD 报告了语音,但 OpenClaw 没有可中断的活动播放。这不应导致音频被截断。

凭据按组件进行解析:voice.model 的 LLM 路由身份验证、tools.media.audio 的 STT 身份验证、tts/voice.tts 的 TTS 身份验证以及 voice.realtime.providers 或提供商的正常身份验证的实时提供商身份验证身份验证配置。

本页有帮助吗?