amrita_plugin_memory 0.1.0__py3-none-any.whl
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/__init__.py +7 -0
- amrita_plugin_memory/config.py +83 -0
- amrita_plugin_memory/embed.py +70 -0
- amrita_plugin_memory/tools.py +378 -0
- amrita_plugin_memory/vector.py +434 -0
- amrita_plugin_memory-0.1.0.dist-info/METADATA +98 -0
- amrita_plugin_memory-0.1.0.dist-info/RECORD +10 -0
- amrita_plugin_memory-0.1.0.dist-info/WHEEL +5 -0
- amrita_plugin_memory-0.1.0.dist-info/licenses/LICENSE +661 -0
- amrita_plugin_memory-0.1.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,434 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import uuid
|
|
4
|
+
from asyncio import to_thread
|
|
5
|
+
from collections import defaultdict
|
|
6
|
+
from collections.abc import Awaitable, Callable, Sequence
|
|
7
|
+
from datetime import datetime
|
|
8
|
+
from functools import wraps
|
|
9
|
+
from inspect import iscoroutinefunction
|
|
10
|
+
from types import CoroutineType
|
|
11
|
+
from typing import Any, Literal, TypeVar, overload
|
|
12
|
+
from uuid import UUID
|
|
13
|
+
|
|
14
|
+
import aiologic
|
|
15
|
+
import chromadb
|
|
16
|
+
from amrita_core.libchat import call_embedding
|
|
17
|
+
from amrita_core.types import EmbeddingChunk
|
|
18
|
+
from amrita_core.weakcache import WeakValueLRUCache
|
|
19
|
+
from chromadb.api import ClientAPI
|
|
20
|
+
from chromadb.api.collection_configuration import (
|
|
21
|
+
CreateCollectionConfiguration,
|
|
22
|
+
)
|
|
23
|
+
from chromadb.api.models.Collection import Collection
|
|
24
|
+
from chromadb.api.types import (
|
|
25
|
+
CollectionMetadata,
|
|
26
|
+
DataLoader,
|
|
27
|
+
DefaultEmbeddingFunction,
|
|
28
|
+
Embeddable,
|
|
29
|
+
EmbeddingFunction,
|
|
30
|
+
Loadable,
|
|
31
|
+
Schema,
|
|
32
|
+
)
|
|
33
|
+
from chromadb.config import DEFAULT_DATABASE
|
|
34
|
+
from nonebot import get_driver, logger
|
|
35
|
+
from pydantic import BaseModel, Field
|
|
36
|
+
from pytz import utc
|
|
37
|
+
|
|
38
|
+
from .config import VECTOR_DB_PATH, build_preset, env_config
|
|
39
|
+
|
|
40
|
+
T = TypeVar("T")
|
|
41
|
+
_collection_lock_pool: defaultdict[str, WeakValueLRUCache[str, aiologic.Lock]] = (
|
|
42
|
+
defaultdict(lambda: WeakValueLRUCache(capacity=1024, loose_mode=True))
|
|
43
|
+
)
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
def get_lock(collection: str, uid: str) -> aiologic.Lock:
|
|
47
|
+
if (lock := _collection_lock_pool[collection].get(uid)) is None:
|
|
48
|
+
lock = aiologic.Lock()
|
|
49
|
+
_collection_lock_pool[collection][uid] = lock
|
|
50
|
+
return lock
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
@overload
|
|
54
|
+
def any_to_thread(
|
|
55
|
+
func: Callable[..., Awaitable[T]], /, *args, **kwargs
|
|
56
|
+
) -> CoroutineType[Any, Any, T]: ...
|
|
57
|
+
@overload
|
|
58
|
+
def any_to_thread(
|
|
59
|
+
func: Callable[..., T], /, *args, **kwargs
|
|
60
|
+
) -> CoroutineType[Any, Any, T]: ...
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
def any_to_thread(func: Callable[..., Awaitable[T] | T], /, *args, **kwargs):
|
|
64
|
+
if not callable(func):
|
|
65
|
+
raise TypeError("func must be callable")
|
|
66
|
+
if iscoroutinefunction(func):
|
|
67
|
+
return func(*args, **kwargs)
|
|
68
|
+
return to_thread(func, *args, **kwargs)
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
def get_db_conn() -> ClientAPI:
|
|
72
|
+
match env_config.vector_db_type:
|
|
73
|
+
case "local":
|
|
74
|
+
db = chromadb.PersistentClient(path=VECTOR_DB_PATH)
|
|
75
|
+
case "remote":
|
|
76
|
+
db = chromadb.HttpClient(
|
|
77
|
+
host=env_config.vector_db_server,
|
|
78
|
+
port=env_config.vector_db_port,
|
|
79
|
+
tenant=env_config.vector_db_tenant,
|
|
80
|
+
database=env_config.vector_db_database,
|
|
81
|
+
headers=env_config.vector_db_remote_headers,
|
|
82
|
+
ssl=env_config.vector_db_server_ssl,
|
|
83
|
+
)
|
|
84
|
+
case _:
|
|
85
|
+
raise ValueError("无效的向量数据库类型")
|
|
86
|
+
return db
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
@get_driver().on_startup
|
|
90
|
+
async def _setup():
|
|
91
|
+
logger.info("正在尝试向量数据库连接...")
|
|
92
|
+
logger.info(
|
|
93
|
+
f"当前向量数据库类型: {env_config.vector_db_type}",
|
|
94
|
+
)
|
|
95
|
+
db = get_db_conn()
|
|
96
|
+
logger.info("创建了向量数据库客户端!")
|
|
97
|
+
logger.info(f"向量数据库版本:{db.get_version()}")
|
|
98
|
+
logger.info(f"所有可用集合:{db.list_collections()}")
|
|
99
|
+
logger.info("完成。")
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
class WrappedClientAPI:
|
|
103
|
+
__client: ClientAPI
|
|
104
|
+
tenant: str
|
|
105
|
+
database: str
|
|
106
|
+
|
|
107
|
+
def __init__(self, client: ClientAPI):
|
|
108
|
+
self.__client = client
|
|
109
|
+
|
|
110
|
+
async def list_collections(
|
|
111
|
+
self,
|
|
112
|
+
limit: int | None = None,
|
|
113
|
+
offset: int | None = None,
|
|
114
|
+
) -> Sequence[Collection]:
|
|
115
|
+
"""List all collections.
|
|
116
|
+
Args:
|
|
117
|
+
limit: The maximum number of entries to return. Defaults to None.
|
|
118
|
+
offset: The number of entries to skip before returning. Defaults to None.
|
|
119
|
+
|
|
120
|
+
Returns:
|
|
121
|
+
Sequence[Collection]: A list of collections.
|
|
122
|
+
|
|
123
|
+
Examples:
|
|
124
|
+
```python
|
|
125
|
+
await client.list_collections()
|
|
126
|
+
# [collection(name="my_collection", metadata={})]
|
|
127
|
+
```
|
|
128
|
+
"""
|
|
129
|
+
return await to_thread(self.__client.list_collections, limit, offset)
|
|
130
|
+
|
|
131
|
+
async def create_collection(
|
|
132
|
+
self,
|
|
133
|
+
name: str,
|
|
134
|
+
schema: Schema | None = None,
|
|
135
|
+
configuration: CreateCollectionConfiguration | None = None,
|
|
136
|
+
metadata: CollectionMetadata | None = None,
|
|
137
|
+
embedding_function: chromadb.EmbeddingFunction[Embeddable]
|
|
138
|
+
| None = DefaultEmbeddingFunction(), # type: ignore
|
|
139
|
+
data_loader: DataLoader[Loadable] | None = None,
|
|
140
|
+
get_or_create: bool = False,
|
|
141
|
+
) -> Collection:
|
|
142
|
+
"""Create a new collection with the given name and metadata.
|
|
143
|
+
Args:
|
|
144
|
+
name: The name of the collection to create.
|
|
145
|
+
metadata: Optional metadata to associate with the collection.
|
|
146
|
+
embedding_function: Optional function to use to embed documents.
|
|
147
|
+
Uses the default embedding function if not provided.
|
|
148
|
+
get_or_create: If True, return the existing collection if it exists.
|
|
149
|
+
data_loader: Optional function to use to load records (documents, images, etc.)
|
|
150
|
+
|
|
151
|
+
Returns:
|
|
152
|
+
Collection: The newly created collection.
|
|
153
|
+
|
|
154
|
+
Raises:
|
|
155
|
+
ValueError: If the collection already exists and get_or_create is False.
|
|
156
|
+
ValueError: If the collection name is invalid.
|
|
157
|
+
|
|
158
|
+
Examples:
|
|
159
|
+
```python
|
|
160
|
+
await client.create_collection("my_collection")
|
|
161
|
+
# collection(name="my_collection", metadata={})
|
|
162
|
+
|
|
163
|
+
await client.create_collection("my_collection", metadata={"foo": "bar"})
|
|
164
|
+
# collection(name="my_collection", metadata={"foo": "bar"})
|
|
165
|
+
```
|
|
166
|
+
"""
|
|
167
|
+
return await to_thread(
|
|
168
|
+
self.__client.create_collection,
|
|
169
|
+
name,
|
|
170
|
+
schema,
|
|
171
|
+
configuration,
|
|
172
|
+
metadata,
|
|
173
|
+
embedding_function,
|
|
174
|
+
data_loader,
|
|
175
|
+
get_or_create,
|
|
176
|
+
)
|
|
177
|
+
|
|
178
|
+
async def get_collection(
|
|
179
|
+
self,
|
|
180
|
+
name: str,
|
|
181
|
+
embedding_function: EmbeddingFunction[Embeddable]
|
|
182
|
+
| None = DefaultEmbeddingFunction(), # pyright: ignore[reportArgumentType]
|
|
183
|
+
data_loader: DataLoader[Loadable] | None = None,
|
|
184
|
+
) -> Collection:
|
|
185
|
+
"""Get a collection with the given name.
|
|
186
|
+
Args:
|
|
187
|
+
name: The name of the collection to get
|
|
188
|
+
embedding_function: Optional function to use to embed documents.
|
|
189
|
+
Uses the default embedding function if not provided.
|
|
190
|
+
data_loader: Optional function to use to load records (documents, images, etc.)
|
|
191
|
+
|
|
192
|
+
Returns:
|
|
193
|
+
Collection: The collection
|
|
194
|
+
|
|
195
|
+
Raises:
|
|
196
|
+
ValueError: If the collection does not exist
|
|
197
|
+
|
|
198
|
+
Examples:
|
|
199
|
+
```python
|
|
200
|
+
await client.get_collection("my_collection")
|
|
201
|
+
# collection(name="my_collection", metadata={})
|
|
202
|
+
```
|
|
203
|
+
"""
|
|
204
|
+
return await to_thread(
|
|
205
|
+
self.__client.get_collection,
|
|
206
|
+
name,
|
|
207
|
+
embedding_function,
|
|
208
|
+
data_loader,
|
|
209
|
+
)
|
|
210
|
+
|
|
211
|
+
async def get_collection_by_id(
|
|
212
|
+
self,
|
|
213
|
+
id: UUID,
|
|
214
|
+
embedding_function: EmbeddingFunction[Embeddable]
|
|
215
|
+
| None = DefaultEmbeddingFunction(), # type: ignore
|
|
216
|
+
data_loader: DataLoader[Loadable] | None = None,
|
|
217
|
+
) -> Collection:
|
|
218
|
+
"""Get a collection by its ID.
|
|
219
|
+
|
|
220
|
+
Args:
|
|
221
|
+
id: The UUID of the collection to get.
|
|
222
|
+
embedding_function: Optional function to use to embed documents.
|
|
223
|
+
Uses the default embedding function if not provided.
|
|
224
|
+
data_loader: Optional function to use to load records (documents, images, etc.)
|
|
225
|
+
|
|
226
|
+
Returns:
|
|
227
|
+
Collection: The collection
|
|
228
|
+
|
|
229
|
+
Raises:
|
|
230
|
+
NotFoundError: If no collection with the given ID exists.
|
|
231
|
+
|
|
232
|
+
Examples:
|
|
233
|
+
```python
|
|
234
|
+
await client.get_collection_by_id(uuid.UUID("..."))
|
|
235
|
+
# collection(name="my_collection", metadata={})
|
|
236
|
+
```
|
|
237
|
+
"""
|
|
238
|
+
return await to_thread(
|
|
239
|
+
self.__client.get_collection_by_id,
|
|
240
|
+
id,
|
|
241
|
+
embedding_function,
|
|
242
|
+
data_loader,
|
|
243
|
+
)
|
|
244
|
+
|
|
245
|
+
async def get_or_create_collection(
|
|
246
|
+
self,
|
|
247
|
+
name: str,
|
|
248
|
+
schema: Schema | None = None,
|
|
249
|
+
configuration: CreateCollectionConfiguration | None = None,
|
|
250
|
+
metadata: CollectionMetadata | None = None,
|
|
251
|
+
embedding_function: EmbeddingFunction[Embeddable]
|
|
252
|
+
| None = DefaultEmbeddingFunction(), # type: ignore
|
|
253
|
+
data_loader: DataLoader[Loadable] | None = None,
|
|
254
|
+
) -> Collection:
|
|
255
|
+
"""Get or create a collection with the given name and metadata.
|
|
256
|
+
Args:
|
|
257
|
+
name: The name of the collection to get or create
|
|
258
|
+
metadata: Optional metadata to associate with the collection. If
|
|
259
|
+
the collection already exists, the metadata provided is ignored.
|
|
260
|
+
If the collection does not exist, the new collection will be created
|
|
261
|
+
with the provided metadata.
|
|
262
|
+
embedding_function: Optional function to use to embed documents
|
|
263
|
+
data_loader: Optional function to use to load records (documents, images, etc.)
|
|
264
|
+
|
|
265
|
+
Returns:
|
|
266
|
+
The collection
|
|
267
|
+
|
|
268
|
+
Examples:
|
|
269
|
+
```python
|
|
270
|
+
await client.get_or_create_collection("my_collection")
|
|
271
|
+
# collection(name="my_collection", metadata={})
|
|
272
|
+
```
|
|
273
|
+
"""
|
|
274
|
+
return await to_thread(
|
|
275
|
+
self.__client.get_or_create_collection,
|
|
276
|
+
name,
|
|
277
|
+
schema,
|
|
278
|
+
configuration,
|
|
279
|
+
metadata,
|
|
280
|
+
embedding_function,
|
|
281
|
+
data_loader,
|
|
282
|
+
)
|
|
283
|
+
|
|
284
|
+
async def set_tenant(self, tenant: str, database: str = DEFAULT_DATABASE) -> None:
|
|
285
|
+
"""Set the tenant and database for the client. Raises an error if the tenant or
|
|
286
|
+
database does not exist.
|
|
287
|
+
|
|
288
|
+
Args:
|
|
289
|
+
tenant: The tenant to set.
|
|
290
|
+
database: The database to set.
|
|
291
|
+
|
|
292
|
+
"""
|
|
293
|
+
return await to_thread(self.__client.set_tenant, tenant, database)
|
|
294
|
+
|
|
295
|
+
async def set_database(self, database: str) -> None:
|
|
296
|
+
"""Set the database for the client. Raises an error if the database does not exist.
|
|
297
|
+
|
|
298
|
+
Args:
|
|
299
|
+
database: The database to set.
|
|
300
|
+
|
|
301
|
+
"""
|
|
302
|
+
return await to_thread(self.__client.set_database, database)
|
|
303
|
+
|
|
304
|
+
@staticmethod
|
|
305
|
+
def clear_system_cache() -> None:
|
|
306
|
+
TypeError("Not implemented")
|
|
307
|
+
|
|
308
|
+
|
|
309
|
+
class MemoryMetadata(BaseModel):
|
|
310
|
+
memory_id: str = Field(
|
|
311
|
+
description="记忆ID", default_factory=lambda: uuid.uuid4().hex
|
|
312
|
+
)
|
|
313
|
+
tags: str = Field(description="标签")
|
|
314
|
+
importance: Literal["low", "medium", "high"] = Field(description="重要程度")
|
|
315
|
+
created_at: datetime = Field(
|
|
316
|
+
default_factory=lambda: datetime.now(utc), description="创建时间"
|
|
317
|
+
)
|
|
318
|
+
|
|
319
|
+
|
|
320
|
+
class AsyncUserMemory:
|
|
321
|
+
api: WrappedClientAPI
|
|
322
|
+
_collection_name: str = "amrita_user_memory"
|
|
323
|
+
_collection: Collection
|
|
324
|
+
|
|
325
|
+
def __init__(
|
|
326
|
+
self,
|
|
327
|
+
client: ClientAPI,
|
|
328
|
+
collection_name: str = "amrita_user_memory",
|
|
329
|
+
) -> None:
|
|
330
|
+
self.api = WrappedClientAPI(client)
|
|
331
|
+
self._collection_name = collection_name
|
|
332
|
+
|
|
333
|
+
async def init(self):
|
|
334
|
+
if not hasattr(self, "_collection"):
|
|
335
|
+
self._collection = await self.api.get_or_create_collection(
|
|
336
|
+
self._collection_name,
|
|
337
|
+
)
|
|
338
|
+
|
|
339
|
+
@staticmethod
|
|
340
|
+
def require_init(
|
|
341
|
+
func,
|
|
342
|
+
):
|
|
343
|
+
@wraps(func)
|
|
344
|
+
async def wrapper(*args, **kwargs):
|
|
345
|
+
self: AsyncUserMemory = args[0]
|
|
346
|
+
await self.init()
|
|
347
|
+
return await func(*args, **kwargs)
|
|
348
|
+
|
|
349
|
+
return wrapper
|
|
350
|
+
|
|
351
|
+
@require_init
|
|
352
|
+
async def add_note(self, user_id: str, note_text: str, metadata: MemoryMetadata):
|
|
353
|
+
async with get_lock(self._collection_name, user_id):
|
|
354
|
+
vector: Sequence[EmbeddingChunk] = await call_embedding(
|
|
355
|
+
note_text, build_preset()
|
|
356
|
+
)
|
|
357
|
+
if len(vector) == 0:
|
|
358
|
+
raise RuntimeError("No embedding returned")
|
|
359
|
+
await any_to_thread(
|
|
360
|
+
self._collection.add,
|
|
361
|
+
ids=[metadata.memory_id],
|
|
362
|
+
metadatas=[
|
|
363
|
+
{
|
|
364
|
+
"user_id": user_id,
|
|
365
|
+
"tags": metadata.tags,
|
|
366
|
+
"importance": metadata.importance,
|
|
367
|
+
"created_at": metadata.created_at.isoformat(),
|
|
368
|
+
}
|
|
369
|
+
],
|
|
370
|
+
embeddings=[vector[0].embedding],
|
|
371
|
+
documents=[note_text],
|
|
372
|
+
)
|
|
373
|
+
|
|
374
|
+
@require_init
|
|
375
|
+
async def query_notes(
|
|
376
|
+
self,
|
|
377
|
+
user_id: str,
|
|
378
|
+
query_text: str,
|
|
379
|
+
importance: Literal["low", "medium", "high"] | None = None,
|
|
380
|
+
top_k: int = 5,
|
|
381
|
+
include: chromadb.Include = ["metadatas", "documents"],
|
|
382
|
+
) -> chromadb.QueryResult:
|
|
383
|
+
async with get_lock(self._collection_name, user_id):
|
|
384
|
+
queue_embedding = await call_embedding(query_text, build_preset())
|
|
385
|
+
assert len(queue_embedding) == 1, "Invalid embedding vector length"
|
|
386
|
+
return await any_to_thread(
|
|
387
|
+
self._collection.query,
|
|
388
|
+
queue_embedding[0].embedding,
|
|
389
|
+
[
|
|
390
|
+
query_text,
|
|
391
|
+
],
|
|
392
|
+
include=include,
|
|
393
|
+
n_results=top_k,
|
|
394
|
+
where={
|
|
395
|
+
"user_id": user_id,
|
|
396
|
+
**({"importance": importance} if importance else {}),
|
|
397
|
+
},
|
|
398
|
+
)
|
|
399
|
+
|
|
400
|
+
@require_init
|
|
401
|
+
async def get_all_notes(
|
|
402
|
+
self, user_id: str, include: chromadb.Include = ["metadatas", "documents"]
|
|
403
|
+
) -> chromadb.GetResult:
|
|
404
|
+
async with get_lock(self._collection_name, user_id):
|
|
405
|
+
return await any_to_thread(
|
|
406
|
+
self._collection.get,
|
|
407
|
+
include=include,
|
|
408
|
+
where={"user_id": user_id},
|
|
409
|
+
)
|
|
410
|
+
|
|
411
|
+
@require_init
|
|
412
|
+
async def delete_note(self, user_id: str, doc_id: str):
|
|
413
|
+
async with get_lock(self._collection_name, user_id):
|
|
414
|
+
await any_to_thread(
|
|
415
|
+
self._collection.delete,
|
|
416
|
+
ids=[doc_id],
|
|
417
|
+
where={"user_id": user_id},
|
|
418
|
+
)
|
|
419
|
+
|
|
420
|
+
@require_init
|
|
421
|
+
async def delete_user_all_notes(self, user_id: str):
|
|
422
|
+
async with get_lock(self._collection_name, user_id):
|
|
423
|
+
await any_to_thread(
|
|
424
|
+
self._collection.delete,
|
|
425
|
+
where={"user_id": user_id},
|
|
426
|
+
)
|
|
427
|
+
|
|
428
|
+
@require_init
|
|
429
|
+
async def count_user_notes(self, user_id: str) -> int:
|
|
430
|
+
async with get_lock(self._collection_name, user_id):
|
|
431
|
+
result = await any_to_thread(
|
|
432
|
+
self._collection.get, where={"user_id": user_id}, include=[]
|
|
433
|
+
)
|
|
434
|
+
return len(result["ids"])
|
|
@@ -0,0 +1,98 @@
|
|
|
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中配置。
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
amrita_plugin_memory/__init__.py,sha256=80xmojHjF8p9TaBZAn8N_XEz-KgBFs4HIaNA0ecvDH0,190
|
|
2
|
+
amrita_plugin_memory/config.py,sha256=VsW6M9k8GCbtVKTzzCbVnfcJ29KTL6bZGbYvOo6hI30,2943
|
|
3
|
+
amrita_plugin_memory/embed.py,sha256=8UvAan8VP4JiMfaj03E9WNmLPvElV3z9rIIpzT8OTPE,2269
|
|
4
|
+
amrita_plugin_memory/tools.py,sha256=7kzW7amQkvhTEKFXJCBurPFMEkry9GOLlDqZqScretE,12468
|
|
5
|
+
amrita_plugin_memory/vector.py,sha256=3NMSxauNR9vPeGPFYRN6diFuAVFTUL8MVifYvBSGSB8,14664
|
|
6
|
+
amrita_plugin_memory-0.1.0.dist-info/licenses/LICENSE,sha256=hIahDEOTzuHCU5J2nd07LWwkLW7Hko4UFO__ffsvB-8,34523
|
|
7
|
+
amrita_plugin_memory-0.1.0.dist-info/METADATA,sha256=tXp7nR9L-BfWYWuzcEIRPakExlGP6-ZxzOdxAxr7SkY,2838
|
|
8
|
+
amrita_plugin_memory-0.1.0.dist-info/WHEEL,sha256=aeYiig01lYGDzBgS8HxWXOg3uV61G9ijOsup-k9o1sk,91
|
|
9
|
+
amrita_plugin_memory-0.1.0.dist-info/top_level.txt,sha256=UuTBA8aUBsuum6OZfPwaynLyDR8fRcVbCPvoBOEs63g,21
|
|
10
|
+
amrita_plugin_memory-0.1.0.dist-info/RECORD,,
|