nonebot-plugin-aigf 0.1.5__tar.gz → 0.2.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (19) hide show
  1. {nonebot-plugin-aigf-0.1.5 → nonebot-plugin-aigf-0.2.0}/PKG-INFO +1 -1
  2. {nonebot-plugin-aigf-0.1.5 → nonebot-plugin-aigf-0.2.0}/nonebot_plugin_aigf/__init__.py +1 -4
  3. {nonebot-plugin-aigf-0.1.5 → nonebot-plugin-aigf-0.2.0}/nonebot_plugin_aigf/memory.py +12 -6
  4. {nonebot-plugin-aigf-0.1.5 → nonebot-plugin-aigf-0.2.0}/nonebot_plugin_aigf/session.py +39 -35
  5. {nonebot-plugin-aigf-0.1.5 → nonebot-plugin-aigf-0.2.0}/nonebot_plugin_aigf.egg-info/PKG-INFO +1 -1
  6. {nonebot-plugin-aigf-0.1.5 → nonebot-plugin-aigf-0.2.0}/setup.py +1 -1
  7. {nonebot-plugin-aigf-0.1.5 → nonebot-plugin-aigf-0.2.0}/README.md +0 -0
  8. {nonebot-plugin-aigf-0.1.5 → nonebot-plugin-aigf-0.2.0}/nonebot_plugin_aigf/client.py +0 -0
  9. {nonebot-plugin-aigf-0.1.5 → nonebot-plugin-aigf-0.2.0}/nonebot_plugin_aigf/config.py +0 -0
  10. {nonebot-plugin-aigf-0.1.5 → nonebot-plugin-aigf-0.2.0}/nonebot_plugin_aigf/image_manager.py +0 -0
  11. {nonebot-plugin-aigf-0.1.5 → nonebot-plugin-aigf-0.2.0}/nonebot_plugin_aigf/mem.py +0 -0
  12. {nonebot-plugin-aigf-0.1.5 → nonebot-plugin-aigf-0.2.0}/nonebot_plugin_aigf/meme_manager.py +0 -0
  13. {nonebot-plugin-aigf-0.1.5 → nonebot-plugin-aigf-0.2.0}/nonebot_plugin_aigf/presets.py +0 -0
  14. {nonebot-plugin-aigf-0.1.5 → nonebot-plugin-aigf-0.2.0}/nonebot_plugin_aigf/vlm.py +0 -0
  15. {nonebot-plugin-aigf-0.1.5 → nonebot-plugin-aigf-0.2.0}/nonebot_plugin_aigf.egg-info/SOURCES.txt +0 -0
  16. {nonebot-plugin-aigf-0.1.5 → nonebot-plugin-aigf-0.2.0}/nonebot_plugin_aigf.egg-info/dependency_links.txt +0 -0
  17. {nonebot-plugin-aigf-0.1.5 → nonebot-plugin-aigf-0.2.0}/nonebot_plugin_aigf.egg-info/requires.txt +0 -0
  18. {nonebot-plugin-aigf-0.1.5 → nonebot-plugin-aigf-0.2.0}/nonebot_plugin_aigf.egg-info/top_level.txt +0 -0
  19. {nonebot-plugin-aigf-0.1.5 → nonebot-plugin-aigf-0.2.0}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: nonebot-plugin-aigf
3
- Version: 0.1.5
3
+ Version: 0.2.0
4
4
  Summary: 基于nonebot-plugin-nyaturingtest重构的群聊特化 LLM 聊天机器人,具有 LLM 驱动的记忆系统和表情包功能。
5
5
  Home-page: https://github.com/Funny1Potato/nonebot-plugin-aigf
6
6
  Author: Funny1Potato
@@ -18,7 +18,6 @@ import asyncio
18
18
  import base64
19
19
  from dataclasses import dataclass, field
20
20
  from datetime import datetime
21
- import json
22
21
  import re
23
22
  import ssl
24
23
  import traceback
@@ -30,7 +29,6 @@ from nonebot.adapters import Message
30
29
  from nonebot.adapters.onebot.v11 import (
31
30
  Bot, Event, GroupMessageEvent, MessageSegment, Message as OneBotMessage, PrivateMessageEvent,
32
31
  )
33
- from nonebot.matcher import Matcher
34
32
  from nonebot.params import CommandArg
35
33
  from nonebot.permission import SUPERUSER
36
34
  from nonebot.plugin import PluginMetadata
@@ -43,14 +41,13 @@ from .config import Config, plugin_config
43
41
  from .image_manager import IMAGE_CACHE_DIR, image_manager
44
42
  from .meme_manager import AtMessage, MemeMessage, TextMessage, meme_manager
45
43
  from .mem import Message as MMessage
46
- from .presets import PRESETS, load_presets, save_preset, RolePreset
44
+ from .presets import PRESETS, load_presets
47
45
  from .session import Session
48
46
 
49
47
  __plugin_meta__ = PluginMetadata(
50
48
  name="AI-group-friend", description="群聊特化LLM聊天机器人,具有记忆和表情包功能",
51
49
  usage="群聊特化LLM聊天机器人", type="application",
52
50
  config=Config, supported_adapters={"~onebot.v11"},
53
- homepage="https://github.com/Funny1Potato/nonebot-plugin-aigf",
54
51
  extra={"author": "Funny1Potato"},
55
52
  )
56
53
 
@@ -20,10 +20,13 @@ import nonebot_plugin_localstore as store
20
20
 
21
21
 
22
22
  class MemoryManager:
23
- def __init__(self):
24
- self.data_dir = store.get_plugin_data_dir() / "memory"
25
- self.friends_dir = self.data_dir / "friends"
23
+ def __init__(self, group_id: str = "default"):
24
+ self.group_id = group_id
25
+ # 短期/长期记忆按群隔离
26
+ self.data_dir = store.get_plugin_data_dir() / "memory" / group_id
26
27
  self.data_dir.mkdir(parents=True, exist_ok=True)
28
+ # 群友信息全局共享
29
+ self.friends_dir = store.get_plugin_data_dir() / "memory" / "friends"
27
30
  self.friends_dir.mkdir(parents=True, exist_ok=True)
28
31
 
29
32
  async def load_short_term(self) -> list[str]:
@@ -75,11 +78,11 @@ class MemoryManager:
75
78
  await f.write(json.dumps(data, ensure_ascii=False, indent=2))
76
79
 
77
80
  async def load_friends_batch(self, users: list[dict[str, str]]) -> dict[str, dict]:
78
- """批量加载群友信息。users 为 [{"id": "123", "name": "小明"}, ...]"""
81
+ """批量加载当前群的群友信息"""
79
82
  result = {}
80
83
  for user in users:
81
84
  data = await self.load_friend(user["id"])
82
- if data:
85
+ if data and self.group_id in data.get("groups", []):
83
86
  result[user["id"]] = data
84
87
  return result
85
88
 
@@ -133,7 +136,10 @@ class MemoryManager:
133
136
  for key, ops in friends_ops.items():
134
137
  # 如果 key 不是纯数字(QQ号),尝试从反查表中找到对应的 QQ 号
135
138
  user_id = key if key.isdigit() else nickname_to_id.get(key, key)
136
- friend = await self.load_friend(user_id) or {"id": user_id, "name": key if not key.isdigit() else key, "info": []}
139
+ friend = await self.load_friend(user_id) or {"id": user_id, "name": key if not key.isdigit() else key, "info": [], "groups": []}
140
+ # 确保当前群在 groups 列表中
141
+ if self.group_id not in friend.get("groups", []):
142
+ friend.setdefault("groups", []).append(self.group_id)
137
143
  info = friend.get("info", [])
138
144
  for idx in sorted([int(i) for i in ops.get("delete", [])], reverse=True):
139
145
  if 0 <= idx < len(info):
@@ -17,9 +17,6 @@
17
17
  import base64
18
18
  import json
19
19
  import re
20
- from dataclasses import asdict
21
- from datetime import datetime
22
- from pathlib import Path
23
20
 
24
21
  import anyio
25
22
  from nonebot import logger
@@ -29,7 +26,7 @@ from .config import plugin_config
29
26
  from .meme_manager import AtMessage, MemeMessage, TextMessage, meme_manager
30
27
  from .memory import MemoryManager
31
28
  from .mem import Message
32
- from .presets import PRESETS, RolePreset
29
+ from .presets import PRESETS
33
30
 
34
31
 
35
32
  class Session:
@@ -37,7 +34,7 @@ class Session:
37
34
  self.id = id
38
35
  self.name = name
39
36
  self.role = role
40
- self.memory = MemoryManager()
37
+ self.memory = MemoryManager(group_id=id)
41
38
  self.recent_messages: list[Message] = []
42
39
 
43
40
  async def load_preset(self, preset_name: str) -> bool:
@@ -65,9 +62,6 @@ class Session:
65
62
  active_users = MemoryManager.get_active_users(messages_chunk)
66
63
  friends = await self.memory.load_friends_batch(active_users)
67
64
 
68
- # 构建 id→昵称映射
69
- id_name_map = {u["id"]: u["name"] for u in active_users}
70
-
71
65
  # LLM 模式:读取缓存图片的 base64
72
66
  sticker_images: list[str] = []
73
67
  if plugin_config.aigf_image_mode == "llm" and cached_stickers:
@@ -167,11 +161,18 @@ class Session:
167
161
 
168
162
  # 群友信息(按 QQ 号存储,显示时带昵称)
169
163
  friends_str = ""
164
+ # 已有记忆的群友
170
165
  for uid, data in friends.items():
171
166
  display_name = data.get("name", uid)
172
167
  info_list = data.get("info", [])
173
168
  info_str = "、".join(info_list) if info_list else "暂无信息"
174
169
  friends_str += f"- {display_name}(QQ:{uid}):{info_str}\n"
170
+ # 当前聊天中但还没有记忆的群友
171
+ active_users = MemoryManager.get_active_users(messages_chunk)
172
+ existing_ids = set(friends.keys())
173
+ new_users = [u for u in active_users if u["id"] not in existing_ids]
174
+ for u in new_users:
175
+ friends_str += f"- {u['name']}(QQ:{u['id']}):暂无信息\n"
175
176
  friends_str = friends_str or "无"
176
177
 
177
178
  # 最近 10 条消息
@@ -188,10 +189,8 @@ class Session:
188
189
  if meme_list:
189
190
  meme_section = f"""
190
191
 
191
- ## 可用的表情包
192
+ ## 可用的表情包(用于发送)
192
193
  {meme_list}
193
-
194
- 如果想发表情包,在 reply 中加入 {{"type": "meme", "id": "表情包id"}}。
195
194
  """
196
195
 
197
196
  # 缓存中的表情包(可供收藏)
@@ -232,27 +231,26 @@ class Session:
232
231
  ### 短期记忆
233
232
  {short_term_str}
234
233
  这是你对近期对话的记忆。你应该积极管理:
235
- - 每次回复时检查并更新,确保反映最新的对话状态
236
- - 过时的内容(如已结束的话题、已解决的问题)应删除
237
- - 新的上下文信息及时添加
238
- - 内容可以详细,不必精简
234
+ - 添加:新的对话内容、临时上下文、有趣的梗
235
+ - 修改:对话有新进展时,更新已有条目(用 index 指定要改哪条)
236
+ - 删除:已结束的话题、已解决的问题、不再 relevant 的内容(用 index 指定要删哪条)
239
237
 
240
238
  ### 长期记忆
241
239
  {long_term_str}
242
240
  这是你记住的重要信息。你应该积极管理:
243
- - 发现新信息与旧记忆矛盾时,立即修改旧记忆
244
- - 某条记忆被证伪或不再适用时,立即删除
245
- - 每次回复时检查是否有值得新增的内容
246
- - 宁可多记多改,不要遗漏重要信息
247
- 不要记:临时性的对话内容、无关紧要的闲聊、可以通过常识推断的信息。
241
+ - 添加:新的事件、知识、规则
242
+ - 修改:发现旧信息不准确或需要更新时(用 index 指定要改哪条)
243
+ - 删除:被证伪、过时、不再适用的信息(用 index 指定要删哪条)
244
+ 不要记:临时性的对话内容、无关紧要的闲聊。
248
245
 
249
246
  ### 相关群友信息
250
247
  {friends_str}
251
248
  这是你对群友的了解。你应该积极管理:
252
- - 群友透露新信息时,立即添加或更新
253
- - 发现之前记错了,立即修改
254
- - 群友改名时,用 update_name 更新昵称
255
- - 每次对话都留意是否有新的有用信息
249
+ - 添加:群友透露的新信息(职业、爱好、性格等)
250
+ - 修改:发现之前记错了,或信息有变化时(用 index 指定要改哪条)
251
+ - 删除:不再准确的信息(用 index 指定要删哪条)
252
+ - update_name:群友改名时更新昵称
253
+ 注意:index 从 0 开始计数,对应上面列表中的第几条。修改和删除时必须指定正确的 index。
256
254
 
257
255
  ## 最近的聊天记录
258
256
  {recent_str}
@@ -278,32 +276,38 @@ class Session:
278
276
  ],
279
277
  "memory": {{
280
278
  "short_term": {{
281
- "add": ["新记住的内容"],
282
- "modify": [{{"index": 0, "content": "修改后的内容"}}],
279
+ "add": ["小明说他周末要去爬山"],
280
+ "modify": [{{"index": 0, "content": "小明说周末要去爬山,小红也想去"}}],
283
281
  "delete": [2]
284
282
  }},
285
283
  "long_term": {{
286
- "add": ["新记住的内容"],
287
- "modify": [{{"index": 0, "content": "修改后的内容"}}],
288
- "delete": [2]
284
+ "add": ["群里组织过一次聚餐"],
285
+ "modify": [{{"index": 0, "content": "群规更新:不允许发广告和链接"}}],
286
+ "delete": [1]
289
287
  }},
290
288
  "friends": {{
291
- "必须用QQ号做key,不要用昵称": {{
292
- "add": ["新发现的信息"],
293
- "modify": [{{"index": 0, "content": "修改后的内容"}}],
289
+ "123456": {{
290
+ "add": ["职业:程序员", "爱好:打游戏"],
291
+ "modify": [{{"index": 0, "content": "职业:前端工程师"}}],
294
292
  "delete": [1],
295
- "update_name": "新昵称(当群友改名时更新)"
293
+ "update_name": "新昵称"
296
294
  }}
297
- }},
298
- "save_meme": [{{"id": "表情包id", "description": "你写的简短描述", "keywords": ["关键词1", "关键词2"]}}](仅当上面列出了可收藏的表情包时才需要,可收藏多个)
295
+ }}
299
296
  }}
300
297
  }}
301
298
  ```
302
299
 
300
+ 示例说明:
301
+ - "modify" 中的 "index" 是要修改的条目在列表中的位置(从 0 开始)
302
+ - "delete" 中的数字是要删除的条目的 index
303
+ - 修改和删除前,请先确认列表中对应 index 的内容是否正确
304
+ - 如果不需要修改或删除,可以省略对应字段
305
+
303
306
  注意:
304
307
  - reply 中的元素也可以是纯字符串,等同于 {{"type": "text", "content": "..."}}
305
308
  - memory 中的所有字段都是可选的,不需要的操作可以省略
306
309
  - at 的 name 必须是上面列出的群友昵称
310
+ - 当群友的话题与你无关、你不感兴趣、或没有值得补充的内容时,不需要回复
307
311
  - 如果不想回复,reply 设为空数组 []
308
312
  - 不要编造表情包 id,只能使用上面列出的
309
313
  - friends 的 key 必须是 QQ 号(数字),绝对不要用昵称做 key
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: nonebot-plugin-aigf
3
- Version: 0.1.5
3
+ Version: 0.2.0
4
4
  Summary: 基于nonebot-plugin-nyaturingtest重构的群聊特化 LLM 聊天机器人,具有 LLM 驱动的记忆系统和表情包功能。
5
5
  Home-page: https://github.com/Funny1Potato/nonebot-plugin-aigf
6
6
  Author: Funny1Potato
@@ -5,7 +5,7 @@ with open("README.md", "r", encoding="utf-8") as fh:
5
5
 
6
6
  setuptools.setup(
7
7
  name="nonebot-plugin-aigf", # 用自己的名替换其中的YOUR_USERNAME_
8
- version="0.1.5", #包版本号,便于维护版本
8
+ version="0.2.0", #包版本号,便于维护版本
9
9
  author="Funny1Potato", #作者,可以写自己的姓名
10
10
  author_email="funny_potato@126.com", #作者联系方式,可写自己的邮箱地址
11
11
  description="基于nonebot-plugin-nyaturingtest重构的群聊特化 LLM 聊天机器人,具有 LLM 驱动的记忆系统和表情包功能。",#包的简述