dsh-nebula-mind 0.3.2 → 0.3.4
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.
- package/README.md +30 -2
- package/package.json +3 -2
- package/python/doctor.py +137 -0
- package/python/xin.py +1 -1
package/README.md
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
给 DeepSeek Harness(DSH)用的**外置长期记忆**插件:把「她记得你」这件事从上下文窗口里拿出来,放进硬盘。
|
|
6
6
|
|
|
7
7
|
- 换会话、压缩上下文、重启应用,记忆都还在;
|
|
8
|
-
- 每一轮开头自动带一段「我是谁 + 你是谁 +
|
|
8
|
+
- 每一轮开头自动带一段「我是谁 + 你是谁 + 最近的几件事」进提示词(除「极简模式」外的会话);
|
|
9
9
|
- 想翻旧事:四路召回(关键词 / 语义向量 / 图扩散 / 情绪共振),一条命令或一次点击;
|
|
10
10
|
- **遗忘不是删除**:记忆会按遗忘曲线慢慢「沉海」,越沉越难被想起,但永远不会消失,情绪刺激还能把它捞回来;
|
|
11
11
|
- 左侧栏有「记忆星图」和「心境」两个面板:星图可看可点,参数可调;心境是环表 + 数值卡 + 七维情绪条。
|
|
@@ -94,6 +94,34 @@ set NEBULA_HOME=E:\nebula # 换根目录,数据在 E:\nebula\memory
|
|
|
94
94
|
|
|
95
95
|
---
|
|
96
96
|
|
|
97
|
+
## 三步跑起来
|
|
98
|
+
|
|
99
|
+
```bash
|
|
100
|
+
# 1) 装(装完彻底退出 DSH 再重开)
|
|
101
|
+
dsh plugin --profile desktop add dsh-nebula-mind
|
|
102
|
+
|
|
103
|
+
# 2) 记下第一条(它同时会把这条算进向量索引)
|
|
104
|
+
python python/xin.py add "今天他提到周五要交画册"
|
|
105
|
+
|
|
106
|
+
# 3) 自检:哪几路检索开着、差什么、下一步干什么
|
|
107
|
+
python python/doctor.py
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
`doctor.py` 会逐项报告,并明确告诉你「缺了也能用」的部分。典型输出:
|
|
111
|
+
|
|
112
|
+
```
|
|
113
|
+
✅ Python 3.14.6
|
|
114
|
+
✅ 数据目录可写 ~/.dsh/nebula-mind/memory(2 本账)
|
|
115
|
+
✅ 记忆 事实 20 / 画像 4
|
|
116
|
+
⚠️ 语义检索 没有 torch/transformers(ModuleNotFoundError)
|
|
117
|
+
↳ 缺它也能用:关键词 / 图扩散 / 情绪共振照常
|
|
118
|
+
✅ 心情联动 ~/.dsh/feiyu-mood/state.json
|
|
119
|
+
✅ 星图生成 nebula-doctor.html(47 KB)
|
|
120
|
+
结论:5 项可用,1 项降级,0 项不可用
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
**没有 torch 也能用**:四路召回里只少「语义向量」这一路,另外三路(关键词、图扩散、情绪共振)照样跑;装了 torch 再跑一次 `python python/embed.py --build` 就有了。
|
|
124
|
+
|
|
97
125
|
## 记忆是怎么长出来的
|
|
98
126
|
|
|
99
127
|
| 账本 | 内容 | 谁写 |
|
|
@@ -167,7 +195,7 @@ python python/xin.py sink 事实 12 # 把某条往下沉一层
|
|
|
167
195
|
- 客户端请求要走**相对** `api/…`(桌面外壳是 `dsh-app://` 页面),写成 `/dsh-...` 会被拦。
|
|
168
196
|
|
|
169
197
|
**3. 提示词里没有【我是谁】那段**
|
|
170
|
-
-
|
|
198
|
+
- **除「极简模式」外都能注入**(标准模式、创造模式等);极简模式用的是另一套精简提示词,不吃动态段;
|
|
171
199
|
- 看日志 `logs/yu-mind-boot.log`,有 `digest ready len=…`(非 0)和 `mounted … via context` 才算挂上。
|
|
172
200
|
|
|
173
201
|
**4. 保存按钮没反应**
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dsh-nebula-mind",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.4",
|
|
4
4
|
"private": false,
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "lib/host.js",
|
|
@@ -46,7 +46,8 @@
|
|
|
46
46
|
"node": ">=20"
|
|
47
47
|
},
|
|
48
48
|
"scripts": {
|
|
49
|
-
"check": "node --check lib/host.js"
|
|
49
|
+
"check": "node --check lib/host.js",
|
|
50
|
+
"doctor": "python python/doctor.py"
|
|
50
51
|
},
|
|
51
52
|
"repository": {
|
|
52
53
|
"type": "git",
|
package/python/doctor.py
ADDED
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
# -*- coding: utf-8 -*-
|
|
3
|
+
"""自检:告诉用户这台机器上,哪些能力开了、哪些还差什么。
|
|
4
|
+
|
|
5
|
+
用法:
|
|
6
|
+
python python/doctor.py
|
|
7
|
+
NEBULA_PYTHON=... python python/doctor.py
|
|
8
|
+
|
|
9
|
+
输出三项:✅ 可用 / ⚠️ 降级可用 / ❌ 不可用(并给一句下一步)。
|
|
10
|
+
"""
|
|
11
|
+
import importlib
|
|
12
|
+
import json
|
|
13
|
+
import os
|
|
14
|
+
import subprocess
|
|
15
|
+
import sys
|
|
16
|
+
import tempfile
|
|
17
|
+
from pathlib import Path
|
|
18
|
+
|
|
19
|
+
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
|
|
20
|
+
import paths as P # noqa: E402
|
|
21
|
+
|
|
22
|
+
OK, WARN, BAD = "✅", "⚠️", "❌"
|
|
23
|
+
rows = []
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def add(level, name, detail, hint=""):
|
|
27
|
+
rows.append((level, name, detail, hint))
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def check_python():
|
|
31
|
+
v = sys.version_info
|
|
32
|
+
if v >= (3, 9):
|
|
33
|
+
add(OK, "Python", f"{v.major}.{v.minor}.{v.micro}")
|
|
34
|
+
else:
|
|
35
|
+
add(BAD, "Python", f"{v.major}.{v.minor}(需要 3.9+)", "装一个新的 Python 再跑")
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
def check_data_dir():
|
|
39
|
+
try:
|
|
40
|
+
P.MIND_DIR = Path(P.MIND)
|
|
41
|
+
P.MIND_DIR.mkdir(parents=True, exist_ok=True)
|
|
42
|
+
t = P.MIND_DIR / ".doctor-probe"
|
|
43
|
+
t.write_text("ok", encoding="utf-8")
|
|
44
|
+
t.unlink()
|
|
45
|
+
n = len(list(P.MIND_DIR.glob("*.jsonl")))
|
|
46
|
+
add(OK, "数据目录可写", f"{P.MIND}({n} 本账)")
|
|
47
|
+
except Exception as e:
|
|
48
|
+
add(BAD, "数据目录不可写", f"{P.MIND}: {e}", "检查权限,或用 NEBULA_HOME 换一个目录")
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
def check_accounts():
|
|
52
|
+
books = {
|
|
53
|
+
"notes.jsonl": "事实",
|
|
54
|
+
"profile.jsonl": "画像",
|
|
55
|
+
"promises.jsonl": "约定",
|
|
56
|
+
"thoughts.jsonl": "念想",
|
|
57
|
+
}
|
|
58
|
+
have = {name: 0 for name in books}
|
|
59
|
+
for f in books:
|
|
60
|
+
p = Path(P.MIND) / f
|
|
61
|
+
if p.exists():
|
|
62
|
+
have[f] = sum(1 for line in p.read_text(encoding="utf-8").splitlines() if line.strip())
|
|
63
|
+
total = sum(have.values())
|
|
64
|
+
if total == 0:
|
|
65
|
+
add(WARN, "记忆", "还是空的", '先记一条:python python/xin.py add "一句话"')
|
|
66
|
+
else:
|
|
67
|
+
detail = " / ".join(f"{books[k]} {v}" for k, v in have.items() if v)
|
|
68
|
+
add(OK, "记忆", detail)
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
def check_vector():
|
|
72
|
+
try:
|
|
73
|
+
importlib.import_module("torch")
|
|
74
|
+
importlib.import_module("transformers")
|
|
75
|
+
except Exception as e:
|
|
76
|
+
add(WARN, "语义检索", f"没有 torch/transformers({type(e).__name__})",
|
|
77
|
+
"缺它也能用:关键词 / 图扩散 / 情绪共振照常;要开语义检索请看 README「向量检索」")
|
|
78
|
+
return
|
|
79
|
+
model = Path(P.BGE_DIR)
|
|
80
|
+
if not model.exists():
|
|
81
|
+
add(WARN, "语义检索", f"模型没下({model})", "见 README「向量检索」的下载步骤,或设 NEBULA_BGE_DIR")
|
|
82
|
+
return
|
|
83
|
+
vec = Path(P.MIND) / "vectors.json"
|
|
84
|
+
if vec.exists():
|
|
85
|
+
try:
|
|
86
|
+
n = len(json.loads(vec.read_text(encoding="utf-8")).get("items", []))
|
|
87
|
+
add(OK, "语义检索", f"模型在 / 索引 {n} 条")
|
|
88
|
+
except Exception:
|
|
89
|
+
add(WARN, "语义检索", "索引坏了", "重建:python python/embed.py --build")
|
|
90
|
+
else:
|
|
91
|
+
add(WARN, "语义检索", "模型在,还没建索引", "python python/embed.py --build")
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
def check_mood():
|
|
95
|
+
p = Path(P.MOOD_STATE)
|
|
96
|
+
if p.exists():
|
|
97
|
+
add(OK, "心情联动", f"{p}")
|
|
98
|
+
else:
|
|
99
|
+
add(WARN, "心情联动", "没找到 dsh-feiyu-mood 的状态文件", "不装心情插件也行,心情按中性算")
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
def check_starmap():
|
|
103
|
+
try:
|
|
104
|
+
out = Path(tempfile.gettempdir()) / "nebula-doctor.html"
|
|
105
|
+
r = subprocess.run([sys.executable, os.path.join(os.path.dirname(os.path.abspath(__file__)), "star_map.py"),
|
|
106
|
+
"--out", str(out)], capture_output=True, text=True,
|
|
107
|
+
encoding="utf-8", errors="replace", timeout=60)
|
|
108
|
+
if r.returncode == 0 and out.exists():
|
|
109
|
+
add(OK, "星图生成", f"{out.name}({out.stat().st_size // 1024} KB)")
|
|
110
|
+
else:
|
|
111
|
+
add(BAD, "星图生成失败", (r.stderr or r.stdout or "").strip()[:120])
|
|
112
|
+
except Exception as e:
|
|
113
|
+
add(BAD, "星图生成失败", str(e)[:120])
|
|
114
|
+
|
|
115
|
+
|
|
116
|
+
def main():
|
|
117
|
+
check_python()
|
|
118
|
+
check_data_dir()
|
|
119
|
+
check_accounts()
|
|
120
|
+
check_vector()
|
|
121
|
+
check_mood()
|
|
122
|
+
check_starmap()
|
|
123
|
+
|
|
124
|
+
print("记忆星图 · 自检\n" + "=" * 46)
|
|
125
|
+
for level, name, detail, hint in rows:
|
|
126
|
+
print(f"{level} {name:<12} {detail}")
|
|
127
|
+
if hint:
|
|
128
|
+
print(f" ↳ {hint}")
|
|
129
|
+
bad = sum(1 for r in rows if r[0] == BAD)
|
|
130
|
+
warn = sum(1 for r in rows if r[0] == WARN)
|
|
131
|
+
print("=" * 46)
|
|
132
|
+
print(f"结论:{len(rows) - bad - warn} 项可用,{warn} 项降级,{bad} 项不可用")
|
|
133
|
+
return 1 if bad else 0
|
|
134
|
+
|
|
135
|
+
|
|
136
|
+
if __name__ == "__main__":
|
|
137
|
+
sys.exit(main())
|
package/python/xin.py
CHANGED
|
@@ -314,7 +314,7 @@ def tokenize(s):
|
|
|
314
314
|
return en + zh + bigrams
|
|
315
315
|
|
|
316
316
|
|
|
317
|
-
EMBED_PY = r"D:\indextts\.venv\Scripts\python.exe"
|
|
317
|
+
EMBED_PY = os.environ.get("NEBULA_EMBED_PYTHON") or r"D:\indextts\.venv\Scripts\python.exe"
|
|
318
318
|
EMBED_SH = os.path.join(os.path.dirname(os.path.abspath(__file__)), "embed.py")
|
|
319
319
|
VEC_JSON = os.path.join(ROOT, "vectors.json")
|
|
320
320
|
|