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.
- {amrita_plugin_memory-0.1.1 → amrita_plugin_memory-0.1.2}/PKG-INFO +9 -3
- {amrita_plugin_memory-0.1.1 → amrita_plugin_memory-0.1.2}/README.md +8 -2
- {amrita_plugin_memory-0.1.1 → amrita_plugin_memory-0.1.2}/amrita_plugin_memory/tools.py +106 -33
- {amrita_plugin_memory-0.1.1 → amrita_plugin_memory-0.1.2}/amrita_plugin_memory/vector.py +5 -0
- {amrita_plugin_memory-0.1.1 → amrita_plugin_memory-0.1.2}/amrita_plugin_memory.egg-info/PKG-INFO +9 -3
- {amrita_plugin_memory-0.1.1 → amrita_plugin_memory-0.1.2}/pyproject.toml +1 -1
- {amrita_plugin_memory-0.1.1 → amrita_plugin_memory-0.1.2}/LICENSE +0 -0
- {amrita_plugin_memory-0.1.1 → amrita_plugin_memory-0.1.2}/amrita_plugin_memory/__init__.py +0 -0
- {amrita_plugin_memory-0.1.1 → amrita_plugin_memory-0.1.2}/amrita_plugin_memory/config.py +0 -0
- {amrita_plugin_memory-0.1.1 → amrita_plugin_memory-0.1.2}/amrita_plugin_memory/embed.py +0 -0
- {amrita_plugin_memory-0.1.1 → amrita_plugin_memory-0.1.2}/amrita_plugin_memory.egg-info/SOURCES.txt +0 -0
- {amrita_plugin_memory-0.1.1 → amrita_plugin_memory-0.1.2}/amrita_plugin_memory.egg-info/dependency_links.txt +0 -0
- {amrita_plugin_memory-0.1.1 → amrita_plugin_memory-0.1.2}/amrita_plugin_memory.egg-info/requires.txt +0 -0
- {amrita_plugin_memory-0.1.1 → amrita_plugin_memory-0.1.2}/amrita_plugin_memory.egg-info/top_level.txt +0 -0
- {amrita_plugin_memory-0.1.1 → amrita_plugin_memory-0.1.2}/setup.cfg +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: amrita_plugin_memory
|
|
3
|
-
Version: 0.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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
21
|
-
|
|
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"]
|
|
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
|
-
|
|
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"
|
|
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(
|
|
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(
|
|
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
|
-
|
|
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"
|
|
224
|
+
logger.debug(f"scope={scope}, partition_id={partition_id}")
|
|
173
225
|
try:
|
|
174
226
|
res: QueryResult = await ope.query_notes(
|
|
175
|
-
|
|
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
|
-
|
|
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"
|
|
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(
|
|
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(
|
|
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
|
-
|
|
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"
|
|
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(
|
|
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(
|
|
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
|
-
|
|
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"
|
|
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(
|
|
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,
|
{amrita_plugin_memory-0.1.1 → amrita_plugin_memory-0.1.2}/amrita_plugin_memory.egg-info/PKG-INFO
RENAMED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: amrita_plugin_memory
|
|
3
|
-
Version: 0.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
|
-
|
|
105
|
+
列出当前用户的所有记忆(返回 scope、标签和 ID)。
|
|
101
106
|
|
|
102
107
|
| 参数 | 类型 | 必需 | 默认值 | 描述 |
|
|
103
108
|
|------|------|------|--------|------|
|
|
104
109
|
| `limit` | integer | ❌ | `5` | 返回数量限制 |
|
|
110
|
+
| `scope` | `"group"` / `"user"` | ✅ | — | 列出范围:群共享或个人专属 |
|
|
105
111
|
|
|
106
112
|
## 配置选项
|
|
107
113
|
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{amrita_plugin_memory-0.1.1 → amrita_plugin_memory-0.1.2}/amrita_plugin_memory.egg-info/SOURCES.txt
RENAMED
|
File without changes
|
|
File without changes
|
{amrita_plugin_memory-0.1.1 → amrita_plugin_memory-0.1.2}/amrita_plugin_memory.egg-info/requires.txt
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|