news 2026/9/10 12:26:58

AIRI Satori Bot 集成指南:通过 Koishi 与 Satori 协议桥接 QQ、Telegram、Discord、Lark 多平台消息

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
AIRI Satori Bot 集成指南:通过 Koishi 与 Satori 协议桥接 QQ、Telegram、Discord、Lark 多平台消息

AIRI Satori Bot 集成指南:通过 Koishi 与 Satori 协议桥接 QQ、Telegram、Discord、Lark 多平台消息

【免费下载链接】airi💖🧸 Self hosted, you-owned Grok Companion, a container of souls of waifu, cyber livings to bring them into our worlds, wishing to achieve Neuro-sama's altitude. Capable of realtime voice chat, Minecraft, Factorio playing. Web / macOS / Windows supported.项目地址: https://gitcode.com/GitHub_Trending/ai/airi

本篇技术指南围绕 AIRI 仓库中的 Satori Bot 集成模块(integrations/satori-bot)展开,讲解如何通过 Koishi 的 server-satori 服务,将 AIRI 的 AI 智能体能力桥接到 QQ、Telegram、Discord、Lark 等聊天平台。读完本文,你将掌握 Satori Bot 的环境配置、启动方式、事件驱动的自治思考循环架构,以及消息从 WebSocket 进入到 LLM 决策、再到平台回复的完整调用链。

Satori Bot 是什么

Satori Bot 是 AIRI 仓库中的一个独立(STANDALONE)集成模块,定位为「基于 Satori 协议、事件驱动的 AI 智能体」。它本身不直接对接任何聊天平台的私有 API,而是通过Koishi 的 server-satori 插件作为统一桥接层,以一套协议同时连接多个平台。按照 integrations/satori-bot/docs/HANDLER.md 的说明,该模块运行在「事件驱动 + 自治循环」的混合模型之上:

  • 事件层(Event Layer):处理原始 WebSocket 信号,负责去重与排队;
  • 调度层(Scheduler Layer):消费事件队列,维护内部的「未读池(Unread Pool)」状态,并触发按频道隔离的处理循环;
  • 规划层(Planner Layer):LLM 扮演 Agent,观察「未读池」与「历史动作(History Actions)」状态,自主决定下一步是read_unread_messages(观察)还是send_message(行动)。

需要特别强调的是模块当前所处的过渡阶段:其src/core/下的 Event Loop、Scheduler、Planner 逻辑是临时占位实现,用于独立运行实验,模拟未来 AIRI Core 的行为,不应视为稳定的 AIRI Core 集成。而Dispatcher 与 Database 会被保留,未来将作为「工具型模块」暴露给 AIRI Core 执行动作与状态持久化;一旦主框架稳定,src/core/的循环/规划逻辑将被移除,本模块将重构为纯粹的Adapter(Satori 协议处理)Capability Provider(动作提供)。当前的临时实现适合实验与维护用途。

前置条件

根据 integrations/satori-bot/README.md 与关联文档,运行 Satori Bot 需要满足以下条件:

前置条件说明
Node.js>= 18.0.0
pnpm>= 8.0.0(且需从仓库根目录执行pnpm i安装依赖)
Koishi 实例运行了server-satori插件的 Koishi 服务,Satori Bot 通过它连接各聊天平台
LLM 服务提供 OpenAI 兼容 API 的模型服务(如 Ollama、vLLM、DeepSeek 等)

依赖统一通过 pnpm workspace 管理,包名为@proj-airi/satori-bot(见 integrations/satori-bot/package.json),其依赖包括@xsai/generate-text(LLM 文本生成)、@electric-sql/pglitedrizzle-orm(持久化)、valibot(配置与协议校验)、ws(WebSocket 客户端)等。

::: warning 凭据安全 Satori token、聊天平台凭据、模型 API Key 等敏感信息只能保存在本地的.env.local文件中,不要提交到版本库、截图或分享给他人。仓库的.gitignore已将该文件排除在版本控制之外。 :::

配置环境变量

配置的第一步是复制示例文件并编辑:

cp integrations/satori-bot/.env integrations/satori-bot/.env.local

然后编辑integrations/satori-bot/.env.local,填写以下变量(示例文件):

# Satori Configuration SATORI_WS_URL=ws://localhost:5140/satori/v1/events SATORI_API_BASE_URL=http://localhost:5140/satori/v1 SATORI_TOKEN=your_satori_token_here # LLM Configuration LLM_API_KEY=your_api_key_here LLM_API_BASE_URL=https://api.openai.com/v1 LLM_MODEL=gpt-4 LLM_RESPONSE_LANGUAGE=English LLM_OLLAMA_DISABLE_THINK=false

各变量含义与默认值

变量必填默认值说明
SATORI_WS_URLws://localhost:5140/satori/v1/eventsKoishi server-satori 暴露的 WebSocket 事件端点,用于接收实时事件
SATORI_API_BASE_URL由 WS URL 推导(/v1/events换成 HTTP 前缀)Satori HTTP API 的基地址,用于发送消息等 REST 调用
SATORI_TOKENSatori 鉴权 token;若 Koishi 未开启鉴权可留空
LLM_API_KEYLLM 服务的 API Key
LLM_API_BASE_URLhttps://api.openai.com/v1OpenAI 兼容 API 的基地址
LLM_MODELgpt-4使用的模型名称
LLM_RESPONSE_LANGUAGEEnglish回复语言偏好
LLM_OLLAMA_DISABLE_THINKfalse针对 Ollama 等推理模型的开关:为true时在请求中附加think: false禁用思考过程
DB_PATHdata/pglite-dbPGlite 数据目录路径

配置加载与校验机制

环境变量由 integrations/satori-bot/src/config.ts 统一读取,并用valibot定义ConfigSchema做运行时校验。核心要点:

  • SATORI_WS_URL缺省时回退到ws://localhost:5140/satori/v1/events,因此本地默认 Koishi 配置下甚至可以省略;
  • parseBoolean会将字符串'true'/'1'解析为布尔值(大小写不敏感),用于LLM_OLLAMA_DISABLE_THINK
  • 校验失败时,进程会打印每条path: message形式的错误详情并以exit(1)退出,例如 LLM 相关变量缺失时能快速定位问题;
  • 在 integrations/satori-bot/src/planner/llm-client.ts 中,还会针对「API Key」关键字错误给出专门提示,引导检查.env.local中的LLM_API_KEYLLM_API_BASE_URLLLM_MODEL

启动 Satori Bot

配置完成后,在仓库根目录执行:

# 开发模式(热重载) pnpm -F @proj-airi/satori-bot dev # 生产模式 pnpm -F @proj-airi/satori-bot start

从 package.json 的脚本定义可以看到,两种模式都是通过tsx加载环境变量后运行 src/index.ts:

"start": "tsx --env-file=.env --env-file-if-exists=.env.local src/index.ts", "dev": "tsx watch --env-file=.env --env-file-if-exists=.env.local src/index.ts"

注意--env-file-if-exists=.env.local意味着.env.local中的值会覆盖示例.env的默认值——这正是「示例配置提交到仓库、真实凭据只留在本地」这一安全策略的实现基础。

启动流程(见 src/index.ts)依次为:

  1. 初始化 PGlite 数据库(initDb,启动时自动执行 drizzle 迁移);
  2. 创建SatoriClient,注入wsUrltokenapiBaseUrl
  3. 创建BotContext,注册readymessage-created事件处理器;
  4. 连接 Satori 服务器(connect),向globalRegistry注册标准动作;
  5. 启动周期性循环(startPeriodicLoop)。

启动后可观察到 Debug 级别日志输出,方便验证「Connected to Satori server」「Periodic loop started」等关键状态。

架构与消息流:从 WebSocket 事件到 LLM 决策

入站阶段(Ingress):协议客户端与事件排队

位置:src/adapter/satori/client.ts → src/core/loop/queue.ts

SatoriClient是协议适配层(该部分将被永久保留)。它负责:

  • WebSocket 连接管理:建立连接后发送IDENTIFY信号(携带 token 与断线续传所需的sn序列号),并每 10 秒发送一次PING心跳;
  • 断线重连:连接断开后 5 秒自动重连,shouldReconnectfalse时才停止;
  • 协议校验:收到的每条原始 JSON 都用 valibot 的SatoriSignalSchemaSatoriReadyBodySchemaSatoriEventSchema做严格校验,非法数据会被拒绝并记录错误;
  • 多平台 API 客户端:收到READY信号后,根据ready.logins中每个{ platform, self_id }组合初始化独立的SatoriAPI客户端(key 为platform:self_id),发送消息时按此映射路由到对应平台;
  • 事件分发on()注册的事件处理器会被按类型触发,同时支持通配符'*'处理器,message-created事件由此进入消息处理链路。

随后在queue.tssetupMessageEventHandler中:以channelId-messageId为 key 检查processedIds集合去重,将{ event, status: 'ready' }推入botContext.eventQueue内存队列,同时通过pushToEventQueue持久化到数据库,保证崩溃后可续处理。

消费与锚定(Consumption):调度器与未读池

位置:src/core/loop/scheduler.ts(onMessageArrival函数)→ src/core/session/context.ts

当系统处理锁空闲时,调度器从队列消费事件:

  1. 提取channelId,调用ensureChatContext按需加载/创建该频道的ChatContext(以event.channel.id作为上下文主键);
  2. 检查selfId:如果发送者就是机器人自己,事件直接从队列移除并丢弃(不计入未读);
  3. 将事件推入botContext.unreadEvents[channelId]并持久化(pushToUnreadEvents),随后从持久化队列中删除(removeFromEventQueue);
  4. 立即调用loopIterationForChannel唤醒该频道的 Agent 循环。

推理阶段(Reasoning):LLM 决策

位置:src/core/loop/scheduler.ts → src/core/planner/llm-client.ts

LLM 的提示词设计核心是:不是让模型「回复这段文本」,而是让模型「基于状态决定下一步动作」。imagineAnAction构造的请求包含:

  • System:注入system-action-gen-v1(工具定义)与personality-v1(人格设定),来自 src/core/planner/prompts/ 下的.velin.md文件;
  • 短期记忆:注入chatContext.messages最近对话轮次;
  • 全局状态(关键):提示词显式写出"You have X unread events."并逐频道列出未读数量;
  • 入站注入:存在新流入事件时,以Incoming events:块追加到提示词末尾;
  • 动作历史:注入chatContext.actions,例如"Last action: read_messages, Result: Success",让 LLM 感知上次尝试的结果。

生成端使用@xsai/generate-text调用 OpenAI 兼容接口;LLM_OLLAMA_DISABLE_THINK=true时会在请求中附加think: false,随后清理<think>...</think>标签。返回文本会剥离 ```json 代码块围栏,用best-effort-json-parser容错解析,再经ActionSchema(valibot)校验,最终得到类似{"action": "read_unread_messages", "channelId": "..."}的严格 JSON 动作。

分发与执行(Dispatch):动作注册表

位置:src/core/dispatcher.ts → src/capabilities/registry.ts

系统根据 JSON 动作在globalRegistry中查找对应 Handler:

  • read_unread_messages(src/capabilities/actions/read-messages.ts):取回指定频道的全部积压未读事件,格式化为单一文本块(如[User]: Content)存入 Action Result,并清空该频道unreadEvents。下一个 Tick 中,LLM 会在 History Actions 里看到这段文本并生成回复;
  • send_message(src/capabilities/actions/send-message.ts):执行前会再次检查未读池——如果生成期间有新消息到达,会中止发送并返回[INTERRUPT]结果,引导 LLM 优先read_unread_messages获取新上下文;通过后调用satoriClient.sendMessageplatform:selfId路由到对应平台,并将回复通过recordMessage持久化到数据库。

循环延续(Loop Continuation)

dispatchAction返回带shouldContinue标志的ActionResult:若为true(通常在读完消息、期待回复时为真),调度器等待LOOP_CONTINUE_DELAY_MS(默认 2.5 秒)后递归调用handleLoopStep。为防止 LLM 幻觉或 API 滥用导致死循环,每轮循环设有硬上限MAX_LOOP_ITERATIONS = 5;达到上限、模型选择终止动作或shouldContinuefalse时循环结束。

持久化与状态恢复:PGlite + Drizzle

根据 integrations/satori-bot/docs/PERSISTENCE.md,模块已从 lowdb(JSON 文件)迁移到PGlite(PostgreSQL 的 WASM/Node 实现)+Drizzle ORM,采用「内存优先 + 关键数据落盘」策略:

  • 内存侧:活跃聊天上下文存放在BotContext中的原生Map<string, ChatContext>,通过ensureChatContext懒加载,进程结束前常驻;在handleLoopStep中对单频道做裁剪(MAX_ACTIONS_IN_CONTEXT = 50ACTIONS_KEEP_ON_TRIM = 20),消息历史动态从数据库取最近 10 条,保持 LLM 上下文精简;
  • 磁盘侧(src/lib/schema.ts):channels(频道元数据)、messages(持久消息日志,按channel_idtimestamp建索引)、event_queue(待处理事件持久队列)、unread_events(各频道未读事件存储)四张表;
  • 增量 I/O:队列项按 ID 单独pushToEventQueue/removeFromEventQueue,未读按频道pushToUnreadEvents/clearUnreadEventsForChannel,避免旧版「整体重写」的开销;
  • 恢复能力eventQueueunreadEvents完全持久化,崩溃后重启可从未处理处继续;对话历史可从messages表重建,保证跨重启的连续性;
  • 迁移管理:drizzle 迁移文件位于 integrations/satori-bot/drizzle/,由drizzle-kit管理(pnpm -F @proj-airi/satori-bot db:generate/db:push),启动时在 src/lib/db.ts 自动应用。

运维注意事项

  1. 凭据隔离SATORI_TOKEN、各平台 token、LLM_API_KEY等敏感值只写入integrations/satori-bot/.env.local。该文件已列入.gitignore,不要提交,也不要将内容截图或发送给任何人。
  2. 过渡期定位src/core/中的循环与规划逻辑是临时模拟实现,仅供实验与维护;真正的长线资产是src/adapter/satori/(协议适配)与src/capabilities/(动作提供),未来将作为 Adapter 与 Capability Provider 挂接到 AIRI Core。
  3. 故障排查:使用pnpm -F @proj-airi/satori-bot dev启动后观察日志:若出现「Configuration validation failed」请检查.env.local必填变量;若 LLM 调用报 API Key 错误,优先核对LLM_API_KEYLLM_API_BASE_URL;若收不到事件,确认 Koishi 的server-satori已开启且SATORI_WS_URL端口一致。
  4. 循环保护:默认单轮最多 5 次迭代、发送前再次校验未读,这两个机制共同防止了「模型自说自话」与「忽略新消息强行回复」两类典型故障。

如需深入了解,可继续阅读模块内文档:HANDLER.md(事件到动作的完整流转)、PERSISTENCE.md(记忆与持久化策略)、EVENT.md 与 PROMPTS.md,以及人格与系统提示词源文件 integrations/satori-bot/src/core/planner/prompts/。

【免费下载链接】airi💖🧸 Self hosted, you-owned Grok Companion, a container of souls of waifu, cyber livings to bring them into our worlds, wishing to achieve Neuro-sama's altitude. Capable of realtime voice chat, Minecraft, Factorio playing. Web / macOS / Windows supported.项目地址: https://gitcode.com/GitHub_Trending/ai/airi

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/10 12:23:48

Python构建全链路通知监控系统:从API采集到Prometheus可视化

1. 项目概述&#xff1a;Python驱动的全链路通知监控系统这个基于Python构建的通知监控系统框架&#xff0c;本质上是一个高度自动化的运维告警中枢。它通过API接口实时抓取业务指标数据&#xff0c;经过阈值判断后触发邮件通知&#xff08;集成Outlook服务&#xff09;&#x…

作者头像 李华
网站建设 2026/9/10 12:21:09

基于深度学习的情感分析在智慧养老系统中的应用

简介&#xff1a;这是一份基于情感分析的智慧养老系统深度学习课程设计项目&#xff0c;包含Python源码、设计文档和演示PPT等完整材料&#xff0c;适合计算机相关专业的在校学生、教师或企业员工用于课程设计、期末大作业、毕业设计及项目立项演示。项目由个人大三学期课程设计…

作者头像 李华
网站建设 2026/9/10 12:19:16

Go语言Context取消信号机制解析与实践

1. Go Context 取消信号机制深度解析在Go语言并发编程中&#xff0c;Context就像一位交通警察&#xff0c;它协调着各个goroutine的运行节奏。特别是在微服务架构和分布式系统中&#xff0c;Context的取消信号机制成为了控制请求生命周期的核心枢纽。今天我们就来彻底拆解这个看…

作者头像 李华
网站建设 2026/9/10 12:14:31

JAVA毕业设计-基于SpringBoot+Vue的画师接单约稿系统的设计与实现 基于SpringBoot+Vue的艺术约稿交易平台的设计(源码+LW+部署文档+全bao+远程调试+代码讲解等)

博主介绍&#xff1a;✌️码农一枚 &#xff0c;专注于大学生项目实战开发、讲解和毕业&#x1f6a2;文撰写修改等。全栈领域优质创作者&#xff0c;博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java、小程序技术领域和毕业项目实战 ✌️技术范围&#xff1a;&am…

作者头像 李华