Metadata-Version: 2.1
Name: nonebot-plugin-aigf
Version: 0.3.1
Summary: 基于nonebot-plugin-nyaturingtest重构的群聊特化 LLM 聊天机器人，具有 LLM 驱动的记忆系统和表情包功能。
Home-page: https://github.com/Funny1Potato/nonebot-plugin-aigf
Author: Funny1Potato
Author-email: funny_potato@126.com
Classifier: Programming Language :: Python :: 3
Classifier: License :: OSI Approved :: GNU Affero General Public License v3
Classifier: Operating System :: OS Independent
Requires-Python: >=3.10
Description-Content-Type: text/markdown
Requires-Dist: nonebot2
Requires-Dist: nonebot-adapter-onebot
Requires-Dist: nonebot-plugin-localstore
Requires-Dist: openai
Requires-Dist: httpx
Requires-Dist: anyio
Requires-Dist: pillow
Requires-Dist: pydantic
Requires-Dist: numpy

<div align="center">
    <a href="https://v2.nonebot.dev/store">
    <img src="https://raw.githubusercontent.com/fllesser/nonebot-plugin-template/refs/heads/resource/.docs/NoneBotPlugin.svg" width="310" alt="logo"></a>

## ✨ AI-group-friend ✨

群聊特化 LLM 聊天机器人，具有 LLM 驱动的记忆系统和表情包功能。

<p>
    <a href="https://github.com/shadow3aaa/nonebot-plugin-nyaturingtest">
    </a>
    <a href="./LICENSE"><img src="https://img.shields.io/github/license/shadow3aaa/nonebot-plugin-nyaturingtest?style=flat-square" alt="license"></a>
    <img src="https://img.shields.io/badge/python-3.10+-blue?style=flat-square&logo=python&logoColor=white" alt="python">
</p>
</div>

## 📖 介绍

> 基于 [shadow3aaa/nonebot-plugin-nyaturingtest](https://github.com/shadow3aaa/nonebot-plugin-nyaturingtest) 重构，移除了 HippoRAG 和情绪系统，改为 LLM 自主管理记忆，并添加表情包存储和发送功能。
* 本项目对原项目代码的修改及重构均有AI高度参与，若有做得不够好的地方，请手下留情。

### 特点:

- 🧠 **LLM 驱动的记忆系统**：短期记忆、长期记忆、群友信息，LLM 自主增删改
- 🖼️ **表情包功能**：AI 自主决定发表情包；自动从群聊中收藏表情包（缓存机制）
- 🔍 **图片理解**：支持 VLM 模式和 LLM 直接看图模式
- 💬 **对话理解**：通过 reply 标记和 @ 理解群聊中的对话关系，自主决定是否发言
- 📝 **预设系统**：支持角色预设，含可编辑的默认预设
- ⚡ **轻量高效**：单次 LLM 调用完成对话 + 记忆管理，节约token

## 💿 安装

> [!IMPORTANT]
> 要使用本插件, 你至少需要
>
> - 一个有效的 openai 规范接口 api key (根据你的 base_url，可以不是 openai 的)，你需要在 `.env` 文件中配置对应的 api 地址

<details open>
<summary>使用 nb-cli 安装</summary>
在 nonebot2 项目的根目录下打开命令行, 输入以下指令即可安装（暂时不行，还未上架）

    nb plugin install nonebot-plugin-aigf --upgrade

</details>

<details>
<summary>使用包管理器安装</summary>

```bash
pip install nonebot-plugin-aigf
```

在 `pyproject.toml` 中添加：

```toml
[tool.nonebot]
plugins = ["nonebot-plugin-aigf"]
```

</details>

## 配置

在 `.env.prod` 中添加：

```env
# === 必填 ===
AIGF_CHAT_OPENAI_API_KEY="***"         # LLM API Key
AIGF_CHAT_OPENAI_BASE_URL="***"        # LLM API 地址
AIGF_CHAT_OPENAI_MODEL="***"           # LLM 模型名称
AIGF_ENABLED_GROUPS=[123456, 789012]   # 启用的群号列表

# === 可选 ===
AIGF_MEME_ENABLED=true                 # 是否启用表情包功能（默认 true）
AIGF_MEME_MAX_COUNT=200                # 自动收集的表情包最大数量（默认 200）
AIGF_DEFAULT_PRESET=default            # 默认预设名称（默认 "default"）

# === VLM 配置（图片理解） ===
AIGF_IMAGE_MODE="vlm"                             # 图片模式: vlm=独立VLM分析, llm=LLM直接看图
AIGF_VLM_ENABLED=true                             # 是否启用VLM（仅 vlm 模式有效，默认 true）
AIGF_VLM_MODEL="Pro/Qwen/Qwen2.5-VL-7B-Instruct"  # VLM 模型名称
AIGF_VLM_BASE_URL="https://api.siliconflow.cn/v1" # VLM API 地址
AIGF_VLM_API_KEY="***"                            # VLM API Key（为空时使用 chat 的 key）
```

## 命令

| 命令 | 说明 | 权限 |
|------|------|------|
| `help` / `帮助` | 显示帮助信息 | SUPERUSER |
| `status` / `状态` | 查看机器人状态（角色、最近消息） | SUPERUSER |
| `set_role <名字> <设定>` | 设置机器人角色 | SUPERUSER |
| `reset` / `重置` | 重置会话（清空所有记忆） | SUPERUSER |
| `presets` | 查看可用的角色预设 | SUPERUSER |
| `set_preset <预设名>` | 加载指定的角色预设 | SUPERUSER |
| `reload_meme` / `重载表情包` | 热重载表情包配置 | SUPERUSER |

## 触发机制

- 攒够 **5 条**新消息，或最后一条消息后 **5 秒**内无新消息，触发一次处理
- 每次处理时，LLM 收到最近 **15 条**聊天记录 + 三层记忆 + 预设 + 表情包列表
- LLM 一次调用同时完成：回复决策 + 记忆管理 + 表情包选择

## 对话理解

机器人通过以下方式理解群聊中的对话关系：

- **reply 标记**：消息中包含 `[回复 xxx 的消息: "yyy"]`，表示在回复某人
- **@ 提及**：`@某人` 表示消息是发给那个人的
- **时间推断**：时间接近的消息通常在互相回复

**回复决策规则**：
- 有人 @ 了机器人 → 回复
- 有人回复了机器人之前的消息 → 回复
- 消息明显是对所有人说的，且有值得补充的内容 → 回复
- 不确定是否在和自己说话 → **不回复**

## 记忆系统

机器人拥有三层记忆，由 LLM 在每次回复时自主管理：

### 短期记忆

存储在 `<插件数据目录>/memory/<群号>/short_term.json`，内容为 LLM 维护的信息列表，包括对话摘要、临时上下文、有趣的梗等。LLM 可以添加、修改、删除条目。

### 长期记忆

存储在 `<插件数据目录>/memory/<群号>/long_term.json`，内容为 LLM 认为值得长期记住的信息，如群内发生的事件、群规、群友分享的有用知识等。LLM 可添加、修改、删除。不应记录临时对话或常识信息。

### 群友信息

存储在 `<插件数据目录>/memory/friends/<QQ号>.json`，每个群友一个文件，以 QQ 号命名。LLM 记录群友的昵称、职业、爱好、说过的话、与其他群友的关系等。具体保存方式如下：
```json
{
  "id": "123456",
  "nickname": "小明",
  "aliases": ["小明哥", "明酱"],
  "past_nicknames": ["明明"],
  "info": ["职业：程序员", "爱好：打游戏"],
  "groups": ["114514","1919810"]
}
```

| 字段 | 来源 | 说明 |
|------|------|------|
| `nickname` | 系统自动更新 | QQ 全局昵称 |
| `aliases` | LLM 管理 | 群友对 ta 的称呼 |
| `past_nicknames` | 系统自动记录 | 曾用 QQ 昵称，便于从记忆中识别人物 |
| `info` | LLM 管理 | 一般信息（职业、爱好等） |
| `groups` | 系统自动维护 | 所在的群列表 |

## 表情包功能

### 工作原理

```
群聊中有人发图片/表情包
    ↓
下载图片 → VLM 分析内容和情感
    ↓
保存到缓存目录（<缓存目录>/sticker_cache/）
    ↓
下一次消息处理时，LLM 在 Prompt 中看到缓存的表情包
    ↓
LLM 决定是否收藏 → 保存到 memes 目录
```

### 表情包素材库

存放在 `<插件数据目录>/memes/` 下：

```
memes/
├── memes.json          ← 管理员手动配置
├── collected.json      ← 机器人自动收集
└── *.jpg/png/gif       ← 表情包图片文件
```

#### 管理员手动配置

编辑 `memes.json`：

```json
[
  {
    "id": "happy_spin",
    "path": "happy_spin.jpg",
    "keywords": ["开心", "高兴", "庆祝"],
    "description": "开心到转圈的小人"
  }
]
```

| 字段 | 必填 | 说明 |
|------|------|------|
| `id` | ✅ | 唯一标识符，AI 用这个选择表情包 |
| `path` | ✅ | 图片文件名（相对于 memes 目录） |
| `keywords` | ✅ | 适用场景关键词 |
| `description` | ✅ | 一句话描述内容 |

修改后执行 `/重载表情包` 即可生效，无需重启。

#### 自动收集

机器人收到图片时，VLM 分析后保存到缓存。LLM 在回复时看到缓存的表情包，决定是否收藏：

```json
{
  "memory": {
    "save_meme": [
      {"id": "a1b2c3d4e5f6", "description": "开心转圈的小人", "keywords": ["开心"]}
    ]
  }
}
```

- 图片按 MD5 hash 去重
- 超过 `AIGF_MEME_MAX_COUNT` 上限时，优先清理最近未使用的
- 缓存中的表情包只处理一次，处理后清空

#### 发送表情包

LLM 在回复中指定表情包 id（来自 memes.json 或 collected.json）：

```json
{"type": "meme", "id": "happy_spin"}
```

## 图片理解模式

通过 `AIGF_IMAGE_MODE` 配置：

| 模式 | 流程 | 适用场景 |
|------|------|---------|
| `vlm`（默认） | 图片 → VLM 分析 → 文字描述给 LLM | LLM 不支持图片输入 |
| `llm` | 图片 → base64 直接附在 LLM prompt 中 | LLM 支持视觉（GPT-4o 等） |

VLM 模式下，描述限制 50 字，情感只输出 3 个词，不识别具体角色名称（只描述外貌特征）。

## 预设系统

首次运行后在 `<插件配置目录>/presets/` 下生成 `default.json`：

```json
{
  "name": "小助手",
  "role": "一个友好的群聊助手，会用轻松的语气和大家聊天",
  "knowledges": [],
  "hidden": false
}
```

### 预设字段

| 字段 | 说明 |
|------|------|
| `name` | 角色名称 |
| `role` | 角色设定 |
| `knowledges` | 预设知识列表（会注入 Prompt） |
| `hidden` | 是否在 `/presets` 中隐藏 |

### 添加新预设

在 `presets/` 目录下创建新的 JSON 文件，如 `猫娘.json`：

```json
{
  "name": "喵喵",
  "role": "一个可爱的群猫娘，群里的其它人是你的主人",
  "knowledges": [
    "猫娘有猫耳和猫尾巴",
    "猫娘喜欢吃鱼"
  ],
  "hidden": false
}
```

然后在群内执行 `set_preset 猫娘` 即可加载。

## 消息格式

LLM 支持以下回复类型：

| 类型 | 格式 | 说明 |
|------|------|------|
| 文本 | `{"type": "text", "content": "..."}` | 纯文本消息 |
| @ | `{"type": "at", "name": "群友昵称"}` | 艾特群友 |
| 表情包 | `{"type": "meme", "id": "表情包id"}` | 发送表情包 |

文本和 @ 会合并为一条消息发送，表情包单独发送。

## 图片理解模式

支持两种图片理解模式，通过 `AIGF_IMAGE_MODE` 配置：

### VLM 模式（默认）

```
图片 → VLM 分析 → 缓存描述 → 文字 prompt 给 LLM
```

- LLM 不需要支持图片输入
- VLM 单独调用，消耗较少 token
- 适合 LLM 不支持视觉的场景

### LLM 模式

```
图片 → 直接以 base64 附在 LLM prompt 中 → LLM 看图决策
```

- LLM 直接看到图片，理解更准确
- 不需要配置 VLM
- 适合支持视觉的模型（如 GPT-4o、Qwen-VL）
- 图片 base64 会消耗更多 token

## 依赖

- NoneBot2 + OneBot V11 适配器
- OpenAI 兼容 API（LLM）
- VLM API（图片理解，可选）
- Pillow（图片处理）
- httpx、anyio
- numpy

## 一些碎碎念
- 本项目移除了原插件的 HippoRAG 和情绪系统，拟人程度远不如原插件
- 本项目的token消耗理论上相较原插件能减少约30-50%，但绝对值仍不低，每次请求约消耗 20K tokens，在记忆数据丰富之后会更高
- 推荐使用价格较为低廉的模型作为llm模型（群友就是要笨笨的才可爱呀），再以识图能力较好的vlm模型作为辅助（要是看不懂表情包还是会比较尴尬的）
