memtide 0.1.0__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.
- memtide-0.1.0/LICENSE +21 -0
- memtide-0.1.0/PKG-INFO +308 -0
- memtide-0.1.0/README.md +288 -0
- memtide-0.1.0/memtide/__init__.py +29 -0
- memtide-0.1.0/memtide/__main__.py +4 -0
- memtide-0.1.0/memtide/cli.py +96 -0
- memtide-0.1.0/memtide/config.py +192 -0
- memtide-0.1.0/memtide/consolidation.py +134 -0
- memtide-0.1.0/memtide/decay.py +89 -0
- memtide-0.1.0/memtide/embeddings.py +187 -0
- memtide-0.1.0/memtide/engine.py +806 -0
- memtide-0.1.0/memtide/gating.py +119 -0
- memtide-0.1.0/memtide/llm.py +184 -0
- memtide-0.1.0/memtide/multimodal.py +360 -0
- memtide-0.1.0/memtide/pgstore.py +529 -0
- memtide-0.1.0/memtide/retrieval.py +309 -0
- memtide-0.1.0/memtide/server.py +506 -0
- memtide-0.1.0/memtide/slots.py +73 -0
- memtide-0.1.0/memtide/storage.py +85 -0
- memtide-0.1.0/memtide/types.py +212 -0
- memtide-0.1.0/memtide/vectorstore.py +134 -0
- memtide-0.1.0/memtide.egg-info/PKG-INFO +308 -0
- memtide-0.1.0/memtide.egg-info/SOURCES.txt +30 -0
- memtide-0.1.0/memtide.egg-info/dependency_links.txt +1 -0
- memtide-0.1.0/memtide.egg-info/entry_points.txt +2 -0
- memtide-0.1.0/memtide.egg-info/requires.txt +1 -0
- memtide-0.1.0/memtide.egg-info/top_level.txt +1 -0
- memtide-0.1.0/pyproject.toml +31 -0
- memtide-0.1.0/setup.cfg +4 -0
- memtide-0.1.0/tests/test_live.py +220 -0
- memtide-0.1.0/tests/test_memtide.py +1771 -0
- memtide-0.1.0/tests/testinfra.py +72 -0
memtide-0.1.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Memtide contributors
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
memtide-0.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,308 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: memtide
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Memtide — a production PostgreSQL + Qdrant agent memory engine (Mem0-style fact extraction, Letta-style core memory blocks, Zep-style temporal audit, hybrid retrieval)
|
|
5
|
+
Author: ysh
|
|
6
|
+
License: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/Yang-Shihui/memtide
|
|
8
|
+
Project-URL: Documentation, https://github.com/Yang-Shihui/memtide/tree/main/docs
|
|
9
|
+
Keywords: agent,memory,llm,rag,mem0,letta,zep
|
|
10
|
+
Classifier: Development Status :: 4 - Beta
|
|
11
|
+
Classifier: Intended Audience :: Developers
|
|
12
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
13
|
+
Classifier: Programming Language :: Python :: 3
|
|
14
|
+
Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
|
|
15
|
+
Requires-Python: >=3.10
|
|
16
|
+
Description-Content-Type: text/markdown
|
|
17
|
+
License-File: LICENSE
|
|
18
|
+
Requires-Dist: psycopg[binary]>=3.2
|
|
19
|
+
Dynamic: license-file
|
|
20
|
+
|
|
21
|
+
<div align="center">
|
|
22
|
+
<img src="docs/assets/logo.svg" width="120" alt="Memtide logo"/>
|
|
23
|
+
|
|
24
|
+
# Memtide
|
|
25
|
+
|
|
26
|
+
[](https://github.com/Yang-Shihui/memtide/actions/workflows/ci.yml)
|
|
27
|
+
[](LICENSE)
|
|
28
|
+

|
|
29
|
+
|
|
30
|
+
**轻量级 Agent 记忆引擎** — 生产级 PostgreSQL + Qdrant、可插拔 LLM
|
|
31
|
+
|
|
32
|
+
波峰 M:字母的两个峰是两道潮,中央负空间的潮谷里藏着一块品牌绿——涨潮强化,落潮遗忘。
|
|
33
|
+
|
|
34
|
+
</div>
|
|
35
|
+
|
|
36
|
+
> **License note**: Memtide is MIT. The Docker image ships PostgreSQL with the
|
|
37
|
+
> ParadeDB `pg_search` extension (AGPL-3.0 community edition) — it runs as a
|
|
38
|
+
> separate database process, so your application code stays MIT.
|
|
39
|
+
|
|
40
|
+
**运行时固定使用 PostgreSQL + Qdrant + 真实 OpenAI 兼容端点**;核心
|
|
41
|
+
HTTP 客户端只用 Python 标准库,PostgreSQL 驱动 psycopg 是唯一运行时依赖。
|
|
42
|
+
|
|
43
|
+
Memtide 融合了 2026 年主流记忆框架的核心设计,外加一个原创机制:
|
|
44
|
+
|
|
45
|
+
| 借鉴自 | 设计 | 在 Memtide 中 |
|
|
46
|
+
|---|---|---|
|
|
47
|
+
| [Mem0](https://github.com/mem0ai/mem0) | 记忆是**原子事实**而非原始消息;写时 LLM 决策 ADD/UPDATE/DELETE/NOOP | `engine.add()` 两阶段管线 |
|
|
48
|
+
| [Letta (MemGPT)](https://github.com/letta-ai/letta) | **常驻要点**:render_context() 按重要度×留存度实时选出 core memory 块注入 system prompt | `render_context()` |
|
|
49
|
+
| [Zep / Graphiti](https://github.com/getzep/graphiti) | **时间线审计**:失效不删除,`valid_at`/`invalid_at` 双时标 + 全量事件日志 | `get_history()` |
|
|
50
|
+
| LangMem / 认知科学 | **检索强化 + 遗忘曲线**:访问越多记得越牢,久不访问自然淡忘 | `decay.py`(Ebbinghaus) |
|
|
51
|
+
| [预测编码](docs/predictive-coding-gate.md)(Rao & Ballard; Itti & Baldi; van Kesteren) | **预测误差门控编码**:完全被先验预测到的信息不写入,越意外记得越牢 | `gating.py`(PredictiveGate) |
|
|
52
|
+
|
|
53
|
+
## 快速开始
|
|
54
|
+
|
|
55
|
+
```python
|
|
56
|
+
from memtide import MemoryEngine, MemoryConfig
|
|
57
|
+
|
|
58
|
+
# 生产模式:PostgreSQL + 任意 OpenAI 兼容 LLM/embedding 端点
|
|
59
|
+
mem = MemoryEngine(MemoryConfig(
|
|
60
|
+
pg_dsn="postgresql://memtide:pw@localhost:5432/memtide",
|
|
61
|
+
llm_base_url="http://your-llm/v1", llm_model="glm-5.3-flash",
|
|
62
|
+
llm_api_key="sk-...", embedding_backend="dashscope", dashscope_api_key="sk-..."))
|
|
63
|
+
|
|
64
|
+
# 1. 写入:对话 → 原子事实 → 与旧记忆冲突消解
|
|
65
|
+
mem.add([{"role": "user", "content": "嗨,我叫李雷,住在杭州,喜欢喝美式咖啡"}], user_id="alice")
|
|
66
|
+
# → AddResult(facts=['用户的名字是李雷', '用户住在杭州', '用户喜欢喝美式咖啡'], added=[...])
|
|
67
|
+
|
|
68
|
+
# 2. 事实变化时自动 UPDATE(不是并存!),旧的值进入审计日志
|
|
69
|
+
mem.add("我搬到上海了", user_id="alice")
|
|
70
|
+
# → AddResult(updated=[...]) '用户住在杭州' 被替换为 '用户住在上海'
|
|
71
|
+
|
|
72
|
+
# 3. 混合检索:向量 + BM25 全文 + 实体三路召回,RRF 融合后按留存度+重要度重排,每路得分可解释
|
|
73
|
+
hits = mem.search("用户现在住在哪里?", user_id="alice")
|
|
74
|
+
# → [{'memory': '用户住在上海', 'score': 0.24,
|
|
75
|
+
# 'components': {'rrf': 0.02, 'semantic': 0.49, 'bm25': 0, 'entity': 0, 'retention': 1.0}}]
|
|
76
|
+
|
|
77
|
+
# 4. 注入 system prompt 的核心记忆块(Letta 风格)
|
|
78
|
+
system_prompt = "你是助理。\n" + mem.render_context(user_id="alice", query="用户的职业?")
|
|
79
|
+
|
|
80
|
+
# 5. 全量审计:ADD → UPDATE → ACCESS,每步带 prev/new 值
|
|
81
|
+
mem.get_history(memory_id=hits[0].memory.id)
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
## 文档
|
|
85
|
+
|
|
86
|
+
| 文档 | 内容 |
|
|
87
|
+
|---|---|
|
|
88
|
+
| [docs/design.md](docs/design.md) | **设计方案**:架构总览、写入/检索/门控/反思/多模态各子系统详细设计与取舍 |
|
|
89
|
+
| [docs/usage.md](docs/usage.md) | **使用说明**:Python/REST API 全参考、Web UI、Docker 部署、配置表、FAQ |
|
|
90
|
+
| [docs/development.md](docs/development.md) | **开发文档**:环境搭建、测试体系、代码约定、扩展后端、UI 构建 |
|
|
91
|
+
| [docs/predictive-coding-gate.md](docs/predictive-coding-gate.md) | 预测编码门控的数学推导与实测标定 |
|
|
92
|
+
|
|
93
|
+
### 接入真实 LLM / embedding / 存储后端(可选)
|
|
94
|
+
|
|
95
|
+
```python
|
|
96
|
+
cfg = MemoryConfig(
|
|
97
|
+
# 存储:PostgreSQL(唯一后端,pg_search BM25 全文检索)
|
|
98
|
+
storage_backend="postgres", pg_dsn="postgresql://memtide:pw@localhost:5432/memtide",
|
|
99
|
+
# 向量:Qdrant ANN(urllib 直连 REST,零 SDK)
|
|
100
|
+
vector_backend="qdrant", qdrant_url="http://localhost:6333",
|
|
101
|
+
# LLM:任何 OpenAI 兼容端点
|
|
102
|
+
llm_backend="openai", llm_base_url="http://your-llm/v1",
|
|
103
|
+
llm_model="GLM-5.3-Flash", llm_api_key="sk-...",
|
|
104
|
+
# embedding:DashScope(qwen3.7-text-embedding),维度自动探测
|
|
105
|
+
embedding_backend="dashscope", dashscope_api_key="sk-...",
|
|
106
|
+
)
|
|
107
|
+
mem = MemoryEngine(cfg)
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
真 LLM 下抽取/消解/反思由 LLM 完成,并输出 slot(易变属性)标签驱动冲突
|
|
111
|
+
更新;真 embedding 下检索为真语义向量(实测中文查询可跨语言命中英文记忆,
|
|
112
|
+
DashScope qwen3.7-text-embedding 同主题 cos≈0.56 / 跨主题 cos≈0.28)。
|
|
113
|
+
|
|
114
|
+
### 更换 LLM / embedding 端点
|
|
115
|
+
|
|
116
|
+
Memtide 与任何 OpenAI 兼容端点协作(DeepSeek、Qwen、vLLM、Ollama 等):
|
|
117
|
+
|
|
118
|
+
```python
|
|
119
|
+
cfg = MemoryConfig(
|
|
120
|
+
llm_backend="openai",
|
|
121
|
+
llm_base_url="https://api.deepseek.com/v1", # 或本地 vLLM/Ollama
|
|
122
|
+
llm_model="deepseek-chat",
|
|
123
|
+
llm_api_key="sk-...", # 或环境变量 OPENAI_API_KEY / LLM_API_KEY
|
|
124
|
+
embedding_backend="dashscope", # 或 openai
|
|
125
|
+
)
|
|
126
|
+
mem = MemoryEngine(cfg)
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
抽取与冲突消解由 LLM 完成(提示词见 `llm.py`);引擎只讲真实 OpenAI 协议,
|
|
130
|
+
测试用本地协议服务器(`tests/fake_openai.py`)保持确定性。
|
|
131
|
+
|
|
132
|
+
## Docker 部署
|
|
133
|
+
|
|
134
|
+
```bash
|
|
135
|
+
cp .env.example .env # 填入 LLM / DASHSCOPE key
|
|
136
|
+
docker compose up -d --build
|
|
137
|
+
curl localhost:8300/stats # 返回真实 LLM / embedding / PostgreSQL / Qdrant 名称
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
栈组成:`paradedb/paradedb:pg16`(PostgreSQL 16 + pg_search BM25 全文检索)、`qdrant`(向量 ANN 索引,
|
|
141
|
+
embedding 模型变更时自动重建集合)、`memtide`(REST 服务)。数据分别在
|
|
142
|
+
pgdata/qdrantdata/mndata volume 中持久化(媒体文件在 mndata:/data/media)。
|
|
143
|
+
|
|
144
|
+
**真实端点验证**:`python3 scripts/live_check.py --docker` 顺序检查 LLM 连通、
|
|
145
|
+
embedding 维度、真实后端全链路(抽取/门控/检索/易变属性更新/LLM 反思/审计链)、
|
|
146
|
+
Docker REST 冒烟。真端点集成测试 `tests/test_live.py`(`MEMTIDE_LIVE=1` 门控,
|
|
147
|
+
默认跳过)。
|
|
148
|
+
|
|
149
|
+
## 可视化管理台(Web UI)
|
|
150
|
+
|
|
151
|
+
马卡龙浅色主题(奶油底 + 开心果绿/草莓粉/蓝莓/薰衣草语义徽章),设计规范由
|
|
152
|
+
[ui-ux-pro-max skill](https://github.com/nextlevelbuilder/ui-ux-pro-max-skill)
|
|
153
|
+
约束:语义色 token、文本对比度 ≥4.5:1、8px 间距体系、可见焦点环、
|
|
154
|
+
prefers-reduced-motion 支持。演示数据可用 `scripts/seed_demo.py` 一键播种
|
|
155
|
+
(在容器内执行,幂等)。
|
|
156
|
+
|
|
157
|
+
REST 服务内置 React 管理台:启动后 **`http://localhost:8300/`** 是官网,**`/console`** 是管理台(五个页面),**`/docs`** 是可在线执行的接口文档:
|
|
158
|
+
|
|
159
|
+
| 页面 | 功能 |
|
|
160
|
+
|---|---|
|
|
161
|
+
| 总览 | 记忆数/失效数/事件分布图、后端信息 |
|
|
162
|
+
| 记忆库 | 过滤浏览(含失效/被取代记忆)、重要度条、门控徽章、surprise 值;编辑/删除(软/硬);点「详情」打开详情抽屉 |
|
|
163
|
+
| ↳ 详情抽屉 | 完整字段 + **审计时间线**(ADD→UPDATE→ACCESS→CONSOLIDATE 可视化)+ superseded_by 跳转 |
|
|
164
|
+
| 检索试玩 | query → 命中结果带**得分成分条**(语义/留存度/RRF/全文/实体命中),直观理解混合检索排序 |
|
|
165
|
+
| 核心记忆 | 渲染 `render_context()` 输出块预览 + 复制 |
|
|
166
|
+
| 操作 | 快速写入并可视化 AddResult(每条事实的编码/拦截决策 + surprise bits)、一键后台反思、重建索引、重置库 |
|
|
167
|
+
|
|
168
|
+
开发模式:`cd webui && npm install && npm run dev`(Vite 5173 端口,API 代理到
|
|
169
|
+
8300)。构建:`npm run build` 产物拷入 `memtide/static/`(Docker 镜像多阶段构建
|
|
170
|
+
自动完成)。`MEMTIDE_STATIC_DIR` 可自定义静态目录;目录不存在时服务退化为纯 API。
|
|
171
|
+
|
|
172
|
+
### REST API 服务
|
|
173
|
+
|
|
174
|
+
内置 HTTP 服务(标准库 `http.server` 实现;psycopg 是唯一运行时依赖),一行启动:
|
|
175
|
+
|
|
176
|
+
```bash
|
|
177
|
+
source .env && python -m memtide serve --port 8300
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
所有能力都以 JSON 接口暴露,任何语言的 agent 都能接入:
|
|
181
|
+
|
|
182
|
+
```bash
|
|
183
|
+
# 写入(带预测编码门控 + 冲突消解,返回完整决策明细)
|
|
184
|
+
curl -X POST localhost:8300/memories \
|
|
185
|
+
-d '{"text": "我叫李雷,住在杭州", "user_id": "alice"}'
|
|
186
|
+
|
|
187
|
+
# 混合检索(返回可解释的 components 得分)
|
|
188
|
+
curl -X POST localhost:8300/search \
|
|
189
|
+
-d '{"query": "用户住在哪里", "user_id": "alice", "limit": 3}'
|
|
190
|
+
|
|
191
|
+
# 其他接口
|
|
192
|
+
curl localhost:8300/memories?user_id=alice # 列出
|
|
193
|
+
curl localhost:8300/memories/<id> # 单条
|
|
194
|
+
curl -X PUT localhost:8300/memories/<id> -d '{"text": "..."}' # 修正
|
|
195
|
+
curl -X DELETE localhost:8300/memories/<id> # 软删除(?hard=true 彻底删)
|
|
196
|
+
curl "localhost:8300/context?user_id=alice&query=..." # core memory 块
|
|
197
|
+
curl -X POST localhost:8300/consolidate -d '{"user_id": "alice"}' # 后台反思
|
|
198
|
+
curl localhost:8300/history # 审计日志
|
|
199
|
+
curl localhost:8300/stats
|
|
200
|
+
```
|
|
201
|
+
|
|
202
|
+
### 多模态记忆
|
|
203
|
+
|
|
204
|
+
OpenAI content-parts 格式的图片 / 音频 / 文件可以直接进管线。做法与 Mem0 一致
|
|
205
|
+
(业界主流):**写入时把媒体归一化为文字描述**(可配置的 vision/STT 端点),
|
|
206
|
+
描述文本走正常的抽取 → 门控 → 冲突消解管线,原始字节以 sha256 内容寻址落盘为
|
|
207
|
+
附件,随记忆一起返回。文字查询即可跨模态召回图片记忆:
|
|
208
|
+
|
|
209
|
+
```python
|
|
210
|
+
eng.add([{"role": "user", "content": [
|
|
211
|
+
{"type": "text", "text": "看看这张图"},
|
|
212
|
+
{"type": "image_url", "image_url": {"url": "data:image/png;base64,..."}}, # 或 https:// / 本地路径
|
|
213
|
+
]}], user_id="alice")
|
|
214
|
+
# → 抽出的原子事实带 attachments 引用(+ metadata.modality)
|
|
215
|
+
# → 检索 "图里有什么" 返回事实 + GET /media/{sha256} 取回原图
|
|
216
|
+
```
|
|
217
|
+
|
|
218
|
+
```bash
|
|
219
|
+
curl -X POST localhost:8300/memories -d '{"messages": [{"role": "user", "content": [
|
|
220
|
+
{"type": "image_url", "image_url": {"url": "https://example.com/photo.jpg"}}]}]}'
|
|
221
|
+
curl localhost:8300/media/<sha256> # 取回原始素材(不可变缓存)
|
|
222
|
+
```
|
|
223
|
+
|
|
224
|
+
vision 端点默认复用主 LLM 配置(任何 OpenAI 兼容视觉模型,如 qwen-vl-max),
|
|
225
|
+
可用 `MEMTIDE_VISION_BASE_URL` / `MEMTIDE_VISION_MODEL` / `MEMTIDE_VISION_API_KEY`
|
|
226
|
+
单独指定;音频转录需配置 `MEMTIDE_STT_MODEL`,未配置时音频仅存引用不转写;
|
|
227
|
+
vision 不可用时优雅降级——素材照存、描述留空,绝不丢数据。
|
|
228
|
+
|
|
229
|
+
### CLI
|
|
230
|
+
|
|
231
|
+
```bash
|
|
232
|
+
source .env && python -m memtide add "我叫李雷,喜欢 Rust"
|
|
233
|
+
python -m memtide search "用户喜欢什么语言"
|
|
234
|
+
python -m memtide context "用户的偏好" # 渲染核心记忆块
|
|
235
|
+
python -m memtide history # 审计日志
|
|
236
|
+
python -m memtide stats
|
|
237
|
+
```
|
|
238
|
+
|
|
239
|
+
## 架构
|
|
240
|
+
|
|
241
|
+
```
|
|
242
|
+
写入: messages ──▶ Extractor ──▶ 原子事实(带 slot/重要度/实体)
|
|
243
|
+
│
|
|
244
|
+
PredictiveGate 预测编码门控 ◀── 记忆先验
|
|
245
|
+
(完全被预测到的 → 拦下;越意外 → 重要度越高)
|
|
246
|
+
│
|
|
247
|
+
对相似旧记忆做冲突消解 ◀── LLM/规则
|
|
248
|
+
│
|
|
249
|
+
ADD / UPDATE / DELETE / NOOP
|
|
250
|
+
│
|
|
251
|
+
存储: PostgreSQL ── memories + BM25(中英可查) + entities + memory_history
|
|
252
|
+
│
|
|
253
|
+
读取: query ──▶ ┌ 向量余弦(top-40) ┐
|
|
254
|
+
├ BM25 全文 ├─▶ RRF 融合 ─▶ + 遗忘曲线留存度
|
|
255
|
+
└ 实体索引匹配 ┘ + 重要度
|
|
256
|
+
│
|
|
257
|
+
检索命中 ──▶ access_count++ (间隔效应: 越用越难忘)
|
|
258
|
+
```
|
|
259
|
+
|
|
260
|
+
### 关键机制
|
|
261
|
+
|
|
262
|
+
- **预测编码门控**:记忆库即生成式先验,新事实按预测误差 S = -log2 p̂ 分流——
|
|
263
|
+
S ≤ 0.5 bits 不存(完全被预测到)、中间正常整合、S ≥ 2.5 bits 加权编码
|
|
264
|
+
(越意外记得越牢);易变属性冲突强制写入。详见
|
|
265
|
+
[docs/predictive-coding-gate.md](docs/predictive-coding-gate.md)。
|
|
266
|
+
- **易变属性槽位(slot)**:`name/location/role/employer/age/stack/plan` 属于"人会变"的事实——同一槽位出现新值时自动 **UPDATE**(旧值进历史),而非叠加矛盾记忆。喜好类(`like`)则允许多条并存,仅在极性冲突(喜欢↔不喜欢)时替换。
|
|
267
|
+
- **后台反思(LangMem 式)**:`mem.consolidate_background()` 定期把同主题高密度
|
|
268
|
+
簇蒸馏成一条概括记忆(重要度取簇内最高 +0.05,使其更常驻 core memory 块,
|
|
269
|
+
且半衰期 ×3 抗衰减),
|
|
270
|
+
原事实标 `invalid_at` 并留 `superseded_by` 链接,审计日志记 CONSOLIDATE
|
|
271
|
+
事件——记忆库越用越小、越用越精,且无信息丢失。
|
|
272
|
+
- **遗忘是软性的**:留存度 `retention = 0.5^(age / (half_life × min(1 + 0.4·ln(1+access_count), 4.0)))`,低于阈值的记忆不再浮现但保留在库中——被再次查询时即可"复忆"。
|
|
273
|
+
- **多租户隔离**:所有读写按 `user_id` / `agent_id` / `run_id` 作用域隔离。
|
|
274
|
+
|
|
275
|
+
## 项目结构
|
|
276
|
+
|
|
277
|
+
```
|
|
278
|
+
memtide/
|
|
279
|
+
├── memtide/
|
|
280
|
+
│ ├── engine.py # MemoryEngine:add/search/render_context/get_history
|
|
281
|
+
│ ├── llm.py # OpenAI 兼容客户端(提示词与 JSON 解析)
|
|
282
|
+
│ ├── embeddings.py # OpenAI / DashScope embedder(auto 按 key 选择)
|
|
283
|
+
│ ├── retrieval.py # 三路召回 + RRF 融合 + 可解释得分
|
|
284
|
+
│ ├── decay.py # Ebbinghaus 遗忘曲线 + 检索强化
|
|
285
|
+
│ ├── gating.py # 预测编码门控:surprise 三分流(novel/integrate/reject)
|
|
286
|
+
│ ├── consolidation.py # 后台反思:聚类 → 概括 → supersede 链
|
|
287
|
+
│ ├── storage.py # StorageBase 契约(PostgreSQL 唯一后端)
|
|
288
|
+
│ ├── pgstore.py # PostgreSQL 后端(psycopg3 + pg_search BM25)
|
|
289
|
+
│ ├── vectorstore.py # Qdrant ANN(urllib REST)
|
|
290
|
+
│ ├── multimodal.py # 多模态接入:媒体 parts → 文字描述 + sha256 附件
|
|
291
|
+
│ ├── types.py # Memory / SearchResult / AddResult 数据类
|
|
292
|
+
│ ├── config.py # MemoryConfig(全部阈值可调)
|
|
293
|
+
│ ├── server.py # REST API(stdlib http.server,含 /media 素材服务)
|
|
294
|
+
│ └── cli.py # 命令行入口
|
|
295
|
+
├── tests/test_memtide.py # 94 个测试(hermetic 回归:全功能/性能路径/多模态/迁移/运维/连接池上限/SSRF 守卫/布尔与 flag 解析)+ 14 个 live 集成测试(真实端点含真实视觉/并发/别名/衰减,MEMTIDE_LIVE=1 门控)
|
|
296
|
+
└── examples/
|
|
297
|
+
```
|
|
298
|
+
|
|
299
|
+
|
|
300
|
+
## 测试
|
|
301
|
+
|
|
302
|
+
```bash
|
|
303
|
+
python3 -m unittest tests.test_memtide # 94 tests, PG/Qdrant + 本地协议服务器,无外部网络
|
|
304
|
+
```
|
|
305
|
+
|
|
306
|
+
## License
|
|
307
|
+
|
|
308
|
+
MIT
|
memtide-0.1.0/README.md
ADDED
|
@@ -0,0 +1,288 @@
|
|
|
1
|
+
<div align="center">
|
|
2
|
+
<img src="docs/assets/logo.svg" width="120" alt="Memtide logo"/>
|
|
3
|
+
|
|
4
|
+
# Memtide
|
|
5
|
+
|
|
6
|
+
[](https://github.com/Yang-Shihui/memtide/actions/workflows/ci.yml)
|
|
7
|
+
[](LICENSE)
|
|
8
|
+

|
|
9
|
+
|
|
10
|
+
**轻量级 Agent 记忆引擎** — 生产级 PostgreSQL + Qdrant、可插拔 LLM
|
|
11
|
+
|
|
12
|
+
波峰 M:字母的两个峰是两道潮,中央负空间的潮谷里藏着一块品牌绿——涨潮强化,落潮遗忘。
|
|
13
|
+
|
|
14
|
+
</div>
|
|
15
|
+
|
|
16
|
+
> **License note**: Memtide is MIT. The Docker image ships PostgreSQL with the
|
|
17
|
+
> ParadeDB `pg_search` extension (AGPL-3.0 community edition) — it runs as a
|
|
18
|
+
> separate database process, so your application code stays MIT.
|
|
19
|
+
|
|
20
|
+
**运行时固定使用 PostgreSQL + Qdrant + 真实 OpenAI 兼容端点**;核心
|
|
21
|
+
HTTP 客户端只用 Python 标准库,PostgreSQL 驱动 psycopg 是唯一运行时依赖。
|
|
22
|
+
|
|
23
|
+
Memtide 融合了 2026 年主流记忆框架的核心设计,外加一个原创机制:
|
|
24
|
+
|
|
25
|
+
| 借鉴自 | 设计 | 在 Memtide 中 |
|
|
26
|
+
|---|---|---|
|
|
27
|
+
| [Mem0](https://github.com/mem0ai/mem0) | 记忆是**原子事实**而非原始消息;写时 LLM 决策 ADD/UPDATE/DELETE/NOOP | `engine.add()` 两阶段管线 |
|
|
28
|
+
| [Letta (MemGPT)](https://github.com/letta-ai/letta) | **常驻要点**:render_context() 按重要度×留存度实时选出 core memory 块注入 system prompt | `render_context()` |
|
|
29
|
+
| [Zep / Graphiti](https://github.com/getzep/graphiti) | **时间线审计**:失效不删除,`valid_at`/`invalid_at` 双时标 + 全量事件日志 | `get_history()` |
|
|
30
|
+
| LangMem / 认知科学 | **检索强化 + 遗忘曲线**:访问越多记得越牢,久不访问自然淡忘 | `decay.py`(Ebbinghaus) |
|
|
31
|
+
| [预测编码](docs/predictive-coding-gate.md)(Rao & Ballard; Itti & Baldi; van Kesteren) | **预测误差门控编码**:完全被先验预测到的信息不写入,越意外记得越牢 | `gating.py`(PredictiveGate) |
|
|
32
|
+
|
|
33
|
+
## 快速开始
|
|
34
|
+
|
|
35
|
+
```python
|
|
36
|
+
from memtide import MemoryEngine, MemoryConfig
|
|
37
|
+
|
|
38
|
+
# 生产模式:PostgreSQL + 任意 OpenAI 兼容 LLM/embedding 端点
|
|
39
|
+
mem = MemoryEngine(MemoryConfig(
|
|
40
|
+
pg_dsn="postgresql://memtide:pw@localhost:5432/memtide",
|
|
41
|
+
llm_base_url="http://your-llm/v1", llm_model="glm-5.3-flash",
|
|
42
|
+
llm_api_key="sk-...", embedding_backend="dashscope", dashscope_api_key="sk-..."))
|
|
43
|
+
|
|
44
|
+
# 1. 写入:对话 → 原子事实 → 与旧记忆冲突消解
|
|
45
|
+
mem.add([{"role": "user", "content": "嗨,我叫李雷,住在杭州,喜欢喝美式咖啡"}], user_id="alice")
|
|
46
|
+
# → AddResult(facts=['用户的名字是李雷', '用户住在杭州', '用户喜欢喝美式咖啡'], added=[...])
|
|
47
|
+
|
|
48
|
+
# 2. 事实变化时自动 UPDATE(不是并存!),旧的值进入审计日志
|
|
49
|
+
mem.add("我搬到上海了", user_id="alice")
|
|
50
|
+
# → AddResult(updated=[...]) '用户住在杭州' 被替换为 '用户住在上海'
|
|
51
|
+
|
|
52
|
+
# 3. 混合检索:向量 + BM25 全文 + 实体三路召回,RRF 融合后按留存度+重要度重排,每路得分可解释
|
|
53
|
+
hits = mem.search("用户现在住在哪里?", user_id="alice")
|
|
54
|
+
# → [{'memory': '用户住在上海', 'score': 0.24,
|
|
55
|
+
# 'components': {'rrf': 0.02, 'semantic': 0.49, 'bm25': 0, 'entity': 0, 'retention': 1.0}}]
|
|
56
|
+
|
|
57
|
+
# 4. 注入 system prompt 的核心记忆块(Letta 风格)
|
|
58
|
+
system_prompt = "你是助理。\n" + mem.render_context(user_id="alice", query="用户的职业?")
|
|
59
|
+
|
|
60
|
+
# 5. 全量审计:ADD → UPDATE → ACCESS,每步带 prev/new 值
|
|
61
|
+
mem.get_history(memory_id=hits[0].memory.id)
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
## 文档
|
|
65
|
+
|
|
66
|
+
| 文档 | 内容 |
|
|
67
|
+
|---|---|
|
|
68
|
+
| [docs/design.md](docs/design.md) | **设计方案**:架构总览、写入/检索/门控/反思/多模态各子系统详细设计与取舍 |
|
|
69
|
+
| [docs/usage.md](docs/usage.md) | **使用说明**:Python/REST API 全参考、Web UI、Docker 部署、配置表、FAQ |
|
|
70
|
+
| [docs/development.md](docs/development.md) | **开发文档**:环境搭建、测试体系、代码约定、扩展后端、UI 构建 |
|
|
71
|
+
| [docs/predictive-coding-gate.md](docs/predictive-coding-gate.md) | 预测编码门控的数学推导与实测标定 |
|
|
72
|
+
|
|
73
|
+
### 接入真实 LLM / embedding / 存储后端(可选)
|
|
74
|
+
|
|
75
|
+
```python
|
|
76
|
+
cfg = MemoryConfig(
|
|
77
|
+
# 存储:PostgreSQL(唯一后端,pg_search BM25 全文检索)
|
|
78
|
+
storage_backend="postgres", pg_dsn="postgresql://memtide:pw@localhost:5432/memtide",
|
|
79
|
+
# 向量:Qdrant ANN(urllib 直连 REST,零 SDK)
|
|
80
|
+
vector_backend="qdrant", qdrant_url="http://localhost:6333",
|
|
81
|
+
# LLM:任何 OpenAI 兼容端点
|
|
82
|
+
llm_backend="openai", llm_base_url="http://your-llm/v1",
|
|
83
|
+
llm_model="GLM-5.3-Flash", llm_api_key="sk-...",
|
|
84
|
+
# embedding:DashScope(qwen3.7-text-embedding),维度自动探测
|
|
85
|
+
embedding_backend="dashscope", dashscope_api_key="sk-...",
|
|
86
|
+
)
|
|
87
|
+
mem = MemoryEngine(cfg)
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
真 LLM 下抽取/消解/反思由 LLM 完成,并输出 slot(易变属性)标签驱动冲突
|
|
91
|
+
更新;真 embedding 下检索为真语义向量(实测中文查询可跨语言命中英文记忆,
|
|
92
|
+
DashScope qwen3.7-text-embedding 同主题 cos≈0.56 / 跨主题 cos≈0.28)。
|
|
93
|
+
|
|
94
|
+
### 更换 LLM / embedding 端点
|
|
95
|
+
|
|
96
|
+
Memtide 与任何 OpenAI 兼容端点协作(DeepSeek、Qwen、vLLM、Ollama 等):
|
|
97
|
+
|
|
98
|
+
```python
|
|
99
|
+
cfg = MemoryConfig(
|
|
100
|
+
llm_backend="openai",
|
|
101
|
+
llm_base_url="https://api.deepseek.com/v1", # 或本地 vLLM/Ollama
|
|
102
|
+
llm_model="deepseek-chat",
|
|
103
|
+
llm_api_key="sk-...", # 或环境变量 OPENAI_API_KEY / LLM_API_KEY
|
|
104
|
+
embedding_backend="dashscope", # 或 openai
|
|
105
|
+
)
|
|
106
|
+
mem = MemoryEngine(cfg)
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
抽取与冲突消解由 LLM 完成(提示词见 `llm.py`);引擎只讲真实 OpenAI 协议,
|
|
110
|
+
测试用本地协议服务器(`tests/fake_openai.py`)保持确定性。
|
|
111
|
+
|
|
112
|
+
## Docker 部署
|
|
113
|
+
|
|
114
|
+
```bash
|
|
115
|
+
cp .env.example .env # 填入 LLM / DASHSCOPE key
|
|
116
|
+
docker compose up -d --build
|
|
117
|
+
curl localhost:8300/stats # 返回真实 LLM / embedding / PostgreSQL / Qdrant 名称
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
栈组成:`paradedb/paradedb:pg16`(PostgreSQL 16 + pg_search BM25 全文检索)、`qdrant`(向量 ANN 索引,
|
|
121
|
+
embedding 模型变更时自动重建集合)、`memtide`(REST 服务)。数据分别在
|
|
122
|
+
pgdata/qdrantdata/mndata volume 中持久化(媒体文件在 mndata:/data/media)。
|
|
123
|
+
|
|
124
|
+
**真实端点验证**:`python3 scripts/live_check.py --docker` 顺序检查 LLM 连通、
|
|
125
|
+
embedding 维度、真实后端全链路(抽取/门控/检索/易变属性更新/LLM 反思/审计链)、
|
|
126
|
+
Docker REST 冒烟。真端点集成测试 `tests/test_live.py`(`MEMTIDE_LIVE=1` 门控,
|
|
127
|
+
默认跳过)。
|
|
128
|
+
|
|
129
|
+
## 可视化管理台(Web UI)
|
|
130
|
+
|
|
131
|
+
马卡龙浅色主题(奶油底 + 开心果绿/草莓粉/蓝莓/薰衣草语义徽章),设计规范由
|
|
132
|
+
[ui-ux-pro-max skill](https://github.com/nextlevelbuilder/ui-ux-pro-max-skill)
|
|
133
|
+
约束:语义色 token、文本对比度 ≥4.5:1、8px 间距体系、可见焦点环、
|
|
134
|
+
prefers-reduced-motion 支持。演示数据可用 `scripts/seed_demo.py` 一键播种
|
|
135
|
+
(在容器内执行,幂等)。
|
|
136
|
+
|
|
137
|
+
REST 服务内置 React 管理台:启动后 **`http://localhost:8300/`** 是官网,**`/console`** 是管理台(五个页面),**`/docs`** 是可在线执行的接口文档:
|
|
138
|
+
|
|
139
|
+
| 页面 | 功能 |
|
|
140
|
+
|---|---|
|
|
141
|
+
| 总览 | 记忆数/失效数/事件分布图、后端信息 |
|
|
142
|
+
| 记忆库 | 过滤浏览(含失效/被取代记忆)、重要度条、门控徽章、surprise 值;编辑/删除(软/硬);点「详情」打开详情抽屉 |
|
|
143
|
+
| ↳ 详情抽屉 | 完整字段 + **审计时间线**(ADD→UPDATE→ACCESS→CONSOLIDATE 可视化)+ superseded_by 跳转 |
|
|
144
|
+
| 检索试玩 | query → 命中结果带**得分成分条**(语义/留存度/RRF/全文/实体命中),直观理解混合检索排序 |
|
|
145
|
+
| 核心记忆 | 渲染 `render_context()` 输出块预览 + 复制 |
|
|
146
|
+
| 操作 | 快速写入并可视化 AddResult(每条事实的编码/拦截决策 + surprise bits)、一键后台反思、重建索引、重置库 |
|
|
147
|
+
|
|
148
|
+
开发模式:`cd webui && npm install && npm run dev`(Vite 5173 端口,API 代理到
|
|
149
|
+
8300)。构建:`npm run build` 产物拷入 `memtide/static/`(Docker 镜像多阶段构建
|
|
150
|
+
自动完成)。`MEMTIDE_STATIC_DIR` 可自定义静态目录;目录不存在时服务退化为纯 API。
|
|
151
|
+
|
|
152
|
+
### REST API 服务
|
|
153
|
+
|
|
154
|
+
内置 HTTP 服务(标准库 `http.server` 实现;psycopg 是唯一运行时依赖),一行启动:
|
|
155
|
+
|
|
156
|
+
```bash
|
|
157
|
+
source .env && python -m memtide serve --port 8300
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
所有能力都以 JSON 接口暴露,任何语言的 agent 都能接入:
|
|
161
|
+
|
|
162
|
+
```bash
|
|
163
|
+
# 写入(带预测编码门控 + 冲突消解,返回完整决策明细)
|
|
164
|
+
curl -X POST localhost:8300/memories \
|
|
165
|
+
-d '{"text": "我叫李雷,住在杭州", "user_id": "alice"}'
|
|
166
|
+
|
|
167
|
+
# 混合检索(返回可解释的 components 得分)
|
|
168
|
+
curl -X POST localhost:8300/search \
|
|
169
|
+
-d '{"query": "用户住在哪里", "user_id": "alice", "limit": 3}'
|
|
170
|
+
|
|
171
|
+
# 其他接口
|
|
172
|
+
curl localhost:8300/memories?user_id=alice # 列出
|
|
173
|
+
curl localhost:8300/memories/<id> # 单条
|
|
174
|
+
curl -X PUT localhost:8300/memories/<id> -d '{"text": "..."}' # 修正
|
|
175
|
+
curl -X DELETE localhost:8300/memories/<id> # 软删除(?hard=true 彻底删)
|
|
176
|
+
curl "localhost:8300/context?user_id=alice&query=..." # core memory 块
|
|
177
|
+
curl -X POST localhost:8300/consolidate -d '{"user_id": "alice"}' # 后台反思
|
|
178
|
+
curl localhost:8300/history # 审计日志
|
|
179
|
+
curl localhost:8300/stats
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
### 多模态记忆
|
|
183
|
+
|
|
184
|
+
OpenAI content-parts 格式的图片 / 音频 / 文件可以直接进管线。做法与 Mem0 一致
|
|
185
|
+
(业界主流):**写入时把媒体归一化为文字描述**(可配置的 vision/STT 端点),
|
|
186
|
+
描述文本走正常的抽取 → 门控 → 冲突消解管线,原始字节以 sha256 内容寻址落盘为
|
|
187
|
+
附件,随记忆一起返回。文字查询即可跨模态召回图片记忆:
|
|
188
|
+
|
|
189
|
+
```python
|
|
190
|
+
eng.add([{"role": "user", "content": [
|
|
191
|
+
{"type": "text", "text": "看看这张图"},
|
|
192
|
+
{"type": "image_url", "image_url": {"url": "data:image/png;base64,..."}}, # 或 https:// / 本地路径
|
|
193
|
+
]}], user_id="alice")
|
|
194
|
+
# → 抽出的原子事实带 attachments 引用(+ metadata.modality)
|
|
195
|
+
# → 检索 "图里有什么" 返回事实 + GET /media/{sha256} 取回原图
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
```bash
|
|
199
|
+
curl -X POST localhost:8300/memories -d '{"messages": [{"role": "user", "content": [
|
|
200
|
+
{"type": "image_url", "image_url": {"url": "https://example.com/photo.jpg"}}]}]}'
|
|
201
|
+
curl localhost:8300/media/<sha256> # 取回原始素材(不可变缓存)
|
|
202
|
+
```
|
|
203
|
+
|
|
204
|
+
vision 端点默认复用主 LLM 配置(任何 OpenAI 兼容视觉模型,如 qwen-vl-max),
|
|
205
|
+
可用 `MEMTIDE_VISION_BASE_URL` / `MEMTIDE_VISION_MODEL` / `MEMTIDE_VISION_API_KEY`
|
|
206
|
+
单独指定;音频转录需配置 `MEMTIDE_STT_MODEL`,未配置时音频仅存引用不转写;
|
|
207
|
+
vision 不可用时优雅降级——素材照存、描述留空,绝不丢数据。
|
|
208
|
+
|
|
209
|
+
### CLI
|
|
210
|
+
|
|
211
|
+
```bash
|
|
212
|
+
source .env && python -m memtide add "我叫李雷,喜欢 Rust"
|
|
213
|
+
python -m memtide search "用户喜欢什么语言"
|
|
214
|
+
python -m memtide context "用户的偏好" # 渲染核心记忆块
|
|
215
|
+
python -m memtide history # 审计日志
|
|
216
|
+
python -m memtide stats
|
|
217
|
+
```
|
|
218
|
+
|
|
219
|
+
## 架构
|
|
220
|
+
|
|
221
|
+
```
|
|
222
|
+
写入: messages ──▶ Extractor ──▶ 原子事实(带 slot/重要度/实体)
|
|
223
|
+
│
|
|
224
|
+
PredictiveGate 预测编码门控 ◀── 记忆先验
|
|
225
|
+
(完全被预测到的 → 拦下;越意外 → 重要度越高)
|
|
226
|
+
│
|
|
227
|
+
对相似旧记忆做冲突消解 ◀── LLM/规则
|
|
228
|
+
│
|
|
229
|
+
ADD / UPDATE / DELETE / NOOP
|
|
230
|
+
│
|
|
231
|
+
存储: PostgreSQL ── memories + BM25(中英可查) + entities + memory_history
|
|
232
|
+
│
|
|
233
|
+
读取: query ──▶ ┌ 向量余弦(top-40) ┐
|
|
234
|
+
├ BM25 全文 ├─▶ RRF 融合 ─▶ + 遗忘曲线留存度
|
|
235
|
+
└ 实体索引匹配 ┘ + 重要度
|
|
236
|
+
│
|
|
237
|
+
检索命中 ──▶ access_count++ (间隔效应: 越用越难忘)
|
|
238
|
+
```
|
|
239
|
+
|
|
240
|
+
### 关键机制
|
|
241
|
+
|
|
242
|
+
- **预测编码门控**:记忆库即生成式先验,新事实按预测误差 S = -log2 p̂ 分流——
|
|
243
|
+
S ≤ 0.5 bits 不存(完全被预测到)、中间正常整合、S ≥ 2.5 bits 加权编码
|
|
244
|
+
(越意外记得越牢);易变属性冲突强制写入。详见
|
|
245
|
+
[docs/predictive-coding-gate.md](docs/predictive-coding-gate.md)。
|
|
246
|
+
- **易变属性槽位(slot)**:`name/location/role/employer/age/stack/plan` 属于"人会变"的事实——同一槽位出现新值时自动 **UPDATE**(旧值进历史),而非叠加矛盾记忆。喜好类(`like`)则允许多条并存,仅在极性冲突(喜欢↔不喜欢)时替换。
|
|
247
|
+
- **后台反思(LangMem 式)**:`mem.consolidate_background()` 定期把同主题高密度
|
|
248
|
+
簇蒸馏成一条概括记忆(重要度取簇内最高 +0.05,使其更常驻 core memory 块,
|
|
249
|
+
且半衰期 ×3 抗衰减),
|
|
250
|
+
原事实标 `invalid_at` 并留 `superseded_by` 链接,审计日志记 CONSOLIDATE
|
|
251
|
+
事件——记忆库越用越小、越用越精,且无信息丢失。
|
|
252
|
+
- **遗忘是软性的**:留存度 `retention = 0.5^(age / (half_life × min(1 + 0.4·ln(1+access_count), 4.0)))`,低于阈值的记忆不再浮现但保留在库中——被再次查询时即可"复忆"。
|
|
253
|
+
- **多租户隔离**:所有读写按 `user_id` / `agent_id` / `run_id` 作用域隔离。
|
|
254
|
+
|
|
255
|
+
## 项目结构
|
|
256
|
+
|
|
257
|
+
```
|
|
258
|
+
memtide/
|
|
259
|
+
├── memtide/
|
|
260
|
+
│ ├── engine.py # MemoryEngine:add/search/render_context/get_history
|
|
261
|
+
│ ├── llm.py # OpenAI 兼容客户端(提示词与 JSON 解析)
|
|
262
|
+
│ ├── embeddings.py # OpenAI / DashScope embedder(auto 按 key 选择)
|
|
263
|
+
│ ├── retrieval.py # 三路召回 + RRF 融合 + 可解释得分
|
|
264
|
+
│ ├── decay.py # Ebbinghaus 遗忘曲线 + 检索强化
|
|
265
|
+
│ ├── gating.py # 预测编码门控:surprise 三分流(novel/integrate/reject)
|
|
266
|
+
│ ├── consolidation.py # 后台反思:聚类 → 概括 → supersede 链
|
|
267
|
+
│ ├── storage.py # StorageBase 契约(PostgreSQL 唯一后端)
|
|
268
|
+
│ ├── pgstore.py # PostgreSQL 后端(psycopg3 + pg_search BM25)
|
|
269
|
+
│ ├── vectorstore.py # Qdrant ANN(urllib REST)
|
|
270
|
+
│ ├── multimodal.py # 多模态接入:媒体 parts → 文字描述 + sha256 附件
|
|
271
|
+
│ ├── types.py # Memory / SearchResult / AddResult 数据类
|
|
272
|
+
│ ├── config.py # MemoryConfig(全部阈值可调)
|
|
273
|
+
│ ├── server.py # REST API(stdlib http.server,含 /media 素材服务)
|
|
274
|
+
│ └── cli.py # 命令行入口
|
|
275
|
+
├── tests/test_memtide.py # 94 个测试(hermetic 回归:全功能/性能路径/多模态/迁移/运维/连接池上限/SSRF 守卫/布尔与 flag 解析)+ 14 个 live 集成测试(真实端点含真实视觉/并发/别名/衰减,MEMTIDE_LIVE=1 门控)
|
|
276
|
+
└── examples/
|
|
277
|
+
```
|
|
278
|
+
|
|
279
|
+
|
|
280
|
+
## 测试
|
|
281
|
+
|
|
282
|
+
```bash
|
|
283
|
+
python3 -m unittest tests.test_memtide # 94 tests, PG/Qdrant + 本地协议服务器,无外部网络
|
|
284
|
+
```
|
|
285
|
+
|
|
286
|
+
## License
|
|
287
|
+
|
|
288
|
+
MIT
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
"""Memtide — a stdlib-core agent memory engine.
|
|
2
|
+
|
|
3
|
+
Atomic, auditable, forgetting-aware memory for LLM agents:
|
|
4
|
+
- Mem0-style atomic-fact extraction & write-time consolidation
|
|
5
|
+
- Letta-style core memory block you inject into the system prompt
|
|
6
|
+
- Zep-style temporal audit (nothing is silently lost; updates/invalidations tracked)
|
|
7
|
+
- Hybrid retrieval: vector + BM25 full-text + entity + Ebbinghaus retention, RRF-fused
|
|
8
|
+
|
|
9
|
+
Production-ready agent memory with PostgreSQL, Qdrant, and any
|
|
10
|
+
OpenAI-compatible LLM/embedding endpoints.
|
|
11
|
+
"""
|
|
12
|
+
|
|
13
|
+
from .config import MemoryConfig
|
|
14
|
+
from .engine import MemoryEngine
|
|
15
|
+
from .types import AddResult, Event, ExtractedFact, Memory, MemoryType, SearchResult
|
|
16
|
+
|
|
17
|
+
__version__ = "0.1.0"
|
|
18
|
+
|
|
19
|
+
__all__ = [
|
|
20
|
+
"MemoryEngine",
|
|
21
|
+
"MemoryConfig",
|
|
22
|
+
"Memory",
|
|
23
|
+
"MemoryType",
|
|
24
|
+
"Event",
|
|
25
|
+
"AddResult",
|
|
26
|
+
"ExtractedFact",
|
|
27
|
+
"SearchResult",
|
|
28
|
+
"__version__",
|
|
29
|
+
]
|