quiet-memo 0.2.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.
- quiet_memo-0.2.0/.github/workflows/workflow.yml +31 -0
- quiet_memo-0.2.0/.gitignore +17 -0
- quiet_memo-0.2.0/PKG-INFO +219 -0
- quiet_memo-0.2.0/README.md +196 -0
- quiet_memo-0.2.0/config.example.toml +20 -0
- quiet_memo-0.2.0/pyproject.toml +44 -0
- quiet_memo-0.2.0/src/quiet_memo/__init__.py +3 -0
- quiet_memo-0.2.0/src/quiet_memo/__main__.py +4 -0
- quiet_memo-0.2.0/src/quiet_memo/agent/__init__.py +0 -0
- quiet_memo-0.2.0/src/quiet_memo/agent/context.py +258 -0
- quiet_memo-0.2.0/src/quiet_memo/agent/runtime.py +303 -0
- quiet_memo-0.2.0/src/quiet_memo/agent/session.py +193 -0
- quiet_memo-0.2.0/src/quiet_memo/agent/tools.py +301 -0
- quiet_memo-0.2.0/src/quiet_memo/app/__init__.py +0 -0
- quiet_memo-0.2.0/src/quiet_memo/app/cli.py +547 -0
- quiet_memo-0.2.0/src/quiet_memo/app/server.py +78 -0
- quiet_memo-0.2.0/src/quiet_memo/infra/__init__.py +0 -0
- quiet_memo-0.2.0/src/quiet_memo/infra/config.py +177 -0
- quiet_memo-0.2.0/src/quiet_memo/infra/gitops.py +100 -0
- quiet_memo-0.2.0/src/quiet_memo/infra/providers.py +218 -0
- quiet_memo-0.2.0/src/quiet_memo/infra/tokens.py +60 -0
- quiet_memo-0.2.0/src/quiet_memo/llm/__init__.py +28 -0
- quiet_memo-0.2.0/src/quiet_memo/llm/client.py +146 -0
- quiet_memo-0.2.0/src/quiet_memo/llm/commit_summary.py +61 -0
- quiet_memo-0.2.0/src/quiet_memo/llm/summarizer.py +72 -0
- quiet_memo-0.2.0/src/quiet_memo/memory/__init__.py +0 -0
- quiet_memo-0.2.0/src/quiet_memo/memory/audit.py +16 -0
- quiet_memo-0.2.0/src/quiet_memo/memory/detect.py +54 -0
- quiet_memo-0.2.0/src/quiet_memo/memory/extractor.py +117 -0
- quiet_memo-0.2.0/src/quiet_memo/memory/models.py +81 -0
- quiet_memo-0.2.0/src/quiet_memo/memory/policy.py +21 -0
- quiet_memo-0.2.0/src/quiet_memo/memory/queue.py +53 -0
- quiet_memo-0.2.0/src/quiet_memo/memory/service.py +301 -0
- quiet_memo-0.2.0/src/quiet_memo/memory/store.py +277 -0
- quiet_memo-0.2.0/src/quiet_memo/retrieval/__init__.py +4 -0
- quiet_memo-0.2.0/src/quiet_memo/retrieval/base.py +21 -0
- quiet_memo-0.2.0/src/quiet_memo/retrieval/keyword.py +65 -0
- quiet_memo-0.2.0/tests/conftest.py +22 -0
- quiet_memo-0.2.0/tests/test_agent.py +181 -0
- quiet_memo-0.2.0/tests/test_cli.py +73 -0
- quiet_memo-0.2.0/tests/test_context.py +124 -0
- quiet_memo-0.2.0/tests/test_detect.py +47 -0
- quiet_memo-0.2.0/tests/test_extractor.py +35 -0
- quiet_memo-0.2.0/tests/test_policy.py +27 -0
- quiet_memo-0.2.0/tests/test_queue.py +42 -0
- quiet_memo-0.2.0/tests/test_search.py +40 -0
- quiet_memo-0.2.0/tests/test_server.py +64 -0
- quiet_memo-0.2.0/tests/test_service.py +117 -0
- quiet_memo-0.2.0/tests/test_session.py +71 -0
- quiet_memo-0.2.0/tests/test_setup.py +63 -0
- quiet_memo-0.2.0/tests/test_store.py +72 -0
- quiet_memo-0.2.0/uv.lock +3249 -0
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
name: publish
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
release:
|
|
5
|
+
types: [published]
|
|
6
|
+
workflow_dispatch:
|
|
7
|
+
|
|
8
|
+
permissions:
|
|
9
|
+
contents: read
|
|
10
|
+
|
|
11
|
+
jobs:
|
|
12
|
+
publish:
|
|
13
|
+
runs-on: ubuntu-latest
|
|
14
|
+
environment:
|
|
15
|
+
name: pypi
|
|
16
|
+
url: https://pypi.org/p/quiet-memo
|
|
17
|
+
permissions:
|
|
18
|
+
id-token: write
|
|
19
|
+
contents: read
|
|
20
|
+
steps:
|
|
21
|
+
- uses: actions/checkout@v4
|
|
22
|
+
|
|
23
|
+
- uses: astral-sh/setup-uv@v6
|
|
24
|
+
with:
|
|
25
|
+
python-version: "3.12"
|
|
26
|
+
|
|
27
|
+
- name: Build
|
|
28
|
+
run: uv build
|
|
29
|
+
|
|
30
|
+
- name: Publish to PyPI
|
|
31
|
+
uses: pypa/gh-action-pypi-publish@release/v1
|
|
@@ -0,0 +1,219 @@
|
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
|
+
Name: quiet-memo
|
|
3
|
+
Version: 0.2.0
|
|
4
|
+
Summary: Quiet-first personal memory: silent writes to local Markdown, powered by Git and MCP
|
|
5
|
+
Author: Shardkiht
|
|
6
|
+
License: MIT
|
|
7
|
+
Keywords: agent,git,markdown,mcp,memory
|
|
8
|
+
Requires-Python: >=3.12
|
|
9
|
+
Requires-Dist: fastmcp>=2.12
|
|
10
|
+
Requires-Dist: jieba>=0.42
|
|
11
|
+
Requires-Dist: keyring>=25.5
|
|
12
|
+
Requires-Dist: litellm>=1.60
|
|
13
|
+
Requires-Dist: openai>=1.60
|
|
14
|
+
Requires-Dist: prompt-toolkit>=3.0
|
|
15
|
+
Requires-Dist: pydantic>=2.9
|
|
16
|
+
Requires-Dist: pyyaml>=6.0
|
|
17
|
+
Requires-Dist: questionary>=2.0
|
|
18
|
+
Requires-Dist: rank-bm25>=0.2.2
|
|
19
|
+
Requires-Dist: rich>=13.9
|
|
20
|
+
Requires-Dist: tiktoken>=0.8
|
|
21
|
+
Requires-Dist: typer>=0.15
|
|
22
|
+
Description-Content-Type: text/markdown
|
|
23
|
+
|
|
24
|
+
# quiet-memo
|
|
25
|
+
|
|
26
|
+
安静的个人记忆库:从日常对话中抽取**你本人**的事实、偏好和计划,默认无感写入本地 Markdown;低置信、冲突或敏感内容才进入待确认队列。
|
|
27
|
+
|
|
28
|
+
- **本地优先**:Markdown 是唯一事实来源,Git 负责版本与审计。
|
|
29
|
+
- **默认静默**:可信内容直接落盘,不逐条打断;只在必要时提醒。
|
|
30
|
+
- **完整 Agent**:交互式对话、会话持久化、上下文压缩、工具循环。
|
|
31
|
+
- **多模型**:LiteLLM 统一接入 DeepSeek、硅基流动、百炼、OpenRouter、OpenAI 等。
|
|
32
|
+
- **可回滚**:每次提交自动生成中文总结,供本人审阅。
|
|
33
|
+
|
|
34
|
+
## 快速开始
|
|
35
|
+
|
|
36
|
+
需要 Python 3.12+、uv、git。
|
|
37
|
+
|
|
38
|
+
```bash
|
|
39
|
+
uv sync
|
|
40
|
+
|
|
41
|
+
# 交互式选择供应商,API Key 存入系统凭据(不写配置文件)
|
|
42
|
+
uv run qm setup
|
|
43
|
+
|
|
44
|
+
# 开始对话
|
|
45
|
+
uv run qm chat
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
`qm setup` 会写入 `quiet-memo.toml`,初始化记忆库,并可选做一次连接测试。Key 也可以用环境变量提供,环境变量优先于 keyring:
|
|
49
|
+
|
|
50
|
+
```bash
|
|
51
|
+
export DEEPSEEK_API_KEY=sk-...
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
手动配置示例:
|
|
55
|
+
|
|
56
|
+
```toml
|
|
57
|
+
vault = "C:/Users/you/.quiet-memo/vault"
|
|
58
|
+
confidence_threshold = 0.8
|
|
59
|
+
auto_push = false
|
|
60
|
+
|
|
61
|
+
[llm]
|
|
62
|
+
provider = "deepseek" # litellm provider:deepseek/siliconflow/dashscope/openrouter/openai/custom
|
|
63
|
+
base_url = "https://api.deepseek.com/v1"
|
|
64
|
+
model = "deepseek-chat"
|
|
65
|
+
api_key_env = "DEEPSEEK_API_KEY"
|
|
66
|
+
fallbacks = [] # 例如 ["openai/gpt-4o-mini"]
|
|
67
|
+
num_retries = 2
|
|
68
|
+
temperature = 0.1
|
|
69
|
+
timeout = 60.0
|
|
70
|
+
|
|
71
|
+
[chat]
|
|
72
|
+
context_window = 32000
|
|
73
|
+
reserve_output_tokens = 1500
|
|
74
|
+
keep_recent_tokens = 6000
|
|
75
|
+
compact_threshold = 0.8
|
|
76
|
+
max_tool_steps = 8
|
|
77
|
+
tool_timeout = 60.0
|
|
78
|
+
auto_recall = 3
|
|
79
|
+
extract_on_exit = true
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
Windows Git Bash 下如果中文输出乱码,加 `export PYTHONUTF8=1`。
|
|
83
|
+
|
|
84
|
+
## 对话 Agent
|
|
85
|
+
|
|
86
|
+
`qm chat` 是完整的多轮 Agent,不是一次性问答:
|
|
87
|
+
|
|
88
|
+
- 每轮自动检索相关长期记忆,注入上下文。
|
|
89
|
+
- 模型可调用 `recall / memorize / correct / forget / review_pending / list_topics`。
|
|
90
|
+
- 会话持久化为 `sessions/<id>.jsonl`,可 `qm chat --resume <id>` 恢复。
|
|
91
|
+
- 退出时抽取本轮尚未沉淀的用户陈述。
|
|
92
|
+
- 记忆条目记录 `session` 和 `message` 来源,可追溯到原始对话。
|
|
93
|
+
|
|
94
|
+
会话内命令:
|
|
95
|
+
|
|
96
|
+
| 命令 | 作用 |
|
|
97
|
+
| --- | --- |
|
|
98
|
+
| `/new` | 结束当前会话并新建 |
|
|
99
|
+
| `/sessions` | 列出历史会话 |
|
|
100
|
+
| `/resume <id>` | 恢复会话 |
|
|
101
|
+
| `/compact` | 立即压缩上下文 |
|
|
102
|
+
| `/summary` | 查看当前会话摘要 |
|
|
103
|
+
| `/status` | 会话、token、待确认状态 |
|
|
104
|
+
| `/rename <title>` | 重命名会话 |
|
|
105
|
+
| `/recall <query>` | 直接检索记忆 |
|
|
106
|
+
| `/remember <text>` | 直接抽取写入 |
|
|
107
|
+
| `/pending` | 查看待确认候选 |
|
|
108
|
+
| `/exit` | 退出 |
|
|
109
|
+
|
|
110
|
+
### 上下文压缩
|
|
111
|
+
|
|
112
|
+
当估算 token 超过预算的 `compact_threshold` 时:
|
|
113
|
+
|
|
114
|
+
1. 按 `keep_recent_tokens` 保留最近对话(以用户消息为边界)。
|
|
115
|
+
2. 把即将被淘汰的用户陈述送入记忆抽取,先沉淀到 Markdown。
|
|
116
|
+
3. 用 LLM 把旧对话压缩成摘要,持久化为 `summary` 记录。
|
|
117
|
+
4. 下一轮只带“摘要 + 最近原文 + 新消息”。
|
|
118
|
+
|
|
119
|
+
摘要记录包含 `covered_records`,恢复会话时不会重复发送已被压缩的历史。压缩失败时回退为抽取式摘要,不阻塞对话。
|
|
120
|
+
|
|
121
|
+
## 命令行
|
|
122
|
+
|
|
123
|
+
```bash
|
|
124
|
+
# 从一段对话抽取并静默写入
|
|
125
|
+
uv run qm memorize "我最近在找市区的房子,预算 4000 以内,不接受合租"
|
|
126
|
+
|
|
127
|
+
# 直接记录(不调用 LLM)
|
|
128
|
+
uv run qm note "喝咖啡偏好清淡、像茶的风味" --topic preferences --title 偏好
|
|
129
|
+
|
|
130
|
+
# 检索 / 查看待确认 / 确认或拒绝
|
|
131
|
+
uv run qm recall "市区"
|
|
132
|
+
uv run qm pending
|
|
133
|
+
uv run qm approve p-20261022-140312-a1b2
|
|
134
|
+
uv run qm reject p-20261022-140312-c3d4
|
|
135
|
+
|
|
136
|
+
# 会话与提交
|
|
137
|
+
uv run qm sessions
|
|
138
|
+
uv run qm status
|
|
139
|
+
uv run qm commit --no-push
|
|
140
|
+
uv run qm history
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
### 接入 MCP
|
|
144
|
+
|
|
145
|
+
```bash
|
|
146
|
+
uv run qm serve --vault ~/.quiet-memo/vault
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
MCP 客户端配置:
|
|
150
|
+
|
|
151
|
+
```json
|
|
152
|
+
{
|
|
153
|
+
"mcpServers": {
|
|
154
|
+
"quiet-memo": {
|
|
155
|
+
"command": "uv",
|
|
156
|
+
"args": ["run", "--directory", "C:/path/to/quiet-memo", "qm", "serve"]
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
暴露工具:`memorize`、`note`、`recall`、`review_pending`、`approve`、`reject`、`list_topics`、`history`、`commit`、`status`。
|
|
163
|
+
|
|
164
|
+
## 写入规则
|
|
165
|
+
|
|
166
|
+
| 情况 | 行为 |
|
|
167
|
+
| --- | --- |
|
|
168
|
+
| 置信度 ≥ 阈值、非敏感、无冲突 | 直接写入 |
|
|
169
|
+
| 置信度低于阈值 | 进入待确认队列 |
|
|
170
|
+
| 数值/否定/高相似度冲突 | 进入待确认队列,保留冲突条目 ID |
|
|
171
|
+
| 敏感内容 | 进入待确认队列 |
|
|
172
|
+
| 密码、验证码、API Key | 直接拦截,不落盘 |
|
|
173
|
+
| 文本完全重复 | 跳过 |
|
|
174
|
+
|
|
175
|
+
记忆按主题存为 `memories/<topic>.md`,条目带稳定 ID、状态、时间戳、来源和置信度:
|
|
176
|
+
|
|
177
|
+
```markdown
|
|
178
|
+
- <!-- id=mem-20261022-194250-8f1e; status=active --> 2026-10-22T19:42:50 — 在找市区的房子 (source: session:s-... , confidence: 0.95, session: s-..., message: m-...)
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
更正采用“新条目 + `supersedes`”,旧条目状态改为 `superseded`;删除是软删除 `deleted`,操作写入 `.quiet-memo/audit.jsonl`,Git 可回滚。
|
|
182
|
+
|
|
183
|
+
## 结构
|
|
184
|
+
|
|
185
|
+
```text
|
|
186
|
+
src/quiet_memo/
|
|
187
|
+
├── app/ cli.py, server.py # 入口:Typer CLI / FastMCP
|
|
188
|
+
├── agent/ runtime.py, context.py, tools.py, session.py
|
|
189
|
+
│ # Agent 循环、压缩、工具、会话
|
|
190
|
+
├── memory/ service.py, store.py, models.py, queue.py,
|
|
191
|
+
│ policy.py, detect.py, extractor.py, audit.py
|
|
192
|
+
│ # 记忆领域与写入裁决
|
|
193
|
+
├── retrieval/ base.py, keyword.py # Retriever 接口 + BM25
|
|
194
|
+
├── llm/ client.py, summarizer.py, commit_summary.py
|
|
195
|
+
│ # LiteLLM 适配、会话/提交摘要
|
|
196
|
+
└── infra/ config.py, providers.py, gitops.py, tokens.py
|
|
197
|
+
# 配置、供应商、Git、token
|
|
198
|
+
```
|
|
199
|
+
|
|
200
|
+
依赖方向:`app → agent → memory → retrieval → infra`,`memory/agent → llm → infra`。
|
|
201
|
+
|
|
202
|
+
- 检索当前是 jieba 分词 + rank-bm25;`Retriever` 是接口,后续可接向量/混合检索。
|
|
203
|
+
- token 预算用 tiktoken 估算,离线失败时回退字符估算。
|
|
204
|
+
- 会话压缩、记忆裁决、来源链是自研核心;模型适配交给 LiteLLM,供应商选择与密码输入用 questionary,Key 用 keyring。
|
|
205
|
+
|
|
206
|
+
## 测试
|
|
207
|
+
|
|
208
|
+
```bash
|
|
209
|
+
uv run pytest
|
|
210
|
+
```
|
|
211
|
+
|
|
212
|
+
当前 59 个测试,覆盖存储、队列、冲突检测、检索、抽取解析、会话、上下文压缩、Agent 工具循环、MCP 和 CLI。LLM 路径用固定抽取器和假模型测试;未接真实 API Key 时不声称已完成线上验证。
|
|
213
|
+
|
|
214
|
+
## Roadmap
|
|
215
|
+
|
|
216
|
+
- 20+ 条抽取/冲突/检索评测用例与指标
|
|
217
|
+
- 真实模型 dogfood 与成本统计
|
|
218
|
+
- 向量/混合检索实现(替换 `Retriever`)
|
|
219
|
+
- 每日定时提交守护进程
|
|
@@ -0,0 +1,196 @@
|
|
|
1
|
+
# quiet-memo
|
|
2
|
+
|
|
3
|
+
安静的个人记忆库:从日常对话中抽取**你本人**的事实、偏好和计划,默认无感写入本地 Markdown;低置信、冲突或敏感内容才进入待确认队列。
|
|
4
|
+
|
|
5
|
+
- **本地优先**:Markdown 是唯一事实来源,Git 负责版本与审计。
|
|
6
|
+
- **默认静默**:可信内容直接落盘,不逐条打断;只在必要时提醒。
|
|
7
|
+
- **完整 Agent**:交互式对话、会话持久化、上下文压缩、工具循环。
|
|
8
|
+
- **多模型**:LiteLLM 统一接入 DeepSeek、硅基流动、百炼、OpenRouter、OpenAI 等。
|
|
9
|
+
- **可回滚**:每次提交自动生成中文总结,供本人审阅。
|
|
10
|
+
|
|
11
|
+
## 快速开始
|
|
12
|
+
|
|
13
|
+
需要 Python 3.12+、uv、git。
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
uv sync
|
|
17
|
+
|
|
18
|
+
# 交互式选择供应商,API Key 存入系统凭据(不写配置文件)
|
|
19
|
+
uv run qm setup
|
|
20
|
+
|
|
21
|
+
# 开始对话
|
|
22
|
+
uv run qm chat
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
`qm setup` 会写入 `quiet-memo.toml`,初始化记忆库,并可选做一次连接测试。Key 也可以用环境变量提供,环境变量优先于 keyring:
|
|
26
|
+
|
|
27
|
+
```bash
|
|
28
|
+
export DEEPSEEK_API_KEY=sk-...
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
手动配置示例:
|
|
32
|
+
|
|
33
|
+
```toml
|
|
34
|
+
vault = "C:/Users/you/.quiet-memo/vault"
|
|
35
|
+
confidence_threshold = 0.8
|
|
36
|
+
auto_push = false
|
|
37
|
+
|
|
38
|
+
[llm]
|
|
39
|
+
provider = "deepseek" # litellm provider:deepseek/siliconflow/dashscope/openrouter/openai/custom
|
|
40
|
+
base_url = "https://api.deepseek.com/v1"
|
|
41
|
+
model = "deepseek-chat"
|
|
42
|
+
api_key_env = "DEEPSEEK_API_KEY"
|
|
43
|
+
fallbacks = [] # 例如 ["openai/gpt-4o-mini"]
|
|
44
|
+
num_retries = 2
|
|
45
|
+
temperature = 0.1
|
|
46
|
+
timeout = 60.0
|
|
47
|
+
|
|
48
|
+
[chat]
|
|
49
|
+
context_window = 32000
|
|
50
|
+
reserve_output_tokens = 1500
|
|
51
|
+
keep_recent_tokens = 6000
|
|
52
|
+
compact_threshold = 0.8
|
|
53
|
+
max_tool_steps = 8
|
|
54
|
+
tool_timeout = 60.0
|
|
55
|
+
auto_recall = 3
|
|
56
|
+
extract_on_exit = true
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
Windows Git Bash 下如果中文输出乱码,加 `export PYTHONUTF8=1`。
|
|
60
|
+
|
|
61
|
+
## 对话 Agent
|
|
62
|
+
|
|
63
|
+
`qm chat` 是完整的多轮 Agent,不是一次性问答:
|
|
64
|
+
|
|
65
|
+
- 每轮自动检索相关长期记忆,注入上下文。
|
|
66
|
+
- 模型可调用 `recall / memorize / correct / forget / review_pending / list_topics`。
|
|
67
|
+
- 会话持久化为 `sessions/<id>.jsonl`,可 `qm chat --resume <id>` 恢复。
|
|
68
|
+
- 退出时抽取本轮尚未沉淀的用户陈述。
|
|
69
|
+
- 记忆条目记录 `session` 和 `message` 来源,可追溯到原始对话。
|
|
70
|
+
|
|
71
|
+
会话内命令:
|
|
72
|
+
|
|
73
|
+
| 命令 | 作用 |
|
|
74
|
+
| --- | --- |
|
|
75
|
+
| `/new` | 结束当前会话并新建 |
|
|
76
|
+
| `/sessions` | 列出历史会话 |
|
|
77
|
+
| `/resume <id>` | 恢复会话 |
|
|
78
|
+
| `/compact` | 立即压缩上下文 |
|
|
79
|
+
| `/summary` | 查看当前会话摘要 |
|
|
80
|
+
| `/status` | 会话、token、待确认状态 |
|
|
81
|
+
| `/rename <title>` | 重命名会话 |
|
|
82
|
+
| `/recall <query>` | 直接检索记忆 |
|
|
83
|
+
| `/remember <text>` | 直接抽取写入 |
|
|
84
|
+
| `/pending` | 查看待确认候选 |
|
|
85
|
+
| `/exit` | 退出 |
|
|
86
|
+
|
|
87
|
+
### 上下文压缩
|
|
88
|
+
|
|
89
|
+
当估算 token 超过预算的 `compact_threshold` 时:
|
|
90
|
+
|
|
91
|
+
1. 按 `keep_recent_tokens` 保留最近对话(以用户消息为边界)。
|
|
92
|
+
2. 把即将被淘汰的用户陈述送入记忆抽取,先沉淀到 Markdown。
|
|
93
|
+
3. 用 LLM 把旧对话压缩成摘要,持久化为 `summary` 记录。
|
|
94
|
+
4. 下一轮只带“摘要 + 最近原文 + 新消息”。
|
|
95
|
+
|
|
96
|
+
摘要记录包含 `covered_records`,恢复会话时不会重复发送已被压缩的历史。压缩失败时回退为抽取式摘要,不阻塞对话。
|
|
97
|
+
|
|
98
|
+
## 命令行
|
|
99
|
+
|
|
100
|
+
```bash
|
|
101
|
+
# 从一段对话抽取并静默写入
|
|
102
|
+
uv run qm memorize "我最近在找市区的房子,预算 4000 以内,不接受合租"
|
|
103
|
+
|
|
104
|
+
# 直接记录(不调用 LLM)
|
|
105
|
+
uv run qm note "喝咖啡偏好清淡、像茶的风味" --topic preferences --title 偏好
|
|
106
|
+
|
|
107
|
+
# 检索 / 查看待确认 / 确认或拒绝
|
|
108
|
+
uv run qm recall "市区"
|
|
109
|
+
uv run qm pending
|
|
110
|
+
uv run qm approve p-20261022-140312-a1b2
|
|
111
|
+
uv run qm reject p-20261022-140312-c3d4
|
|
112
|
+
|
|
113
|
+
# 会话与提交
|
|
114
|
+
uv run qm sessions
|
|
115
|
+
uv run qm status
|
|
116
|
+
uv run qm commit --no-push
|
|
117
|
+
uv run qm history
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
### 接入 MCP
|
|
121
|
+
|
|
122
|
+
```bash
|
|
123
|
+
uv run qm serve --vault ~/.quiet-memo/vault
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
MCP 客户端配置:
|
|
127
|
+
|
|
128
|
+
```json
|
|
129
|
+
{
|
|
130
|
+
"mcpServers": {
|
|
131
|
+
"quiet-memo": {
|
|
132
|
+
"command": "uv",
|
|
133
|
+
"args": ["run", "--directory", "C:/path/to/quiet-memo", "qm", "serve"]
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
暴露工具:`memorize`、`note`、`recall`、`review_pending`、`approve`、`reject`、`list_topics`、`history`、`commit`、`status`。
|
|
140
|
+
|
|
141
|
+
## 写入规则
|
|
142
|
+
|
|
143
|
+
| 情况 | 行为 |
|
|
144
|
+
| --- | --- |
|
|
145
|
+
| 置信度 ≥ 阈值、非敏感、无冲突 | 直接写入 |
|
|
146
|
+
| 置信度低于阈值 | 进入待确认队列 |
|
|
147
|
+
| 数值/否定/高相似度冲突 | 进入待确认队列,保留冲突条目 ID |
|
|
148
|
+
| 敏感内容 | 进入待确认队列 |
|
|
149
|
+
| 密码、验证码、API Key | 直接拦截,不落盘 |
|
|
150
|
+
| 文本完全重复 | 跳过 |
|
|
151
|
+
|
|
152
|
+
记忆按主题存为 `memories/<topic>.md`,条目带稳定 ID、状态、时间戳、来源和置信度:
|
|
153
|
+
|
|
154
|
+
```markdown
|
|
155
|
+
- <!-- id=mem-20261022-194250-8f1e; status=active --> 2026-10-22T19:42:50 — 在找市区的房子 (source: session:s-... , confidence: 0.95, session: s-..., message: m-...)
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
更正采用“新条目 + `supersedes`”,旧条目状态改为 `superseded`;删除是软删除 `deleted`,操作写入 `.quiet-memo/audit.jsonl`,Git 可回滚。
|
|
159
|
+
|
|
160
|
+
## 结构
|
|
161
|
+
|
|
162
|
+
```text
|
|
163
|
+
src/quiet_memo/
|
|
164
|
+
├── app/ cli.py, server.py # 入口:Typer CLI / FastMCP
|
|
165
|
+
├── agent/ runtime.py, context.py, tools.py, session.py
|
|
166
|
+
│ # Agent 循环、压缩、工具、会话
|
|
167
|
+
├── memory/ service.py, store.py, models.py, queue.py,
|
|
168
|
+
│ policy.py, detect.py, extractor.py, audit.py
|
|
169
|
+
│ # 记忆领域与写入裁决
|
|
170
|
+
├── retrieval/ base.py, keyword.py # Retriever 接口 + BM25
|
|
171
|
+
├── llm/ client.py, summarizer.py, commit_summary.py
|
|
172
|
+
│ # LiteLLM 适配、会话/提交摘要
|
|
173
|
+
└── infra/ config.py, providers.py, gitops.py, tokens.py
|
|
174
|
+
# 配置、供应商、Git、token
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
依赖方向:`app → agent → memory → retrieval → infra`,`memory/agent → llm → infra`。
|
|
178
|
+
|
|
179
|
+
- 检索当前是 jieba 分词 + rank-bm25;`Retriever` 是接口,后续可接向量/混合检索。
|
|
180
|
+
- token 预算用 tiktoken 估算,离线失败时回退字符估算。
|
|
181
|
+
- 会话压缩、记忆裁决、来源链是自研核心;模型适配交给 LiteLLM,供应商选择与密码输入用 questionary,Key 用 keyring。
|
|
182
|
+
|
|
183
|
+
## 测试
|
|
184
|
+
|
|
185
|
+
```bash
|
|
186
|
+
uv run pytest
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
当前 59 个测试,覆盖存储、队列、冲突检测、检索、抽取解析、会话、上下文压缩、Agent 工具循环、MCP 和 CLI。LLM 路径用固定抽取器和假模型测试;未接真实 API Key 时不声称已完成线上验证。
|
|
190
|
+
|
|
191
|
+
## Roadmap
|
|
192
|
+
|
|
193
|
+
- 20+ 条抽取/冲突/检索评测用例与指标
|
|
194
|
+
- 真实模型 dogfood 与成本统计
|
|
195
|
+
- 向量/混合检索实现(替换 `Retriever`)
|
|
196
|
+
- 每日定时提交守护进程
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
# quiet-memo 配置示例
|
|
2
|
+
# 默认读取顺序:$QUIET_MEMO_CONFIG → ./quiet-memo.toml → ~/.config/quiet-memo/config.toml
|
|
3
|
+
|
|
4
|
+
# 记忆库位置(Markdown + Git)。环境变量 QUIET_MEMO_VAULT 可覆盖。
|
|
5
|
+
vault = "~/.quiet-memo/vault"
|
|
6
|
+
|
|
7
|
+
# 低于该置信度的候选不直接写入,进入待确认队列。
|
|
8
|
+
confidence_threshold = 0.8
|
|
9
|
+
|
|
10
|
+
# 提交后是否自动 push(需要已配置 remote)。
|
|
11
|
+
auto_push = false
|
|
12
|
+
|
|
13
|
+
[llm]
|
|
14
|
+
# 任意 OpenAI 兼容接口。DeepSeek 示例:
|
|
15
|
+
base_url = "https://api.deepseek.com/v1"
|
|
16
|
+
model = "deepseek-chat"
|
|
17
|
+
# 只写环境变量名,不写 Key 本身。
|
|
18
|
+
api_key_env = "DEEPSEEK_API_KEY"
|
|
19
|
+
temperature = 0.1
|
|
20
|
+
timeout = 60.0
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
[project]
|
|
2
|
+
name = "quiet-memo"
|
|
3
|
+
version = "0.2.0"
|
|
4
|
+
description = "Quiet-first personal memory: silent writes to local Markdown, powered by Git and MCP"
|
|
5
|
+
readme = "README.md"
|
|
6
|
+
requires-python = ">=3.12"
|
|
7
|
+
license = { text = "MIT" }
|
|
8
|
+
authors = [{ name = "Shardkiht" }]
|
|
9
|
+
keywords = ["memory", "mcp", "markdown", "git", "agent"]
|
|
10
|
+
dependencies = [
|
|
11
|
+
"fastmcp>=2.12",
|
|
12
|
+
"jieba>=0.42",
|
|
13
|
+
"keyring>=25.5",
|
|
14
|
+
"litellm>=1.60",
|
|
15
|
+
"openai>=1.60",
|
|
16
|
+
"prompt-toolkit>=3.0",
|
|
17
|
+
"pydantic>=2.9",
|
|
18
|
+
"pyyaml>=6.0",
|
|
19
|
+
"questionary>=2.0",
|
|
20
|
+
"rank-bm25>=0.2.2",
|
|
21
|
+
"rich>=13.9",
|
|
22
|
+
"tiktoken>=0.8",
|
|
23
|
+
"typer>=0.15",
|
|
24
|
+
]
|
|
25
|
+
|
|
26
|
+
[project.scripts]
|
|
27
|
+
qm = "quiet_memo.app.cli:app"
|
|
28
|
+
|
|
29
|
+
[dependency-groups]
|
|
30
|
+
dev = [
|
|
31
|
+
"pytest>=8.3",
|
|
32
|
+
"pytest-asyncio>=0.24",
|
|
33
|
+
]
|
|
34
|
+
|
|
35
|
+
[build-system]
|
|
36
|
+
requires = ["hatchling"]
|
|
37
|
+
build-backend = "hatchling.build"
|
|
38
|
+
|
|
39
|
+
[tool.hatch.build.targets.wheel]
|
|
40
|
+
packages = ["src/quiet_memo"]
|
|
41
|
+
|
|
42
|
+
[tool.pytest.ini_options]
|
|
43
|
+
testpaths = ["tests"]
|
|
44
|
+
asyncio_mode = "auto"
|
|
File without changes
|