amrita_plugin_memory 0.1.0__tar.gz → 0.1.1__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 (18) hide show
  1. amrita_plugin_memory-0.1.1/PKG-INFO +181 -0
  2. amrita_plugin_memory-0.1.1/README.md +166 -0
  3. {amrita_plugin_memory-0.1.0 → amrita_plugin_memory-0.1.1}/amrita_plugin_memory/config.py +4 -1
  4. {amrita_plugin_memory-0.1.0 → amrita_plugin_memory-0.1.1}/amrita_plugin_memory/embed.py +4 -28
  5. {amrita_plugin_memory-0.1.0 → amrita_plugin_memory-0.1.1}/amrita_plugin_memory/tools.py +85 -152
  6. {amrita_plugin_memory-0.1.0 → amrita_plugin_memory-0.1.1}/amrita_plugin_memory/vector.py +28 -7
  7. amrita_plugin_memory-0.1.1/amrita_plugin_memory.egg-info/PKG-INFO +181 -0
  8. {amrita_plugin_memory-0.1.0 → amrita_plugin_memory-0.1.1}/amrita_plugin_memory.egg-info/requires.txt +1 -1
  9. {amrita_plugin_memory-0.1.0 → amrita_plugin_memory-0.1.1}/pyproject.toml +2 -2
  10. amrita_plugin_memory-0.1.0/PKG-INFO +0 -98
  11. amrita_plugin_memory-0.1.0/README.md +0 -83
  12. amrita_plugin_memory-0.1.0/amrita_plugin_memory.egg-info/PKG-INFO +0 -98
  13. {amrita_plugin_memory-0.1.0 → amrita_plugin_memory-0.1.1}/LICENSE +0 -0
  14. {amrita_plugin_memory-0.1.0 → amrita_plugin_memory-0.1.1}/amrita_plugin_memory/__init__.py +0 -0
  15. {amrita_plugin_memory-0.1.0 → amrita_plugin_memory-0.1.1}/amrita_plugin_memory.egg-info/SOURCES.txt +0 -0
  16. {amrita_plugin_memory-0.1.0 → amrita_plugin_memory-0.1.1}/amrita_plugin_memory.egg-info/dependency_links.txt +0 -0
  17. {amrita_plugin_memory-0.1.0 → amrita_plugin_memory-0.1.1}/amrita_plugin_memory.egg-info/top_level.txt +0 -0
  18. {amrita_plugin_memory-0.1.0 → amrita_plugin_memory-0.1.1}/setup.cfg +0 -0
@@ -0,0 +1,181 @@
1
+ Metadata-Version: 2.4
2
+ Name: amrita_plugin_memory
3
+ Version: 0.1.1
4
+ Summary: Amrita长期记忆插件
5
+ Requires-Python: <3.14,>=3.10
6
+ Description-Content-Type: text/markdown
7
+ License-File: LICENSE
8
+ Requires-Dist: aiohttp>=3.13.3
9
+ Requires-Dist: aiologic>=0.16.0
10
+ Requires-Dist: amrita-core>=0.8.5
11
+ Requires-Dist: amrita>=1.3
12
+ Requires-Dist: chromadb>=1.5.8
13
+ Requires-Dist: nonebot-plugin-localstore>=0.7.4
14
+ Dynamic: license-file
15
+
16
+ # amrita_plugin_memory
17
+
18
+ Amrita 长期记忆插件 —— 基于 ChromaDB 向量数据库的智能记忆管理系统,让 AI 助手能够持久化地记住关于用户的重要信息。
19
+
20
+ ## 技术栈
21
+
22
+ | 组件 | 技术 |
23
+ |------|------|
24
+ | 后端框架 | Python 3.10+ / NoneBot2 / Amrita Core |
25
+ | 向量数据库 | ChromaDB(支持本地 PersistentClient 与远程 HttpClient) |
26
+ | 嵌入模型 | OpenAI Embedding API / Ollama Embedding API |
27
+ | 配置管理 | Pydantic + TOML |
28
+ | 异步处理 | asyncio + aiologic + aiohttp |
29
+ | 代码质量 | Ruff + Pyright |
30
+
31
+ ## 架构概览
32
+
33
+ ```
34
+ amrita_plugin_memory/
35
+ ├── __init__.py # 插件入口,注册依赖
36
+ ├── config.py # 配置模型(ConfigFile / EnvConfig / DataManager)
37
+ ├── embed.py # Ollama 嵌入适配器
38
+ ├── vector.py # ChromaDB 异步封装(WrappedClientAPI / AsyncUserMemory)
39
+ └── tools.py # 工具函数定义与注册(write/read/update/delete/list)
40
+ ```
41
+
42
+ - **config.py**: 定义配置文件(过期天数、记忆上限)和环境变量(数据库类型、嵌入模型地址等),通过 Pydantic 模型校验。
43
+ - **embed.py**: 实现 Ollama 协议的嵌入适配器,通过 HTTP API 调用 `/api/embed` 端点。
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 的工具系统中。
46
+
47
+ ## 功能特点
48
+
49
+ - **长期记忆存储**:将用户偏好、项目信息等持久化存入 ChromaDB 向量数据库
50
+ - **语义检索**:基于嵌入向量的语义相似度检索,支持中英文关键词
51
+ - **智能标签**:支持使用自定义标签对记忆进行分类(如 `preference`、`project`)
52
+ - **重要性分级**:三级重要程度(`low` / `medium` / `high`),支持按级别过滤检索
53
+ - **完整 CRUD**:支持记忆的创建、查询、更新、删除和列表查看
54
+ - **并发安全**:按用户 ID 粒度加锁,确保同一用户的操作串行化
55
+ - **多租户支持**:通过 ChromaDB remote 模式支持远程向量数据库
56
+
57
+ ## 工具函数
58
+
59
+ ### 1. `write_memory` —— 写入记忆
60
+
61
+ 将当前用户(或群组)的重要信息存入长期记忆。
62
+
63
+ | 参数 | 类型 | 必需 | 描述 |
64
+ |------|------|------|------|
65
+ | `content` | string | ✅ | 记忆内容,简洁明了 |
66
+ | `tags` | string | ✅ | 分类标签,如 `preference`、`project` |
67
+ | `importance` | `"low"` / `"medium"` / `"high"` | ✅ | 重要性等级 |
68
+
69
+ ### 2. `read_memory` —— 检索记忆
70
+
71
+ 从记忆库中按语义相似度检索用户相关信息。
72
+
73
+ | 参数 | 类型 | 必需 | 默认值 | 描述 |
74
+ |------|------|------|--------|------|
75
+ | `query` | string | ✅ | — | 关键词字符串,空格分隔 |
76
+ | `top_k` | integer | ❌ | `5` | 返回结果数量 |
77
+ | `importance` | `"low"` / `"medium"` / `"high"` | ❌ | — | 按重要性过滤 |
78
+
79
+ ### 3. `update_memory` —— 更新记忆
80
+
81
+ 更新指定 ID 的记忆内容、标签或重要性。
82
+
83
+ | 参数 | 类型 | 必需 | 描述 |
84
+ |------|------|------|------|
85
+ | `id` | string | ✅ | 要更新的记忆 ID |
86
+ | `content` | string | ❌ | 新的记忆内容 |
87
+ | `tags` | string | ❌ | 新的标签 |
88
+ | `importance` | `"low"` / `"medium"` / `"high"` | ❌ | 新的重要性等级 |
89
+
90
+ ### 4. `delete_memory` —— 删除记忆
91
+
92
+ 删除指定 ID 的记忆。
93
+
94
+ | 参数 | 类型 | 必需 | 描述 |
95
+ |------|------|------|------|
96
+ | `id` | string | ✅ | 要删除的记忆 ID |
97
+
98
+ ### 5. `list_memory` —— 列出记忆
99
+
100
+ 列出当前用户的所有记忆(返回标签和 ID)。
101
+
102
+ | 参数 | 类型 | 必需 | 默认值 | 描述 |
103
+ |------|------|------|--------|------|
104
+ | `limit` | integer | ❌ | `5` | 返回数量限制 |
105
+
106
+ ## 配置选项
107
+
108
+ ### 文件配置(`config/amrita_plugin_memory/config.toml`)
109
+
110
+ | 配置项 | 类型 | 默认值 | 描述 |
111
+ |--------|------|--------|------|
112
+ | `short_term_expiry_days` | int | `3` | 短期记忆过期天数 |
113
+ | `long_term_expiry_days` | int | `30` | 长期记忆过期天数 |
114
+ | `permanent_expiry_days` | int | `365` | 永久记忆过期天数 |
115
+ | `per_session_memory_limit` | int | `50` | 每个会话的记忆数量上限 |
116
+
117
+ ### 环境变量(`.env` 或系统环境变量)
118
+
119
+ | 变量 | 类型 | 默认值 | 描述 |
120
+ |------|------|--------|------|
121
+ | `VECTOR_DB_TYPE` | `local` / `remote` | `local` | ChromaDB 数据库类型 |
122
+ | `VECTOR_DB_SERVER` | string | `127.0.0.1` | 远程 ChromaDB 地址 |
123
+ | `VECTOR_DB_PORT` | int | `8000` | 远程 ChromaDB 端口 |
124
+ | `VECTOR_DB_SERVER_SSL` | bool | `false` | 是否启用 SSL |
125
+ | `VECTOR_DB_REMOTE_HEADERS` | dict | `{}` | 远程请求头 |
126
+ | `VECTOR_DB_TENANT` | string | `default` | 租户名称 |
127
+ | `VECTOR_DB_DATABASE` | string | `default` | 数据库名称 |
128
+ | `EMBEDDING_MODEL_URL` | string | `http://127.0.0.1:11434` | 嵌入模型地址 |
129
+ | `EMBEDDING_MODEL_NAME` | string | `auto` | 嵌入模型名称 |
130
+ | `EMBEDDING_PROCTOL` | `openai` / `ollama-embed` | `ollama-embed` | 嵌入模型协议 |
131
+
132
+ ## 安装与使用
133
+
134
+ ### 前置条件
135
+
136
+ - Python 3.10 ~ 3.13
137
+ - 已部署的 [AmritaBot](https://github.com/AmritaBot) 实例
138
+ - (本地模式)无需额外组件
139
+ - (远程模式)已运行的 ChromaDB 服务端
140
+ - 已运行的嵌入模型服务(如 Ollama)
141
+
142
+ ### 安装
143
+
144
+ ```bash
145
+ ambot plugin install amrita_plugin_memory
146
+ ```
147
+
148
+ ### 配置
149
+
150
+ 插件安装后会自动生成配置文件。你可以通过以下方式配置:
151
+
152
+ 1. **WebUI 配置**:在 Amrita 的 Web 管理界面中直接修改
153
+ 2. **手动编辑**:修改 `config/amrita_plugin_memory/config.toml`
154
+ 3. **环境变量**:通过 `.env` 文件或系统环境变量设置数据库和嵌入模型参数
155
+
156
+ ### 验证
157
+
158
+ 启动后查看日志,确认向量数据库连接成功:
159
+
160
+ ```
161
+ 正在尝试向量数据库连接...
162
+ 当前向量数据库类型: local
163
+ 创建了向量数据库客户端!
164
+ 向量数据库版本:x.x.x
165
+ 完成。
166
+ ```
167
+
168
+ ## 开发
169
+
170
+ ```bash
171
+ # 安装开发依赖
172
+ uv sync
173
+
174
+ # 代码格式化与检查
175
+ ruff format .
176
+ ruff check .
177
+
178
+ # 类型检查
179
+ pyright
180
+ ```
181
+
@@ -0,0 +1,166 @@
1
+ # amrita_plugin_memory
2
+
3
+ Amrita 长期记忆插件 —— 基于 ChromaDB 向量数据库的智能记忆管理系统,让 AI 助手能够持久化地记住关于用户的重要信息。
4
+
5
+ ## 技术栈
6
+
7
+ | 组件 | 技术 |
8
+ |------|------|
9
+ | 后端框架 | Python 3.10+ / NoneBot2 / Amrita Core |
10
+ | 向量数据库 | ChromaDB(支持本地 PersistentClient 与远程 HttpClient) |
11
+ | 嵌入模型 | OpenAI Embedding API / Ollama Embedding API |
12
+ | 配置管理 | Pydantic + TOML |
13
+ | 异步处理 | asyncio + aiologic + aiohttp |
14
+ | 代码质量 | Ruff + Pyright |
15
+
16
+ ## 架构概览
17
+
18
+ ```
19
+ amrita_plugin_memory/
20
+ ├── __init__.py # 插件入口,注册依赖
21
+ ├── config.py # 配置模型(ConfigFile / EnvConfig / DataManager)
22
+ ├── embed.py # Ollama 嵌入适配器
23
+ ├── vector.py # ChromaDB 异步封装(WrappedClientAPI / AsyncUserMemory)
24
+ └── tools.py # 工具函数定义与注册(write/read/update/delete/list)
25
+ ```
26
+
27
+ - **config.py**: 定义配置文件(过期天数、记忆上限)和环境变量(数据库类型、嵌入模型地址等),通过 Pydantic 模型校验。
28
+ - **embed.py**: 实现 Ollama 协议的嵌入适配器,通过 HTTP API 调用 `/api/embed` 端点。
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 的工具系统中。
31
+
32
+ ## 功能特点
33
+
34
+ - **长期记忆存储**:将用户偏好、项目信息等持久化存入 ChromaDB 向量数据库
35
+ - **语义检索**:基于嵌入向量的语义相似度检索,支持中英文关键词
36
+ - **智能标签**:支持使用自定义标签对记忆进行分类(如 `preference`、`project`)
37
+ - **重要性分级**:三级重要程度(`low` / `medium` / `high`),支持按级别过滤检索
38
+ - **完整 CRUD**:支持记忆的创建、查询、更新、删除和列表查看
39
+ - **并发安全**:按用户 ID 粒度加锁,确保同一用户的操作串行化
40
+ - **多租户支持**:通过 ChromaDB remote 模式支持远程向量数据库
41
+
42
+ ## 工具函数
43
+
44
+ ### 1. `write_memory` —— 写入记忆
45
+
46
+ 将当前用户(或群组)的重要信息存入长期记忆。
47
+
48
+ | 参数 | 类型 | 必需 | 描述 |
49
+ |------|------|------|------|
50
+ | `content` | string | ✅ | 记忆内容,简洁明了 |
51
+ | `tags` | string | ✅ | 分类标签,如 `preference`、`project` |
52
+ | `importance` | `"low"` / `"medium"` / `"high"` | ✅ | 重要性等级 |
53
+
54
+ ### 2. `read_memory` —— 检索记忆
55
+
56
+ 从记忆库中按语义相似度检索用户相关信息。
57
+
58
+ | 参数 | 类型 | 必需 | 默认值 | 描述 |
59
+ |------|------|------|--------|------|
60
+ | `query` | string | ✅ | — | 关键词字符串,空格分隔 |
61
+ | `top_k` | integer | ❌ | `5` | 返回结果数量 |
62
+ | `importance` | `"low"` / `"medium"` / `"high"` | ❌ | — | 按重要性过滤 |
63
+
64
+ ### 3. `update_memory` —— 更新记忆
65
+
66
+ 更新指定 ID 的记忆内容、标签或重要性。
67
+
68
+ | 参数 | 类型 | 必需 | 描述 |
69
+ |------|------|------|------|
70
+ | `id` | string | ✅ | 要更新的记忆 ID |
71
+ | `content` | string | ❌ | 新的记忆内容 |
72
+ | `tags` | string | ❌ | 新的标签 |
73
+ | `importance` | `"low"` / `"medium"` / `"high"` | ❌ | 新的重要性等级 |
74
+
75
+ ### 4. `delete_memory` —— 删除记忆
76
+
77
+ 删除指定 ID 的记忆。
78
+
79
+ | 参数 | 类型 | 必需 | 描述 |
80
+ |------|------|------|------|
81
+ | `id` | string | ✅ | 要删除的记忆 ID |
82
+
83
+ ### 5. `list_memory` —— 列出记忆
84
+
85
+ 列出当前用户的所有记忆(返回标签和 ID)。
86
+
87
+ | 参数 | 类型 | 必需 | 默认值 | 描述 |
88
+ |------|------|------|--------|------|
89
+ | `limit` | integer | ❌ | `5` | 返回数量限制 |
90
+
91
+ ## 配置选项
92
+
93
+ ### 文件配置(`config/amrita_plugin_memory/config.toml`)
94
+
95
+ | 配置项 | 类型 | 默认值 | 描述 |
96
+ |--------|------|--------|------|
97
+ | `short_term_expiry_days` | int | `3` | 短期记忆过期天数 |
98
+ | `long_term_expiry_days` | int | `30` | 长期记忆过期天数 |
99
+ | `permanent_expiry_days` | int | `365` | 永久记忆过期天数 |
100
+ | `per_session_memory_limit` | int | `50` | 每个会话的记忆数量上限 |
101
+
102
+ ### 环境变量(`.env` 或系统环境变量)
103
+
104
+ | 变量 | 类型 | 默认值 | 描述 |
105
+ |------|------|--------|------|
106
+ | `VECTOR_DB_TYPE` | `local` / `remote` | `local` | ChromaDB 数据库类型 |
107
+ | `VECTOR_DB_SERVER` | string | `127.0.0.1` | 远程 ChromaDB 地址 |
108
+ | `VECTOR_DB_PORT` | int | `8000` | 远程 ChromaDB 端口 |
109
+ | `VECTOR_DB_SERVER_SSL` | bool | `false` | 是否启用 SSL |
110
+ | `VECTOR_DB_REMOTE_HEADERS` | dict | `{}` | 远程请求头 |
111
+ | `VECTOR_DB_TENANT` | string | `default` | 租户名称 |
112
+ | `VECTOR_DB_DATABASE` | string | `default` | 数据库名称 |
113
+ | `EMBEDDING_MODEL_URL` | string | `http://127.0.0.1:11434` | 嵌入模型地址 |
114
+ | `EMBEDDING_MODEL_NAME` | string | `auto` | 嵌入模型名称 |
115
+ | `EMBEDDING_PROCTOL` | `openai` / `ollama-embed` | `ollama-embed` | 嵌入模型协议 |
116
+
117
+ ## 安装与使用
118
+
119
+ ### 前置条件
120
+
121
+ - Python 3.10 ~ 3.13
122
+ - 已部署的 [AmritaBot](https://github.com/AmritaBot) 实例
123
+ - (本地模式)无需额外组件
124
+ - (远程模式)已运行的 ChromaDB 服务端
125
+ - 已运行的嵌入模型服务(如 Ollama)
126
+
127
+ ### 安装
128
+
129
+ ```bash
130
+ ambot plugin install amrita_plugin_memory
131
+ ```
132
+
133
+ ### 配置
134
+
135
+ 插件安装后会自动生成配置文件。你可以通过以下方式配置:
136
+
137
+ 1. **WebUI 配置**:在 Amrita 的 Web 管理界面中直接修改
138
+ 2. **手动编辑**:修改 `config/amrita_plugin_memory/config.toml`
139
+ 3. **环境变量**:通过 `.env` 文件或系统环境变量设置数据库和嵌入模型参数
140
+
141
+ ### 验证
142
+
143
+ 启动后查看日志,确认向量数据库连接成功:
144
+
145
+ ```
146
+ 正在尝试向量数据库连接...
147
+ 当前向量数据库类型: local
148
+ 创建了向量数据库客户端!
149
+ 向量数据库版本:x.x.x
150
+ 完成。
151
+ ```
152
+
153
+ ## 开发
154
+
155
+ ```bash
156
+ # 安装开发依赖
157
+ uv sync
158
+
159
+ # 代码格式化与检查
160
+ ruff format .
161
+ ruff check .
162
+
163
+ # 类型检查
164
+ pyright
165
+ ```
166
+
@@ -14,6 +14,8 @@ PLUGIN_IM = "amrita_plugin_memory"
14
14
 
15
15
 
16
16
  class ConfigFile(BaseModel):
17
+ """配置文件"""
18
+
17
19
  short_term_expiry_days: int = Field(
18
20
  default=3, description="短期记忆的过期天数,默认为3天"
19
21
  )
@@ -60,12 +62,13 @@ class EnvConfig(BaseModel):
60
62
  default="http://127.0.0.1:11434", description="Embedding模型地址"
61
63
  )
62
64
  embedding_model_name: str = Field(default="auto", description="Embedding模型名称")
63
- embedding_proctol: Literal["openai-embed", "ollama-embed"] = Field(
65
+ embedding_proctol: Literal["openai", "ollama-embed"] = Field(
64
66
  default="ollama-embed", description="Embedding模型协议"
65
67
  )
66
68
 
67
69
 
68
70
  class DataManager(BaseDataManager[ConfigFile]):
71
+ _owner_name = PLUGIN_IM
69
72
  config: ConfigFile
70
73
 
71
74
 
@@ -1,13 +1,14 @@
1
- from collections.abc import Iterable, Sequence
1
+ from collections.abc import Iterable, Sequence # noqa: I001
2
2
  from typing import Any, Literal
3
+ from typing_extensions import override
3
4
 
4
5
  import aiohttp
5
- from amrita_core.protocol import ModelAdapter
6
+ from amrita_core.base.adapter import ModelAdapter
6
7
  from amrita_core.types import EmbeddingChunk
7
- from openai import AsyncOpenAI
8
8
 
9
9
 
10
10
  class OllamaEmbeddingAdapter(ModelAdapter):
11
+ @override
11
12
  async def call_embed(
12
13
  self, texts: Iterable[str], **kwargs
13
14
  ) -> Sequence[EmbeddingChunk]:
@@ -43,28 +44,3 @@ class OllamaEmbeddingAdapter(ModelAdapter):
43
44
  @staticmethod
44
45
  def get_type() -> Literal["embed"]:
45
46
  return "embed"
46
-
47
-
48
- class OpenAIEmbeddingAdapter(ModelAdapter):
49
- async def call_embed(
50
- self, texts: Iterable[str], **kwargs
51
- ) -> Sequence[EmbeddingChunk]:
52
- ps = self.preset
53
- client = AsyncOpenAI(
54
- base_url=ps.base_url, api_key=ps.api_key, max_retries=5, timeout=60
55
- )
56
- if isinstance(texts, str):
57
- texts = [texts]
58
- texts = list(texts)
59
- embedd = await client.embeddings.create(
60
- input=texts, model=ps.model, **kwargs, timeout=60
61
- )
62
- return [EmbeddingChunk.model_validate(data) for data in embedd.data]
63
-
64
- @staticmethod
65
- def get_adapter_protocol() -> Literal["openai-embed"]:
66
- return "openai-embed"
67
-
68
- @staticmethod
69
- def get_type() -> Literal["embed"]:
70
- return "embed"
@@ -1,7 +1,8 @@
1
1
  import json
2
2
  from datetime import datetime
3
+ from typing import Any
3
4
 
4
- from amrita.plugins.chat.runtime import AmritaChatObject
5
+ from amrita.plugins.chat.runtime import AmritaBotContext
5
6
  from amrita_core import (
6
7
  FunctionDefinitionSchema,
7
8
  FunctionParametersSchema,
@@ -15,6 +16,30 @@ from nonebot import logger
15
16
  from .config import DataManager
16
17
  from .vector import AsyncUserMemory, MemoryMetadata, get_db_conn
17
18
 
19
+
20
+ def _get_session_id(ctx: ToolContext) -> str:
21
+ """从 ToolContext 中提取会话 ID"""
22
+ amrictx: AmritaBotContext = ctx.ctx.chat_object._hook_kwargs["amrita"]
23
+ return amrictx["event"].get_session_id()
24
+
25
+
26
+ def _make_operator() -> AsyncUserMemory:
27
+ """创建 AsyncUserMemory 实例"""
28
+ return AsyncUserMemory(get_db_conn())
29
+
30
+
31
+ def _ok(**extra: Any) -> str:
32
+ """构建成功响应 JSON"""
33
+ return json.dumps({"status": "success", **extra}, ensure_ascii=False, indent=4)
34
+
35
+
36
+ def _err(message: str) -> str:
37
+ """构建错误响应 JSON"""
38
+ return json.dumps(
39
+ {"status": "error", "message": message}, ensure_ascii=False, indent=4
40
+ )
41
+
42
+
18
43
  WRITE_MEMORY_FUN = FunctionDefinitionSchema(
19
44
  name="write_memory",
20
45
  description="将当前用户(或群组)的重要信息存入长期记忆",
@@ -117,34 +142,22 @@ LIST_MEMORY_FUN = FunctionDefinitionSchema(
117
142
 
118
143
  @on_tools(WRITE_MEMORY_FUN, custom_run=True, strict=True)
119
144
  async def w(ctx: ToolContext) -> str:
120
- assert isinstance(ctx.ctx.chat_object, AmritaChatObject)
121
- nb_event = ctx.ctx.chat_object.event
122
- ope = AsyncUserMemory(get_db_conn())
145
+ session_id = _get_session_id(ctx)
146
+ ope = _make_operator()
123
147
  await ope.init()
124
148
  logger.debug("开始写入记忆...")
125
- logger.debug(f"会话ID: {nb_event.get_session_id()}")
149
+ logger.debug(f"会话ID: {session_id}")
126
150
  logger.debug(f"记忆Payload {ctx.data['content']}")
127
151
  if (
128
- await ope.count_user_notes(nb_event.get_session_id())
129
- > (await DataManager().safe_get_config()).per_session_memory_limit
152
+ await ope.count_user_notes(session_id)
153
+ >= (await DataManager().safe_get_config()).per_session_memory_limit
130
154
  ):
131
- return json.dumps(
132
- {
133
- "status": "error",
134
- "message": "当前会话的记忆数量已超过限制,请清理后再试",
135
- },
136
- ensure_ascii=False,
137
- indent=4,
138
- )
155
+ return _err("当前会话的记忆数量已超过限制,请清理后再试")
139
156
  meta = MemoryMetadata(
140
157
  importance=ctx.data["importance"],
141
158
  tags=ctx.data["tags"],
142
159
  )
143
- await ope.add_note(
144
- nb_event.get_session_id(),
145
- ctx.data["content"],
146
- metadata=meta,
147
- )
160
+ await ope.add_note(session_id, ctx.data["content"], metadata=meta)
148
161
  dmp = meta.model_dump()
149
162
  dmp["status"] = "success"
150
163
  return json.dumps(dmp, ensure_ascii=False, indent=4)
@@ -152,80 +165,53 @@ async def w(ctx: ToolContext) -> str:
152
165
 
153
166
  @on_tools(READ_MEMORY_FUN, custom_run=True, strict=True)
154
167
  async def r(ctx: ToolContext) -> str:
155
- assert isinstance(ctx.ctx.chat_object, AmritaChatObject)
156
- nb_event = ctx.ctx.chat_object.event
157
- ope = AsyncUserMemory(get_db_conn())
168
+ session_id = _get_session_id(ctx)
169
+ ope = _make_operator()
158
170
  await ope.init()
159
171
  logger.debug("开始检索记忆...")
160
- logger.debug(f"会话ID: {nb_event.get_session_id()}")
161
- res: QueryResult = await ope.query_notes(
162
- nb_event.get_session_id(),
163
- ctx.data["query"],
164
- top_k=ctx.data["top_k"],
165
- importance=ctx.data.get("importance"),
166
- )
167
- return json.dumps(res, ensure_ascii=False, indent=4)
172
+ logger.debug(f"会话ID: {session_id}")
173
+ try:
174
+ res: QueryResult = await ope.query_notes(
175
+ session_id,
176
+ ctx.data["query"],
177
+ top_k=ctx.data["top_k"],
178
+ importance=ctx.data.get("importance"),
179
+ )
180
+ return json.dumps(res, ensure_ascii=False, indent=4)
181
+ except Exception as e:
182
+ logger.error(f"检索记忆时发生错误: {e}")
183
+ return _err(f"检索记忆失败: {e!s}")
168
184
 
169
185
 
170
186
  @on_tools(UPDATE_FUN, custom_run=True, strict=True)
171
187
  async def update_memory(ctx: ToolContext) -> str:
172
- assert isinstance(ctx.ctx.chat_object, AmritaChatObject)
173
- nb_event = ctx.ctx.chat_object.event
174
- ope = AsyncUserMemory(get_db_conn())
188
+ session_id = _get_session_id(ctx)
189
+ ope = _make_operator()
175
190
  await ope.init()
176
191
  logger.debug("开始更新记忆...")
177
- logger.debug(f"会话ID: {nb_event.get_session_id()}")
192
+ logger.debug(f"会话ID: {session_id}")
178
193
  logger.debug(f"更新记忆ID: {ctx.data['id']}")
179
194
 
180
195
  try:
181
- result = await ope.get_all_notes(
182
- nb_event.get_session_id(), include=["metadatas", "documents"]
183
- )
196
+ result = await ope.get_all_notes(session_id, include=["metadatas", "documents"])
184
197
  if not result or not result.get("ids"):
185
- return json.dumps(
186
- {
187
- "status": "error",
188
- "message": "未找到指定ID的记忆",
189
- },
190
- ensure_ascii=False,
191
- indent=4,
192
- )
198
+ return _err("未找到指定ID的记忆")
193
199
 
194
200
  # 查找要更新的记忆
195
- target_index = -1
196
201
  ids = result["ids"]
197
- for i, doc_id in enumerate(ids):
198
- if doc_id == ctx.data["id"]:
199
- target_index = i
200
- break
202
+ try:
203
+ target_index = ids.index(ctx.data["id"])
204
+ except ValueError:
205
+ return _err("未找到指定ID的记忆")
201
206
 
202
- if target_index == -1:
203
- return json.dumps(
204
- {
205
- "status": "error",
206
- "message": "未找到指定ID的记忆",
207
- },
208
- ensure_ascii=False,
209
- indent=4,
210
- )
211
-
212
- # 获取现有数据,处理可能为None的情况
207
+ # 获取现有数据
213
208
  documents = result.get("documents") or []
214
209
  metadatas = result.get("metadatas") or []
215
-
216
210
  existing_doc = documents[target_index] if target_index < len(documents) else ""
217
211
  existing_meta = metadatas[target_index] if target_index < len(metadatas) else {}
218
212
 
219
- # 确保有必要的元数据
220
213
  if not existing_meta:
221
- return json.dumps(
222
- {
223
- "status": "error",
224
- "message": "记忆元数据损坏",
225
- },
226
- ensure_ascii=False,
227
- indent=4,
228
- )
214
+ return _err("记忆元数据损坏")
229
215
 
230
216
  # 构建更新后的数据
231
217
  new_content = ctx.data.get("content", existing_doc)
@@ -234,113 +220,71 @@ async def update_memory(ctx: ToolContext) -> str:
234
220
  "importance", existing_meta.get("importance", "medium")
235
221
  )
236
222
 
237
- # 处理创建时间
223
+ # 保留原始创建时间
238
224
  created_at_str = existing_meta.get("created_at", "")
239
- if isinstance(created_at_str, str) and created_at_str:
240
- # 处理ISO格式的时间字符串
241
- if created_at_str.endswith("Z"):
242
- created_at_str = created_at_str.replace("Z", "+00:00")
243
- created_at = datetime.fromisoformat(created_at_str)
225
+ if isinstance(created_at_str, str):
226
+ try:
227
+ created_at = datetime.fromisoformat(created_at_str)
228
+ except ValueError:
229
+ created_at = datetime.now()
244
230
  else:
245
231
  created_at = datetime.now()
246
- await ope.delete_note(nb_event.get_session_id(), ctx.data["id"])
232
+
233
+ # 使用原生 update,原子操作,不会丢失数据
247
234
  meta = MemoryMetadata(
248
235
  memory_id=ctx.data["id"],
249
236
  importance=new_importance,
250
237
  tags=new_tags,
251
238
  created_at=created_at,
252
239
  )
253
- await ope.add_note(
254
- nb_event.get_session_id(),
255
- new_content,
256
- metadata=meta,
257
- )
240
+ await ope.update_note(session_id, new_content, metadata=meta)
258
241
 
259
- return json.dumps(
260
- {"status": "success", "message": "记忆更新成功", "id": ctx.data["id"]},
261
- ensure_ascii=False,
262
- indent=4,
263
- )
242
+ return _ok(message="记忆更新成功", id=ctx.data["id"])
264
243
  except Exception as e:
265
244
  logger.error(f"更新记忆时发生错误: {e}")
266
- return json.dumps(
267
- {
268
- "status": "error",
269
- "message": f"更新记忆失败: {e!s}",
270
- },
271
- ensure_ascii=False,
272
- indent=4,
273
- )
245
+ return _err(f"更新记忆失败: {e!s}")
274
246
 
275
247
 
276
248
  @on_tools(DELETE_FUN, custom_run=True, strict=True)
277
249
  async def delete_memory(ctx: ToolContext) -> str:
278
- assert isinstance(ctx.ctx.chat_object, AmritaChatObject)
279
- nb_event = ctx.ctx.chat_object.event
280
- ope = AsyncUserMemory(get_db_conn())
250
+ session_id = _get_session_id(ctx)
251
+ ope = _make_operator()
281
252
  await ope.init()
282
253
  logger.debug("开始删除记忆...")
283
- logger.debug(f"会话ID: {nb_event.get_session_id()}")
254
+ logger.debug(f"会话ID: {session_id}")
284
255
  logger.debug(f"删除记忆ID: {ctx.data['id']}")
285
256
 
286
257
  try:
287
- # 检查记忆是否存在
288
- result = await ope.get_all_notes(
289
- nb_event.get_session_id(), include=["metadatas"]
290
- )
258
+ result = await ope.get_all_notes(session_id, include=["metadatas"])
291
259
  if (
292
260
  not result
293
261
  or not result.get("ids")
294
262
  or ctx.data["id"] not in (result.get("ids") or [])
295
263
  ):
296
- return json.dumps(
297
- {
298
- "status": "error",
299
- "message": "未找到指定ID的记忆",
300
- },
301
- ensure_ascii=False,
302
- indent=4,
303
- )
264
+ return _err("未找到指定ID的记忆")
304
265
 
305
- await ope.delete_note(nb_event.get_session_id(), ctx.data["id"])
306
- return json.dumps(
307
- {"status": "success", "message": "记忆删除成功", "id": ctx.data["id"]},
308
- ensure_ascii=False,
309
- indent=4,
310
- )
266
+ await ope.delete_note(session_id, ctx.data["id"])
267
+ return _ok(message="记忆删除成功", id=ctx.data["id"])
311
268
  except Exception as e:
312
269
  logger.error(f"删除记忆时发生错误: {e}")
313
- return json.dumps(
314
- {
315
- "status": "error",
316
- "message": f"删除记忆失败: {e!s}",
317
- },
318
- ensure_ascii=False,
319
- indent=4,
320
- )
270
+ return _err(f"删除记忆失败: {e!s}")
321
271
 
322
272
 
323
273
  @on_tools(LIST_MEMORY_FUN, custom_run=True, strict=True)
324
274
  async def list_memory(ctx: ToolContext) -> str:
325
- assert isinstance(ctx.ctx.chat_object, AmritaChatObject)
326
- nb_event = ctx.ctx.chat_object.event
327
- ope = AsyncUserMemory(get_db_conn())
275
+ session_id = _get_session_id(ctx)
276
+ ope = _make_operator()
328
277
  await ope.init()
329
278
  logger.debug("开始列出记忆...")
330
- logger.debug(f"会话ID: {nb_event.get_session_id()}")
279
+ logger.debug(f"会话ID: {session_id}")
331
280
 
332
281
  try:
333
282
  limit = ctx.data.get("limit", 5)
334
- result = await ope.get_all_notes(
335
- nb_event.get_session_id(), include=["metadatas", "documents"]
336
- )
283
+ result = await ope.get_all_notes(session_id, include=["metadatas", "documents"])
337
284
 
338
285
  if not result or not result.get("ids"):
339
- return json.dumps(
340
- {"status": "success", "memories": [], "total": 0},
341
- ensure_ascii=False,
342
- indent=4,
343
- )
286
+ return _ok(memories=[], total=0)
287
+
344
288
  memories = []
345
289
  ids = result["ids"]
346
290
  documents = result.get("documents") or [""] * len(ids)
@@ -361,18 +305,7 @@ async def list_memory(ctx: ToolContext) -> str:
361
305
  }
362
306
  )
363
307
 
364
- return json.dumps(
365
- {"status": "success", "memories": memories, "total": len(ids)},
366
- ensure_ascii=False,
367
- indent=4,
368
- )
308
+ return _ok(memories=memories, total=len(ids))
369
309
  except Exception as e:
370
310
  logger.error(f"列出记忆时发生错误: {e}")
371
- return json.dumps(
372
- {
373
- "status": "error",
374
- "message": f"列出记忆失败: {e!s}",
375
- },
376
- ensure_ascii=False,
377
- indent=4,
378
- )
311
+ return _err(f"列出记忆失败: {e!s}")
@@ -7,7 +7,6 @@ from collections.abc import Awaitable, Callable, Sequence
7
7
  from datetime import datetime
8
8
  from functools import wraps
9
9
  from inspect import iscoroutinefunction
10
- from types import CoroutineType
11
10
  from typing import Any, Literal, TypeVar, overload
12
11
  from uuid import UUID
13
12
 
@@ -15,7 +14,7 @@ import aiologic
15
14
  import chromadb
16
15
  from amrita_core.libchat import call_embedding
17
16
  from amrita_core.types import EmbeddingChunk
18
- from amrita_core.weakcache import WeakValueLRUCache
17
+ from amrita_sense.weakcache import WeakValueLRUCache
19
18
  from chromadb.api import ClientAPI
20
19
  from chromadb.api.collection_configuration import (
21
20
  CreateCollectionConfiguration,
@@ -53,14 +52,12 @@ def get_lock(collection: str, uid: str) -> aiologic.Lock:
53
52
  @overload
54
53
  def any_to_thread(
55
54
  func: Callable[..., Awaitable[T]], /, *args, **kwargs
56
- ) -> CoroutineType[Any, Any, T]: ...
55
+ ) -> Awaitable[T]: ...
57
56
  @overload
58
- def any_to_thread(
59
- func: Callable[..., T], /, *args, **kwargs
60
- ) -> CoroutineType[Any, Any, T]: ...
57
+ def any_to_thread(func: Callable[..., T], /, *args, **kwargs) -> Awaitable[T]: ...
61
58
 
62
59
 
63
- def any_to_thread(func: Callable[..., Awaitable[T] | T], /, *args, **kwargs):
60
+ def any_to_thread(func: Callable[..., Awaitable[T] | T], /, *args, **kwargs) -> Any:
64
61
  if not callable(func):
65
62
  raise TypeError("func must be callable")
66
63
  if iscoroutinefunction(func):
@@ -371,6 +368,30 @@ class AsyncUserMemory:
371
368
  documents=[note_text],
372
369
  )
373
370
 
371
+ @require_init
372
+ async def update_note(self, user_id: str, note_text: str, metadata: MemoryMetadata):
373
+ """更新指定记忆,使用 ChromaDB 原生 update 保证原子性"""
374
+ async with get_lock(self._collection_name, user_id):
375
+ vector: Sequence[EmbeddingChunk] = await call_embedding(
376
+ note_text, build_preset()
377
+ )
378
+ if len(vector) == 0:
379
+ raise RuntimeError("No embedding returned")
380
+ await any_to_thread(
381
+ self._collection.update,
382
+ ids=[metadata.memory_id],
383
+ metadatas=[
384
+ {
385
+ "user_id": user_id,
386
+ "tags": metadata.tags,
387
+ "importance": metadata.importance,
388
+ "created_at": metadata.created_at.isoformat(),
389
+ }
390
+ ],
391
+ embeddings=[vector[0].embedding],
392
+ documents=[note_text],
393
+ )
394
+
374
395
  @require_init
375
396
  async def query_notes(
376
397
  self,
@@ -0,0 +1,181 @@
1
+ Metadata-Version: 2.4
2
+ Name: amrita_plugin_memory
3
+ Version: 0.1.1
4
+ Summary: Amrita长期记忆插件
5
+ Requires-Python: <3.14,>=3.10
6
+ Description-Content-Type: text/markdown
7
+ License-File: LICENSE
8
+ Requires-Dist: aiohttp>=3.13.3
9
+ Requires-Dist: aiologic>=0.16.0
10
+ Requires-Dist: amrita-core>=0.8.5
11
+ Requires-Dist: amrita>=1.3
12
+ Requires-Dist: chromadb>=1.5.8
13
+ Requires-Dist: nonebot-plugin-localstore>=0.7.4
14
+ Dynamic: license-file
15
+
16
+ # amrita_plugin_memory
17
+
18
+ Amrita 长期记忆插件 —— 基于 ChromaDB 向量数据库的智能记忆管理系统,让 AI 助手能够持久化地记住关于用户的重要信息。
19
+
20
+ ## 技术栈
21
+
22
+ | 组件 | 技术 |
23
+ |------|------|
24
+ | 后端框架 | Python 3.10+ / NoneBot2 / Amrita Core |
25
+ | 向量数据库 | ChromaDB(支持本地 PersistentClient 与远程 HttpClient) |
26
+ | 嵌入模型 | OpenAI Embedding API / Ollama Embedding API |
27
+ | 配置管理 | Pydantic + TOML |
28
+ | 异步处理 | asyncio + aiologic + aiohttp |
29
+ | 代码质量 | Ruff + Pyright |
30
+
31
+ ## 架构概览
32
+
33
+ ```
34
+ amrita_plugin_memory/
35
+ ├── __init__.py # 插件入口,注册依赖
36
+ ├── config.py # 配置模型(ConfigFile / EnvConfig / DataManager)
37
+ ├── embed.py # Ollama 嵌入适配器
38
+ ├── vector.py # ChromaDB 异步封装(WrappedClientAPI / AsyncUserMemory)
39
+ └── tools.py # 工具函数定义与注册(write/read/update/delete/list)
40
+ ```
41
+
42
+ - **config.py**: 定义配置文件(过期天数、记忆上限)和环境变量(数据库类型、嵌入模型地址等),通过 Pydantic 模型校验。
43
+ - **embed.py**: 实现 Ollama 协议的嵌入适配器,通过 HTTP API 调用 `/api/embed` 端点。
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 的工具系统中。
46
+
47
+ ## 功能特点
48
+
49
+ - **长期记忆存储**:将用户偏好、项目信息等持久化存入 ChromaDB 向量数据库
50
+ - **语义检索**:基于嵌入向量的语义相似度检索,支持中英文关键词
51
+ - **智能标签**:支持使用自定义标签对记忆进行分类(如 `preference`、`project`)
52
+ - **重要性分级**:三级重要程度(`low` / `medium` / `high`),支持按级别过滤检索
53
+ - **完整 CRUD**:支持记忆的创建、查询、更新、删除和列表查看
54
+ - **并发安全**:按用户 ID 粒度加锁,确保同一用户的操作串行化
55
+ - **多租户支持**:通过 ChromaDB remote 模式支持远程向量数据库
56
+
57
+ ## 工具函数
58
+
59
+ ### 1. `write_memory` —— 写入记忆
60
+
61
+ 将当前用户(或群组)的重要信息存入长期记忆。
62
+
63
+ | 参数 | 类型 | 必需 | 描述 |
64
+ |------|------|------|------|
65
+ | `content` | string | ✅ | 记忆内容,简洁明了 |
66
+ | `tags` | string | ✅ | 分类标签,如 `preference`、`project` |
67
+ | `importance` | `"low"` / `"medium"` / `"high"` | ✅ | 重要性等级 |
68
+
69
+ ### 2. `read_memory` —— 检索记忆
70
+
71
+ 从记忆库中按语义相似度检索用户相关信息。
72
+
73
+ | 参数 | 类型 | 必需 | 默认值 | 描述 |
74
+ |------|------|------|--------|------|
75
+ | `query` | string | ✅ | — | 关键词字符串,空格分隔 |
76
+ | `top_k` | integer | ❌ | `5` | 返回结果数量 |
77
+ | `importance` | `"low"` / `"medium"` / `"high"` | ❌ | — | 按重要性过滤 |
78
+
79
+ ### 3. `update_memory` —— 更新记忆
80
+
81
+ 更新指定 ID 的记忆内容、标签或重要性。
82
+
83
+ | 参数 | 类型 | 必需 | 描述 |
84
+ |------|------|------|------|
85
+ | `id` | string | ✅ | 要更新的记忆 ID |
86
+ | `content` | string | ❌ | 新的记忆内容 |
87
+ | `tags` | string | ❌ | 新的标签 |
88
+ | `importance` | `"low"` / `"medium"` / `"high"` | ❌ | 新的重要性等级 |
89
+
90
+ ### 4. `delete_memory` —— 删除记忆
91
+
92
+ 删除指定 ID 的记忆。
93
+
94
+ | 参数 | 类型 | 必需 | 描述 |
95
+ |------|------|------|------|
96
+ | `id` | string | ✅ | 要删除的记忆 ID |
97
+
98
+ ### 5. `list_memory` —— 列出记忆
99
+
100
+ 列出当前用户的所有记忆(返回标签和 ID)。
101
+
102
+ | 参数 | 类型 | 必需 | 默认值 | 描述 |
103
+ |------|------|------|--------|------|
104
+ | `limit` | integer | ❌ | `5` | 返回数量限制 |
105
+
106
+ ## 配置选项
107
+
108
+ ### 文件配置(`config/amrita_plugin_memory/config.toml`)
109
+
110
+ | 配置项 | 类型 | 默认值 | 描述 |
111
+ |--------|------|--------|------|
112
+ | `short_term_expiry_days` | int | `3` | 短期记忆过期天数 |
113
+ | `long_term_expiry_days` | int | `30` | 长期记忆过期天数 |
114
+ | `permanent_expiry_days` | int | `365` | 永久记忆过期天数 |
115
+ | `per_session_memory_limit` | int | `50` | 每个会话的记忆数量上限 |
116
+
117
+ ### 环境变量(`.env` 或系统环境变量)
118
+
119
+ | 变量 | 类型 | 默认值 | 描述 |
120
+ |------|------|--------|------|
121
+ | `VECTOR_DB_TYPE` | `local` / `remote` | `local` | ChromaDB 数据库类型 |
122
+ | `VECTOR_DB_SERVER` | string | `127.0.0.1` | 远程 ChromaDB 地址 |
123
+ | `VECTOR_DB_PORT` | int | `8000` | 远程 ChromaDB 端口 |
124
+ | `VECTOR_DB_SERVER_SSL` | bool | `false` | 是否启用 SSL |
125
+ | `VECTOR_DB_REMOTE_HEADERS` | dict | `{}` | 远程请求头 |
126
+ | `VECTOR_DB_TENANT` | string | `default` | 租户名称 |
127
+ | `VECTOR_DB_DATABASE` | string | `default` | 数据库名称 |
128
+ | `EMBEDDING_MODEL_URL` | string | `http://127.0.0.1:11434` | 嵌入模型地址 |
129
+ | `EMBEDDING_MODEL_NAME` | string | `auto` | 嵌入模型名称 |
130
+ | `EMBEDDING_PROCTOL` | `openai` / `ollama-embed` | `ollama-embed` | 嵌入模型协议 |
131
+
132
+ ## 安装与使用
133
+
134
+ ### 前置条件
135
+
136
+ - Python 3.10 ~ 3.13
137
+ - 已部署的 [AmritaBot](https://github.com/AmritaBot) 实例
138
+ - (本地模式)无需额外组件
139
+ - (远程模式)已运行的 ChromaDB 服务端
140
+ - 已运行的嵌入模型服务(如 Ollama)
141
+
142
+ ### 安装
143
+
144
+ ```bash
145
+ ambot plugin install amrita_plugin_memory
146
+ ```
147
+
148
+ ### 配置
149
+
150
+ 插件安装后会自动生成配置文件。你可以通过以下方式配置:
151
+
152
+ 1. **WebUI 配置**:在 Amrita 的 Web 管理界面中直接修改
153
+ 2. **手动编辑**:修改 `config/amrita_plugin_memory/config.toml`
154
+ 3. **环境变量**:通过 `.env` 文件或系统环境变量设置数据库和嵌入模型参数
155
+
156
+ ### 验证
157
+
158
+ 启动后查看日志,确认向量数据库连接成功:
159
+
160
+ ```
161
+ 正在尝试向量数据库连接...
162
+ 当前向量数据库类型: local
163
+ 创建了向量数据库客户端!
164
+ 向量数据库版本:x.x.x
165
+ 完成。
166
+ ```
167
+
168
+ ## 开发
169
+
170
+ ```bash
171
+ # 安装开发依赖
172
+ uv sync
173
+
174
+ # 代码格式化与检查
175
+ ruff format .
176
+ ruff check .
177
+
178
+ # 类型检查
179
+ pyright
180
+ ```
181
+
@@ -1,6 +1,6 @@
1
1
  aiohttp>=3.13.3
2
2
  aiologic>=0.16.0
3
3
  amrita-core>=0.8.5
4
- amrita[full]>=1.1.9
4
+ amrita>=1.3
5
5
  chromadb>=1.5.8
6
6
  nonebot-plugin-localstore>=0.7.4
@@ -1,12 +1,12 @@
1
1
  [project]
2
2
  name = "amrita_plugin_memory"
3
3
  description = "Amrita长期记忆插件"
4
- version = "0.1.0"
4
+ version = "0.1.1"
5
5
  dependencies = [
6
6
  "aiohttp>=3.13.3",
7
7
  "aiologic>=0.16.0",
8
8
  "amrita-core>=0.8.5",
9
- "amrita[full]>=1.1.9",
9
+ "amrita>=1.3",
10
10
  "chromadb>=1.5.8",
11
11
  "nonebot-plugin-localstore>=0.7.4",
12
12
  ]
@@ -1,98 +0,0 @@
1
- Metadata-Version: 2.4
2
- Name: amrita_plugin_memory
3
- Version: 0.1.0
4
- Summary: Amrita长期记忆插件
5
- Requires-Python: <3.14,>=3.10
6
- Description-Content-Type: text/markdown
7
- License-File: LICENSE
8
- Requires-Dist: aiohttp>=3.13.3
9
- Requires-Dist: aiologic>=0.16.0
10
- Requires-Dist: amrita-core>=0.8.5
11
- Requires-Dist: amrita[full]>=1.1.9
12
- Requires-Dist: chromadb>=1.5.8
13
- Requires-Dist: nonebot-plugin-localstore>=0.7.4
14
- Dynamic: license-file
15
-
16
- # amrita_plugin_memory
17
-
18
- Amrita 长期记忆插件 - 一个智能记忆管理系统,用于帮助 AI 助手记住关于用户的长期信息。
19
-
20
- ## 技术栈
21
-
22
- - **后端**: Python 3.x 插件架构
23
- - **向量数据库**: ChromaDB (支持本地和远程模式)
24
- - **嵌入模型**: 支持 OpenAI Embedding API 和 Ollama Embedding API
25
- - **框架**: NoneBot2 + Amrita Core + AmritaBot
26
- - **配置管理**: Pydantic + TOML
27
- - **异步处理**: asyncio + aiologic + aiohttp
28
-
29
- ## 功能特点
30
-
31
- - **长期记忆存储**:持久化存储关于用户的长期重要信息
32
- - **智能标签系统**:支持使用标签对记忆进行分类管理
33
- - **重要性分级**:支持设置记忆的重要程度(low/medium/high)
34
- - **灵活检索**:支持按关键词检索记忆内容
35
- - **完整管理**:支持记忆的增删改查和列表查看
36
-
37
- ## 工具函数
38
-
39
- 插件提供了五个主要工具函数:
40
-
41
- ### 1. write_memory
42
-
43
- 将重要信息存入长期记忆
44
-
45
- 参数:
46
-
47
- - content: 记忆内容,简洁明了(必需)
48
- - tags: 分类标签,如 `"preference"` 或 `"project"`(必需)
49
- - importance: 重要性等级(必需,可选值:`"low"`, `"medium"`, `"high"`)
50
-
51
- ### 2. read_memory
52
-
53
- 从记忆库检索用户相关信息
54
-
55
- 参数:
56
-
57
- - query: 关键词字符串,用空格分割(必需)
58
- - top_k: 返回数量,默认5条(可选)
59
- - importance: 重要性等级过滤(可选,可选值:`"low"`, `"medium"`, `"high"`)
60
-
61
- ### 3. update_memory
62
-
63
- 更新指定ID的记忆内容
64
-
65
- 参数:
66
-
67
- - id: 要更新的记忆ID(必需)
68
- - content: 新的记忆内容(可选)
69
- - tags: 新的标签(可选)
70
- - importance: 新的重要性等级(可选,可选值:`"low"`, `"medium"`, `"high"`)
71
-
72
- ### 4. delete_memory
73
-
74
- 删除指定ID的记忆
75
-
76
- 参数:
77
-
78
- - id: 要删除的记忆ID(必需)
79
-
80
- ### 5. list_memory
81
-
82
- 列出当前用户的所有记忆
83
-
84
- 参数:
85
-
86
- - limit: 返回数量限制,默认5条(可选)
87
-
88
- ## 配置选项
89
-
90
- - `short_term_expiry_days`: 短期记忆的过期天数,默认为3天
91
- - `long_term_expiry_days`: 长期记忆的过期天数,默认为30天
92
- - `permanent_expiry_days`: 永久记忆的过期天数,默认为1年
93
- - `per_session_memory_limit`: 每个会话的记忆数量限制,默认为50条
94
-
95
- ## 安装和使用
96
-
97
- 1. 安装插件:`amrita plugin install amrita_plugin_memory`
98
- 2. 配置插件:插件会自动生成配置文件,或者您可以在WebUI中配置。
@@ -1,83 +0,0 @@
1
- # amrita_plugin_memory
2
-
3
- Amrita 长期记忆插件 - 一个智能记忆管理系统,用于帮助 AI 助手记住关于用户的长期信息。
4
-
5
- ## 技术栈
6
-
7
- - **后端**: Python 3.x 插件架构
8
- - **向量数据库**: ChromaDB (支持本地和远程模式)
9
- - **嵌入模型**: 支持 OpenAI Embedding API 和 Ollama Embedding API
10
- - **框架**: NoneBot2 + Amrita Core + AmritaBot
11
- - **配置管理**: Pydantic + TOML
12
- - **异步处理**: asyncio + aiologic + aiohttp
13
-
14
- ## 功能特点
15
-
16
- - **长期记忆存储**:持久化存储关于用户的长期重要信息
17
- - **智能标签系统**:支持使用标签对记忆进行分类管理
18
- - **重要性分级**:支持设置记忆的重要程度(low/medium/high)
19
- - **灵活检索**:支持按关键词检索记忆内容
20
- - **完整管理**:支持记忆的增删改查和列表查看
21
-
22
- ## 工具函数
23
-
24
- 插件提供了五个主要工具函数:
25
-
26
- ### 1. write_memory
27
-
28
- 将重要信息存入长期记忆
29
-
30
- 参数:
31
-
32
- - content: 记忆内容,简洁明了(必需)
33
- - tags: 分类标签,如 `"preference"` 或 `"project"`(必需)
34
- - importance: 重要性等级(必需,可选值:`"low"`, `"medium"`, `"high"`)
35
-
36
- ### 2. read_memory
37
-
38
- 从记忆库检索用户相关信息
39
-
40
- 参数:
41
-
42
- - query: 关键词字符串,用空格分割(必需)
43
- - top_k: 返回数量,默认5条(可选)
44
- - importance: 重要性等级过滤(可选,可选值:`"low"`, `"medium"`, `"high"`)
45
-
46
- ### 3. update_memory
47
-
48
- 更新指定ID的记忆内容
49
-
50
- 参数:
51
-
52
- - id: 要更新的记忆ID(必需)
53
- - content: 新的记忆内容(可选)
54
- - tags: 新的标签(可选)
55
- - importance: 新的重要性等级(可选,可选值:`"low"`, `"medium"`, `"high"`)
56
-
57
- ### 4. delete_memory
58
-
59
- 删除指定ID的记忆
60
-
61
- 参数:
62
-
63
- - id: 要删除的记忆ID(必需)
64
-
65
- ### 5. list_memory
66
-
67
- 列出当前用户的所有记忆
68
-
69
- 参数:
70
-
71
- - limit: 返回数量限制,默认5条(可选)
72
-
73
- ## 配置选项
74
-
75
- - `short_term_expiry_days`: 短期记忆的过期天数,默认为3天
76
- - `long_term_expiry_days`: 长期记忆的过期天数,默认为30天
77
- - `permanent_expiry_days`: 永久记忆的过期天数,默认为1年
78
- - `per_session_memory_limit`: 每个会话的记忆数量限制,默认为50条
79
-
80
- ## 安装和使用
81
-
82
- 1. 安装插件:`amrita plugin install amrita_plugin_memory`
83
- 2. 配置插件:插件会自动生成配置文件,或者您可以在WebUI中配置。
@@ -1,98 +0,0 @@
1
- Metadata-Version: 2.4
2
- Name: amrita_plugin_memory
3
- Version: 0.1.0
4
- Summary: Amrita长期记忆插件
5
- Requires-Python: <3.14,>=3.10
6
- Description-Content-Type: text/markdown
7
- License-File: LICENSE
8
- Requires-Dist: aiohttp>=3.13.3
9
- Requires-Dist: aiologic>=0.16.0
10
- Requires-Dist: amrita-core>=0.8.5
11
- Requires-Dist: amrita[full]>=1.1.9
12
- Requires-Dist: chromadb>=1.5.8
13
- Requires-Dist: nonebot-plugin-localstore>=0.7.4
14
- Dynamic: license-file
15
-
16
- # amrita_plugin_memory
17
-
18
- Amrita 长期记忆插件 - 一个智能记忆管理系统,用于帮助 AI 助手记住关于用户的长期信息。
19
-
20
- ## 技术栈
21
-
22
- - **后端**: Python 3.x 插件架构
23
- - **向量数据库**: ChromaDB (支持本地和远程模式)
24
- - **嵌入模型**: 支持 OpenAI Embedding API 和 Ollama Embedding API
25
- - **框架**: NoneBot2 + Amrita Core + AmritaBot
26
- - **配置管理**: Pydantic + TOML
27
- - **异步处理**: asyncio + aiologic + aiohttp
28
-
29
- ## 功能特点
30
-
31
- - **长期记忆存储**:持久化存储关于用户的长期重要信息
32
- - **智能标签系统**:支持使用标签对记忆进行分类管理
33
- - **重要性分级**:支持设置记忆的重要程度(low/medium/high)
34
- - **灵活检索**:支持按关键词检索记忆内容
35
- - **完整管理**:支持记忆的增删改查和列表查看
36
-
37
- ## 工具函数
38
-
39
- 插件提供了五个主要工具函数:
40
-
41
- ### 1. write_memory
42
-
43
- 将重要信息存入长期记忆
44
-
45
- 参数:
46
-
47
- - content: 记忆内容,简洁明了(必需)
48
- - tags: 分类标签,如 `"preference"` 或 `"project"`(必需)
49
- - importance: 重要性等级(必需,可选值:`"low"`, `"medium"`, `"high"`)
50
-
51
- ### 2. read_memory
52
-
53
- 从记忆库检索用户相关信息
54
-
55
- 参数:
56
-
57
- - query: 关键词字符串,用空格分割(必需)
58
- - top_k: 返回数量,默认5条(可选)
59
- - importance: 重要性等级过滤(可选,可选值:`"low"`, `"medium"`, `"high"`)
60
-
61
- ### 3. update_memory
62
-
63
- 更新指定ID的记忆内容
64
-
65
- 参数:
66
-
67
- - id: 要更新的记忆ID(必需)
68
- - content: 新的记忆内容(可选)
69
- - tags: 新的标签(可选)
70
- - importance: 新的重要性等级(可选,可选值:`"low"`, `"medium"`, `"high"`)
71
-
72
- ### 4. delete_memory
73
-
74
- 删除指定ID的记忆
75
-
76
- 参数:
77
-
78
- - id: 要删除的记忆ID(必需)
79
-
80
- ### 5. list_memory
81
-
82
- 列出当前用户的所有记忆
83
-
84
- 参数:
85
-
86
- - limit: 返回数量限制,默认5条(可选)
87
-
88
- ## 配置选项
89
-
90
- - `short_term_expiry_days`: 短期记忆的过期天数,默认为3天
91
- - `long_term_expiry_days`: 长期记忆的过期天数,默认为30天
92
- - `permanent_expiry_days`: 永久记忆的过期天数,默认为1年
93
- - `per_session_memory_limit`: 每个会话的记忆数量限制,默认为50条
94
-
95
- ## 安装和使用
96
-
97
- 1. 安装插件:`amrita plugin install amrita_plugin_memory`
98
- 2. 配置插件:插件会自动生成配置文件,或者您可以在WebUI中配置。