- 人工智能
- AI Agent
- MCP 服务
- 工具调用
- GUI 自动化
- 语音
- 数字人
- 交互助手
【免费下载链接】airi
💖🧸 自托管、归你拥有的 Grok 风格 AI 伴侣与 waifu / 赛博生命灵魂容器,目标是接近 Neuro-sama 的高度;支持实时语音聊天、Minecraft 和 Factorio 游玩,支持 Web / macOS / Windows。
本指南围绕 integrations/discord-bot/README.md 展开,讲解如何将 Airi(アイリ)以 Discord Bot 的形式接入你的服务器:用户既可以在文字频道通过 @提及或私信与 Airi 对话,也可以在语音频道直接开口说话,让 Airi 实时聆听、转写并回复。读完本文,你将掌握完整的部署流程(Discord 开发者后台配置、环境变量、启动命令),并理解其背后的双向桥接架构、语音处理管线(Opus 解码 → 转写 → 上下文注入)与可热更新的配置机制,可直接照抄配置跑通一个属于自己的 Discord AI 角色。
一、discord-bot 在 Airi 项目中的定位
integrations/discord-bot是 Airi 官方仓库中的一个独立集成模块(pnpm workspace 中的@proj-airi/discord-bot),其目标正如 README 第一行所述:让 Airi 能在 Discord 语音频道中与你以及众多其他用户对话。它并不自己实现模型推理,而是作为一个"桥接适配器":
- 面向 Discord 一侧,使用
discord.js与@discordjs/voice负责登录、收发消息、加入语音频道、订阅用户音频; - 面向 Airi 一侧,使用工作区内部的 server-sdk 客户端(
@proj-airi/server-sdk)通过 WebSocket 连接 Airi 的 server-runtime,发送input:text/input:text:voice等输入事件,并订阅output:gen-ai:chat:message等输出事件。
换句话说,Discord 只是 Airi 的"一张嘴"和"一对耳朵",真正的角色人格、记忆与模型对话能力由 Airi 核心服务提供。这也解释了为什么package.json中同时依赖了discord.js、@discordjs/voice和@proj-airi/server-sdk、@proj-airi/server-shared。
二、工作原理总览:Discord ⇄ Airi 的双向事件桥
从 airi-adapter.ts 的构造函数可以看出整个桥的核心是两个客户端对象:
discordClient:discord.js的Client,intents 包含Guilds、GuildVoiceStates、GuildMessages、MessageContent、DirectMessages,并启用Partials.Channel;airiClient:ServerChannel(即 server-sdk 的Client),以名称discord注册,声明了它关心的possibleEvents:input:text(文字输入)input:text:voice(语音转写文本输入)input:voice(原始语音输入)module:configure(来自 UI 的配置下发)output:gen-ai:chat:message(Airi 的对话回复)
数据流大致如下:
- 用户在 Discord 文字频道 @ 机器人或发私信 → 适配器组装
discord元数据并发送input:text; - 用户在语音频道说话 → 音频被解码、缓冲、转写为文字 → 发送
input:text:voice与input:text; - Airi 核心生成回复 → 广播
output:gen-ai:chat:message→ 适配器按事件中的discord.channelId把文本回复发送回对应频道(超过 2000 字符自动分块)。
这些事件类型在协议层有严格定义,参见 plugin-protocol 的事件类型(WebSocketEventInputText、WebSocketEventInputTextVoice、WebSocketEventInputVoice、OutputGenAiChatMessageEvent),其中discord输入源的元数据结构为Discord(含guildMember、guildId、guildName、channelId)。
注意:
input:text:voice的数据结构为{ transcription, ... },input:voice的数据结构为{ audio: ArrayBuffer, ... },而input:text的数据结构为{ text, textRaw, overrides, contextUpdates, ... },三者面向不同粒度的输入场景。
三、快速开始:创建 Discord 应用并开启所需 Intents
3.1 环境准备
该模块是 Airi monorepo 的一部分,需要先获取仓库并安装依赖(依赖管理使用 pnpm workspace):
git clone git@github.com:moeru-ai/airi.git pnpm i安装完成后进入模块目录即可看到.env模板文件:
cd integrations/discord-bot cp .env .env.local3.2 在 Discord Developer Portal 创建应用
- 打开 Discord Developer Portal(discord.com/developers),点击New Application创建一个新应用——这个应用就是稍后要添加到你服务器的机器人。
- 进入Bot标签页,找到Privileged Gateway Intents(特权网关意图)区域,打开以下两项开关:
- Server Members Intent(服务器成员意图)
- Message Content Intent(消息内容意图)
这两个 Intents 与 airi-adapter.ts 中discordClient的配置一一对应:GuildMessages+MessageContent用于读取消息内容,GuildVoiceStates用于感知语音频道状态,而 Server Members Intent 用于在summon.ts中通过guild.members.fetch(userId)获取说话成员信息。
- 在Privileged Gateway Intents区域上方是Token区域:对于新建的机器人,点击Reset Token生成一个新 Token,复制保存备用。
- 注意:如果日后忘记或丢失 Token,随时可以再次点击Reset Token重新生成;但生成后需要同步更新到
.env.local(或通过下文介绍的 UI 配置方式更新),否则机器人无法登录。
- 注意:如果日后忘记或丢失 Token,随时可以再次点击Reset Token重新生成;但生成后需要同步更新到
提示:若勾选这些特权 Intent 后机器人仍无法读取消息,请检查应用是否处于审核模式且机器人不在大型服务器上;本地开发/测试建议在小规模私人服务器中进行。
四、环境变量配置详解
创建.env.local后,按 README 要求填写如下凭证。DISCORD_TOKEN与DISCORD_BOT_CLIENT_ID是必填项,其余为 AI 能力相关配置:
DISCORD_TOKEN='' DISCORD_BOT_CLIENT_ID='' OPENAI_MODEL='' OPENAI_API_KEY='' OPENAI_API_BASE_URL='' ELEVENLABS_API_KEY='' ELEVENLABS_API_BASE_URL=''下表汇总了 README 与仓库内.env模板的全部配置项及其在源码中的实际用途:
| 配置项 | 用途 | 源码依据 |
|---|---|---|
DISCORD_TOKEN | Discord 机器人登录令牌,用于client.login() | airi-adapter.ts |
DISCORD_BOT_CLIENT_ID | 机器人应用 ID(README 要求配置) | .env模板;从源码结构看,斜杠命令注册时动态使用登录后的readyClient.user.id |
OPENAI_MODEL/OPENAI_API_KEY/OPENAI_API_BASE_URL | 对话模型相关配置(README 要求配置) | .env模板 |
OPENAI_STT_API_BASE_URL/OPENAI_STT_API_KEY/OPENAI_STT_MODEL | 远程语音转写(STT)服务配置,用于语音频道转写 | tts.ts 中的openaiTranscribe() |
ELEVENLABS_API_KEY/ELEVENLABS_API_BASE_URL | TTS 语音合成服务预留配置 | .env模板 |
AIRI_TOKEN | Airi 服务端 WebSocket 鉴权令牌,默认'abcd' | index.ts |
AIRI_URL | Airi server-runtime 的 WebSocket 地址,默认ws://localhost:6121/ws | index.ts |
两点补充说明(均以仓库源码为准):
AIRI_TOKEN与AIRI_URL不在 README 的配置清单里,但入口文件 index.ts 在构造DiscordAdapter时提供了默认值兜底:airiToken: env.AIRI_TOKEN || 'abcd'、airiUrl: env.AIRI_URL || 'ws://localhost:6121/ws'。默认地址6121端口与 server-runtime 的默认监听配置({ port: 6121, hostname: '127.0.0.1' })一致,即本地跑起 Airi 服务端后无需额外配置即可连通。- STT 是独立配置组:
OPENAI_MODEL/API_KEY/API_BASE_URL用于对话生成,而语音转写走的是OPENAI_STT_*三个变量。此外 tts.ts 还内置了一个基于@huggingface/transformers的本地 Whisper 管线(Xenova/whisper-medium.en,16kHz、32 位浮点输入),可在无云端 STT 时离线转写;当前语音频道流程(summon.ts)实际调用的是openaiTranscribe()。
五、启动机器人
在仓库根目录(或模块目录)执行:
pnpm run -F @proj-airi/discord-bot start对应的 npm script 在 package.json 中定义为:
"start": "tsx --env-file=.env --env-file-if-exists=.env.local src/index.ts"tsx负责直接运行 TypeScript 入口,--env-file=.env与--env-file-if-exists=.env.local依次加载模板与本地覆盖配置(if-exists意味着.env.local不存在也不会报错)。启动流程见 index.ts:
- 设置全局日志格式与级别(
@guiiai/logg的Format.Pretty/LogLevel.Log); - 用环境变量构造
DiscordAdapter; - 调用
adapter.start():若DISCORD_TOKEN存在则直接登录 Discord,否则仅启动 WebSocket 客户端并等待 UI 下发配置; - 注册
SIGINT/SIGTERM处理,收到信号后调用adapter.stop()优雅退出(销毁 Discord 客户端并关闭 WebSocket)。
启动成功后,Discord 机器人上线时会打印Discord bot ready! User: ...,并自动通过 REST API 注册两个斜杠命令:
/ping:回复Pong!,用于连通性测试(见 ping.ts);/summon:把机器人召唤到你当前所在的语音频道(见 summon.ts 的handleJoinChannelCommand)。
六、文字对话:@提及、私信与会话隔离
文字消息处理逻辑位于 airi-adapter.ts 的Events.MessageCreate监听器中,触发条件有两个:
- 消息 @ 了机器人(
message.mentions.has(this.discordClient.user)); - 消息是私信(
!message.guild)。
机器人自己的消息会被跳过(if (message.author.bot) return)。@ 提及时会用正则/<@!?\d+>/g把消息中的提及语法剥掉,只把剩余正文发给 Airi;私信则直接使用原文。
发送给 Airi 的input:text事件包含三块关键信息(对应协议类型 WebSocketEventInputText):
overrides.messagePrefix:注入"这句话来自谁"的前缀,例如(From Discord user xxx on server 'yyy'):或(From Discord user xxx in Direct Message):,让角色模型知道上下文来源;overrides.sessionId:会话隔离的依据。有guildId时使用discord-guild-{guildId},私信时使用discord-dm-{userId},保证不同服务器、不同私信对各自保持独立记忆;contextUpdates:采用ContextUpdateStrategy.AppendSelf策略,把discord元数据(频道 ID、服务器 ID、成员显示名/昵称)追加进上下文,供后续输出路由使用。
回复链路同样在 airi-adapter.ts 中:监听output:gen-ai:chat:message,从事件数据中取出message.content和gen-ai:chat.input.data.discord.channelId,然后channels.fetch对应频道并发送文本。若回复超过 Discord 单条消息的2000 字符上限,会按"换行 → 空格 → 硬切"的优先级自动分块发送。
七、语音对话:从 Opus 音频到文本回复的完整管线
语音能力是integrations/discord-bot最有技术含量、也是 README 定位("在语音频道与你和其他用户对话")的核心所在,实现在 summon.ts 的VoiceManager中。整条管线分为四个阶段:
7.1 加入语音频道
/summon命令触发handleJoinChannelCommand:校验调用者当前是否在语音频道(不在则回复Please join a voice channel first.),随后调用joinChannel():
- 若该服务器已有旧连接,先
destroy()并清理所有 stream 与 monitor; - 通过
joinVoiceChannel()(来自@discordjs/voice)以selfDeaf: false、selfMute: false加入,确保机器人能正常"听到"并"说话"; - 用
Promise.race([entersState(Ready), entersState(Signalling)])在20 秒超时内确认连接建立,成功后回复Joined: {channel}.; - 为连接注册
stateChange/error监听,并在receiver.speaking上监听用户的start/end说话事件; - 若机器人有
DeafenMembers权限,会尝试setDeaf(false)与setMute(false)确保自身不静音。
断线处理也内置了自动重连:状态变为Disconnected时先尝试 5 秒内恢复到Signalling或Connecting,若超时则判定为真断开,销毁连接并清理资源。
7.2 音频接收与 Opus 解码
当用户开始说话,monitorMember()通过connection.receiver.subscribe(userId)订阅该用户的音频流,随后:
- 用 opus.ts 中的
OpusDecoder(基于opusscript,采样率DECODE_SAMPLE_RATE = 16000、单声道,见 audio.ts 常量)把 Opus 数据解码为 PCM; - 通过
pipeline()把接收流接到解码器上,形成"Discord 接收流 → OpusDecoder → PCM"的 Node 流式管线; - 解码出的 PCM 数据由 audio-monitor.ts 的
AudioMonitor缓冲(默认上限 10,000,000 字节,超出则从头部丢弃),并在speakingStopped或流结束时把缓冲数据作为完整片段交给下一步。
7.3 缓冲、防抖与转写
缓冲数据进入handleUserStream(),每收到一段就刷新"最后活跃时间"并触发debouncedProcessTranscription():
- 防抖窗口 1500ms:等待用户说完(1.5 秒静默)再开始转写,避免把半句话提前送进 STT;
- 若机器人正在播放回复(
activeAudioPlayer存在)或正在处理上一次转写(processingVoice),则丢弃新缓冲,防止自问自答; - 防抖到期后调用
processTranscription():Buffer.concat合并本次缓冲;- 通过 audio.ts 的
convertOpusToWav()生成 44 字节 WAV 头(16bit PCM 单声道)并拼接成完整 WAV; - 调用 tts.ts 的
openaiTranscribe()(Blob封装 +@xsai/generate-transcription),使用OPENAI_STT_MODEL完成语音转写; - 校验转写结果非空且不含
[BLANK_AUDIO]占位(isValidTranscription); - 同时发送
input:text:voice(带transcription字段)与input:text(带text字段)两个事件给 Airi,均携带discord上下文。
7.4 回复播放与打断机制
VoiceManager同时提供了playAudioStream()用于播放 TTS 音频(createAudioPlayer+createAudioResource,NoSubscriberBehavior.Pause),并且在 PCM 数据监听中内置了语音打断逻辑:当机器人正在播放时,计算用户音频的滑动窗口音量(VOLUME_WINDOW_SIZE = 30帧,SPEAKING_THRESHOLD = 0.05),一旦用户音量均值超过阈值就cleanupAudioPlayer()停止当前播放,把说话权交还给用户。该设计借鉴了 elizaOS 的 client-discord voice 实现(源码注释中明确标注了出处)。
需要说明的是:
package.json已声明@xsai/generate-speech依赖、.env也预留了ELEVENLABS_API_KEY/ELEVENLABS_API_BASE_URL,且playAudioStream已就绪;但从当前源码结构看,output:gen-ai:chat:message处理器仅把文本回复发回文字频道,尚未发现把回复文本送入 TTS 生成并调用playAudioStream的接线代码——即语音"听"(STT)链路完整,而"说"(TTS)链路已具备全部构件、有待进一步接入。
八、通过 UI 热更新配置:module:configure
除了.env.local,机器人还支持运行时从 Airi 的 UI 下发配置。适配器订阅了module:configure事件(见 airi-adapter.ts),配置对象形如{ token?: string, enabled?: boolean },并经过isDiscordConfig类型守卫校验。其行为矩阵如下:
| 配置 | 行为 |
|---|---|
enabled === false | 若机器人已就绪则client.destroy()下线 |
enabled === true但无token | 警告并停止机器人(如果正在运行) |
token变化或客户端未就绪 | 销毁旧连接,用新 Token 重新login() |
| 配置非法 | 打日志跳过,不影响现有连接 |
整个处理用isReconnecting标志做了重入保护:若上一次配置尚未处理完,新的module:configure事件会被跳过并记录警告。这也呼应了 README 的提示——Token 丢失时既可以直接改.env.local,也可以"通过 UI 配置"完成更新。
九、事件协议一览
以下事件是discord-bot与 Airi 核心之间通信的全部协议面(声明于DiscordAdapter的possibleEvents,类型定义见 plugin-protocol/src/types/events.ts):
| 事件名 | 方向 | 数据要点 | 触发场景 |
|---|---|---|---|
input:text | Bot → Airi | text、overrides、contextUpdates、discord | 文字频道 @ 提及 / 私信;语音转写结果二次送入 |
input:text:voice | Bot → Airi | transcription、discord | 语音频道说话并完成转写 |
input:voice | Bot → Airi | audio: ArrayBuffer | 原始语音输入(已声明,当前流程未实际发送) |
module:configure | Airi → Bot | config: { token, enabled } | UI 下发 Discord 配置,支持热更新/重连 |
output:gen-ai:chat:message | Airi → Bot | message.content、gen-ai:chat.input.data.discord | Airi 回复生成,路由回对应 Discord 频道 |
十、参考与致谢
本模块的实现站在了多个开源项目的肩膀上(详见 README 致谢段):
- 音频接收与处理实现参考了 CharacterAIVoice 项目的
VoiceHandler.js; - 机器人骨架与语音示例参考了 discord.js 官方的
voice-examples; - 底层使用
discord.js与@discordjs/voice这两个优秀库; - 语音监听与打断的部分思路在源码注释中标注了与 elizaOS
client-discord/src/voice.ts的渊源。
如果你想快速验证整条链路,建议按以下顺序自测:先确认 Airi server-runtime 已在本机6121端口运行 → 启动discord-bot→ 在服务器中/ping验证连通 → @ 机器人发送一句文字验证文字对话 → 进入语音频道/summon验证语音聆听与转写。全部通过后,一个"住在 Discord 里的 Airi"就正式上线了。
- 人工智能
- AI Agent
- MCP 服务
- 工具调用
- GUI 自动化
- 语音
- 数字人
- 交互助手
【免费下载链接】airi
💖🧸 自托管、归你拥有的 Grok 风格 AI 伴侣与 waifu / 赛博生命灵魂容器,目标是接近 Neuro-sama 的高度;支持实时语音聊天、Minecraft 和 Factorio 游玩,支持 Web / macOS / Windows。
相关推荐
Project AIRI Discord Bot 集成指南:让 AI 伴侣在 Discord 语音与文字频道中与你实时对话
Project AIRI Discord Bot 集成指南:让 AI 伴侣在 Discord 语音与文字频道中与你实时对话 导读 本文面向希望为 Project
人工智能AI AgentMCP 服务工具调用GUI 自动化语音数字人交互助手后端AIRI Discord 集成实战指南:将自托管 AI 伴侣接入 Discord 文字与语音频道
AIRI Discord 集成实战指南:将自托管 AI 伴侣接入 Discord 文字与语音频道 本文以 AIRI 官方文档 Discord Bot https
人工智能AI AgentMCP 服务工具调用GUI 自动化语音数字人交互助手后端如何用 Wand-Enhancer 免费解锁 WeMod Pro:从本地构建到完成修补的完整指南
如何用 Wand Enhancer 免费解锁 WeMod Pro:从本地构建到完成修补的完整指南 Wand Enhancer 是一个开源的 WeMod 本地修补
桌面应用前端AI 应用
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考