mem1 0.1.1__py3-none-any.whl → 0.1.2__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.
- mem1/config.py +75 -32
- mem1/memory.py +34 -5
- mem1/prompts.py +147 -100
- mem1/storage.py +238 -0
- {mem1-0.1.1.dist-info → mem1-0.1.2.dist-info}/METADATA +10 -5
- mem1-0.1.2.dist-info/RECORD +12 -0
- mem1-0.1.1.dist-info/RECORD +0 -12
- {mem1-0.1.1.dist-info → mem1-0.1.2.dist-info}/WHEEL +0 -0
mem1/config.py
CHANGED
|
@@ -41,6 +41,9 @@ class MemoryConfig(BaseModel):
|
|
|
41
41
|
save_assistant_messages: bool # 是否保存 assistant 回复
|
|
42
42
|
max_assistant_chars: int # assistant 回复超过此长度触发摘要
|
|
43
43
|
context_days_limit: int # get_context 检索最近几天的对话
|
|
44
|
+
# 时间衰减配置(借鉴 supermemory Smart Forgetting)
|
|
45
|
+
decay_recent_days: int # [近期] 阈值,默认 7 天
|
|
46
|
+
decay_earlier_days: int # [较早] 阈值,默认 30 天(超过为 [久远])
|
|
44
47
|
|
|
45
48
|
|
|
46
49
|
class ESConfig(BaseModel):
|
|
@@ -49,6 +52,18 @@ class ESConfig(BaseModel):
|
|
|
49
52
|
index_name: str
|
|
50
53
|
|
|
51
54
|
|
|
55
|
+
class SQLiteConfig(BaseModel):
|
|
56
|
+
"""SQLite 配置"""
|
|
57
|
+
db_path: str
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
class StorageConfig(BaseModel):
|
|
61
|
+
"""存储配置"""
|
|
62
|
+
backend: str # sqlite / elasticsearch
|
|
63
|
+
sqlite: Optional[SQLiteConfig] = None
|
|
64
|
+
es: Optional[ESConfig] = None
|
|
65
|
+
|
|
66
|
+
|
|
52
67
|
class ImagesConfig(BaseModel):
|
|
53
68
|
"""图片存储配置"""
|
|
54
69
|
images_dir: str
|
|
@@ -59,59 +74,88 @@ class Mem1Config(BaseModel):
|
|
|
59
74
|
llm: LLMConfig
|
|
60
75
|
vl: VLConfig
|
|
61
76
|
memory: MemoryConfig
|
|
62
|
-
|
|
77
|
+
storage: StorageConfig
|
|
63
78
|
images: ImagesConfig
|
|
64
79
|
|
|
80
|
+
# 兼容旧接口
|
|
81
|
+
@property
|
|
82
|
+
def es(self) -> Optional[ESConfig]:
|
|
83
|
+
return self.storage.es
|
|
84
|
+
|
|
65
85
|
@classmethod
|
|
66
86
|
def from_env(cls) -> "Mem1Config":
|
|
67
87
|
"""从环境变量加载配置
|
|
68
88
|
|
|
89
|
+
存储后端配置:
|
|
90
|
+
- MEM1_STORAGE_BACKEND: sqlite(默认)/ elasticsearch
|
|
91
|
+
- MEM1_SQLITE_PATH: SQLite 数据库路径(默认 {memory_dir}/mem1.db)
|
|
92
|
+
- MEM1_ES_HOSTS: ES 地址(仅 elasticsearch 后端需要)
|
|
93
|
+
- MEM1_ES_INDEX: ES 索引名(仅 elasticsearch 后端需要)
|
|
94
|
+
|
|
69
95
|
必需的环境变量:
|
|
70
96
|
- MEM1_LLM_API_KEY: LLM API 密钥
|
|
71
97
|
- MEM1_LLM_BASE_URL: LLM API 地址(OpenAI 兼容)
|
|
72
98
|
- MEM1_LLM_MODEL: LLM 模型名
|
|
73
|
-
- MEM1_ES_HOSTS: ES 地址
|
|
74
|
-
- MEM1_ES_INDEX: ES 索引名
|
|
75
99
|
- MEM1_MEMORY_DIR: 记忆存储目录
|
|
76
|
-
- MEM1_AUTO_UPDATE_PROFILE: 是否自动更新画像 (true/false)
|
|
77
|
-
- MEM1_MAX_PROFILE_CHARS: 画像最大字符数
|
|
78
|
-
- MEM1_UPDATE_INTERVAL_ROUNDS: 画像更新间隔轮数
|
|
79
|
-
- MEM1_UPDATE_INTERVAL_MINUTES: 画像更新间隔分钟数
|
|
80
100
|
|
|
81
|
-
可选的环境变量(VL
|
|
82
|
-
-
|
|
83
|
-
-
|
|
101
|
+
可选的环境变量(VL 模型):
|
|
102
|
+
- MEM1_VL_PROVIDER: qwen / doubao
|
|
103
|
+
- MEM1_VL_MODEL: VL 模型名
|
|
104
|
+
- MEM1_VL_API_KEY: API 密钥
|
|
84
105
|
"""
|
|
85
|
-
#
|
|
106
|
+
# 存储后端选择
|
|
107
|
+
storage_backend = os.getenv("MEM1_STORAGE_BACKEND", "sqlite").lower()
|
|
108
|
+
|
|
109
|
+
# 基础必需配置
|
|
86
110
|
required_vars = {
|
|
87
111
|
"MEM1_LLM_API_KEY": os.getenv("MEM1_LLM_API_KEY"),
|
|
88
112
|
"MEM1_LLM_BASE_URL": os.getenv("MEM1_LLM_BASE_URL"),
|
|
89
113
|
"MEM1_LLM_MODEL": os.getenv("MEM1_LLM_MODEL"),
|
|
90
|
-
"MEM1_ES_HOSTS": os.getenv("MEM1_ES_HOSTS"),
|
|
91
|
-
"MEM1_ES_INDEX": os.getenv("MEM1_ES_INDEX"),
|
|
92
114
|
"MEM1_MEMORY_DIR": os.getenv("MEM1_MEMORY_DIR"),
|
|
93
|
-
"MEM1_AUTO_UPDATE_PROFILE": os.getenv("MEM1_AUTO_UPDATE_PROFILE"),
|
|
94
|
-
"MEM1_MAX_PROFILE_CHARS": os.getenv("MEM1_MAX_PROFILE_CHARS"),
|
|
95
|
-
"MEM1_UPDATE_INTERVAL_ROUNDS": os.getenv("MEM1_UPDATE_INTERVAL_ROUNDS"),
|
|
96
|
-
"MEM1_UPDATE_INTERVAL_MINUTES": os.getenv("MEM1_UPDATE_INTERVAL_MINUTES"),
|
|
97
|
-
"MEM1_SAVE_ASSISTANT_MESSAGES": os.getenv("MEM1_SAVE_ASSISTANT_MESSAGES"),
|
|
98
|
-
"MEM1_MAX_ASSISTANT_CHARS": os.getenv("MEM1_MAX_ASSISTANT_CHARS"),
|
|
99
|
-
"MEM1_LLM_TEMPERATURE": os.getenv("MEM1_LLM_TEMPERATURE"),
|
|
100
|
-
"MEM1_CONTEXT_DAYS_LIMIT": os.getenv("MEM1_CONTEXT_DAYS_LIMIT"),
|
|
115
|
+
"MEM1_AUTO_UPDATE_PROFILE": os.getenv("MEM1_AUTO_UPDATE_PROFILE", "true"),
|
|
116
|
+
"MEM1_MAX_PROFILE_CHARS": os.getenv("MEM1_MAX_PROFILE_CHARS", "3000"),
|
|
117
|
+
"MEM1_UPDATE_INTERVAL_ROUNDS": os.getenv("MEM1_UPDATE_INTERVAL_ROUNDS", "5"),
|
|
118
|
+
"MEM1_UPDATE_INTERVAL_MINUTES": os.getenv("MEM1_UPDATE_INTERVAL_MINUTES", "3"),
|
|
119
|
+
"MEM1_SAVE_ASSISTANT_MESSAGES": os.getenv("MEM1_SAVE_ASSISTANT_MESSAGES", "true"),
|
|
120
|
+
"MEM1_MAX_ASSISTANT_CHARS": os.getenv("MEM1_MAX_ASSISTANT_CHARS", "500"),
|
|
121
|
+
"MEM1_LLM_TEMPERATURE": os.getenv("MEM1_LLM_TEMPERATURE", "0.2"),
|
|
122
|
+
"MEM1_CONTEXT_DAYS_LIMIT": os.getenv("MEM1_CONTEXT_DAYS_LIMIT", "31"),
|
|
123
|
+
"MEM1_DECAY_RECENT_DAYS": os.getenv("MEM1_DECAY_RECENT_DAYS", "7"),
|
|
124
|
+
"MEM1_DECAY_EARLIER_DAYS": os.getenv("MEM1_DECAY_EARLIER_DAYS", "31"),
|
|
101
125
|
}
|
|
102
126
|
|
|
103
|
-
|
|
127
|
+
# 检查基础必需配置
|
|
128
|
+
base_required = ["MEM1_LLM_API_KEY", "MEM1_LLM_BASE_URL", "MEM1_LLM_MODEL", "MEM1_MEMORY_DIR"]
|
|
129
|
+
missing = [k for k in base_required if not required_vars.get(k)]
|
|
104
130
|
if missing:
|
|
105
131
|
raise ValueError(f"缺少必需的环境变量: {', '.join(missing)}")
|
|
106
132
|
|
|
107
|
-
# ES hosts 支持逗号分隔的多个地址
|
|
108
|
-
es_hosts = [h.strip() for h in required_vars["MEM1_ES_HOSTS"].split(",")]
|
|
109
|
-
|
|
110
|
-
# 图片目录基于记忆目录
|
|
111
133
|
memory_dir = required_vars["MEM1_MEMORY_DIR"]
|
|
112
134
|
images_dir = f"{memory_dir}/images"
|
|
113
135
|
|
|
114
|
-
#
|
|
136
|
+
# 构建存储配置
|
|
137
|
+
storage_config: StorageConfig
|
|
138
|
+
if storage_backend == "elasticsearch":
|
|
139
|
+
es_hosts = os.getenv("MEM1_ES_HOSTS")
|
|
140
|
+
es_index = os.getenv("MEM1_ES_INDEX")
|
|
141
|
+
if not es_hosts or not es_index:
|
|
142
|
+
raise ValueError("elasticsearch 后端需要配置 MEM1_ES_HOSTS 和 MEM1_ES_INDEX")
|
|
143
|
+
storage_config = StorageConfig(
|
|
144
|
+
backend="elasticsearch",
|
|
145
|
+
es=ESConfig(
|
|
146
|
+
hosts=[h.strip() for h in es_hosts.split(",")],
|
|
147
|
+
index_name=es_index
|
|
148
|
+
)
|
|
149
|
+
)
|
|
150
|
+
else:
|
|
151
|
+
# 默认 SQLite
|
|
152
|
+
sqlite_path = os.getenv("MEM1_SQLITE_PATH", f"{memory_dir}/mem1.db")
|
|
153
|
+
storage_config = StorageConfig(
|
|
154
|
+
backend="sqlite",
|
|
155
|
+
sqlite=SQLiteConfig(db_path=sqlite_path)
|
|
156
|
+
)
|
|
157
|
+
|
|
158
|
+
# VL 模型配置(可选)
|
|
115
159
|
vl_config = VLConfig(
|
|
116
160
|
provider=os.getenv("MEM1_VL_PROVIDER", ""),
|
|
117
161
|
model=os.getenv("MEM1_VL_MODEL", ""),
|
|
@@ -136,12 +180,11 @@ class Mem1Config(BaseModel):
|
|
|
136
180
|
update_interval_minutes=int(required_vars["MEM1_UPDATE_INTERVAL_MINUTES"]),
|
|
137
181
|
save_assistant_messages=required_vars["MEM1_SAVE_ASSISTANT_MESSAGES"].lower() == "true",
|
|
138
182
|
max_assistant_chars=int(required_vars["MEM1_MAX_ASSISTANT_CHARS"]),
|
|
139
|
-
context_days_limit=int(required_vars["MEM1_CONTEXT_DAYS_LIMIT"])
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
hosts=es_hosts,
|
|
143
|
-
index_name=required_vars["MEM1_ES_INDEX"]
|
|
183
|
+
context_days_limit=int(required_vars["MEM1_CONTEXT_DAYS_LIMIT"]),
|
|
184
|
+
decay_recent_days=int(required_vars["MEM1_DECAY_RECENT_DAYS"]),
|
|
185
|
+
decay_earlier_days=int(required_vars["MEM1_DECAY_EARLIER_DAYS"])
|
|
144
186
|
),
|
|
187
|
+
storage=storage_config,
|
|
145
188
|
images=ImagesConfig(
|
|
146
189
|
images_dir=images_dir
|
|
147
190
|
)
|
mem1/memory.py
CHANGED
|
@@ -10,7 +10,7 @@ from pathlib import Path
|
|
|
10
10
|
from mem1.config import Mem1Config
|
|
11
11
|
from mem1.llm import LLMClient, VLClient
|
|
12
12
|
from mem1.prompts import ProfileTemplate, RECALL_DECISION_PROMPT, IMAGE_SEARCH_PROMPT, ASSISTANT_SUMMARY_PROMPT, CONTEXT_SUFFICIENT_PROMPT
|
|
13
|
-
from mem1.storage import StorageBackend, ESStorage
|
|
13
|
+
from mem1.storage import StorageBackend, ESStorage, SQLiteStorage
|
|
14
14
|
|
|
15
15
|
logger = logging.getLogger(__name__)
|
|
16
16
|
|
|
@@ -55,8 +55,11 @@ class Mem1Memory:
|
|
|
55
55
|
# 存储后端(可插拔)
|
|
56
56
|
if storage:
|
|
57
57
|
self.storage = storage
|
|
58
|
+
elif config.storage.backend == "elasticsearch":
|
|
59
|
+
self.storage = ESStorage(config.storage.es.hosts, config.storage.es.index_name)
|
|
58
60
|
else:
|
|
59
|
-
|
|
61
|
+
# 默认 SQLite
|
|
62
|
+
self.storage = SQLiteStorage(config.storage.sqlite.db_path)
|
|
60
63
|
|
|
61
64
|
# LLM 客户端
|
|
62
65
|
self.llm = LLMClient(config.llm)
|
|
@@ -74,6 +77,9 @@ class Mem1Memory:
|
|
|
74
77
|
self.update_interval_minutes = config.memory.update_interval_minutes
|
|
75
78
|
self.save_assistant_messages = config.memory.save_assistant_messages
|
|
76
79
|
self.max_assistant_chars = config.memory.max_assistant_chars
|
|
80
|
+
# 时间衰减配置
|
|
81
|
+
self.decay_recent_days = config.memory.decay_recent_days
|
|
82
|
+
self.decay_earlier_days = config.memory.decay_earlier_days
|
|
77
83
|
|
|
78
84
|
# ========== 图片处理 ==========
|
|
79
85
|
|
|
@@ -546,14 +552,37 @@ class Mem1Memory:
|
|
|
546
552
|
return summary
|
|
547
553
|
|
|
548
554
|
def _format_conversations_for_llm(self, conversations: List[Dict[str, Any]]) -> str:
|
|
549
|
-
"""格式化对话记录为文本(带引用 ID
|
|
555
|
+
"""格式化对话记录为文本(带引用 ID 和时间衰减标记)
|
|
556
|
+
|
|
557
|
+
时间衰减设计(借鉴 supermemory Smart Forgetting):
|
|
558
|
+
- [近期] decay_recent_days 天内:权重高,优先保留
|
|
559
|
+
- [较早] decay_recent_days ~ decay_earlier_days 天:权重中等
|
|
560
|
+
- [久远] 超过 decay_earlier_days 天:权重低,Dynamic 信息可淘汰
|
|
561
|
+
"""
|
|
562
|
+
from datetime import datetime
|
|
563
|
+
now = datetime.now()
|
|
564
|
+
|
|
550
565
|
output = []
|
|
551
566
|
for idx, conv in enumerate(conversations, start=1):
|
|
552
567
|
conv_id = f"对话-{idx:03d}" # 001, 002, 003...
|
|
553
|
-
|
|
568
|
+
timestamp_str = conv.get("timestamp", "未知时间")
|
|
554
569
|
metadata = conv.get("metadata", {})
|
|
555
570
|
|
|
556
|
-
|
|
571
|
+
# 计算时间衰减标记
|
|
572
|
+
decay_tag = ""
|
|
573
|
+
try:
|
|
574
|
+
conv_time = datetime.strptime(timestamp_str, '%Y-%m-%d %H:%M:%S')
|
|
575
|
+
days_ago = (now - conv_time).days
|
|
576
|
+
if days_ago <= self.decay_recent_days:
|
|
577
|
+
decay_tag = "[近期]"
|
|
578
|
+
elif days_ago <= self.decay_earlier_days:
|
|
579
|
+
decay_tag = "[较早]"
|
|
580
|
+
else:
|
|
581
|
+
decay_tag = "[久远]"
|
|
582
|
+
except (ValueError, TypeError):
|
|
583
|
+
decay_tag = ""
|
|
584
|
+
|
|
585
|
+
title = f"### [{conv_id}] {decay_tag} {timestamp_str}"
|
|
557
586
|
if metadata:
|
|
558
587
|
tags = " ".join([f"[{k}:{v}]" for k, v in metadata.items()])
|
|
559
588
|
title += f" {tags}"
|
mem1/prompts.py
CHANGED
|
@@ -20,41 +20,45 @@ class ProfileTemplate:
|
|
|
20
20
|
- 业务场景通过 ProfileTemplate 定义画像结构和提示词
|
|
21
21
|
"""
|
|
22
22
|
|
|
23
|
-
#
|
|
24
|
-
DEFAULT_SECTIONS = """##
|
|
23
|
+
# 默认通用模板(借鉴 supermemory 的 static/dynamic 设计)
|
|
24
|
+
DEFAULT_SECTIONS = """## 🔒 稳定信息(Static)
|
|
25
|
+
> 核心事实,很少变化,压缩时必须保留
|
|
26
|
+
|
|
25
27
|
- 姓名/称呼:
|
|
26
28
|
- 身份/角色:
|
|
27
29
|
- 所属组织:
|
|
30
|
+
- 长期偏好:(沟通风格、关注重点等)
|
|
28
31
|
- 背景信息:
|
|
29
32
|
|
|
30
|
-
##
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
33
|
+
## 🔄 动态信息(Dynamic)
|
|
34
|
+
> 近期状态,经常更新,可精简合并
|
|
35
|
+
|
|
36
|
+
### 当前关注
|
|
37
|
+
- 进行中项目:
|
|
38
|
+
- 近期兴趣:
|
|
39
|
+
- 当前任务:
|
|
34
40
|
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
- 例:[每周一] 提交周报、[每周五] 制定下周计划、[每月底] 月度汇总
|
|
41
|
+
### 周期性任务
|
|
42
|
+
(固定日程,格式:[周期] 任务内容)
|
|
38
43
|
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
- 例:处置 **97起** 案件、涉及金额 **230万元**、完成 **365个** 检查点
|
|
44
|
+
### 关键数据
|
|
45
|
+
(重要数字,用 **加粗** 标记)
|
|
42
46
|
|
|
43
|
-
|
|
44
|
-
|
|
47
|
+
### 任务时间线
|
|
48
|
+
(计划/截止日期,格式:[YYYY-MM-DD] 事项)
|
|
45
49
|
|
|
46
|
-
|
|
47
|
-
|
|
50
|
+
### 待办事项
|
|
51
|
+
(未完成请求,格式:- [ ] 事项(MM-DD 提出))
|
|
48
52
|
|
|
49
|
-
## 待澄清事项
|
|
50
|
-
|
|
53
|
+
## ⚠️ 待澄清事项
|
|
54
|
+
(前后矛盾的信息,格式:⚠️ 矛盾描述)
|
|
51
55
|
|
|
52
|
-
## 用户反馈
|
|
53
|
-
-
|
|
54
|
-
-
|
|
56
|
+
## 📝 用户反馈
|
|
57
|
+
- 正向反馈:
|
|
58
|
+
- 负向反馈:
|
|
55
59
|
|
|
56
|
-
## 注意事项
|
|
57
|
-
|
|
60
|
+
## 🚫 注意事项
|
|
61
|
+
(规避话题、历史问题等)"""
|
|
58
62
|
|
|
59
63
|
def __init__(
|
|
60
64
|
self,
|
|
@@ -100,7 +104,7 @@ class ProfileTemplate:
|
|
|
100
104
|
|
|
101
105
|
DEFAULT_PROFILE_UPDATE_PROMPT = """你是用户画像分析专家。从对话记录中提取用户信息,更新用户画像。
|
|
102
106
|
|
|
103
|
-
##
|
|
107
|
+
## 对话记录(带时间衰减标记)
|
|
104
108
|
{normal_content}
|
|
105
109
|
|
|
106
110
|
## 现有用户画像
|
|
@@ -112,33 +116,53 @@ DEFAULT_PROFILE_UPDATE_PROMPT = """你是用户画像分析专家。从对话记
|
|
|
112
116
|
## 输出格式
|
|
113
117
|
直接输出完整的 Markdown 格式用户画像,保持现有画像的章节结构。
|
|
114
118
|
|
|
119
|
+
## Static vs Dynamic 分类原则(借鉴 supermemory)
|
|
120
|
+
画像分为两类信息,更新时需区分处理:
|
|
121
|
+
|
|
122
|
+
### 🔒 稳定信息(Static)—— 必须保留
|
|
123
|
+
- 核心身份:姓名、职业、所属组织
|
|
124
|
+
- 长期偏好:沟通风格、关注重点
|
|
125
|
+
- 背景信息:专业领域、工作经历
|
|
126
|
+
- 特点:很少变化,除非用户明确否定
|
|
127
|
+
|
|
128
|
+
### 🔄 动态信息(Dynamic)—— 可更新/淘汰
|
|
129
|
+
- 当前状态:进行中项目、近期兴趣
|
|
130
|
+
- 时效信息:任务时间线、待办事项
|
|
131
|
+
- 临时数据:具体事件、阶段性目标
|
|
132
|
+
- 特点:随时间变化,旧信息可被新信息替换
|
|
133
|
+
|
|
134
|
+
## 记忆关系处理(借鉴 supermemory Graph Memory)
|
|
135
|
+
- **Updates(替换)**:新信息与旧信息矛盾时,用新的替换旧的(仅限 Dynamic 部分)
|
|
136
|
+
- **Extends(补充)**:新信息丰富旧信息时,合并保留
|
|
137
|
+
- **矛盾检测**:Static 信息出现矛盾时,记录到「待澄清事项」,不直接覆盖
|
|
138
|
+
|
|
139
|
+
## 时间衰减处理
|
|
140
|
+
- 对话记录中标注了 [近期]、[较早]、[久远] 等时间标记
|
|
141
|
+
- [近期] 信息权重高,优先保留
|
|
142
|
+
- [久远] 的 Dynamic 信息可以精简或删除
|
|
143
|
+
- Static 信息不受时间衰减影响
|
|
144
|
+
|
|
115
145
|
## 整理原则
|
|
116
|
-
1.
|
|
117
|
-
2.
|
|
146
|
+
1. Static 信息:不能丢失,除非用户明确否定
|
|
147
|
+
2. Dynamic 信息:新信息可替换旧信息
|
|
118
148
|
3. 用户反馈(表扬或批评)必须保留
|
|
119
149
|
4. 只记录用户明确表达的信息,不要推测
|
|
120
|
-
5.
|
|
121
|
-
|
|
122
|
-
## 周期性任务提取
|
|
123
|
-
6. 识别固定日程:用户提到"每周一"、"每周五"、"每天"、"每月"等周期性任务
|
|
124
|
-
7. 记录到「周期性任务」章节,格式:[周期] 任务内容
|
|
125
|
-
8. 示例:[每周一] 提交周报、[每周五] 制定下周计划
|
|
150
|
+
5. 空章节保留标题
|
|
126
151
|
|
|
127
152
|
## 关键数字保留【重要】
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
153
|
+
- 所有具体数字必须原样保留并用 **加粗** 标记
|
|
154
|
+
- 禁止概括为"多个"、"若干"、"大量"
|
|
155
|
+
- 正确:处置 **97起** 案件 ✓
|
|
156
|
+
- 错误:处置多起案件 ✗
|
|
132
157
|
|
|
133
|
-
##
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
158
|
+
## 时间敏感信息
|
|
159
|
+
- 任务时间线:提取日期、截止时间、里程碑
|
|
160
|
+
- 待办事项:识别"下次"、"回头"、"稍后"等挂起信号
|
|
161
|
+
- 已完成事项:标记为 [x] 或移除
|
|
137
162
|
|
|
138
163
|
## 矛盾检测
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
18. 只有用户明确澄清后,才能更新对应信息并移除待澄清标记
|
|
164
|
+
- Static 信息矛盾:记录到「待澄清事项」,格式:⚠️ 用户曾说"...",但又说"..."
|
|
165
|
+
- Dynamic 信息矛盾:直接用新信息替换旧信息
|
|
142
166
|
|
|
143
167
|
---
|
|
144
168
|
*最后更新: {timestamp}*
|
|
@@ -152,12 +176,31 @@ DEFAULT_PROFILE_COMPRESS_PROMPT = """你是用户画像压缩专家。当前用
|
|
|
152
176
|
## 任务
|
|
153
177
|
将用户画像压缩到 {max_chars} 字符以内,保留最重要的信息。
|
|
154
178
|
|
|
155
|
-
##
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
179
|
+
## 压缩优先级(借鉴 supermemory 的 Static/Dynamic 设计)
|
|
180
|
+
|
|
181
|
+
### 🔒 必须保留(Static)
|
|
182
|
+
1. 核心身份信息(姓名、职业、组织)
|
|
183
|
+
2. 长期偏好(沟通风格、关注重点)
|
|
184
|
+
3. 负向反馈(用户不满意的地方)
|
|
185
|
+
4. 注意事项(规避话题)
|
|
186
|
+
|
|
187
|
+
### 🔄 可精简(Dynamic)
|
|
188
|
+
5. 当前关注:只保留最近的 2-3 项
|
|
189
|
+
6. 周期性任务:合并相似项
|
|
190
|
+
7. 关键数据:只保留最重要的数字
|
|
191
|
+
8. 任务时间线:删除已过期的事项
|
|
192
|
+
|
|
193
|
+
### ❌ 可删除
|
|
194
|
+
9. 已完成的待办事项
|
|
195
|
+
10. 过期的时间线事项
|
|
196
|
+
11. 重复或相似的条目
|
|
197
|
+
12. 具体事件细节(Episodes)
|
|
198
|
+
|
|
199
|
+
## 压缩技巧
|
|
200
|
+
- 合并相似条目
|
|
201
|
+
- 用概括替代列举(但保留关键数字)
|
|
202
|
+
- 删除冗余描述
|
|
203
|
+
- 保持章节结构完整
|
|
161
204
|
|
|
162
205
|
## 输出格式
|
|
163
206
|
直接输出压缩后的完整 Markdown 格式用户画像。
|
|
@@ -249,105 +292,109 @@ IMAGE_SEARCH_PROMPT = """根据用户查询,从图片列表中找出匹配的
|
|
|
249
292
|
# 舆情行业模板
|
|
250
293
|
YUQING_PROFILE_TEMPLATE = ProfileTemplate(
|
|
251
294
|
description="舆情行业用户画像",
|
|
252
|
-
sections="""##
|
|
295
|
+
sections="""## 🔒 稳定信息(Static)
|
|
253
296
|
- 姓名/称呼:
|
|
254
297
|
- 职位/角色:
|
|
255
298
|
- 所属机构:
|
|
256
299
|
- 职责范围:
|
|
257
|
-
|
|
258
|
-
## 偏好习惯
|
|
259
|
-
- 报告风格:(详细/简洁、图表/文字等)
|
|
300
|
+
- 报告风格偏好:(详细/简洁、图表/文字等)
|
|
260
301
|
- 沟通方式:(直接/委婉、正式/随意等)
|
|
302
|
+
|
|
303
|
+
## 🔄 动态信息(Dynamic)
|
|
304
|
+
|
|
305
|
+
### 当前关注
|
|
261
306
|
- 关注重点:(时效性/准确性/全面性等)
|
|
262
|
-
-
|
|
307
|
+
- 进行中任务:
|
|
263
308
|
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
- 例:[每周一] 提交周报、[每周五] 制定下周计划、[每月底] 月度汇总
|
|
309
|
+
### 周期性任务
|
|
310
|
+
(格式:[周期] 任务内容)
|
|
267
311
|
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
- 例:处置 **97起** 案件、涉及金额 **230万元**
|
|
312
|
+
### 关键数据
|
|
313
|
+
(用 **加粗** 标记重要数字)
|
|
271
314
|
|
|
272
|
-
|
|
273
|
-
|
|
315
|
+
### 任务时间线
|
|
316
|
+
(报告截止、汇报计划,格式:[YYYY-MM-DD] 事项)
|
|
274
317
|
|
|
275
|
-
|
|
276
|
-
|
|
318
|
+
### 待办事项
|
|
319
|
+
(格式:- [ ] 事项(MM-DD 提出))
|
|
277
320
|
|
|
278
|
-
## 待澄清事项
|
|
279
|
-
(用户前后矛盾或模糊的信息,需要下次确认)
|
|
321
|
+
## ⚠️ 待澄清事项
|
|
280
322
|
|
|
281
|
-
## 用户反馈
|
|
323
|
+
## 📝 用户反馈
|
|
282
324
|
- 正向反馈:
|
|
283
325
|
- 负向反馈:
|
|
284
326
|
|
|
285
|
-
## 注意事项
|
|
286
|
-
|
|
327
|
+
## 🚫 注意事项
|
|
328
|
+
(敏感话题、规避要点等)"""
|
|
287
329
|
)
|
|
288
330
|
|
|
289
331
|
|
|
290
332
|
# 电商客服模板
|
|
291
333
|
ECOMMERCE_PROFILE_TEMPLATE = ProfileTemplate(
|
|
292
334
|
description="电商客户画像",
|
|
293
|
-
sections="""##
|
|
335
|
+
sections="""## 🔒 稳定信息(Static)
|
|
294
336
|
- 称呼:
|
|
295
337
|
- 会员等级:
|
|
296
338
|
- 常用收货地址:
|
|
297
|
-
|
|
298
|
-
## 购物偏好
|
|
299
339
|
- 常购品类:
|
|
300
|
-
- 价格敏感度:
|
|
301
340
|
- 品牌偏好:
|
|
341
|
+
- 沟通风格偏好:
|
|
342
|
+
|
|
343
|
+
## 🔄 动态信息(Dynamic)
|
|
302
344
|
|
|
303
|
-
|
|
345
|
+
### 当前关注
|
|
346
|
+
- 近期购买意向:
|
|
347
|
+
- 进行中订单:
|
|
348
|
+
|
|
349
|
+
### 服务记录
|
|
304
350
|
- 历史订单问题:
|
|
305
351
|
- 退换货记录:
|
|
306
|
-
- 投诉记录:
|
|
307
352
|
|
|
308
|
-
|
|
309
|
-
|
|
353
|
+
### 待办事项
|
|
354
|
+
(未处理售后,格式:- [ ] 事项(MM-DD 提出))
|
|
310
355
|
|
|
311
|
-
## 待澄清事项
|
|
312
|
-
|
|
356
|
+
## ⚠️ 待澄清事项
|
|
357
|
+
(地址、规格等矛盾信息)
|
|
313
358
|
|
|
314
|
-
##
|
|
315
|
-
-
|
|
316
|
-
-
|
|
359
|
+
## 📝 用户反馈
|
|
360
|
+
- 正向反馈:
|
|
361
|
+
- 负向反馈:
|
|
317
362
|
|
|
318
|
-
## 注意事项
|
|
319
|
-
(特殊需求、敏感话题等)"""
|
|
363
|
+
## 🚫 注意事项"""
|
|
320
364
|
)
|
|
321
365
|
|
|
322
366
|
|
|
323
367
|
# 医疗助手模板
|
|
324
368
|
MEDICAL_PROFILE_TEMPLATE = ProfileTemplate(
|
|
325
369
|
description="患者健康档案摘要",
|
|
326
|
-
sections="""##
|
|
370
|
+
sections="""## 🔒 稳定信息(Static)
|
|
327
371
|
- 称呼:
|
|
328
372
|
- 年龄段:
|
|
329
|
-
- 主要健康关注:
|
|
330
|
-
|
|
331
|
-
## 健康背景
|
|
332
373
|
- 已知病史:
|
|
333
374
|
- 过敏信息:
|
|
334
|
-
-
|
|
375
|
+
- 咨询偏好:(信息详细程度、关注重点)
|
|
335
376
|
|
|
336
|
-
##
|
|
337
|
-
|
|
377
|
+
## 🔄 动态信息(Dynamic)
|
|
378
|
+
|
|
379
|
+
### 当前关注
|
|
380
|
+
- 主要健康关注:
|
|
381
|
+
- 当前用药:
|
|
338
382
|
|
|
339
|
-
|
|
340
|
-
|
|
383
|
+
### 任务时间线
|
|
384
|
+
(复诊、用药周期,格式:[YYYY-MM-DD] 事项)
|
|
341
385
|
|
|
342
|
-
|
|
343
|
-
|
|
386
|
+
### 待办事项
|
|
387
|
+
(待预约检查、待确认用药等)
|
|
344
388
|
|
|
345
|
-
##
|
|
346
|
-
|
|
347
|
-
|
|
389
|
+
## ⚠️ 待澄清事项
|
|
390
|
+
(症状、用药信息矛盾)
|
|
391
|
+
|
|
392
|
+
## 📝 用户反馈
|
|
393
|
+
- 正向反馈:
|
|
394
|
+
- 负向反馈:
|
|
348
395
|
|
|
349
|
-
## 注意事项
|
|
350
|
-
|
|
396
|
+
## 🚫 注意事项
|
|
397
|
+
(沟通禁忌等)"""
|
|
351
398
|
)
|
|
352
399
|
|
|
353
400
|
|
mem1/storage.py
CHANGED
|
@@ -397,3 +397,241 @@ class ESStorage(StorageBackend):
|
|
|
397
397
|
sort=[{"timestamp": {"order": "asc"}}]
|
|
398
398
|
)
|
|
399
399
|
return [hit["_source"] for hit in response["hits"]["hits"]]
|
|
400
|
+
|
|
401
|
+
|
|
402
|
+
class SQLiteStorage(StorageBackend):
|
|
403
|
+
"""SQLite 存储后端(零依赖,适合单机/开发环境)"""
|
|
404
|
+
|
|
405
|
+
def __init__(self, db_path: str):
|
|
406
|
+
"""
|
|
407
|
+
Args:
|
|
408
|
+
db_path: 数据库文件路径
|
|
409
|
+
"""
|
|
410
|
+
import sqlite3
|
|
411
|
+
import json
|
|
412
|
+
from pathlib import Path
|
|
413
|
+
|
|
414
|
+
self.db_path = db_path
|
|
415
|
+
self.json = json
|
|
416
|
+
|
|
417
|
+
# 确保目录存在
|
|
418
|
+
Path(db_path).parent.mkdir(parents=True, exist_ok=True)
|
|
419
|
+
|
|
420
|
+
self.conn = sqlite3.connect(db_path, check_same_thread=False)
|
|
421
|
+
self.conn.row_factory = sqlite3.Row
|
|
422
|
+
self.ensure_schema()
|
|
423
|
+
|
|
424
|
+
def ensure_schema(self) -> None:
|
|
425
|
+
"""创建表结构"""
|
|
426
|
+
cursor = self.conn.cursor()
|
|
427
|
+
|
|
428
|
+
# 对话记录表
|
|
429
|
+
cursor.execute("""
|
|
430
|
+
CREATE TABLE IF NOT EXISTS conversations (
|
|
431
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
432
|
+
user_id TEXT NOT NULL,
|
|
433
|
+
topic_id TEXT NOT NULL,
|
|
434
|
+
timestamp TEXT NOT NULL,
|
|
435
|
+
messages TEXT NOT NULL,
|
|
436
|
+
metadata TEXT,
|
|
437
|
+
images TEXT,
|
|
438
|
+
created_at TEXT DEFAULT CURRENT_TIMESTAMP
|
|
439
|
+
)
|
|
440
|
+
""")
|
|
441
|
+
cursor.execute("CREATE INDEX IF NOT EXISTS idx_conv_user ON conversations(user_id)")
|
|
442
|
+
cursor.execute("CREATE INDEX IF NOT EXISTS idx_conv_topic ON conversations(user_id, topic_id)")
|
|
443
|
+
cursor.execute("CREATE INDEX IF NOT EXISTS idx_conv_time ON conversations(timestamp)")
|
|
444
|
+
|
|
445
|
+
# 用户画像表
|
|
446
|
+
cursor.execute("""
|
|
447
|
+
CREATE TABLE IF NOT EXISTS profiles (
|
|
448
|
+
user_id TEXT PRIMARY KEY,
|
|
449
|
+
content TEXT NOT NULL,
|
|
450
|
+
updated_at TEXT NOT NULL
|
|
451
|
+
)
|
|
452
|
+
""")
|
|
453
|
+
|
|
454
|
+
# 用户状态表
|
|
455
|
+
cursor.execute("""
|
|
456
|
+
CREATE TABLE IF NOT EXISTS user_states (
|
|
457
|
+
user_id TEXT PRIMARY KEY,
|
|
458
|
+
rounds INTEGER NOT NULL DEFAULT 0,
|
|
459
|
+
last_update TEXT
|
|
460
|
+
)
|
|
461
|
+
""")
|
|
462
|
+
|
|
463
|
+
self.conn.commit()
|
|
464
|
+
|
|
465
|
+
# ========== 对话记录 ==========
|
|
466
|
+
|
|
467
|
+
def save_conversation(self, conversation: Dict[str, Any]) -> str:
|
|
468
|
+
cursor = self.conn.cursor()
|
|
469
|
+
cursor.execute("""
|
|
470
|
+
INSERT INTO conversations (user_id, topic_id, timestamp, messages, metadata, images)
|
|
471
|
+
VALUES (?, ?, ?, ?, ?, ?)
|
|
472
|
+
""", (
|
|
473
|
+
conversation["user_id"],
|
|
474
|
+
conversation["topic_id"],
|
|
475
|
+
conversation["timestamp"],
|
|
476
|
+
self.json.dumps(conversation.get("messages", []), ensure_ascii=False),
|
|
477
|
+
self.json.dumps(conversation.get("metadata", {}), ensure_ascii=False),
|
|
478
|
+
self.json.dumps(conversation.get("images", []), ensure_ascii=False) if conversation.get("images") else None
|
|
479
|
+
))
|
|
480
|
+
self.conn.commit()
|
|
481
|
+
return str(cursor.lastrowid)
|
|
482
|
+
|
|
483
|
+
def get_conversations(
|
|
484
|
+
self,
|
|
485
|
+
user_id: str,
|
|
486
|
+
topic_id: Optional[str] = None,
|
|
487
|
+
start_time: Optional[datetime] = None,
|
|
488
|
+
end_time: Optional[datetime] = None,
|
|
489
|
+
metadata_filter: Optional[Dict[str, Any]] = None,
|
|
490
|
+
limit: int = 1000
|
|
491
|
+
) -> List[Dict[str, Any]]:
|
|
492
|
+
query = "SELECT * FROM conversations WHERE user_id = ?"
|
|
493
|
+
params = [user_id]
|
|
494
|
+
|
|
495
|
+
if topic_id:
|
|
496
|
+
query += " AND topic_id = ?"
|
|
497
|
+
params.append(topic_id)
|
|
498
|
+
|
|
499
|
+
if start_time:
|
|
500
|
+
query += " AND timestamp >= ?"
|
|
501
|
+
params.append(start_time.strftime('%Y-%m-%d %H:%M:%S'))
|
|
502
|
+
|
|
503
|
+
if end_time:
|
|
504
|
+
query += " AND timestamp < ?"
|
|
505
|
+
params.append(end_time.strftime('%Y-%m-%d %H:%M:%S'))
|
|
506
|
+
|
|
507
|
+
query += " ORDER BY timestamp ASC LIMIT ?"
|
|
508
|
+
params.append(limit)
|
|
509
|
+
|
|
510
|
+
cursor = self.conn.cursor()
|
|
511
|
+
cursor.execute(query, params)
|
|
512
|
+
|
|
513
|
+
results = []
|
|
514
|
+
for row in cursor.fetchall():
|
|
515
|
+
conv = {
|
|
516
|
+
"user_id": row["user_id"],
|
|
517
|
+
"topic_id": row["topic_id"],
|
|
518
|
+
"timestamp": row["timestamp"],
|
|
519
|
+
"messages": self.json.loads(row["messages"]),
|
|
520
|
+
"metadata": self.json.loads(row["metadata"]) if row["metadata"] else {}
|
|
521
|
+
}
|
|
522
|
+
if row["images"]:
|
|
523
|
+
conv["images"] = self.json.loads(row["images"])
|
|
524
|
+
|
|
525
|
+
# metadata_filter 过滤
|
|
526
|
+
if metadata_filter:
|
|
527
|
+
match = all(conv.get("metadata", {}).get(k) == v for k, v in metadata_filter.items())
|
|
528
|
+
if not match:
|
|
529
|
+
continue
|
|
530
|
+
|
|
531
|
+
results.append(conv)
|
|
532
|
+
|
|
533
|
+
return results
|
|
534
|
+
|
|
535
|
+
def delete_conversations(self, user_id: str, topic_id: Optional[str] = None) -> int:
|
|
536
|
+
query = "DELETE FROM conversations WHERE user_id = ?"
|
|
537
|
+
params = [user_id]
|
|
538
|
+
|
|
539
|
+
if topic_id:
|
|
540
|
+
query += " AND topic_id = ?"
|
|
541
|
+
params.append(topic_id)
|
|
542
|
+
|
|
543
|
+
cursor = self.conn.cursor()
|
|
544
|
+
cursor.execute(query, params)
|
|
545
|
+
self.conn.commit()
|
|
546
|
+
return cursor.rowcount
|
|
547
|
+
|
|
548
|
+
# ========== 用户画像 ==========
|
|
549
|
+
|
|
550
|
+
def get_profile(self, user_id: str) -> Optional[Dict[str, Any]]:
|
|
551
|
+
cursor = self.conn.cursor()
|
|
552
|
+
cursor.execute("SELECT content, updated_at FROM profiles WHERE user_id = ?", (user_id,))
|
|
553
|
+
row = cursor.fetchone()
|
|
554
|
+
if row:
|
|
555
|
+
return {"content": row["content"], "updated_at": row["updated_at"]}
|
|
556
|
+
return None
|
|
557
|
+
|
|
558
|
+
def save_profile(self, user_id: str, content: str) -> None:
|
|
559
|
+
cursor = self.conn.cursor()
|
|
560
|
+
cursor.execute("""
|
|
561
|
+
INSERT OR REPLACE INTO profiles (user_id, content, updated_at)
|
|
562
|
+
VALUES (?, ?, ?)
|
|
563
|
+
""", (user_id, content, datetime.now().strftime('%Y-%m-%d %H:%M:%S')))
|
|
564
|
+
self.conn.commit()
|
|
565
|
+
|
|
566
|
+
def delete_profile(self, user_id: str) -> bool:
|
|
567
|
+
cursor = self.conn.cursor()
|
|
568
|
+
cursor.execute("DELETE FROM profiles WHERE user_id = ?", (user_id,))
|
|
569
|
+
self.conn.commit()
|
|
570
|
+
return cursor.rowcount > 0
|
|
571
|
+
|
|
572
|
+
# ========== 用户状态 ==========
|
|
573
|
+
|
|
574
|
+
def get_user_state(self, user_id: str) -> Optional[Dict[str, Any]]:
|
|
575
|
+
cursor = self.conn.cursor()
|
|
576
|
+
cursor.execute("SELECT rounds, last_update FROM user_states WHERE user_id = ?", (user_id,))
|
|
577
|
+
row = cursor.fetchone()
|
|
578
|
+
if row:
|
|
579
|
+
return {"user_id": user_id, "rounds": row["rounds"], "last_update": row["last_update"]}
|
|
580
|
+
return None
|
|
581
|
+
|
|
582
|
+
def save_user_state(self, user_id: str, rounds: int, last_update: Optional[str] = None) -> None:
|
|
583
|
+
cursor = self.conn.cursor()
|
|
584
|
+
cursor.execute("""
|
|
585
|
+
INSERT OR REPLACE INTO user_states (user_id, rounds, last_update)
|
|
586
|
+
VALUES (?, ?, ?)
|
|
587
|
+
""", (user_id, rounds, last_update))
|
|
588
|
+
self.conn.commit()
|
|
589
|
+
|
|
590
|
+
def delete_user_state(self, user_id: str) -> bool:
|
|
591
|
+
cursor = self.conn.cursor()
|
|
592
|
+
cursor.execute("DELETE FROM user_states WHERE user_id = ?", (user_id,))
|
|
593
|
+
self.conn.commit()
|
|
594
|
+
return cursor.rowcount > 0
|
|
595
|
+
|
|
596
|
+
# ========== 聚合查询 ==========
|
|
597
|
+
|
|
598
|
+
def get_user_list(self) -> List[str]:
|
|
599
|
+
cursor = self.conn.cursor()
|
|
600
|
+
cursor.execute("SELECT DISTINCT user_id FROM conversations")
|
|
601
|
+
return [row["user_id"] for row in cursor.fetchall()]
|
|
602
|
+
|
|
603
|
+
def get_topic_list(self, user_id: str) -> List[Dict[str, Any]]:
|
|
604
|
+
cursor = self.conn.cursor()
|
|
605
|
+
cursor.execute("""
|
|
606
|
+
SELECT topic_id, COUNT(*) as count, MAX(timestamp) as latest
|
|
607
|
+
FROM conversations
|
|
608
|
+
WHERE user_id = ?
|
|
609
|
+
GROUP BY topic_id
|
|
610
|
+
""", (user_id,))
|
|
611
|
+
|
|
612
|
+
return [{
|
|
613
|
+
"topic_id": row["topic_id"],
|
|
614
|
+
"conversation_count": row["count"],
|
|
615
|
+
"last_active": row["latest"]
|
|
616
|
+
} for row in cursor.fetchall()]
|
|
617
|
+
|
|
618
|
+
def get_conversations_with_images(self, user_id: str) -> List[Dict[str, Any]]:
|
|
619
|
+
"""获取用户所有带图片的对话"""
|
|
620
|
+
cursor = self.conn.cursor()
|
|
621
|
+
cursor.execute("""
|
|
622
|
+
SELECT * FROM conversations
|
|
623
|
+
WHERE user_id = ? AND images IS NOT NULL AND images != '[]'
|
|
624
|
+
ORDER BY timestamp ASC
|
|
625
|
+
""", (user_id,))
|
|
626
|
+
|
|
627
|
+
results = []
|
|
628
|
+
for row in cursor.fetchall():
|
|
629
|
+
results.append({
|
|
630
|
+
"user_id": row["user_id"],
|
|
631
|
+
"topic_id": row["topic_id"],
|
|
632
|
+
"timestamp": row["timestamp"],
|
|
633
|
+
"messages": self.json.loads(row["messages"]),
|
|
634
|
+
"metadata": self.json.loads(row["metadata"]) if row["metadata"] else {},
|
|
635
|
+
"images": self.json.loads(row["images"])
|
|
636
|
+
})
|
|
637
|
+
return results
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: mem1
|
|
3
|
-
Version: 0.1.
|
|
4
|
-
Summary:
|
|
3
|
+
Version: 0.1.2
|
|
4
|
+
Summary: AI 用户记忆系统:Static/Dynamic 画像分层 + 时间衰减遗忘 + SQLite/ES 可插拔存储 + 图片记忆 + 话题隔离。借鉴 supermemory 设计,不用向量数据库。
|
|
5
5
|
Project-URL: Homepage, https://github.com/sougannkyou/mem1
|
|
6
6
|
Project-URL: Repository, https://github.com/sougannkyou/mem1
|
|
7
7
|
Author: Song
|
|
8
8
|
License: MIT
|
|
9
|
-
Keywords: langchain,llm,memory,user-profile
|
|
9
|
+
Keywords: langchain,llm,memory,supermemory,user-profile
|
|
10
10
|
Classifier: Development Status :: 3 - Alpha
|
|
11
11
|
Classifier: Intended Audience :: Developers
|
|
12
12
|
Classifier: License :: OSI Approved :: MIT License
|
|
@@ -14,13 +14,18 @@ Classifier: Programming Language :: Python :: 3
|
|
|
14
14
|
Classifier: Programming Language :: Python :: 3.12
|
|
15
15
|
Classifier: Programming Language :: Python :: 3.13
|
|
16
16
|
Requires-Python: >=3.12
|
|
17
|
-
Requires-Dist: dashscope>=1.14.0
|
|
18
|
-
Requires-Dist: elasticsearch>=8.0.0
|
|
19
17
|
Requires-Dist: openai>=1.0.0
|
|
20
18
|
Requires-Dist: pydantic>=2.0.0
|
|
21
19
|
Requires-Dist: python-dotenv>=1.0.0
|
|
20
|
+
Provides-Extra: all
|
|
21
|
+
Requires-Dist: dashscope>=1.14.0; extra == 'all'
|
|
22
|
+
Requires-Dist: elasticsearch>=8.0.0; extra == 'all'
|
|
22
23
|
Provides-Extra: dev
|
|
23
24
|
Requires-Dist: ipython>=8.0.0; extra == 'dev'
|
|
25
|
+
Provides-Extra: elasticsearch
|
|
26
|
+
Requires-Dist: elasticsearch>=8.0.0; extra == 'elasticsearch'
|
|
27
|
+
Provides-Extra: vl
|
|
28
|
+
Requires-Dist: dashscope>=1.14.0; extra == 'vl'
|
|
24
29
|
Description-Content-Type: text/markdown
|
|
25
30
|
|
|
26
31
|
# mem1 - 用户记忆系统
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
mem1/__init__.py,sha256=Xr8VYSkb4fnvgHHqVyDnK9B5TX9CxAMtppiSn-_TG6s,582
|
|
2
|
+
mem1/config.py,sha256=u21hC2VqjQUxi5OHrG-9Y324VvUptpcaAffbE9BeFJs,7349
|
|
3
|
+
mem1/langchain_middleware.py,sha256=h2mG7K2Tq1N7IovXMvCyvOhsAwTWOR1NAqivF4db2AE,6648
|
|
4
|
+
mem1/llm.py,sha256=MgqlefPrXpIf0FpGruDEjz8SbI7iKu-wxpiJCPCX6co,4439
|
|
5
|
+
mem1/memory.py,sha256=YTAQqIeq5aUUjI4Ynn1-wLq9OLWe14v-sBaED65uK7M,25872
|
|
6
|
+
mem1/memory_md.py,sha256=uu_TvdBoUpAncT1eissOSe1Y3vCy3iWMcuvCy3vCjEA,26258
|
|
7
|
+
mem1/memory_tools.py,sha256=b1YBiRNet0gXnW-KGIZ2KQclluB9Q6dli_DbWLS571k,3646
|
|
8
|
+
mem1/prompts.py,sha256=B-i8u8eRsoVzSsS8zm2rtCj01znQe3p7s5dD4s0P4Lk,11218
|
|
9
|
+
mem1/storage.py,sha256=wf4mYKvJ42tIponBrKD2TNxC5emYoxCvQkBUG1V_Pys,21477
|
|
10
|
+
mem1-0.1.2.dist-info/METADATA,sha256=i0LQ4ocDjvRxBynfbg-PHtRDHd0d7yNk7S7A2DkAfCM,10913
|
|
11
|
+
mem1-0.1.2.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
|
|
12
|
+
mem1-0.1.2.dist-info/RECORD,,
|
mem1-0.1.1.dist-info/RECORD
DELETED
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
mem1/__init__.py,sha256=Xr8VYSkb4fnvgHHqVyDnK9B5TX9CxAMtppiSn-_TG6s,582
|
|
2
|
-
mem1/config.py,sha256=BqF3O4FQnhmnr7iCynK6g5BUxaqnZ0cVnggR4hNsDcQ,5665
|
|
3
|
-
mem1/langchain_middleware.py,sha256=h2mG7K2Tq1N7IovXMvCyvOhsAwTWOR1NAqivF4db2AE,6648
|
|
4
|
-
mem1/llm.py,sha256=MgqlefPrXpIf0FpGruDEjz8SbI7iKu-wxpiJCPCX6co,4439
|
|
5
|
-
mem1/memory.py,sha256=NPsPzphb1tYMp8rK9pxr1rtipocVNIrDueihU-DOWPU,24515
|
|
6
|
-
mem1/memory_md.py,sha256=uu_TvdBoUpAncT1eissOSe1Y3vCy3iWMcuvCy3vCjEA,26258
|
|
7
|
-
mem1/memory_tools.py,sha256=b1YBiRNet0gXnW-KGIZ2KQclluB9Q6dli_DbWLS571k,3646
|
|
8
|
-
mem1/prompts.py,sha256=5HUG-yvTD7iBUzzXwO-WnRomDLkz0UJWox3z3zcT0kI,10599
|
|
9
|
-
mem1/storage.py,sha256=J2JUTjPEXe3dO21LVoj3sl8_78qKOECad2Ol5R9kvCU,12774
|
|
10
|
-
mem1-0.1.1.dist-info/METADATA,sha256=chkdyE8MYG-3izQHSRbAO1gMMZDyALYszm4vFdZGMnc,10541
|
|
11
|
-
mem1-0.1.1.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
|
|
12
|
-
mem1-0.1.1.dist-info/RECORD,,
|
|
File without changes
|