Skip to content

HuanCheng65/mio

Repository files navigation

koishi-plugin-mio

不是聊天机器人,是群友。

澪(みお)是一个 Koishi 插件,用大语言模型驱动一个拟人化的 QQ 群成员。她不会有求必应,不会每条消息都回复,大部分时候只是安静地看着聊天记录。碰到感兴趣的话题才搭两句,觉得没什么好说的就沉默——就像群里那个在线但不怎么说话的人。

设计她的时候有一个很简单的出发点:群聊机器人最大的问题不是不够聪明,是太像机器人。抢着回复、每条必应、说话像客服——哪怕用了最好的模型,一开口就知道不是人。澪想解决的是这个问题。她的核心不是「能做什么」,而是「什么时候选择不做」。

她能感知什么

澪读得懂群里大部分类型的消息。文字和图片自不必说,QQ 表情、分享卡片(网易云、B站、抖音之类的她都能认出来源)、合并转发、戳一戳,甚至撤回消息——如果撤回得不够快,她还能看到撤回了什么。

每条消息她不只是读内容。谁 @ 了谁、引用了谁说的话、有没有人给消息点了表情回应、发言者是不是管理员——这些上下文她都会注意到。不是刻意去分析,更像是一个正常群友自然会注意到的那些东西。

图片会交给视觉模型去看。她不只是知道「这是一张图」,而是能理解图里有什么、这是不是表情包、什么情绪、什么场景适合用。小尺寸的图和带 marketface 标记的会直接被识别为表情包,不用再费模型的力气。同一张图(即使 URL 不同)只分析一次,结果会缓存下来。

她怎么回话

澪的回复不只是一条文字消息。她的 LLM 输出是一个结构化的动作列表,她可以在一次回复里做好几件事——或者什么都不做。

说话。 最普通的回复方式。长消息不会一整段砸出来,而是像人打字一样,按换行和句子拆开,一条一条发,中间有间隔。

引用回复。 可以针对群里某条具体的消息来回话。聊天记录里每条消息都有短编号(m1、m2、m3),她指定一个编号就能引用对应的那条。

表情回应。 给某条消息点一个 QQ 表情 reaction——就是那种点在消息下面的小表情。她说出 emoji 名字就行,系统会模糊匹配(打错字也能认出来)。

发表情包。 从自己的收藏里找一张合适的发出去。她只需要描述一下想表达什么意思(比如「太好笑了 幸灾乐祸」),系统按语义去匹配。

沉默。 大部分时候她的选择是这个。觉得没什么好说的就不说,这也是一种回应。

搜索。 聊到不确定的事情,她可以自己决定去查一下,查完再说话。搜索和其他动作互斥——如果决定去查,就不会同时回复,像是「等下让我搜搜」的感觉。

发消息的细节

她发消息不是立刻就发出去的。会先等一个随机的打字时间——基础两到六秒,加上一点随机抖动,长消息按字数追加。消息里如果 @ 了人,会自动把名字匹配成真实的 QQ 用户(模糊匹配,不用完全打对名字)。

对话节奏

这部分是让她像群友而不是客服的关键。

等别人说完再开口。 群里有人正聊着的时候她不会插嘴。等消息流安静了一小段时间(默认五秒),她才开始想要不要回。但如果群一直在刷屏停不下来,最多等三十秒她也会回一次。被 @ 或者被叫名字可以跳过这个等待,立刻响应。

不刷屏。 每小时有回复上限(默认 25 条),两次回复之间至少隔十五秒,连续说了四条之后会自己停下来。这些不是硬性规则,是她「不想当话唠」的表现。

知道什么时候该放弃。 她在思考的时候如果来了更重要的消息(比如被 @),会中断当前的思考重新来。不会固执地把已经过时的回复发出去。

记忆

澪有一套三层加缓冲区的记忆系统。不是为了展示技术,是因为「记住别人说过的话」是群友最基本的素质。

情景记忆

群里发生过什么事。吵过的架、聊过的番、深夜的胡说八道,都会被概括成一段段记忆存下来。每段记忆带着参与者、情绪、重要度评分,还有她自己的参与程度——是主动聊的,还是在旁边看的,还是被人提到了。

记忆会慢慢变淡。时间衰减的半衰期是两周,但被反复想起的记忆衰减得更慢。活跃的记忆池有上限(默认 200 条),装不下了就把最不重要的存档。

关系记忆

对每个人的印象。不是一个冷冰冰的数据库条目,而是两段话——一段是长期的核心印象(「这个人挺有趣的,聊 gal 很对频率」),一段是近期的感觉(「最近好像在忙考试,发言少了」)。

亲密度会随互动自动变化:从陌生人到点头之交到熟人到朋友,反过来三十天不说话也会慢慢疏远。熟了之后说话方式真的会变——就像人和不同亲密度的人说话,用词和语气本来就不一样。

她还会记住别人的名字。不是用户名,是群友自己说「叫我某某」或者别人怎么称呼 ta 的那种名字。

语义记忆

零碎的事实知识。「某某喜欢 KEY 社」「某某不吃香菜」「群里有个梗是关于某某某的」——这类碎片信息,从聊天里慢慢积累出来。每条事实带置信度,新信息进来可能会更新旧的认知。有去重机制,不会反复存同一件事。

记忆是怎么形成的

她回完消息之后会在后台默默整理记忆,分两条线:

快速通道先扫一遍新消息,判断值不值得记——大部分日常水群是不值得的。慢速通道处理那些被标记为「有意思」的内容,按话题分段,跳过灌水和刷屏,提取出情景摘要和关系变化。她参与过的对话、被提到的话题优先处理,其他的随机抽样。

每日蒸馏

每天凌晨(默认三点)自动跑一次大整理:从最近的情景记忆里提取事实更新到语义记忆,给每个最近互动过的人更新近期印象,有重大事件的时候更新核心印象,清理过期的和质量低的记忆。像是睡前把今天的事在脑子里过一遍。

记忆检索

回话之前会从记忆库里找和当前话题相关的内容。混合语义相似度、时间远近和重要度打分,取最相关的几条注入上下文。所以她有时候会突然提起之前聊过的事——不是在炫耀记忆力,是自然地想起来了。

搜索

聊到拿不准的事情,她会自己决定去查一下。不需要你叫她去搜,她自己觉得「这个我不太确定」就会动手。

根据话题自动选搜索源。聊番剧去 Bangumi 查,聊 Galgame 去 Bangumi 加 VNDB,其他的走 SearXNG。所有来源并行请求,全局三秒超时——搜不到就搜不到,不会卡着不说话。

搜回来的结果不会原样甩出来。经过 LLM 压缩成自然语言,以「你刚用手机搜了一下」的口吻融进上下文,然后重新生成回复。所以她说出来的是「我记得这个好像是……」,不是「搜索结果如下:」。

表情包

看到喜欢的表情包会自动收藏。用感知哈希去重(同一张图换个格式发她认得出来),用向量嵌入建立语义索引——不是按文件名分类,是按情绪、场景、内容三个维度理解每张表情包的意思。

回复的时候如果觉得适合发表情包,会从收藏里找最合适的。匹配的时候综合考虑情绪契合度、场景相关性、内容匹配、使用频率、新鲜度和质量分。最近刚发过的会被压低权重,避免反复发同一张。相关度不够的宁可不发。

收藏池有大小限制(默认 80 张,硬上限 120)。每天更新质量分,每周做去重——如果两张表情包情绪和风格太接近,用得少的那张会被淘汰。像是一个人的表情包收藏夹,会自然地新陈代谢。

Prompt 设计

Prompt 系统分四层,从静态到动态排列,这样设计是为了最大化 LLM 的前缀缓存命中率——前三层基本不变,只有最后一层每次请求都不同。

内容 变化频率
Layer 0 认知框架:你是谁、你怎么想事情 不变
Layer 1 行为准则:沉默优先、选择性参与 不变
Layer 2 输出格式:JSON schema、动作定义 不变
Layer 3 人设 + 动态上下文 每次请求

最后一层装着当前时间、人设文件、对在场群友的印象、检索到的记忆、群梗、表情包收藏摘要,还有最近二三十条聊天记录。LLM 的输出是结构化 JSON——思考过程、是否沉默、搜索请求、动作列表,不是裸文本。

架构

src/
├── llm/          # LLM 调用层(OpenAI 兼容 + Gemini)
├── perception/   # 消息感知 & 标准化
├── pipeline/     # 消息缓冲、防抖、图片处理
├── context/      # 分层 prompt 构建
├── delivery/     # 消息发送、@解析、拟人化延迟
├── memory/       # 情景 / 关系 / 语义记忆 + 蒸馏
├── search/       # 多源搜索 + 结果压缩
├── sticker/      # 表情包收集 / 检索 / 维护
└── types/        # 响应结构定义

LLM 支持

支持 OpenAI 兼容接口(DeepSeek、OpenAI、任何兼容的自托管端点)和 Google Gemini 原生 API。可以配多个提供商,不同任务用不同模型——聊天用一个、看图用一个、做嵌入用一个、提取记忆和蒸馏各用一个。Gemini 模型还支持扩展思考预算。

配置

# 基础
botName: 
botAliases: [みお, 小澪]
personaFile: mio.md                 # 人设文件
enableGroups: ['123456']            # 启用的群号

# LLM 提供商(可配多个)
providers:
  - id: deepseek
    name: DeepSeek
    type: openai
    apiKey: sk-...
    baseUrl: https://api.deepseek.com/v1

# 模型路由
models:
  chat: { providerId: deepseek, modelName: deepseek-chat }
  vision: { providerId: gemini, modelName: gemini-2.0-flash }

# 记忆
memory:
  enabled: true
  distillationHour: 3               # 凌晨几点蒸馏
  embedding: { providerId: openai, modelName: text-embedding-3-small }

# 搜索
search:
  enabled: true
  searxngBaseUrl: http://localhost:8080

# 表情包
sticker:
  enabled: true
  imageDir: ./data/stickers

具体的配置项和默认值见 src/index.tsx 里的 Config 接口和 Schema 定义。

依赖

  • Koishi v4
  • OneBot 适配器(QQ 接入)
  • OpenAI SDK / Google GenAI SDK(LLM 调用)
  • jimp(图片处理)

开始使用

yarn
yarn build

作为 Koishi 插件加载即可。需要先配好至少一个 LLM 提供商和 OneBot 适配器。

关于人设

data/persona/mio.md 定义了澪这个人。不是提示词模板,更像是一份关于她的备忘录——她的日常、性格、跟人的距离感、脑子里装的东西。插件在构建 prompt 时会把这份文件注入系统消息。

你可以改这个文件来创造完全不同的角色,但整个系统的行为逻辑——沉默优先、慢热、选择性参与——是围绕「正常群友」这个前提设计的。如果你想做一个有求必应的助手,这个插件可能不太适合。

License

MIT

About

No description, website, or topics provided.

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors