amrita_plugin_memory 0.1.1__tar.gz → 0.1.2__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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: amrita_plugin_memory
3
- Version: 0.1.1
3
+ Version: 0.1.2
4
4
  Summary: Amrita长期记忆插件
5
5
  Requires-Python: <3.14,>=3.10
6
6
  Description-Content-Type: text/markdown
@@ -42,11 +42,12 @@ amrita_plugin_memory/
42
42
  - **config.py**: 定义配置文件(过期天数、记忆上限)和环境变量(数据库类型、嵌入模型地址等),通过 Pydantic 模型校验。
43
43
  - **embed.py**: 实现 Ollama 协议的嵌入适配器,通过 HTTP API 调用 `/api/embed` 端点。
44
44
  - **vector.py**: 对 ChromaDB 原生 API 进行 `asyncio.to_thread` 封装,提供异步的集合管理与记忆 CRUD。使用 `aiologic.Lock` 按用户粒度加锁,保证并发安全。
45
- - **tools.py**: 定义 5 个 Function Calling 工具(`write_memory` / `read_memory` / `update_memory` / `delete_memory` / `list_memory`),注册到 Amrita 的工具系统中。
45
+ - **tools.py**: 定义 5 个 Function Calling 工具(`write_memory` / `read_memory` / `update_memory` / `delete_memory` / `list_memory`),注册到 Amrita 的工具系统中。通过 `scope` 参数支持群共享记忆与用户专属记忆的分区隔离。
46
46
 
47
47
  ## 功能特点
48
48
 
49
49
  - **长期记忆存储**:将用户偏好、项目信息等持久化存入 ChromaDB 向量数据库
50
+ - **群/用户记忆隔离**:LLM 可通过 `scope` 参数选择存入群共享记忆(群内所有人可见)或用户专属记忆(仅自己可见)
50
51
  - **语义检索**:基于嵌入向量的语义相似度检索,支持中英文关键词
51
52
  - **智能标签**:支持使用自定义标签对记忆进行分类(如 `preference`、`project`)
52
53
  - **重要性分级**:三级重要程度(`low` / `medium` / `high`),支持按级别过滤检索
@@ -65,6 +66,7 @@ amrita_plugin_memory/
65
66
  | `content` | string | ✅ | 记忆内容,简洁明了 |
66
67
  | `tags` | string | ✅ | 分类标签,如 `preference`、`project` |
67
68
  | `importance` | `"low"` / `"medium"` / `"high"` | ✅ | 重要性等级 |
69
+ | `scope` | `"group"` / `"user"` | ✅ | `group`=群共享,`user`=个人专属(私聊不可用 `group`) |
68
70
 
69
71
  ### 2. `read_memory` —— 检索记忆
70
72
 
@@ -75,6 +77,7 @@ amrita_plugin_memory/
75
77
  | `query` | string | ✅ | — | 关键词字符串,空格分隔 |
76
78
  | `top_k` | integer | ❌ | `5` | 返回结果数量 |
77
79
  | `importance` | `"low"` / `"medium"` / `"high"` | ❌ | — | 按重要性过滤 |
80
+ | `scope` | `"group"` / `"user"` | ✅ | — | 检索范围:群共享或个人专属 |
78
81
 
79
82
  ### 3. `update_memory` —— 更新记忆
80
83
 
@@ -83,6 +86,7 @@ amrita_plugin_memory/
83
86
  | 参数 | 类型 | 必需 | 描述 |
84
87
  |------|------|------|------|
85
88
  | `id` | string | ✅ | 要更新的记忆 ID |
89
+ | `scope` | `"group"` / `"user"` | ✅ | 记忆所属范围 |
86
90
  | `content` | string | ❌ | 新的记忆内容 |
87
91
  | `tags` | string | ❌ | 新的标签 |
88
92
  | `importance` | `"low"` / `"medium"` / `"high"` | ❌ | 新的重要性等级 |
@@ -94,14 +98,16 @@ amrita_plugin_memory/
94
98
  | 参数 | 类型 | 必需 | 描述 |
95
99
  |------|------|------|------|
96
100
  | `id` | string | ✅ | 要删除的记忆 ID |
101
+ | `scope` | `"group"` / `"user"` | ✅ | 记忆所属范围 |
97
102
 
98
103
  ### 5. `list_memory` —— 列出记忆
99
104
 
100
- 列出当前用户的所有记忆(返回标签和 ID)。
105
+ 列出当前用户的所有记忆(返回 scope、标签和 ID)。
101
106
 
102
107
  | 参数 | 类型 | 必需 | 默认值 | 描述 |
103
108
  |------|------|------|--------|------|
104
109
  | `limit` | integer | ❌ | `5` | 返回数量限制 |
110
+ | `scope` | `"group"` / `"user"` | ✅ | — | 列出范围:群共享或个人专属 |
105
111
 
106
112
  ## 配置选项
107
113
 
@@ -27,11 +27,12 @@ amrita_plugin_memory/
27
27
  - **config.py**: 定义配置文件(过期天数、记忆上限)和环境变量(数据库类型、嵌入模型地址等),通过 Pydantic 模型校验。
28
28
  - **embed.py**: 实现 Ollama 协议的嵌入适配器,通过 HTTP API 调用 `/api/embed` 端点。
29
29
  - **vector.py**: 对 ChromaDB 原生 API 进行 `asyncio.to_thread` 封装,提供异步的集合管理与记忆 CRUD。使用 `aiologic.Lock` 按用户粒度加锁,保证并发安全。
30
- - **tools.py**: 定义 5 个 Function Calling 工具(`write_memory` / `read_memory` / `update_memory` / `delete_memory` / `list_memory`),注册到 Amrita 的工具系统中。
30
+ - **tools.py**: 定义 5 个 Function Calling 工具(`write_memory` / `read_memory` / `update_memory` / `delete_memory` / `list_memory`),注册到 Amrita 的工具系统中。通过 `scope` 参数支持群共享记忆与用户专属记忆的分区隔离。
31
31
 
32
32
  ## 功能特点
33
33
 
34
34
  - **长期记忆存储**:将用户偏好、项目信息等持久化存入 ChromaDB 向量数据库
35
+ - **群/用户记忆隔离**:LLM 可通过 `scope` 参数选择存入群共享记忆(群内所有人可见)或用户专属记忆(仅自己可见)
35
36
  - **语义检索**:基于嵌入向量的语义相似度检索,支持中英文关键词
36
37
  - **智能标签**:支持使用自定义标签对记忆进行分类(如 `preference`、`project`)
37
38
  - **重要性分级**:三级重要程度(`low` / `medium` / `high`),支持按级别过滤检索
@@ -50,6 +51,7 @@ amrita_plugin_memory/
50
51
  | `content` | string | ✅ | 记忆内容,简洁明了 |
51
52
  | `tags` | string | ✅ | 分类标签,如 `preference`、`project` |
52
53
  | `importance` | `"low"` / `"medium"` / `"high"` | ✅ | 重要性等级 |
54
+ | `scope` | `"group"` / `"user"` | ✅ | `group`=群共享,`user`=个人专属(私聊不可用 `group`) |
53
55
 
54
56
  ### 2. `read_memory` —— 检索记忆
55
57
 
@@ -60,6 +62,7 @@ amrita_plugin_memory/
60
62
  | `query` | string | ✅ | — | 关键词字符串,空格分隔 |
61
63
  | `top_k` | integer | ❌ | `5` | 返回结果数量 |
62
64
  | `importance` | `"low"` / `"medium"` / `"high"` | ❌ | — | 按重要性过滤 |
65
+ | `scope` | `"group"` / `"user"` | ✅ | — | 检索范围:群共享或个人专属 |
63
66
 
64
67
  ### 3. `update_memory` —— 更新记忆
65
68
 
@@ -68,6 +71,7 @@ amrita_plugin_memory/
68
71
  | 参数 | 类型 | 必需 | 描述 |
69
72
  |------|------|------|------|
70
73
  | `id` | string | ✅ | 要更新的记忆 ID |
74
+ | `scope` | `"group"` / `"user"` | ✅ | 记忆所属范围 |
71
75
  | `content` | string | ❌ | 新的记忆内容 |
72
76
  | `tags` | string | ❌ | 新的标签 |
73
77
  | `importance` | `"low"` / `"medium"` / `"high"` | ❌ | 新的重要性等级 |
@@ -79,14 +83,16 @@ amrita_plugin_memory/
79
83
  | 参数 | 类型 | 必需 | 描述 |
80
84
  |------|------|------|------|
81
85
  | `id` | string | ✅ | 要删除的记忆 ID |
86
+ | `scope` | `"group"` / `"user"` | ✅ | 记忆所属范围 |
82
87
 
83
88
  ### 5. `list_memory` —— 列出记忆
84
89
 
85
- 列出当前用户的所有记忆(返回标签和 ID)。
90
+ 列出当前用户的所有记忆(返回 scope、标签和 ID)。
86
91
 
87
92
  | 参数 | 类型 | 必需 | 默认值 | 描述 |
88
93
  |------|------|------|--------|------|
89
94
  | `limit` | integer | ❌ | `5` | 返回数量限制 |
95
+ | `scope` | `"group"` / `"user"` | ✅ | — | 列出范围:群共享或个人专属 |
90
96
 
91
97
  ## 配置选项
92
98
 
@@ -1,6 +1,6 @@
1
1
  import json
2
2
  from datetime import datetime
3
- from typing import Any
3
+ from typing import Any, Literal, cast
4
4
 
5
5
  from amrita.plugins.chat.runtime import AmritaBotContext
6
6
  from amrita_core import (
@@ -12,15 +12,36 @@ from amrita_core import (
12
12
  )
13
13
  from chromadb import QueryResult
14
14
  from nonebot import logger
15
+ from nonebot.adapters.onebot.v11 import Event as OB11Event
15
16
 
16
17
  from .config import DataManager
17
18
  from .vector import AsyncUserMemory, MemoryMetadata, get_db_conn
18
19
 
20
+ Scope = Literal["group", "user"] # type alias
19
21
 
20
- def _get_session_id(ctx: ToolContext) -> str:
21
- """从 ToolContext 中提取会话 ID"""
22
+
23
+ def _get_event(ctx: ToolContext) -> OB11Event:
24
+ """从 ToolContext 中提取 OneBot V11 原始事件"""
22
25
  amrictx: AmritaBotContext = ctx.ctx.chat_object._hook_kwargs["amrita"]
23
- return amrictx["event"].get_session_id()
26
+ return amrictx["event"]
27
+
28
+
29
+ def _resolve_scope_id(ctx: ToolContext, scope: str) -> str:
30
+ """根据 scope 返回对应的分区 key
31
+
32
+ - scope="group": 群共享记忆,返回 f"group_{group_id}"
33
+ - scope="user": 用户专属记忆,返回 event.get_session_id()
34
+ """
35
+ event = _get_event(ctx)
36
+ if scope == "group":
37
+ group_id: int | None = getattr(event, "group_id", None)
38
+ if group_id is None:
39
+ raise ValueError("当前不在群聊中,无法使用群共享记忆")
40
+ return f"group_{group_id}"
41
+ elif scope == "user":
42
+ return event.get_session_id()
43
+ else:
44
+ raise ValueError(f"无效的 scope: {scope}")
24
45
 
25
46
 
26
47
  def _make_operator() -> AsyncUserMemory:
@@ -40,6 +61,18 @@ def _err(message: str) -> str:
40
61
  )
41
62
 
42
63
 
64
+ # 公共参数:scope
65
+ _SCOPE_PROP = FunctionPropertySchema(
66
+ type="string",
67
+ description=(
68
+ "记忆范围:`group`=群共享(群内所有人可见),"
69
+ "`user`=个人专属(仅自己可见)。私聊中不可用`group`"
70
+ ),
71
+ enum=["group", "user"],
72
+ )
73
+
74
+ # Function Schema 定义 ─
75
+
43
76
  WRITE_MEMORY_FUN = FunctionDefinitionSchema(
44
77
  name="write_memory",
45
78
  description="将当前用户(或群组)的重要信息存入长期记忆",
@@ -58,8 +91,9 @@ WRITE_MEMORY_FUN = FunctionDefinitionSchema(
58
91
  description="重要性等级",
59
92
  enum=["low", "medium", "high"],
60
93
  ),
94
+ "scope": _SCOPE_PROP,
61
95
  },
62
- required=["content", "tags", "importance"],
96
+ required=["content", "tags", "importance", "scope"],
63
97
  ),
64
98
  )
65
99
  READ_MEMORY_FUN = FunctionDefinitionSchema(
@@ -81,8 +115,9 @@ READ_MEMORY_FUN = FunctionDefinitionSchema(
81
115
  enum=["low", "medium", "high"],
82
116
  nullable=True,
83
117
  ),
118
+ "scope": _SCOPE_PROP,
84
119
  },
85
- required=["query"],
120
+ required=["query", "scope"],
86
121
  ),
87
122
  )
88
123
 
@@ -106,8 +141,9 @@ UPDATE_FUN = FunctionDefinitionSchema(
106
141
  description="重要性等级(如不修改可不传)",
107
142
  enum=["low", "medium", "high"],
108
143
  ),
144
+ "scope": _SCOPE_PROP,
109
145
  },
110
- required=["id"],
146
+ required=["id", "scope"],
111
147
  ),
112
148
  )
113
149
 
@@ -118,9 +154,10 @@ DELETE_FUN = FunctionDefinitionSchema(
118
154
  parameters=FunctionParametersSchema(
119
155
  type="object",
120
156
  properties={
121
- "id": FunctionPropertySchema(type="string", description="要删除的记忆ID")
157
+ "id": FunctionPropertySchema(type="string", description="要删除的记忆ID"),
158
+ "scope": _SCOPE_PROP,
122
159
  },
123
- required=["id"],
160
+ required=["id", "scope"],
124
161
  ),
125
162
  )
126
163
 
@@ -134,30 +171,40 @@ LIST_MEMORY_FUN = FunctionDefinitionSchema(
134
171
  "limit": FunctionPropertySchema(
135
172
  type="integer", description="返回数量,默认5条", default=5
136
173
  ),
174
+ "scope": _SCOPE_PROP,
137
175
  },
138
- required=[],
176
+ required=["scope"],
139
177
  ),
140
178
  )
141
179
 
142
180
 
181
+ # Handler 实现 ─
182
+
183
+
143
184
  @on_tools(WRITE_MEMORY_FUN, custom_run=True, strict=True)
144
185
  async def w(ctx: ToolContext) -> str:
145
- session_id = _get_session_id(ctx)
186
+ scope: str = ctx.data["scope"]
187
+ try:
188
+ partition_id = _resolve_scope_id(ctx, scope)
189
+ except ValueError as e:
190
+ return _err(str(e))
191
+
146
192
  ope = _make_operator()
147
193
  await ope.init()
148
194
  logger.debug("开始写入记忆...")
149
- logger.debug(f"会话ID: {session_id}")
195
+ logger.debug(f"scope={scope}, partition_id={partition_id}")
150
196
  logger.debug(f"记忆Payload {ctx.data['content']}")
151
197
  if (
152
- await ope.count_user_notes(session_id)
198
+ await ope.count_user_notes(partition_id)
153
199
  >= (await DataManager().safe_get_config()).per_session_memory_limit
154
200
  ):
155
201
  return _err("当前会话的记忆数量已超过限制,请清理后再试")
156
202
  meta = MemoryMetadata(
157
203
  importance=ctx.data["importance"],
158
204
  tags=ctx.data["tags"],
205
+ scope=cast(Scope, scope),
159
206
  )
160
- await ope.add_note(session_id, ctx.data["content"], metadata=meta)
207
+ await ope.add_note(partition_id, ctx.data["content"], metadata=meta)
161
208
  dmp = meta.model_dump()
162
209
  dmp["status"] = "success"
163
210
  return json.dumps(dmp, ensure_ascii=False, indent=4)
@@ -165,14 +212,19 @@ async def w(ctx: ToolContext) -> str:
165
212
 
166
213
  @on_tools(READ_MEMORY_FUN, custom_run=True, strict=True)
167
214
  async def r(ctx: ToolContext) -> str:
168
- session_id = _get_session_id(ctx)
215
+ scope: str = ctx.data["scope"]
216
+ try:
217
+ partition_id = _resolve_scope_id(ctx, scope)
218
+ except ValueError as e:
219
+ return _err(str(e))
220
+
169
221
  ope = _make_operator()
170
222
  await ope.init()
171
223
  logger.debug("开始检索记忆...")
172
- logger.debug(f"会话ID: {session_id}")
224
+ logger.debug(f"scope={scope}, partition_id={partition_id}")
173
225
  try:
174
226
  res: QueryResult = await ope.query_notes(
175
- session_id,
227
+ partition_id,
176
228
  ctx.data["query"],
177
229
  top_k=ctx.data["top_k"],
178
230
  importance=ctx.data.get("importance"),
@@ -185,26 +237,31 @@ async def r(ctx: ToolContext) -> str:
185
237
 
186
238
  @on_tools(UPDATE_FUN, custom_run=True, strict=True)
187
239
  async def update_memory(ctx: ToolContext) -> str:
188
- session_id = _get_session_id(ctx)
240
+ scope: str = ctx.data["scope"]
241
+ try:
242
+ partition_id = _resolve_scope_id(ctx, scope)
243
+ except ValueError as e:
244
+ return _err(str(e))
245
+
189
246
  ope = _make_operator()
190
247
  await ope.init()
191
248
  logger.debug("开始更新记忆...")
192
- logger.debug(f"会话ID: {session_id}")
249
+ logger.debug(f"scope={scope}, partition_id={partition_id}")
193
250
  logger.debug(f"更新记忆ID: {ctx.data['id']}")
194
251
 
195
252
  try:
196
- result = await ope.get_all_notes(session_id, include=["metadatas", "documents"])
253
+ result = await ope.get_all_notes(
254
+ partition_id, include=["metadatas", "documents"]
255
+ )
197
256
  if not result or not result.get("ids"):
198
257
  return _err("未找到指定ID的记忆")
199
258
 
200
- # 查找要更新的记忆
201
259
  ids = result["ids"]
202
260
  try:
203
261
  target_index = ids.index(ctx.data["id"])
204
262
  except ValueError:
205
263
  return _err("未找到指定ID的记忆")
206
264
 
207
- # 获取现有数据
208
265
  documents = result.get("documents") or []
209
266
  metadatas = result.get("metadatas") or []
210
267
  existing_doc = documents[target_index] if target_index < len(documents) else ""
@@ -213,14 +270,12 @@ async def update_memory(ctx: ToolContext) -> str:
213
270
  if not existing_meta:
214
271
  return _err("记忆元数据损坏")
215
272
 
216
- # 构建更新后的数据
217
273
  new_content = ctx.data.get("content", existing_doc)
218
274
  new_tags = ctx.data.get("tags", existing_meta.get("tags", ""))
219
275
  new_importance = ctx.data.get(
220
276
  "importance", existing_meta.get("importance", "medium")
221
277
  )
222
278
 
223
- # 保留原始创建时间
224
279
  created_at_str = existing_meta.get("created_at", "")
225
280
  if isinstance(created_at_str, str):
226
281
  try:
@@ -230,14 +285,19 @@ async def update_memory(ctx: ToolContext) -> str:
230
285
  else:
231
286
  created_at = datetime.now()
232
287
 
233
- # 使用原生 update,原子操作,不会丢失数据
234
288
  meta = MemoryMetadata(
235
289
  memory_id=ctx.data["id"],
236
290
  importance=new_importance,
237
291
  tags=new_tags,
292
+ scope=cast(
293
+ Scope,
294
+ existing_meta.get("scope", scope)
295
+ if isinstance(existing_meta.get("scope"), str)
296
+ else scope,
297
+ ),
238
298
  created_at=created_at,
239
299
  )
240
- await ope.update_note(session_id, new_content, metadata=meta)
300
+ await ope.update_note(partition_id, new_content, metadata=meta)
241
301
 
242
302
  return _ok(message="记忆更新成功", id=ctx.data["id"])
243
303
  except Exception as e:
@@ -247,15 +307,20 @@ async def update_memory(ctx: ToolContext) -> str:
247
307
 
248
308
  @on_tools(DELETE_FUN, custom_run=True, strict=True)
249
309
  async def delete_memory(ctx: ToolContext) -> str:
250
- session_id = _get_session_id(ctx)
310
+ scope: str = ctx.data["scope"]
311
+ try:
312
+ partition_id = _resolve_scope_id(ctx, scope)
313
+ except ValueError as e:
314
+ return _err(str(e))
315
+
251
316
  ope = _make_operator()
252
317
  await ope.init()
253
318
  logger.debug("开始删除记忆...")
254
- logger.debug(f"会话ID: {session_id}")
319
+ logger.debug(f"scope={scope}, partition_id={partition_id}")
255
320
  logger.debug(f"删除记忆ID: {ctx.data['id']}")
256
321
 
257
322
  try:
258
- result = await ope.get_all_notes(session_id, include=["metadatas"])
323
+ result = await ope.get_all_notes(partition_id, include=["metadatas"])
259
324
  if (
260
325
  not result
261
326
  or not result.get("ids")
@@ -263,7 +328,7 @@ async def delete_memory(ctx: ToolContext) -> str:
263
328
  ):
264
329
  return _err("未找到指定ID的记忆")
265
330
 
266
- await ope.delete_note(session_id, ctx.data["id"])
331
+ await ope.delete_note(partition_id, ctx.data["id"])
267
332
  return _ok(message="记忆删除成功", id=ctx.data["id"])
268
333
  except Exception as e:
269
334
  logger.error(f"删除记忆时发生错误: {e}")
@@ -272,15 +337,22 @@ async def delete_memory(ctx: ToolContext) -> str:
272
337
 
273
338
  @on_tools(LIST_MEMORY_FUN, custom_run=True, strict=True)
274
339
  async def list_memory(ctx: ToolContext) -> str:
275
- session_id = _get_session_id(ctx)
340
+ scope: str = ctx.data["scope"]
341
+ try:
342
+ partition_id = _resolve_scope_id(ctx, scope)
343
+ except ValueError as e:
344
+ return _err(str(e))
345
+
276
346
  ope = _make_operator()
277
347
  await ope.init()
278
348
  logger.debug("开始列出记忆...")
279
- logger.debug(f"会话ID: {session_id}")
349
+ logger.debug(f"scope={scope}, partition_id={partition_id}")
280
350
 
281
351
  try:
282
352
  limit = ctx.data.get("limit", 5)
283
- result = await ope.get_all_notes(session_id, include=["metadatas", "documents"])
353
+ result = await ope.get_all_notes(
354
+ partition_id, include=["metadatas", "documents"]
355
+ )
284
356
 
285
357
  if not result or not result.get("ids"):
286
358
  return _ok(memories=[], total=0)
@@ -299,6 +371,7 @@ async def list_memory(ctx: ToolContext) -> str:
299
371
  memories.append(
300
372
  {
301
373
  "id": doc_id,
374
+ "scope": meta.get("scope", "user"),
302
375
  "tags": meta.get("tags", ""),
303
376
  "importance": meta.get("importance", "medium"),
304
377
  "content_preview": doc[:50] + "..." if len(doc) > 50 else doc,
@@ -309,6 +309,9 @@ class MemoryMetadata(BaseModel):
309
309
  )
310
310
  tags: str = Field(description="标签")
311
311
  importance: Literal["low", "medium", "high"] = Field(description="重要程度")
312
+ scope: Literal["group", "user"] = Field(
313
+ default="user", description="记忆范围:群共享(group)或个人专属(user)"
314
+ )
312
315
  created_at: datetime = Field(
313
316
  default_factory=lambda: datetime.now(utc), description="创建时间"
314
317
  )
@@ -358,6 +361,7 @@ class AsyncUserMemory:
358
361
  ids=[metadata.memory_id],
359
362
  metadatas=[
360
363
  {
364
+ "scope": metadata.scope,
361
365
  "user_id": user_id,
362
366
  "tags": metadata.tags,
363
367
  "importance": metadata.importance,
@@ -382,6 +386,7 @@ class AsyncUserMemory:
382
386
  ids=[metadata.memory_id],
383
387
  metadatas=[
384
388
  {
389
+ "scope": metadata.scope,
385
390
  "user_id": user_id,
386
391
  "tags": metadata.tags,
387
392
  "importance": metadata.importance,
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: amrita_plugin_memory
3
- Version: 0.1.1
3
+ Version: 0.1.2
4
4
  Summary: Amrita长期记忆插件
5
5
  Requires-Python: <3.14,>=3.10
6
6
  Description-Content-Type: text/markdown
@@ -42,11 +42,12 @@ amrita_plugin_memory/
42
42
  - **config.py**: 定义配置文件(过期天数、记忆上限)和环境变量(数据库类型、嵌入模型地址等),通过 Pydantic 模型校验。
43
43
  - **embed.py**: 实现 Ollama 协议的嵌入适配器,通过 HTTP API 调用 `/api/embed` 端点。
44
44
  - **vector.py**: 对 ChromaDB 原生 API 进行 `asyncio.to_thread` 封装,提供异步的集合管理与记忆 CRUD。使用 `aiologic.Lock` 按用户粒度加锁,保证并发安全。
45
- - **tools.py**: 定义 5 个 Function Calling 工具(`write_memory` / `read_memory` / `update_memory` / `delete_memory` / `list_memory`),注册到 Amrita 的工具系统中。
45
+ - **tools.py**: 定义 5 个 Function Calling 工具(`write_memory` / `read_memory` / `update_memory` / `delete_memory` / `list_memory`),注册到 Amrita 的工具系统中。通过 `scope` 参数支持群共享记忆与用户专属记忆的分区隔离。
46
46
 
47
47
  ## 功能特点
48
48
 
49
49
  - **长期记忆存储**:将用户偏好、项目信息等持久化存入 ChromaDB 向量数据库
50
+ - **群/用户记忆隔离**:LLM 可通过 `scope` 参数选择存入群共享记忆(群内所有人可见)或用户专属记忆(仅自己可见)
50
51
  - **语义检索**:基于嵌入向量的语义相似度检索,支持中英文关键词
51
52
  - **智能标签**:支持使用自定义标签对记忆进行分类(如 `preference`、`project`)
52
53
  - **重要性分级**:三级重要程度(`low` / `medium` / `high`),支持按级别过滤检索
@@ -65,6 +66,7 @@ amrita_plugin_memory/
65
66
  | `content` | string | ✅ | 记忆内容,简洁明了 |
66
67
  | `tags` | string | ✅ | 分类标签,如 `preference`、`project` |
67
68
  | `importance` | `"low"` / `"medium"` / `"high"` | ✅ | 重要性等级 |
69
+ | `scope` | `"group"` / `"user"` | ✅ | `group`=群共享,`user`=个人专属(私聊不可用 `group`) |
68
70
 
69
71
  ### 2. `read_memory` —— 检索记忆
70
72
 
@@ -75,6 +77,7 @@ amrita_plugin_memory/
75
77
  | `query` | string | ✅ | — | 关键词字符串,空格分隔 |
76
78
  | `top_k` | integer | ❌ | `5` | 返回结果数量 |
77
79
  | `importance` | `"low"` / `"medium"` / `"high"` | ❌ | — | 按重要性过滤 |
80
+ | `scope` | `"group"` / `"user"` | ✅ | — | 检索范围:群共享或个人专属 |
78
81
 
79
82
  ### 3. `update_memory` —— 更新记忆
80
83
 
@@ -83,6 +86,7 @@ amrita_plugin_memory/
83
86
  | 参数 | 类型 | 必需 | 描述 |
84
87
  |------|------|------|------|
85
88
  | `id` | string | ✅ | 要更新的记忆 ID |
89
+ | `scope` | `"group"` / `"user"` | ✅ | 记忆所属范围 |
86
90
  | `content` | string | ❌ | 新的记忆内容 |
87
91
  | `tags` | string | ❌ | 新的标签 |
88
92
  | `importance` | `"low"` / `"medium"` / `"high"` | ❌ | 新的重要性等级 |
@@ -94,14 +98,16 @@ amrita_plugin_memory/
94
98
  | 参数 | 类型 | 必需 | 描述 |
95
99
  |------|------|------|------|
96
100
  | `id` | string | ✅ | 要删除的记忆 ID |
101
+ | `scope` | `"group"` / `"user"` | ✅ | 记忆所属范围 |
97
102
 
98
103
  ### 5. `list_memory` —— 列出记忆
99
104
 
100
- 列出当前用户的所有记忆(返回标签和 ID)。
105
+ 列出当前用户的所有记忆(返回 scope、标签和 ID)。
101
106
 
102
107
  | 参数 | 类型 | 必需 | 默认值 | 描述 |
103
108
  |------|------|------|--------|------|
104
109
  | `limit` | integer | ❌ | `5` | 返回数量限制 |
110
+ | `scope` | `"group"` / `"user"` | ✅ | — | 列出范围:群共享或个人专属 |
105
111
 
106
112
  ## 配置选项
107
113
 
@@ -1,7 +1,7 @@
1
1
  [project]
2
2
  name = "amrita_plugin_memory"
3
3
  description = "Amrita长期记忆插件"
4
- version = "0.1.1"
4
+ version = "0.1.2"
5
5
  dependencies = [
6
6
  "aiohttp>=3.13.3",
7
7
  "aiologic>=0.16.0",