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 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.2",
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",
@@ -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