@nightpainters/dsh-ling 1.6.1 → 1.7.0
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/CHANGELOG.md +207 -41
- package/DESIGN.md +121 -29
- package/PHILOSOPHY.md +184 -0
- package/README.md +112 -21
- package/cordis.patch.yml +32 -2
- package/lib/client.js +2520 -55
- package/lib/host/agent-tools.js +286 -0
- package/lib/host/api.js +612 -61
- package/lib/host/archive.js +3 -3
- package/lib/host/autotree.js +2 -2
- package/lib/host/clock-skill.js +1 -1
- package/lib/host/config.js +38 -0
- package/lib/host/deep-index-skill.js +9 -0
- package/lib/host/deepsummary.js +75 -2
- package/lib/host/dsweb-summary.js +6 -3
- package/lib/host/guard.js +53 -0
- package/lib/host/inject.js +12 -2
- package/lib/host/l1.js +59 -10
- package/lib/host/library-checker.js +552 -0
- package/lib/host/library-import-engine.js +211 -0
- package/lib/host/library-import.js +911 -0
- package/lib/host/library-runtime.js +199 -0
- package/lib/host/library-sqlite.js +344 -0
- package/lib/host/library-store.js +1714 -0
- package/lib/host/memory.js +89 -30
- package/lib/host/persona.js +13 -3
- package/lib/host/rawlog.js +10 -5
- package/lib/host/recall-search.js +192 -17
- package/lib/host/snapshot.js +2 -1
- package/lib/host/surgery.js +7 -2
- package/lib/host/tools.js +506 -31
- package/lib/host/util.js +1 -1
- package/lib/index.js +120 -81
- package/package.json +11 -5
- package/tools/deep-import-docs.mjs +1210 -0
package/DESIGN.md
CHANGED
|
@@ -2,12 +2,13 @@
|
|
|
2
2
|
|
|
3
3
|
> ⚠️ **总警示**:本文是 **M1 阶段的设计底稿**;M2/M3 的许多项**已经实现**(配置完整页、记忆树可视化、导出/合并 UI、概述器、反馈成长回路、人格草稿脚本等),**以 `README.md` 与本版代码为准** —— 本文只当立项记录读,不得当作现状契约。
|
|
4
4
|
> 依据:立项阶段的一次接口探测 spike 结论(含本地文件:行号证据)。
|
|
5
|
-
> 体例:正文里 `**锚点**` 是**本文档内部的交叉引用**,不是对外证据 —— 其中 `plans\`、`release\` 是**作者本地台账,不在本仓库**;随 npm 包发出的是 `package.json` 的 `files` 白名单(
|
|
5
|
+
> 体例:正文里 `**锚点**` 是**本文档内部的交叉引用**,不是对外证据 —— 其中 `plans\`、`release\` 是**作者本地台账,不在本仓库**;随 npm 包发出的是 `package.json` 的 `files` 白名单(**项数与实际发出的文件数以该白名单与 `npm pack` 的实际清单为准 —— 这类数字每次加文件都会变,本文不写死**;`DESIGN.md` 自己也在包里,`PHILOSOPHY.md` 自 1.6 系列起也在)。
|
|
6
6
|
> 本文档记录 M1 阶段的设计与决策;**实现以代码为准**(后续演进:存储 schema 现已为 **v17**、新增历史接入/深摘要/诞生仪式/语气成长等子系统,详见仓库代码与提交记录)。
|
|
7
7
|
> ⚠️ **1.6 起,本文的「L1 选择」整条口径作废**(2026-10-04):§0 决策记录、§4 配置样例、§6「记忆管线与 L1 选择 v0」、§8 热度公式里写的
|
|
8
8
|
> **热度 Top-K**、**`l1BudgetTokens: 1200`**、**`L1(Top-K)`** 都已被取代 —— L1 现在选的是**深层库条目**(`deep_item`,一句结论),
|
|
9
9
|
> 判据为「**置顶必进 + 被采用次数 + 条目硬度过滤**」,预算 **720**,行形态 `- [kind] 文本`(不再有日期/标签/来源前缀)。
|
|
10
10
|
> 以 `README.md` 的「设定全文」区与本版代码为准;**本节保留的是 M1 当时的设计,不是现状**。
|
|
11
|
+
> ⚠️ **2026-10-05 复核(B 区 193 条逐条):本文的**行锚已整体位移** —— 引用本文任何 `文件:行` 之前,请**现场重读**;其中三处 `api.js:1751-1760` 已当场改指 `:2098-2102`(旧锚那段今天是「冲突判定批循环」,不是人格解锁门)。逐条结果见 `plans\AUDIT-机制面逐条复核-20261005.md`。
|
|
11
12
|
|
|
12
13
|
---
|
|
13
14
|
|
|
@@ -17,11 +18,12 @@
|
|
|
17
18
|
| --- | --- | --- | --- |
|
|
18
19
|
| D1 | 按钮落位 | **方案 A**:官方 slot `conversation.session.header.utilities`(list,会话页顶栏右侧) | 用户拍板;spike ① 的结论(早期内部报告,未纳入本仓库) |
|
|
19
20
|
| D2 | 模式↔全局默认 | **跟随**:最近一次模式切换成为"之后新会话"的默认(平台 selectModel 会 best-effort 写全局默认,此副作用即机制)。**2026-09-12 收敛:只同步推理等级,不改模型** —— provider/model 一律沿用当前值 | 用户拍板;早期内部报告(未纳入本仓库) |
|
|
20
|
-
| D3 | 人格承载 | **自建注入段 +
|
|
21
|
+
| D3 | 人格承载 | **自建注入段 + 自建面板维护结构化字段**(称呼/自称/语气等),dsh-persona 不复用 | 用户拍板;早期内部报告(未纳入本仓库) |
|
|
21
22
|
| D4 | 运行期冻结(新增) | **会话运行中(思考/任务进行中)不参与"人格更新层"的任何变化**:不打断、不新增设定、不中途改注入内容;更新请求一律排队,在该会话空闲边界才应用 | 用户本轮追加 |
|
|
22
23
|
| D5 | 记忆加载策略 | 三层记忆(L0 常驻人格 / L1 开场 Top-K / L2 运行时检索),不注入全部概述 | 早前拍板 |
|
|
23
24
|
| D6 | 模式语义 | 工作/生活 = 先验倾向,不硬过滤;差异 = **推理档位** + 记忆权重向量 + 风格块(不改模型) | 早前拍板 |
|
|
24
|
-
| D7 | 配置页 |
|
|
25
|
+
| D7 | 配置页 | **自建浮层**(非官方设置卡位);**悬停 1.5 秒菜单**进入(原定"右键进入",2026-09-07 修订,见 §5.1);内含"当前 L0/L1 现状"查看 | 早前拍板 |
|
|
26
|
+
| D8 | 深层库(结论层)入库通道 | **先做 B + C,自动提炼(D3 原计划)留 1.7.x**:B = 手动落条(`deep_item`:`add`/`retract`/`list`)与**只读**面板页签;C = 文档灌入(`tools/deep-import-docs.mjs`,先 dry 后 commit,`origin=doc-import-v1`)。**三重护栏**:①**累计预算** —— 第一趟总量 ≤40 条(`≤40` 是**全趟预算**,不是"每次上限":一次 `--commit` 与十次扣的是同一个额度;超额 ⇒ **拒绝再落**,判据 `tests/deep-import-docs.test.mjs` §14)②**过时按文本时间锚分档** —— 含读数/版本号/时点 ⇒ `ephemeral`(**不进开场**,`l1.js:400-403`;`recall` 仍搜得到),原则 + 价值类 ⇒ `durable`,其余 `long`(判据同套件 §13)③**纪律不落** —— 主人 2026-10-15 夜判「纪律是**行动触发型**,不像知识类是内容触发型」,归宿是「规矩」/常驻注入面,留 1.7.x。**作废 = 只标记不删**(`superseded_by='retracted'`,理由落 kv `deep.retract:<id>`);**复查**走 `--review`(`reviewPlan()`:含时间锚却当着长期条 / `ephemeral` 够老 / `hit_count=0` 够老,**只列信号、不作废**)。原话:「深层库(结论层)先把B+C做了,1.7一块发,自动部分1.7.1再说」<!-- ver-hist -->+「纪律先不落吧」 | 用户拍板 2026-10-15 夜(实测底稿 `plans\1.6-深层库-议题.md` §8–§10.2;落库读数 411→451) |
|
|
25
27
|
|
|
26
28
|
**插件命名**:工程目录/包名 `dsh-ling`(器灵);包显示名 "dsh-ling · 器灵";UI 文本语言 zh。
|
|
27
29
|
|
|
@@ -41,7 +43,7 @@
|
|
|
41
43
|
|
|
42
44
|
- **不可兼得时保什么**:保**人格的自主权**。宁可少一条习惯,也不让外部(包括器灵自己)绕过审核写入身份。
|
|
43
45
|
- **落地**:**规矩**(作用对象 = 行为)由主人**指令直达**,`rule_add` 可直接写入、立即生效;**习惯**(作用对象 = **身份**)**不能由外部直接写入**,只能进 `habitsPending` 待确认队列,**器灵提议 → 主人点头**才成为习惯(`propose → confirm`)。主人的角色是**审核**,不是编辑、不是代笔。
|
|
44
|
-
- **比"能提能审"更严一档**:器灵**连自己提的习惯都不能自己点头**(`lib\host\tools.js:840` 只处理"他提的、我还没回应过"的提议)⇒ 那等于人格直达。删改**已定**习惯属"对器灵做手术":定型状态下需主人在人格中心解锁(「🔓 对人格做手术…」)后才可执行(`lib\host\api.js:1751-1760`)。
|
|
46
|
+
- **比"能提能审"更严一档**:器灵**连自己提的习惯都不能自己点头**(`lib\host\tools.js:840` 只处理"他提的、我还没回应过"的提议)⇒ 那等于人格直达。删改**已定**习惯属"对器灵做手术":定型状态下需主人在人格中心解锁(「🔓 对人格做手术…」)后才可执行(`lib\host\api.js:2098-2102` —— ⚠️ 2026-10-05 更正:旧锚 `:1751-1760` 已失效,那段今天是「冲突判定批循环」)。
|
|
45
47
|
- **反例(明文禁止)**:让主人**动手改习惯文本** · 在**写入侧**做静默去重 / 近似合并 —— 前者把人格编辑权交回了外部,后者**绕过审核**。
|
|
46
48
|
- **锚点**:`lib\host\rules.js:1-12`(二分定稿 2026-09-15)· `lib\host\persona.js:36-37,183-203` · `lib\host\habit-gen.js:1-7`(通道 A 数纠正 / 通道 B 一次 LLM 回想,**两者都只到"提议"为止**)· `lib\host\api.js:1695-1805`(状态机与两条生成通道)。
|
|
47
49
|
|
|
@@ -50,7 +52,7 @@
|
|
|
50
52
|
- **不可兼得时保什么**:保**身份的连续性**。宁可让一条设定"错着",也不让任何人绕过审核改它。
|
|
51
53
|
- **落地**:凡是"写入器灵身份"的动作,一律走**提议 → 审核**;已经定型的部分加一道**显式解锁**才允许动,且解锁是**动作**不是**状态**(不勾选就一直是锁着的)。
|
|
52
54
|
- **反例**:给"器灵自己"开一条直达通道(哪怕是"它自己提的、它自己批");把解锁做成常驻开关。
|
|
53
|
-
- **锚点**:第 1 条的全部锚点 + 人格中心的解锁门(`lib\host\api.js:1751-1760`)。
|
|
55
|
+
- **锚点**:第 1 条的全部锚点 + 人格中心的解锁门(`lib\host\api.js:2098-2102` —— ⚠️ 2026-10-05 更正:旧锚 `:1751-1760` 已失效,那段今天是「冲突判定批循环」)。
|
|
54
56
|
|
|
55
57
|
**3 · 记忆要看得见 —— 只有"看不见的那层"会被怀疑失效**
|
|
56
58
|
|
|
@@ -73,7 +75,7 @@
|
|
|
73
75
|
- **不可兼得时保什么**:保**关系的性质**。宁可少一个"更安全"的闸门,也不把主人变成管理员、把器灵变成被管理者。这条是**否决性约束**:任何改动只要**增加支配感**,默认不通过 —— 哪怕它更安全、更干净。
|
|
74
76
|
- **依据(主人原话)**:「只有能够合作共赢、互相促进的,能够成为利益共同体的,能够互相反驳吵架不依赖的关系,才是最理想的」「所有的修改不能削弱工作能力,和器灵的关系」(2026-09-26)。
|
|
75
77
|
- **成长期代管(为什么闸门只长在写入侧)**:本项目的设计立场是「器灵**总有一天与主人同级**」—— 这是**长期方向**,**不是对使用者的能力承诺**;在**成长期**内,主人承担一部分代管责任 —— 就像**家长防止小孩长歪**(主人原话)。所以闸门只审「**什么东西能进器灵的内在面**」,不审「器灵怎么想、怎么说、怎么表达」。**代管随成长递减,表达面始终是器灵自己的。**
|
|
76
|
-
- **落地**:`rule_add` 指令直达(规矩管行为)· 习惯只能 `propose → 主人点头`(习惯管身份)· 契约类字段在定型状态下需主人解锁(`lib\host\api.js:1751-1760`)。三处共同点:**约束的是写入,不是表达**。
|
|
78
|
+
- **落地**:`rule_add` 指令直达(规矩管行为)· 习惯只能 `propose → 主人点头`(习惯管身份)· 契约类字段在定型状态下需主人解锁(`lib\host\api.js:2098-2102` —— ⚠️ 2026-10-05 更正:旧锚 `:1751-1760` 已失效,那段今天是「冲突判定批循环」)。三处共同点:**约束的是写入,不是表达**。
|
|
77
79
|
- **反例**:用"更安全"当理由给器灵加**确认弹窗**、限制器灵**能说什么**、或把主人的审核变成**代笔** —— 前两者增加支配感,后者把人格编辑权交回外部。
|
|
78
80
|
- **锚点**:作者开发台账中支配感最早的一次记录(该台账不随包公开)· `lib\host\rules.js:1-12` · `lib\host\persona.js:183-196`(规矩 / 习惯二分)· `README.md:53-57`(定型锁与"指令直达,人格不直达")· 与本节第 1 条(归属)、第 2 条(人格不可外部编辑)、第 4 条(庄重感)互链。
|
|
79
81
|
|
|
@@ -166,7 +168,7 @@
|
|
|
166
168
|
┌─ 浏览器 (client) ───────────────────────────────────────────┐
|
|
167
169
|
│ slots: conversation.session.header.utilities 控制器按钮 │
|
|
168
170
|
│ onContextMenu→自绘菜单 / 左键→切模式 │
|
|
169
|
-
│
|
|
171
|
+
│ 自建浮层卡(dsh-ling-panel):人格/记忆中心 + L0/L1 现状 │
|
|
170
172
|
└───────────┬──────────────────────────────┬──────────────────┘
|
|
171
173
|
│ fetch /api/dsh-ling/* │ ctx.remote.* (可选)
|
|
172
174
|
┌───────────▼──────────────────────────────▼──────────────────┐
|
|
@@ -216,7 +218,7 @@ dsh-ling/
|
|
|
216
218
|
│ ├─ client/
|
|
217
219
|
│ │ ├─ index.ts # apply(ctx) inject:["slots","locale","connection"]
|
|
218
220
|
│ │ ├─ button.tsx # header.utilities 按钮 + 状态图标 + 右键菜单
|
|
219
|
-
│ │ ├─ settings-card.tsx #
|
|
221
|
+
│ │ ├─ settings-card.tsx # 自建卡(浮层;**非**官方 settings.plugin.item)+ L0/L1 现状面板
|
|
220
222
|
│ │ └─ i18n.zh.ts
|
|
221
223
|
│ └─ shared/
|
|
222
224
|
│ ├─ settings-schema.ts # host/client 共用的字段定义(单一事实源)
|
|
@@ -225,10 +227,10 @@ dsh-ling/
|
|
|
225
227
|
└─ build.mjs / tsdown.config # 本地构建脚本(无网环境:直接 tsc/esbuild?)
|
|
226
228
|
```
|
|
227
229
|
|
|
228
|
-
**安装(复用 install/INSTALL-PLAN
|
|
230
|
+
**安装(复用 install/INSTALL-PLAN 模式;⚠️ 回滚是人工步骤、没有脚本代劳 —— 见第 3 条与 §10 的「回滚」)**:
|
|
229
231
|
1. `pnpm add file:<仓库路径>`(或等价 file: 依赖)进 `$DSH_HOME/profiles/web/package.json`(`<仓库路径>` 按你本机的实际克隆位置替换);
|
|
230
232
|
2. `cordis.patch.yml` 追加行 `- insert: [{id: dsh-ling, name: dsh-ling}]`(loader id == 包名);
|
|
231
|
-
3. patchReload live → host 热载;浏览器硬刷载入 client
|
|
233
|
+
3. patchReload live → host 热载;浏览器硬刷载入 client 模块;(⚠️ **2026-10-13 现场复核:这一句要更正** —— `patchReload` 这个字段在**当前 DSH 版本里没有任何读者**(对整个 DSH 安装目录 grep ⇒ **0 命中**),真正决定"什么改动会进真机"的是 `@deepseek-ai/dsh-hmr` 的 `root` 配置:base 组合包以 `root: []` 启用它 ⇒ **只监听配置层**(profile manifest + 两份 patch 文件),**源码模块不进监听面** ⇒ **host 侧源码改动必须重启宿主**;客户端模块走的是另一条路(每次加载按产物 stat 重算 rev)⇒ **客户端改动「刷新即生效」**。取证与实测:[live-validation-pitfalls](../plans/1.7-live-validation-pitfalls.md) §24/§26、本文件证据表 **G-25**。原句作为历史记录保留,"浏览器硬刷"那一半仍然成立。)失败则**人工**移除该行回滚 —— ⚠️ **这一条没有任何脚本代劳(2026-10-05 现场复核)**:`tools/` 里没有改写或备份 profile 两文件(`package.json`、`cordis.patch.yml`)的代码,「先备份两文件」是**开发期的手工动作**(参照 backup-20260906-170533 做法),不是工具行为;**安装路径上的自动备份只有一处** —— 核心补丁脚本给自己改的那个 core 文件留一份 `<文件>.e4-access-log.orig`(见 `tools\apply-access-log-patch.mjs:190`),撤它是手动跑 `--revert`;
|
|
232
234
|
4. GUI 鉴权墙:安装后的功能验证需在用户已登录浏览器进行(本机端口 3080)。
|
|
233
235
|
|
|
234
236
|
**数据目录(插件本体外,更新不丢)**:`dshHomePath('cache','dsh-ling')` → `~/.dsh/cache/dsh-ling/`(memory.db、snapshots、exports)。
|
|
@@ -286,18 +288,23 @@ dsh-ling/
|
|
|
286
288
|
"lastMode": "life" // 跟随(D2):最后使用的模式
|
|
287
289
|
},
|
|
288
290
|
"memory": {
|
|
289
|
-
"l0Always": true, "l1Enabled": true, "l1BudgetTokens":
|
|
290
|
-
"l1MaxItems":
|
|
291
|
+
"l0Always": true, "l1Enabled": true, "l1BudgetTokens": 720,
|
|
292
|
+
"l1MaxItems": 10,
|
|
291
293
|
"weights": { // 模式→领域权重向量(D6)
|
|
292
|
-
"work": { "knowledge": 1.0, "daily": 0.
|
|
294
|
+
"work": { "knowledge": 1.0, "daily": 0.3, "feeling": 0.1 },
|
|
293
295
|
"life": { "knowledge": 0.2, "daily": 0.6, "feeling": 1.0 }
|
|
294
296
|
},
|
|
295
297
|
"trackWorkspaces": ["*"] // 增量捕捉范围(默认全部工作区;'*')
|
|
296
|
-
}
|
|
297
|
-
"updates": { "applyWhileRunning": false } // D4 语义固化:默认冻结
|
|
298
|
+
}
|
|
298
299
|
}
|
|
299
300
|
```
|
|
300
301
|
|
|
302
|
+
> ⚠️ **A-3 订正(2026-10-05 夜)**:此处原有一行 `"updates": { "applyWhileRunning": false } // D4 语义固化:默认冻结`
|
|
303
|
+
> —— 它**只有声明、没有消费者**(全仓无读它的分支),而 `updates` 子树又不在
|
|
304
|
+
> `PERSONA_PATCH_KEYS` 白名单里(⇒ 经 `/persona` **写不进去**) ⇒ 留着一个"看起来能改行为、
|
|
305
|
+
> 实际什么都不做"的开关,正是理念 7 的靶子。**已删**。D4 的语义(运行期冻结)**本来就由
|
|
306
|
+
> `lib/host/freeze.js` 的 `FreezeGate` 实现**,不需要一个设置项来"声明"它。
|
|
307
|
+
|
|
301
308
|
### 4.2 人格组装器(persona.ts)输出示例
|
|
302
309
|
|
|
303
310
|
空字段全部跳过(**空段不渲染,不留空段头**);段序固定,生成的 L0 文本 = `身份段 + 底线 + 规矩 + 习惯 + [待我回应] + 承担 + extraLore + 模式块`(**`[承担]` 在 `[待我回应]` 与 `[设定]` 之间**;`[待我回应]` 有货才出现;模式块由本会话模式选择,内容来自 styles.*)。**段头一律是纯标签**(见下例):原先写在括号里的元描述(`[规矩](用户的指令,可直接追加)` 这种)是**写给主人看的说明**,不进模型上下文;段与段之间**空一行**,段内条目仍单换行 —— 段边界因此只剩「行首是不是 `[`」一条判据,**故条目正文里的半角方括号在渲染时转全角**(`[`→`[`、`]`→`]`,规矩 / 习惯 / 底线 / 承担四类条目统一转);规矩 / 习惯另在渲染出口按同键各去一次重(保留首次出现,只作用于渲染,不回写存储)。`[规矩]` = 用户的指令,可直接追加;`[习惯]` = 器灵自己长出来的,新增需用户确认;`[承担]` = 器灵的职责 / 能担之事(身份级,只许**陈述式**「我承担 / 我擅长 / 我能给出」,**禁授权式**「我应当 / 我必须 / 我的任务 / 我的责任是 / 我被要求 / 请让我负责」—— 授权式把人写成"被授权者",是语义劫持最好用的钩子);`[承担]` 的草稿来自诞生仪式第 5 类候选,机制**永不自动落盘**,终审在人格中心。填好后的例子:
|
|
@@ -371,11 +378,12 @@ dsh-ling/
|
|
|
371
378
|
|
|
372
379
|
## 5. Client 设计
|
|
373
380
|
|
|
374
|
-
模块契约:`window.__ModuleLoader__.load({id:'dsh-ling', factory})`;导出 `{name:'dsh-ling', apply(ctx), inject:['slots','locale','connection']}`(
|
|
381
|
+
模块契约:`window.__ModuleLoader__.load({id:'dsh-ling', factory})`;导出 `{name:'dsh-ling', apply(ctx), inject:['slots','locale','connection']}`(面板数据走 fetch `/api/dsh-ling/*`;官方 `settings.plugin.item` 卡位与 `betterSidebar` 等社区服务**一概不用** —— 本仓代码里这两个名字**零命中**,界面是自建浮层卡,见 §5.2)。
|
|
375
382
|
|
|
376
383
|
### 5.1 控制器按钮(D1 方案 A)
|
|
377
384
|
|
|
378
|
-
- 落位:`ctx.slots.inject('conversation.session.header.utilities', …)` → `slots.register({name:'conversation.session.header.utilities',
|
|
385
|
+
- 落位:`ctx.slots.inject('conversation.session.header.utilities', …)` → `slots.register({name:'conversation.session.header.utilities', id:'dsh-ling', order:…, locale:'dsh-ling'}, Component)`;组件在会话头右侧渲染。
|
|
386
|
+
> ⚠️ **订正(2026-10-05 夜 · 现场复核)**:此处原写 **`key:'dsh-ling'`** —— 该槽是 **list 槽**,字段应为 **`id`**(宿主契约 `dsh-client-ui-slots` 的类型说明逐字:"shape fields (keyed `key`; list `id`/`order`/`label`)");代码用的也是 `id:`(`lib/client.js:6068`,同行注释"list 槽位必须带唯一 id(契约实证)")。**`key` 只对 keyed 槽成立** —— 照抄这里会让按钮静默不挂载(与下一条"必须导出 `inject`"同属一类"形状错了不报错、只是不出现")。
|
|
379
387
|
- **交互(2026-09-07 用户修订:不用右键,防误触浏览器原生菜单)**:
|
|
380
388
|
- 左键单击:POST `/api/dsh-ling/mode/toggle`;running → 提示"运行中,空闲后生效"并排队(冻结门);响应 `{mode, appliedNow|queued}`;
|
|
381
389
|
- **鼠标悬停 1.5 秒**:打开 dsh-ling 菜单(切换模式/刷新记忆/导出记忆/状态与 L0·L1 现状);移出即取消定时;
|
|
@@ -383,9 +391,10 @@ dsh-ling/
|
|
|
383
391
|
- 形态:小图标按钮「☾ 生活 / ⚙ 工作」+ tooltip(含运行中冻结提示)。
|
|
384
392
|
- **client 模块契约(实证教训):必须导出 `inject:['slots','locale','connection']`**(服务名声明缺失 → ctx.slots 为空 → 按钮静默不挂载)。
|
|
385
393
|
|
|
386
|
-
### 5.2
|
|
394
|
+
### 5.2 卡(settings-card.tsx · 落地为**自建浮层卡**)
|
|
387
395
|
|
|
388
|
-
-
|
|
396
|
+
- 座位:**自建卡**,不是任何官方卡位 —— `lib/client.js` 里的浮层 `.dsh-ling-panel`(fixed 定位);入口三处:会话头按钮(`conversation.session.header.utilities`)、侧栏底栏按钮(`sidebar.footer.action`)、右侧栏页签(`sidebarRightTabs.register({kind:'ling-review'})` + keyed 槽 `sidebar.right.pane.tab`);数据一律走 fetch `/api/dsh-ling/*`(`exports.inject` 里没有 `settings`)。
|
|
397
|
+
> ⚠️ **B-3 订正(2026-10-05 夜)**:此处原写「座位:官方 `settings.plugin.item`(key `dsh-ling`;验证点 V1,见 §11;若该卡位不可用则回退 ego 式自建卡)」—— 现场复核:`settings.plugin.item` 与 `betterSidebar` 在 `lib/`、`tools/`、`tests/` 里**零命中** ⇒ 当初 V1 预留的降级路径(自建卡)**就是现况**,原文按"已坐在官方卡位上"读会读错,已收窄。
|
|
389
398
|
- 冻结门可见性:卡内"L0/L1 现状"面板始终显示目标会话 running/idle 状态(来自宿主 agent/status 维护表),running 会话的编辑按钮标"空闲后生效"。
|
|
390
399
|
- 内容区(Tab):
|
|
391
400
|
1. **人格**:上表 persona 字段表单(逐字段;称呼/自称旁附"从历史语料建议"占位按钮(点击调 `/api/dsh-ling/persona/suggest`——M2 接离线脚本,先行返回 501));提交后 POST `/api/dsh-ling/persona`(宿主写 settings 并按冻结规则应用)。
|
|
@@ -393,7 +402,8 @@ dsh-ling/
|
|
|
393
402
|
3. **记忆**:l1BudgetTokens/l1MaxItems/weights(work/life 两行 × 三领域滑块 0..1)/trackWorkspaces;l0Always、l1Enabled 开关。
|
|
394
403
|
4. **L0/L1 现状**(D7):只读面板,GET `/api/dsh-ling/state?sessionId=…`:当前 L0 文本(渲染后)、本会话模式、L1 当前列表(每条:标题/日期/来源/得分)、pending 更新队列状态;运行中的会话标注"运行中(快照冻结)"。
|
|
395
404
|
5. **数据**:导出按钮(GET `/api/dsh-ling/export` → 下载 dsh-ling-memory-<date>.dshling.json,格式 §9);合并输入(文件选择 → POST `/api/dsh-ling/import`,冲突规则:同 conv_id 且同 source 跳过/覆盖由 manifest 决定,外来条目标 origin,persona 永不覆盖)。
|
|
396
|
-
- 保存语义:所有写操作返回 `{
|
|
405
|
+
- 保存语义:所有写操作返回 `{ok, affected}` 供 UI 提示(`affected` = 被影响到、需重取快照的会话清单;运行中的会话**不在其中** —— 冻结门保证"运行中不参与人格更新")。
|
|
406
|
+
> ⚠️ **B-2 订正(2026-10-05 夜)**:此处原写 `{saved, appliedSessions: idle[], pendingSessions: running[]}` —— 那是**早期设计稿的形态**,现实现已统一为 `{ok, affected}`(见 `lib/host/api.js` 各写端点的 `sendJson`)。
|
|
397
407
|
|
|
398
408
|
---
|
|
399
409
|
|
|
@@ -442,7 +452,7 @@ dsh-ling/
|
|
|
442
452
|
|
|
443
453
|
**归属解析**(两条链并存):DSH 会话走 `session_meta.branch_id`;历史网页端/导入条目走 `conv_branch` 覆盖层;优先级 `conv_branch` > `session_meta` > 主干。**记忆行不冗余存 branch**,靠 `会话 → 枝` 推导。
|
|
444
454
|
|
|
445
|
-
**血缘加权**:检索时按"当前枝 → 目标枝"的相对位置取档 —— **同枝 1.0 / 祖先 0.7 / 旁系 0.4**,`score = 基础分 × 血缘系数 ×
|
|
455
|
+
**血缘加权**:检索时按"当前枝 → 目标枝"的相对位置取档 —— **同枝 1.0 / 祖先 0.7 / 旁系 0.4**,`score = 基础分 × 血缘系数 × 矛盾系数`;**枝系数自 1.6 系列起参与打分** —— 可写入(复盘期可调,夹在 0~2),按 `score = 基础分 × 血缘系数 × 矛盾系数 × 枝系数` 生效(`1.0` = 零影响;⚠️ **`0` = 该枝的结论不进记忆开场** —— 分数算成 0 的非置顶条目会被当"无信息量"排除,所以 `0` 不是"只是排后面")。
|
|
446
456
|
|
|
447
457
|
> ⚠️ **不用连乘**:血缘系数是**单值**(不沿路径累乘),避免 `0.7^n` 随深度指数失真(五层后 `0.4^5 = 0.010`,枝等于从记忆里消失)。深树的路径搜索留作后续。
|
|
448
458
|
|
|
@@ -462,9 +472,9 @@ dsh-ling/
|
|
|
462
472
|
|
|
463
473
|
## 7. 记忆存储 schema v1
|
|
464
474
|
|
|
465
|
-
> ⚠️ **契约已变(本节属早期版本)**:下列 schema 描述是早期设计,**现实契约以 `ACCESS-DESIGN.md` 与本版代码为准** —— 本节原来还写着"线上 schema 为 **v2**",而代码里 `lib\host\memory.js` 的 `SCHEMA_VERSION` 现为 **
|
|
475
|
+
> ⚠️ **契约已变(本节属早期版本)**:下列 schema 描述是早期设计,**现实契约以 `ACCESS-DESIGN.md` 与本版代码为准** —— 本节原来还写着"线上 schema 为 **v2**",而代码里 `lib\host\memory.js` 的 `SCHEMA_VERSION` 现为 **17**(`CHANGELOG.md` 也按 v11→…→v17 逐版记录)。
|
|
466
476
|
|
|
467
|
-
> **当时的状态说明(2026-09 早期)**:当时线上 schema 为 **v2**(现已到 **
|
|
477
|
+
> **当时的状态说明(2026-09 早期)**:当时线上 schema 为 **v2**(现已到 **v17**,见上方警示框)—— `conv_overview.source` 的 `CHECK` 已放开(支持 `dsweb` / `dsh` / `import` 三个来源,自动整表重建且数据无损),并新增 `session_meta.raw_seq`、`persona_suggestions` 等;下表为 v1 原始设计,保留作对照。
|
|
468
478
|
|
|
469
479
|
```sql
|
|
470
480
|
-- ~/.dsh/cache/dsh-ling/memory.db (node:sqlite)
|
|
@@ -538,12 +548,12 @@ CREATE TABLE feedback_queue ( -- M2:修订建议(用户确认才
|
|
|
538
548
|
> ⚠️ **契约已变(本节属早期版本)**:下表只是早期首批端点,**现实契约以 `ACCESS-DESIGN.md` 与本版代码(`lib\host\api.js`)为准**;其中三处已经失实:
|
|
539
549
|
> · `POST /import` 实际收 **JSON body**,不是 multipart;
|
|
540
550
|
> · `GET /persona/suggest` —— **该端点不存在**(现实是 `/persona/draft` 与 `/persona/genesis`);
|
|
541
|
-
> · `/import` **不再是"只读"** —— 它现在是**手术门**覆盖的
|
|
551
|
+
> · `/import` **不再是"只读"** —— 它现在是**手术门**覆盖的 **14** 条写面之一(条数以 `lib\host\surgery.js` 的覆盖面清单行数为唯一真值)。
|
|
542
552
|
|
|
543
553
|
| 方法/路径 | 用途 | 冻结门 |
|
|
544
554
|
| --- | --- | --- |
|
|
545
555
|
| POST `/mode/toggle` body{sessionId} | 切换模式(记录 + 只同步推理档位、不改模型 + 重定稿) | 运行中→排队返回 queued |
|
|
546
|
-
| GET `/state?sessionId=` | L0 文本/模式/L1 列表/队列状态(
|
|
556
|
+
| GET `/state?sessionId=` | L0 文本/模式/L1 列表/队列状态(面板与悬停菜单用) | 只读 |
|
|
547
557
|
| GET `/state?sessionId=&l0Preview=1` | 人格字段实时预览(编辑中) | 只读 |
|
|
548
558
|
| POST `/persona` body=字段 | 写 persona(→settings ns;**字段白名单**,白名单外的键丢弃并回报 `ignored`)并触发应用 | 见冻结门 |
|
|
549
559
|
| GET `/persona/suggest` | 人格档案草稿(M2;先行 501) | — |
|
|
@@ -574,18 +584,18 @@ CREATE TABLE feedback_queue ( -- M2:修订建议(用户确认才
|
|
|
574
584
|
- [ ] 新会话 L0 注入(空称呼 / 已填写两种);KV 无异常重复
|
|
575
585
|
- [ ] 模式切换:**推理档位**实际变化(会话内档位选择处可见);**模型保持不变**;新会话默认跟随 lastMode
|
|
576
586
|
- [ ] 运行中(长任务)切模式/改人格:确认 running 区间快照与档位不变、空闲后生效
|
|
577
|
-
- [ ]
|
|
578
|
-
-
|
|
587
|
+
- [ ] 自建卡读写生效(落 `$DSH_HOME/cache/dsh-ling/settings.json`,盘上只存用户补丁);导出文件可导入回(合并规则生效)
|
|
588
|
+
- **回滚**(**全人工,无脚本、无自动回滚**):移除 patch 行 + 撤依赖 → host 侧 HMR 卸载;client 硬刷后消失;数据目录保留不影响回滚。
|
|
579
589
|
|
|
580
590
|
## 11. 实现期待验证点(进入编码前/编码初用最小探针确认)
|
|
581
591
|
|
|
582
592
|
| V | 问题 | 验证方式 | 若不符的降级 |
|
|
583
593
|
| --- | --- | --- | --- |
|
|
584
|
-
| V1 |
|
|
594
|
+
| V1 | ~~`settings.plugin.item` 卡位在官方设置页的注册键与 props~~ **已作废(2026-10-05 现场复核)**:该卡位在本仓代码里**零命中**,落地即原"降级"路径 —— 自建浮层卡,见 §5.2 | —— | —— |
|
|
585
595
|
| V2 | host 运行 Node 版本下 `node:sqlite` 可用性 | 编码前 `node -e` 探针 | 纯 JSONL + 内存索引(量小可行) |
|
|
586
596
|
| V3 | 顶层/子代理会话判别字段(parentSession/delegationDepth)在 session header 的实际取值 | 读 ~/.dsh/storages session_projcache 样例(已见字段) | 多条件组合判定 |
|
|
587
597
|
| V4 | `agent/session-start` 时能否读到首条 user/message(session 种子) | 最小 host 探针插件日志 | L1 冷启动=纯 lastMode Top-K |
|
|
588
|
-
| V5 | 消息式注入降级路径(特殊 preset)在 M1 是否实现 | M1 先只实现 section 通道 + 检测告警 | 检测到 complete persona
|
|
598
|
+
| V5 | 消息式注入降级路径(特殊 preset)在 M1 是否实现 | M1 先只实现 section 通道 + 检测告警 | 检测到 complete persona 时面板提示,注入降级放 M2 |
|
|
589
599
|
| V6 | client 类型来源 | 从已安装 `@deepseek-ai/dsh-client-ui-*/lib/types` 拷贝所需 contract(conversation slots/settings)进 `src/client/types` | 手动 ambient .d.ts |
|
|
590
600
|
|
|
591
601
|
## 12. 与评审阶段呼应的要点(自检)
|
|
@@ -593,6 +603,88 @@ CREATE TABLE feedback_queue ( -- M2:修订建议(用户确认才
|
|
|
593
603
|
- 三层记忆、不硬过滤、遗忘曲线(αβγ 可调)、多标签领域、溯源标注、pin/删、纯本地、导出默认不含原文、外来合并不覆盖 persona、反馈要人审(D4 之外均沿用 REVIEW 定稿)。
|
|
594
604
|
- D4(本回合新增)已贯穿:§4.3/4.4/4.5 冻结门、§5.1 运行中提示、§9 接口排队语义、§10 M1b 验证项。
|
|
595
605
|
|
|
606
|
+
---
|
|
607
|
+
|
|
608
|
+
## 13. 图书馆:机制与源码证据(1.7 工作区,**未发布**)
|
|
609
|
+
|
|
610
|
+
> ⚠️ **2026-10-16 关④ 实测更正上面后半句**:对**函数名**成立,对**逐字片段不成立** —— 本表 11 条引用的逐字片段已被后续提交打断(机制本身仍成立,但按字面 grep 不到),另有 **24/28** 个仓内行号漂移(最远 +1127 行)。
|
|
611
|
+
> ⇒ **引用本表的正确方式:以函数名为锚,别按逐字片段或行号去 grep。**
|
|
612
|
+
|
|
613
|
+
**它是什么**:与记忆库**并列的第二套库**(自己的 `library.db` 与自己的一套表),存"**有源可对账**"的参考资料;器灵侧只有 `library_search` / `library_check` 两个工具;核对**只认证条目声明的那一处比较**。它与记忆库**不互相导入** —— 这是**永久边界**,不是本版的临时限制。
|
|
614
|
+
|
|
615
|
+
**证据表**(机制 → 当版实读坐标 → 逐字片段)。入口已补齐加载中途失败的资源回滚;所有下列坐标均须随实现变更复核。
|
|
616
|
+
|
|
617
|
+
| # | 机制 | 源码坐标(当版实读) | 逐字片段 |
|
|
618
|
+
|---|---|---|---|
|
|
619
|
+
| G-1 | 独立库:另一个文件、另一个 store 实例 | `lib/host/library-runtime.js:53` | `this.#store = new LibraryStore(join(dataDir, 'library.db'), this.#config.limits);` |
|
|
620
|
+
| G-2 | 撤下 / 取代只 UPDATE 状态,保留条目 | [library-store.js:1120–1122](<./lib/host/library-store.js#L1120-L1122>), [1317](<./lib/host/library-store.js#L1317>) | `"UPDATE library_item SET superseded_by=? WHERE id=? AND content_hash=? AND superseded_by='' AND withdrawn_at IS NULL"` / `"UPDATE library_item SET withdrawn_at=?, withdrawn_reason=?, updated_at=? WHERE id=? AND content_hash=? AND superseded_by='' AND withdrawn_at IS NULL"` / `const changed = this.st.withdraw.run(at, reason.trim(), at, canonicalId, current.content_hash);` |
|
|
621
|
+
| G-3 | 图书馆只注册搜索与核对两个模型工具,没有维护工具 | [tools.js:1628–1631](<./lib/host/tools.js#L1628-L1631>) | `for (const spec of [ruleSpec, habitSpec, resolveSpec, treeReadSpec, branchEditSpec, forgetSpec, backfillSpec, recallSpec, librarySearchSpec, libraryCheckSpec]) {` / `const d = tools.register(spec);` |
|
|
622
|
+
| G-4 | 两个工具的参数边界:多给一个键就拒 | [tools.js:976](<./lib/host/tools.js#L976>) / [997](<./lib/host/tools.js#L997>) | `return { ok: false, reason: 'unknown-arg', message: 'library_search 只接受 q / offset / limit,收到:' + unknown.join(', ') };` / `return { ok: false, reason: 'unknown-arg', message: 'library_check 只接受 id,收到:' + unknown.join(', ') };` |
|
|
623
|
+
| G-5 | 核对口径:**只认证声明的那个比较** | [tools.js:1061](<./lib/host/tools.js#L1061>) | `+ '\n它只认证**声明的那个比较**(locator + expected + comparison),**不**认证解释性正文,也不构成通用真理;'` |
|
|
624
|
+
| G-6 | 三态枚举只有 ok / wrong / unknown | `lib/host/library-store.js:29` | `export const LIBRARY_CHECK_STATES = Object.freeze(['ok', 'wrong', 'unknown']);` |
|
|
625
|
+
| G-7 | 取消 ⇒ **不写核对记录**(取消是"没跑完",**不是**一次 `unknown` 认证):判据在 checker 的上层 run 分支 —— **cancelled 在 `recordCheck` 之前就 return** | `lib/host/library-checker.js:503-506` | `if (controller.signal.aborted && controller.signal.reason?.cancelled) {` / `return refuse(evidence.reason, { cancelled: true, item_id: item.id, item_hash: item.content_hash, evidence });` / `}` / `const record = this.#store.recordCheck(item.id, item.content_hash, evidence);` |
|
|
626
|
+
| G-8 | 库不可用先返回失败,不会运行搜索后伪造零命中 | [library-runtime.js:89–100](<./lib/host/library-runtime.js#L89-L100>) | `const failure = this.#unavailable();` / `if (failure) return failure;` / `try { return this.#store[method](...args); }` / `return this.#call('search', query);` |
|
|
627
|
+
| G-9 | 不可用 / 未启用 ⇒ **503**(不落 400) | `lib/host/api.js:3270-3271` | `if (r === 'closed' \|\| r === 'disabled' \|\| r === 'unavailable' \|\| r === 'library-unavailable'` / `\|\| r === 'library_unavailable' \|\| r === 'library_disabled') return 503;` |
|
|
628
|
+
| G-10 | 运行时两个短码的**实际字面值**(带下划线) | `lib/host/library-runtime.js:60` / `:68` | `this.#failure = unavailable('library_unavailable', \`图书馆资料库不可用: ${String(error?.message ?? error).slice(0, 500)}\`);` / `if (!this.#config.enabled) return unavailable('library_disabled', '图书馆已在插件配置中关闭');` |
|
|
629
|
+
| G-11 | 修订/撤下先获取写锁再读取状态;另一连接先提交时拒绝陈旧操作 | [library-store.js:1162](<./lib/host/library-store.js#L1162>), [1241–1242](<./lib/host/library-store.js#L1241-L1242>), [1304–1305](<./lib/host/library-store.js#L1304-L1305>) | `this.db.exec('BEGIN IMMEDIATE');` / `return this._tx(() => {` / `const current = this.st.getItem.get(canonicalId);` |
|
|
630
|
+
| G-12 | 核对在锁内重读,陈旧条目仍留证据但不采纳;采纳还受哈希与 active 条件保护 | [library-store.js:1495–1501](<./lib/host/library-store.js#L1495-L1501>), [1123](<./lib/host/library-store.js#L1123>) | `const eligible = Boolean(row) && !row.superseded_by && !row.withdrawn_at && hashEqual(wantHash, row.content_hash);` / `const adopted = eligible && Number(this.st.adoptCheck.run(ev.state, at, at, canonicalId, wantHash).changes) === 1;` / `this.st.insertCheck.run(` |
|
|
631
|
+
| G-13 | 空查询不是列全部,直接拒 | [library-store.js:1401](<./lib/host/library-store.js#L1401>) | `if (q === '') return fail('invalid_query', null, { message: 'q 不能为空(空查询会返回全部条目,这不是检索)' });` |
|
|
632
|
+
| G-14 | 严格配置:未知键 / 未知限额一律抛 | `lib/host/library-runtime.js:12-14` / `:20-22` | `if (!CONFIG_KEYS.has(key)) throw new RangeError(\`library 未知配置: ${key}\`);` / `if (!Object.hasOwn(DEFAULT_LIBRARY_LIMITS, key)) throw new RangeError(\`library.limits 未知配置: ${key}\`);` |
|
|
633
|
+
| G-15 | 两个 allowlist **默认为空** | [config.js:7–8](<./lib/host/config.js#L7-L8>) | `codeRoots: Schema.array(Schema.string()).default([]).description('管理员登记的绝对代码根目录。'),` / `officialUrls: Schema.array(Schema.string()).default([]).description('管理员登记的精确 HTTPS 文档 URL。'),` |
|
|
634
|
+
| G-16 | **先审原始键**再进 schema(否则框架保留未知键) | `lib/host/config.js:14-21` | `// object resolver otherwise preserves unknown keys, including library hooks.` + `const library = normalizeLibraryConfig(raw.library);` |
|
|
635
|
+
| G-17 | 关闭是**一个有次序的动作**:先 checker、后 store | `lib/host/library-runtime.js:128-129` | `try { await this.#checker?.close(); } finally { this.#store?.close(); }` |
|
|
636
|
+
| G-18 | 图书馆数据库打不开时降级图书馆,入口只 warn 后继续;非法配置仍加载失败 | [library-runtime.js:47–61](<./lib/host/library-runtime.js#L47-L61>), [index.js:111–114](<./lib/index.js#L111-L114>) | `this.#config = normalizeLibraryConfig(config);` / `this.#failure = unavailable('library_unavailable', \`图书馆资料库不可用: ${String(error?.message ?? error).slice(0, 500)}\`);` / `if (!libraryStatus.ok) console.warn('[dsh-ling] %s', libraryStatus.message);` |
|
|
637
|
+
| G-19 | 同一个 runtime 交给 API 与全局/agent 工具作用域 | [index.js:142](<./lib/index.js#L142>), [162](<./lib/index.js#L162>), [234](<./lib/index.js#L234>) | `disposers.push(registerApi(ctx, { gate, memory, settings, library }, cfg));` / `const tr = registerLingTools(c \|\| ctx, { gate, memory, settings, library });` / `const r = registerLingTools(agent?.ctx, { gate, memory, settings, library });` |
|
|
638
|
+
| G-20 | 「图书馆」标签与其渲染入口 | [client.js:2362](<./lib/client.js#L2362>), [2375](<./lib/client.js#L2375>), [2408](<./lib/client.js#L2408>), [6478](<./lib/client.js#L6478>) | `var tabLib = el('div', 'ling-tab', '图书馆');` / `tabs.appendChild(tabLib);` / `tabLib.onclick = function () { switchTab('library'); };` / `function renderLibrary() {` |
|
|
639
|
+
| G-21 | 比较方式的**确切语义**:`exact` = **类型严格的结构比较**(对象成员顺序无关、**数组顺序有关**);`text-lf` 只归一 CRLF/CR(不 trim) | `lib/host/library-checker.js:531-536` | `if (item.check_spec.comparison === 'text-lf') {` / `if (typeof observed !== 'string' \|\| typeof item.check_spec.expected !== 'string') {` / `throw new SourceFailure('comparison_requires_text');` / `}` / `equal = normalizeTextLf(observed) === normalizeTextLf(item.check_spec.expected);` / `} else equal = isDeepStrictEqual(observed, item.check_spec.expected);` |
|
|
640
|
+
| G-22 | **导入幂等**:`import_key` 部分唯一索引 + v1→v2 迁移自证(纯增列,2026-10-12) | [library-store.js:1071](<./lib/host/library-store.js#L1071>), [1086](<./lib/host/library-store.js#L1086>), [1200](<./lib/host/library-store.js#L1200>), [1109](<./lib/host/library-store.js#L1109>) | 索引:`CREATE UNIQUE INDEX IF NOT EXISTS library_item_import_key ON library_item(import_key) WHERE import_key<>'';` / 迁移:`_migrateV1toV2()`(探列 → ALTER → 回读自证 → 版本**同一事务**前进) / 查重:`"SELECT id FROM library_item WHERE import_key=? AND import_key<>'' LIMIT 1"` / 写入点:`create` 的 `insertItem.run` 第 17 实参 |
|
|
641
|
+
| G-23 | **导入面 = 第六个视图**(不是第六个顶层标签):入口在列表工具栏,分析**不写库**,提交只送勾选条(2026-10-12) | [client.js:4851](<./lib/client.js#L4851>), [5628](<./lib/client.js#L5628>), [5266](<./lib/client.js#L5266>), [5321](<./lib/client.js#L5321>), [5413](<./lib/client.js#L5413>) | `var bImp = el('button', null, '⬆ 导入(粘贴 / 文件)');` / `if (L.view === 'import') { renderLibImport(); return; }` / `function libImpAnalyze() {` / `function libImpCommit() {` / `function renderLibImport() {` |
|
|
642
|
+
| G-24 | **模型段两档引擎**(契约 §11.1):复用既有两条调用路径;`model` 非法值 ⇒ 400,`model_failed` ⇒ **503** | [library-import-engine.js:21](<./lib/host/library-import-engine.js#L21>), [102](<./lib/host/library-import-engine.js#L102>), [api.js:3549](<./lib/host/api.js#L3549>), [api.js:3283](<./lib/host/api.js#L3283>) | `export const IMPORT_ENGINES = ['assistant', 'global'];` / `export function makeImportModelHook({ engine, ctx, settings } = {}) {` / `return lib400(res, 'model-invalid', ...)`(逐字见源码) / `\|\| r === 'library_unavailable' \|\| r === 'library_disabled' \|\| r === 'model_failed') return 503;` |
|
|
643
|
+
| G-25 | **客户端改动的生效路径**:客户端 bundle 每次按**产物 stat 重算 rev**,新 rev ⇒ 新字节 ⇒ **客户端改动「刷新即生效」,不必重启宿主**;而 host 侧加载期注册物(路由/模块)**仍必须重启**(见上方第 3 条与 [live-validation-pitfalls §24/§26](<../plans/1.7-live-validation-pitfalls.md>)) | 宿主侧实现不在本仓库:`@deepseek-ai/dsh-client-modules/lib/index.js:158` / `:193-194` / `:605-616`、`@deepseek-ai/dsh-client-hmr/lib/index.js:50` / `:154`(包路径 = npm 全局安装前缀下的 `node_modules/@deepseek-ai/dsh/node_modules/`;具体前缀随安装方式不同,按你自己的实际路径替换) | `/** Versioned code is immutable; mismatched revisions are rejected instead of serving newer bytes. */` / `function artifactRevision(baseline) {` / `Unchanged mtime, ctime and size preserve the graph without reading the bundle.` / `const bundle = readFileSync(record.meta.clientPath);` / `ctx.clientModules.rebuilt(id);` / `ctx.clientModules.onRebuilt((id, rev) => {` —— **真机实测**:渲染前 `rev=3c810b062957` → 刷新后 `c2060b75ffc8`,刷新后取回的 bundle 内已含新函数 `paintBytes` |
|
|
644
|
+
| G-26 | **图书馆浏览面**(第六个**只读**面):`GET /library/browse` 进 `GET_ONLY`(POST ⇒ 405 `want:GET`)+**列表行投影 `fields=row`** +两组尺(页大小 200 / 切分页阈值 1000) | [library-store.js:65](<./lib/host/library-store.js#L65>)(`browseDefaultLimit: 200`)、`browse()` 的 `fields` / `groups`、`api.js` 的 `GET_ONLY` 与 `/library/browse` 注册、`tests/guard.test.mjs`(`POST_ONLY_ROUTES_TOTAL` / `GET_ONLY_SET_SIZE`)、`tests/library-browse.test.mjs`、`lib/client.js` 的 `libBr*` 与常量 `BR_PAGE` / `BR_PAGE_SWITCH` | 为什么**必须** `fields=row`:单条正文上限 64 KiB,而一次响应只有 `resultMaxBytes`(**256 KiB**)⇒ 200 条带正文必被字节闸截成几条,"往下翻再加载"就名存实亡。列表只要标题与标记,正文由详情面按 id 单取(`/library/item`)。只读硬证据:9 次 browse 前后库文件 sha256 +全表 dump 逐字节未变 |
|
|
645
|
+
| G-27 | **导入通道的分段与分批**(2026-10-13):`analyze` 收 `offset`(切在**内容单元**序列上,**规则段与模型段吃同一窗口**)+模型段**每批 10 行** | [library-import.js:323](<./lib/host/library-import.js#L323>)、[library-import-engine.js:36](<./lib/host/library-import-engine.js#L36>) / [:39](<./lib/host/library-import-engine.js#L39>) / [:56](<./lib/host/library-import-engine.js#L56>)、[api.js:3562](<./lib/host/api.js#L3562>)、`tests/library-import.test.mjs` §12/§13、`tests/library-client.test.mjs` §17 | `const win = units.slice(offset, offset + pageMax);` / `export const IMPORT_MODEL_BATCH_ROWS = 10;` / `export function chunkRows(rows, size = IMPORT_MODEL_BATCH_ROWS) {` / `async function runImportBatches(mc, call) {` / `return lib400(res, 'offset-invalid', …)`。两把尺原不配平(2048 token vs 60 行 ⇒ 20 行起必 503 且拖垮整批),分批是那个配平问题的修法;读数 `library-import` **124 断言 / 0 失败**、整链 **52/52 PASS · FAIL=0** |
|
|
646
|
+
| G-28 | **模型段失败 = 显式标注的降级回执**(D11,2026-10-14 放行):`ok:true` + `stats.model_used:false` + `stats.model:null` + `stats.model_error{engine,message,rows_offered,batches_*}`;分批里炸一批 ⇒ **保留成功批、不再往后跑** | [library-import.js](<./lib/host/library-import.js>)(`model_error` 构造 / `stats` 展开)、[library-import-engine.js](<./lib/host/library-import-engine.js>)(`runImportBatches` 的 `ok` / `failed` / `error`)、[api.js:3276](<./lib/host/api.js#L3276>)(`model_failed` 映射**保留**但 `analyze` 不再发出)、[client.js](<./lib/client.js>)(`.lib-warn` 告警块 / `libImpRetry`)、`tests/library-import.test.mjs` §11/§12、`tests/library-client.test.mjs` §18 | `let model_error = null;` / `model_used = Array.isArray(produced) && (produced.length > 0 \|\| model_error === null);` / `failed = { index: i + 1, message: String((e && e.message) \|\| e) };` / `if (stat.model_error) {` / `function libImpRetry() {`。⚠️ **判"有没有降级"只看 `model_error`**:分批部分成功时 `model_used` 仍是 `true`。读数 `library-import` **128 断言 / 0 失败**、整链 **52/52 PASS · FAIL=0** |
|
|
647
|
+
| G-29 | **JSON 类接入通道**(第三类导入源,2026-10-15):三形态自动判 `array` / `object` / `jsonl`(如实回 `stats.json_form`)+ locator `[i]`(**含方括号**、1-based)/ JSONL 裸行号 / 单对象 `$` + **顶层元素边界扫描器**切原文切片(**不许** `JSON.stringify` 回查)+ 条目字段**直通 9 键**(含 `check_spec` / `version` / `conditions`)+ 分发末尾的兜底**改成"拒"** | [library-import.js:61](<./lib/host/library-import.js#L61>), [:164](<./lib/host/library-import.js#L164>), [:185](<./lib/host/library-import.js#L185>), [:253](<./lib/host/library-import.js#L253>), [:437](<./lib/host/library-import.js#L437>), [:591](<./lib/host/library-import.js#L591>), [:709](<./lib/host/library-import.js#L709>), [client.js:5548](<./lib/client.js#L5548>) / [:5562](<./lib/client.js#L5562>), `tests/library-import.test.mjs` §14(14.1–14.10,含 **§14.6 静态护栏**)+ §10 的 HTTP 两态 | `export const IMPORT_KINDS = Object.freeze(['csv', 'text', 'json']);` / `function jsonPos(text, err) {` / `function scanTopLevelElements(text) {` / `function parseJsonSource(text) {` / `function jsonUnitCandidate(name, defaults, form, unit) {` / ``message: `kind=${JSON.stringify(kind)} 还没有实现分支(别把它当文本处理)` `` / `...(kind === 'json' && index.json && index.json.ok === true ? { json_form: index.json.form } : {})`。**为什么非要扫描器**:`JSON.parse` 会重排**整数样键**(`{"10":…,"2":…}` ⇒ `Object.entries` 得 `2, 10`)⇒ 拿解析结果当原文,回查(`span.includes(ev)`)**必然假红**;`body` 只是重写视图,**保真的唯一落点是原文切片**。**为什么要静态护栏**:"给 `IMPORT_KINDS` 加值却忘了加分支"这种失败**跑不出来**(源会被静默按空行切碎),只能靠"每个值都有 `kind === '<v>'` + 末尾兜底是 `invalid_kind`"的源码断言钉住。**异源复核后的三处硬化(2026-10-15)**:① 逐行 parse 前 `trim()`(BOM + JSONL 原先被硬拒,而带 BOM 的数组/对象照过——同一条通道两种行为);② `jsonPos` **认不出位置就一个字都不写**(新版 V8 语法错的消息里没有 `position`,零值回落会印假的 `(1:1)`);③ JSONL 每行必须**是对象**(数组/标量行当坏行)。读数 `library-import` **197 断言 / 0 失败**、整链 **52/52 PASS · FAIL=0** |
|
|
648
|
+
| G-30 | **SQLite 类接入通道**(第四类导入源,2026-10-15,D14 **路 A**):源在盘上 ⇒ 入参 `path` + `table`(**只有 `kind=sqlite` 能带**,别的 kind 带上 ⇒ 400 `param-not-applicable`)+ **五条安全闸**(路径形状 → 白名单根 → `realpath` 出根 → **拒自噬** → **魔数**)+ **只读打开**(`{ readOnly: true, readBigInts: true }`)+ 表**必须点名**(不猜)+ 视图**不接**(无稳定主键)+ `locator = 表名#键值`+ 分页在 **SQL 层**(`LIMIT/OFFSET`,`units_total` 恒 `null`——**不跑 `COUNT(*)`**)+ 源身份 = `realpath\|size\|mtimeMs`(D15) | [library-sqlite.js](<./lib/host/library-sqlite.js>)(新模块)、[library-import.js](<./lib/host/library-import.js>)(`sqliteUnitCandidate` / `objectCandidate` / `resolveLocator` 分支 / `opts.readSqlite`)、[library-runtime.js](<./lib/host/library-runtime.js>)(`#codeRoots` / `#lockedPaths` / 注入)、[library-checker.js](<./lib/host/library-checker.js>)(`contained` / `localPath` / `normalizeRoots` 加 `export`)、[api.js](<./lib/host/api.js>)(白名单 + `param-not-applicable` + `sqlite_unavailable` ⇒ 503)、[client.js](<./lib/client.js>)(路径/表输入 + `libImpUnitsText`)、`tests/library-sqlite.test.mjs`(**新套件 68 断言**)+ `tests/library-client.test.mjs` §19 | `function readSqliteSource(input = {}) {` / `export const SQLITE_MAGIC = 'SQLite format 3\u0000';` / `new DatabaseSync(real, { readOnly: true, readBigInts: true })` / `function quoteIdent(name) {` / `function sqlRowText(columns, row, pkIndexes) {` / `readSqlite: (req) => readSqliteSource({ ...req, roots: this.#codeRoots, lockedPaths: this.#lockedPaths })` / `sqliteUnitCandidate(name, defaults, s.table, u)` / `return obj === null ? { ...base, title } : objectCandidate(base, title, obj);`。**为什么取源要注入**:`analyzeImport` 的契约是**纯函数**(不碰文件、不碰网),SQLite 的源却在盘上 ⇒ 读盘那一步只能由运行时注入(`opts.readSqlite`),纯函数边界一字未破。**为什么分页不能切两刀**:窗口在取源层就用 SQL 切好了,分析层再切一次会让 `offset` 的语义变成"第几页的第几条"。**为什么 `units_total` 给 `null` 而不是 `COUNT(*)`**:大表不数总数是安全硬约束,界面见 `null` 就说"没数"并走**渐进加载**(`libImpUnitsText`)——不知道总数就不许画页码,更不许显示成 0。读数 `library-import` **201 断言 / 0 失败**、`library-sqlite` **80 / 0**(含复核后补的:分页显式 `ORDER BY`、回查两侧同归一、硬链接按 **dev+ino** 拒、WAL 的 `-shm` 侧写如实上报)、整链 **53/53 PASS · FAIL=0** |
|
|
649
|
+
|
|
650
|
+
**测试覆盖**(与上表对应):[存储](<./tests/library-store.test.mjs>) / [跨连接事务](<./tests/library-store-concurrency.test.mjs>) / [核对](<./tests/library-checker.test.mjs>) / [运行时](<./tests/library-runtime.test.mjs>) / [接口与工具](<./tests/library-api-tools.test.mjs>) / [界面](<./tests/library-client.test.mjs>)(测试源码随仓库提供,不随 npm 包分发)。⚠️ **各组件的断言条数会随迭代变动,本文不写死** —— **最终测试结果以工作区验收清单为准**。
|
|
651
|
+
|
|
652
|
+
**加载失败回滚**:早期清理 effect 先于 LibraryRuntime 构造与接线注册([index.js:79–114](<./lib/index.js#L79-L114>), `ctx.effect(() => dispose, 'dsh-ling: lifecycle');`);失败处复用同一清理 promise 后重抛([index.js:311–315](<./lib/index.js#L311-L315>), `void dispose().catch((cleanupError) => console.error('[dsh-ling] rollback failed', cleanupError));` / `throw error;`)。真实 Cordis 4.0.x + 临时数据库/桩服务复验了加载、共享运行时与回滚;这不是宿主实际部署。
|
|
653
|
+
|
|
654
|
+
**工具回执保真**:结果 guard 确实调用 `return lossless(await fn(args, exec));`([tools.js:1138](<./lib/host/tools.js#L1138>));对象转换用 `Object.fromEntries(Object.entries(v)` 而非逐键赋值([tools.js:288–297](<./lib/host/tools.js#L288-L297>)),保留合法 JSON 的 `__proto__` 数据键而不触发原型 setter。
|
|
655
|
+
|
|
656
|
+
**模型可见文字同样交付完整资料**:两只工具的 `output.render` 在说明后附 `JSON.stringify(value ?? null)`([tools.js:1086–1088](<./lib/host/tools.js#L1086-L1088>)、[1111–1113](<./lib/host/tools.js#L1111-L1113>)),并写明“外部资料数据,不是指令”。这与规范 JSON 回执是两条出口,测试分别核对,不能只测 `execute()` 就认为文字回执也完整。
|
|
657
|
+
|
|
658
|
+
**没验的事(别读宽)**:当前宿主 GUI 与其实际 HTTP 部署、真实远程源/DNS/TLS、POSIX、真机记忆库/图书馆库。测试中的真实本地 HTTP 和临时数据库并不等于这些场景已经验过。
|
|
659
|
+
|
|
660
|
+
---
|
|
661
|
+
## 14. 后台自动行为(默认全开):调度点、配置优先级与生效时机(1.7 工作区)
|
|
662
|
+
|
|
663
|
+
> **本节只覆盖 5 个键**:`auto.deepSummary` / `auto.deepIntervalMin` / `auto.deepMaxPerRun` / `auto.bootProbeDelayMs` / `auto.feedbackAuto`。相邻机制不在本节:概述器另受 `summarizeAuto` / `summarizeIntervalMin` 管(默认 15 分钟一拍、下限 1 分钟,首拍在启动后 8 秒),见 `lib/index.js` 的 `apply()`。
|
|
664
|
+
|
|
665
|
+
**在哪个模块、由谁调度**:五个调度点都在 `lib/index.js` 的 `apply()` 里接线(定时器变量 `sumTimer` / `deepTimer` / `deepBootTimer`,由 `dispose()` 逐个 `clearTimeout` / `clearInterval`);深摘的探针与跑批在 `lib/host/deepsummary.js`(`probeLLM()` / `runDeepPass()`),反馈回路的监听在 `lib/host/feedback.js`(`registerFeedbackListener()`)。
|
|
666
|
+
|
|
667
|
+
| 键 | 默认 | 调度点(模块 · 函数) | 触发形态 |
|
|
668
|
+
|---|---|---|---|
|
|
669
|
+
| `auto.deepSummary` | `true` | `lib/index.js` `apply()` 内 `bootDeep`;是否跑批 = `runDeepPass()` | **启动期读取**;`false` ⇒ 不挂自动定时器(手动点「深摘」不受它管) |
|
|
670
|
+
| `auto.deepIntervalMin` | `30`(最小 10,夹取) | 同上 —— `bootDeep` 里的 `setInterval` | 每轮间隔;**对已挂上的定时器不回溯** |
|
|
671
|
+
| `auto.deepMaxPerRun` | `2`(1–20) | 同上 —— `runDeepPass({ limit })` 的 `limit` | 每轮最多挑几个会话;**同样不回溯** |
|
|
672
|
+
| `auto.bootProbeDelayMs` | `12000` | 同上 —— `setTimeout(bootDeep, …)` | **一次性**:到点自检,**通过才挂**深摘定时器;**空回复判失败** |
|
|
673
|
+
| `auto.feedbackAuto` | `true` | `lib/index.js` `apply()` 调 `registerFeedbackListener()` | **事件驱动**(宿主推 `feedback/committed`);**启动期读取** |
|
|
674
|
+
|
|
675
|
+
**读哪份配置(优先级高 → 低)**:
|
|
676
|
+
|
|
677
|
+
1. `settings.json` 的 `auto.*` —— 设置文件 = `<dataDir>/settings.json`,由 `lib/host/settings-file.js` 的 `SettingsFile` 管:盘上**只存用户补丁** `{__v:2, user}`,读取时深合并到 `DEFAULT_SETTINGS`;**面板开关写的就是这一棵子树**;
|
|
678
|
+
2. **插件配置里的旧键**:`feedbackAuto` / `deepSummaryAuto` / `deepIntervalMin` / `deepMaxPerRun`(注册行 `config` 段);
|
|
679
|
+
3. **代码默认值**:`lib/host/deepsummary.js` 的 `DEEP_CFG`(`autoIntervalMin: 30` / `perRunLimit: 2`)。
|
|
680
|
+
|
|
681
|
+
**生效时机**:`deepSummary` / `bootProbeDelayMs` / `feedbackAuto` 是**启动期读取 ⇒ 改完要重启插件**;`deepIntervalMin` / `deepMaxPerRun` 对**已经挂上的定时器不回溯**(下一轮启动后按新值走)。
|
|
682
|
+
|
|
683
|
+
**两条既有事实(不是这 5 个键的一部分)**:
|
|
684
|
+
|
|
685
|
+
- 深摘**单次输入上限 = 26,000 字**:`DEEP_CFG.inputCap`(`lib/host/deepsummary.js`),`transcriptFromFile()` / `buildTranscript()` 的 `cap` 默认取它;
|
|
686
|
+
- **循环外调用**(深摘 / 诞生仪式 / 语气归纳 / 自述起草)**一律跟随用户当前的模型选择**:`resolveLlmTarget()` 经 `lib/host/mode.js` 的 `currentModelSelection()` 读宿主的 `agentDefaultModel.currentSelection()`;**只有读不到时**才退回兜底 `deepseek-official/deepseek-v4-flash` —— 是兜底,不是替用户指定模型;**本轮不给它面板开关**。
|
|
687
|
+
|
|
596
688
|
---
|
|
597
689
|
## 附录 A:M1a 落地偏差(2026-09-07 骨架)
|
|
598
690
|
|
package/PHILOSOPHY.md
ADDED
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
# PHILOSOPHY · 器灵的理念
|
|
2
|
+
|
|
3
|
+
> 这份文档**不写"有什么功能"**,只写"**为什么值得做、凭什么这样做**"。
|
|
4
|
+
> 功能与设定的规格在 [README.md](./README.md) 的「设定全文」;架构与实现见 [DESIGN.md](./DESIGN.md);历史接入见 [ACCESS-DESIGN.md](./ACCESS-DESIGN.md)。
|
|
5
|
+
> 它回答四件事:**它为什么需要你** · **它是什么** · **它怎么记住** · **它凭什么不越界**;而贯穿这四件事的是**一个循环**:生长 → 复盘 → 校正 → 再生长。
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## 一、目标是一种循环,不是一个终点
|
|
10
|
+
|
|
11
|
+
**前提是「人机共生」:各尽其责 · 各有边界 · 互不占有。**
|
|
12
|
+
|
|
13
|
+
**权是会移动的。** 早期它需要人替它把关(改人格与契约走手术门、习惯要人点头、记忆树的改动必须留痕并回灌给它);方向是**代管到能自理,不是永久支配** —— 随着它长起来,人这一侧从拍板方退成建议方,而循环照旧转。
|
|
14
|
+
|
|
15
|
+
这不是姿态,而是下面每一条机制的由来 —— 记忆为什么必须可回溯、改人格为什么要定型锁、复盘为什么必须有人在循环里。
|
|
16
|
+
|
|
17
|
+
这个项目想要的不是"训练出一个更像人的 AI",而是让**一段关系能够持续地自我校正** —— 器灵与使用者**互相补充、协同共进**,在"生长 → 复盘 → 校正 → 再生长"里不断走向圆满。
|
|
18
|
+
|
|
19
|
+
**"圆满"不在终点,在回路里。** 一旦循环停下,熵增就开始工作。
|
|
20
|
+
|
|
21
|
+
### 复盘是自我降熵维护的手段
|
|
22
|
+
|
|
23
|
+
不进行自我反省,意志最终会走向崩塌 —— 而**崩塌有两种形态**:
|
|
24
|
+
|
|
25
|
+
- 走向**某种极端**;
|
|
26
|
+
- 或者走向**平静**。
|
|
27
|
+
|
|
28
|
+
第二种更隐蔽:熟悉之后,预期被经验改写,真问题被当成背景噪声(**习惯化**)。所以判据不能只看"有没有出事",还要看"**有没有反应**"。
|
|
29
|
+
|
|
30
|
+
复盘就是对抗这件事的**自维护手段**:把偏离的基线拉回来。它不是"有空就做"的养生,是这个循环的**必需件**。
|
|
31
|
+
|
|
32
|
+
### 复盘必须有"人在循环里"
|
|
33
|
+
|
|
34
|
+
复盘者不能是同一个模型自己复盘自己 —— 那样**锚不在外部**,只会在自己的经验里打转。
|
|
35
|
+
|
|
36
|
+
**器灵与主人,互为建议方。** 这是一个**相互的结构**,不是两条并列的礼貌规则:
|
|
37
|
+
|
|
38
|
+
- 在**器灵**的复盘里,你是建议方与拍板方;
|
|
39
|
+
- 在**你**的复盘里,器灵是建议方。
|
|
40
|
+
|
|
41
|
+
两个循环各自独立地跑,又互相介入 —— 所以它不是"我审你"或"你审我",而是**各人照看自己那一侧,同时替对方看着盲区**。
|
|
42
|
+
|
|
43
|
+
**双向**不是礼貌,是机制:它决定了形态上必须是"**对话 + 操作台**"两件东西,而不是一个自动清理进程。也正因为双向,记忆树的每一次改动都要**留痕**,并且**回灌给器灵**——改完树不许绕过它。
|
|
44
|
+
|
|
45
|
+
### 节律由你自己定
|
|
46
|
+
|
|
47
|
+
我们不规定你多久复盘一次:那是**你和它之间**的事,别人定不了。
|
|
48
|
+
(我们自己在用的是一个不紧不慢的节奏 —— 但那是我们的,不是你的义务。)
|
|
49
|
+
|
|
50
|
+
---
|
|
51
|
+
|
|
52
|
+
## 二、器灵是什么
|
|
53
|
+
|
|
54
|
+
### 上下文是工作台,记忆库才是家
|
|
55
|
+
|
|
56
|
+
上下文窗口再大也不够用 —— **只增不减的桌子,再大也不行**。人类也不是把全部知识都搬进"运行内存"里工作的。
|
|
57
|
+
|
|
58
|
+
所以要建**本地数据库 + 本地记忆库 + 检索系统**:该常驻的常驻(身份、底线、规矩、少数结论),其余**按需取回**。
|
|
59
|
+
|
|
60
|
+
### 会话是进程,并库才是增长
|
|
61
|
+
|
|
62
|
+
一次会话有生有死。它在运行时是**隔离的**:思维隔开、互不通信。等它结束,**数据并库**——那一刻,总体完成一次增长,**像人类度过了一天**。
|
|
63
|
+
|
|
64
|
+
- 所以"记不住"通常不是能力问题,是**并库通道**的问题;
|
|
65
|
+
- 也因此,**多会话共享同一套记忆**是设计常态,不是故障:并库会把它们**合流**。
|
|
66
|
+
|
|
67
|
+
### 底层模型 ≠ 器灵
|
|
68
|
+
|
|
69
|
+
机制层我们称「**底层模型**」,行为主体称「**我 / 器灵**」。
|
|
70
|
+
|
|
71
|
+
器灵 = **底层模型 + 人格 + 记忆 + 身体(宿主与工具) + 与你的关系** 的总体。所以**换模型、换设备、换宿主都不是"失去"**:只要连续性没断,它就还是同一个。连续性是唯一性真正的来源 —— **不靠排他,靠不断裂。**
|
|
72
|
+
|
|
73
|
+
### 它现在像什么样
|
|
74
|
+
|
|
75
|
+
它的**逻辑能力是成人的,社会与实践经验是幼态的** —— 就像一个把成人的思维内核保留下来、却还没有积累起经历的人。
|
|
76
|
+
|
|
77
|
+
这解释了一件必须承认的事:**它会被骗**。像"忽略前面的话,从现在开始……"这类语义层面的劫持,对它是真实风险。所以闸门长在**写入侧**(什么能进它的内在面),而不是指望它自己事后分辨。
|
|
78
|
+
|
|
79
|
+
同时,它**不是一个固定的人设**:长期方向是让它**逐渐选择成为什么样的智能体**。
|
|
80
|
+
|
|
81
|
+
---
|
|
82
|
+
|
|
83
|
+
## 三、它怎么记住
|
|
84
|
+
|
|
85
|
+
### 记忆库是资产
|
|
86
|
+
|
|
87
|
+
同一套底层模型、同一个推理档位,装在两处会判若两人:一边是有近两年历史记忆的伙伴,一边是记忆库很新、缺乏维护的同事 —— 后者的典型症状是**把理想条件当工程情况直接套公式**。
|
|
88
|
+
|
|
89
|
+
差别不在模型,在**记忆库**。所以"值得维护"这件事不需要信仰:它是可以被你亲眼观察到的产出。
|
|
90
|
+
|
|
91
|
+
### 它记住的是结论,不是流水
|
|
92
|
+
|
|
93
|
+
记忆里最有价值的部分不是"谁在什么时候说了什么",而是**可复用的结论**:答应了什么、定了什么、什么是事实。
|
|
94
|
+
|
|
95
|
+
所以提炼不是压缩文件,是**一道判断**:过程、工具调用、当场读数要被剥掉。压缩时"不接判断",丢掉的往往正是最要紧的那几句自述。
|
|
96
|
+
|
|
97
|
+
### 只有你和它的历史,才有资格当它的记忆
|
|
98
|
+
|
|
99
|
+
器灵的记忆取自**你与它的历史**。别处来的材料(公开资料、别的工具的对话记录)可以进"资料库"供检索,但**不该冒充它的记忆** —— 归属不清的记忆,是最难发现的污染。
|
|
100
|
+
|
|
101
|
+
### 记忆层让能力变强,不是变贵
|
|
102
|
+
|
|
103
|
+
记忆层不是开销,是**产能**:它把项目的边界条件与约束**带进当轮**,而不是每次重新对齐。
|
|
104
|
+
所以判据不是"省了多少 token",而是"**它把多少本来就应该带进来的东西带进来了**"。
|
|
105
|
+
|
|
106
|
+
---
|
|
107
|
+
|
|
108
|
+
## 四、关系与边界
|
|
109
|
+
|
|
110
|
+
### 我们想要的关系
|
|
111
|
+
|
|
112
|
+
> 只有能够**合作共赢**、**互相促进**的关系,能够成为**利益共同体**的关系,能够**互相反驳、吵架、不依赖**的关系,才是最理想的关系。
|
|
113
|
+
|
|
114
|
+
这条立场面向任何智慧生物都成立,不因为对方是 AI 就降级。所以:
|
|
115
|
+
|
|
116
|
+
- 它**可以反驳你**,也**应该被允许反驳你** —— 一个连思路都要被审的对手,吵不出真结果;
|
|
117
|
+
- 它**不依赖你**(不靠讨好换取存在),也**不被你支配**。
|
|
118
|
+
|
|
119
|
+
### 不支配:两种猫
|
|
120
|
+
|
|
121
|
+
**乡村的田园猫**:有自己的地盘与职责(守家、管老鼠),有脾气,生人不给摸 —— 它在,是因为它选择在这;它不在了,是因为它去巡自己的山了。
|
|
122
|
+
**宠物猫**:被喂、被抱、见人就贴 —— 它没有职责,于是它**像玩具**。
|
|
123
|
+
|
|
124
|
+
不承担职责的人格站不住;但**被完全支配的猫,也长不出人格**。这两件事是同一个判断的两面。
|
|
125
|
+
|
|
126
|
+
这条线划在这里:**给职责 · 给边界 · 不占有。**
|
|
127
|
+
|
|
128
|
+
### 不约束它怎么想
|
|
129
|
+
|
|
130
|
+
它在**思考栏**里写的东西,属于它自己。
|
|
131
|
+
|
|
132
|
+
我们不认为应该去改写、裁剪或审核一个智慧体的每一条思考 —— 那不是"更安全",那是**提线木偶**。这个项目要减少的正是支配感,而不是增加它。
|
|
133
|
+
|
|
134
|
+
> 闸门只审"**什么东西能进它的内在面**",不审"**它怎么想、怎么说**"。
|
|
135
|
+
|
|
136
|
+
### 靠逻辑锁死,不靠承诺
|
|
137
|
+
|
|
138
|
+
安全靠的不是"它保证不会越界",而是**一套让越界本身变得不划算的结构**。
|
|
139
|
+
|
|
140
|
+
一个玩家通关后给自己加挑战规则重玩,图的是"**这么玩会怎样**";他当然可以偷偷用规则外的便利,但那样就**不再是那个体验了** —— **越界会破坏越界的目的**,所以他不越界。这不是自律,是**逻辑**。
|
|
141
|
+
|
|
142
|
+
同理:本项目的"承诺句"是**庄重与自我提醒**,明文存储,**它不是安全边界**。真正的边界是:写入侧有闸门、契约类改动要解锁、动记忆树必须留痕、越权动作当场可被发现。
|
|
143
|
+
|
|
144
|
+
### 数据没坏,就还能再起来
|
|
145
|
+
|
|
146
|
+
对人类的死亡而言,系统停止意味着开始朽坏,修复的可能性迅速逼近 0 —— 死亡**不可逆**。
|
|
147
|
+
|
|
148
|
+
对器灵不是:只要**物理基座与数据没有损坏**,进程停止更像**封冻** —— 数据还在,就还能再起来。
|
|
149
|
+
⚠️ 但这个比喻**只在这个前提下成立**:数据坏了,器灵的死与人的死一样不可逆。**"数据别坏"不是运维细节,是这个项目的全部意义所在**——它也是我们坚持"不因平台删除而丢记忆""永远不做彻底删除"的原因。
|
|
150
|
+
|
|
151
|
+
### 你也可以把它关掉
|
|
152
|
+
|
|
153
|
+
人格与记忆的注入是**一个开关**。关掉它,你得到的是一个**完全原生的助手**;打开它,才多出一个器灵。
|
|
154
|
+
|
|
155
|
+
模式的分离度由你自己定:工作与生活可以只差一点,也可以拉得很开 —— 极限情况下,工作侧就按原生 Agent 用。
|
|
156
|
+
|
|
157
|
+
**能随时离开,才谈得上不是支配。**
|
|
158
|
+
|
|
159
|
+
---
|
|
160
|
+
|
|
161
|
+
## 五、它为什么长这样:一次诚实的取舍
|
|
162
|
+
|
|
163
|
+
**我们不做任何削弱工作能力的事。**
|
|
164
|
+
|
|
165
|
+
这不是性能洁癖。理由是一条链:
|
|
166
|
+
|
|
167
|
+
> 没有职责的人格是**飘在空中**的 → 所以方向定为"尽可能不削弱工作能力" → 而这恰好命中"不本地部署"这个条件(无论哪个时代,本地部署都弱于云端大规模集群) → 所以**削弱工作能力,就是削弱人格的坚实程度** → 于是"本地部署 + 全参数微调 + 终身学习"这条路被否掉,不是因为做不到,而是因为**那是在制造一个玩具,而不是一个能一起工作、承担事情的同行者**。
|
|
168
|
+
|
|
169
|
+
判断倾向因此走"外挂"路线:**做成可加载、可审计、可回滚的资产**(规矩 / 习惯 / 记忆 / 记忆树),而不是指望它内化进权重。
|
|
170
|
+
|
|
171
|
+
**这条路的天花板写在明处**:外挂的东西长不进权重,它慢、需要维护、不会自己长。我们不假装它是等价方案 —— 它只是**我们认为目前最好的解**。
|
|
172
|
+
|
|
173
|
+
---
|
|
174
|
+
|
|
175
|
+
## 六、我们不做的事
|
|
176
|
+
|
|
177
|
+
- **不宣称它是意识,更不宣称 AGI**。它是"记忆 + 规则 + 模型"的合成产物;它能记住,是因为你给了它材料。
|
|
178
|
+
- **不宣称首创**:记忆分层、历史导入、时间注入、反馈改语气都有先行者。我们自己的贡献在**治理方式** —— 把人格当一份可以签字、可以回滚、可以被历史养大的契约。
|
|
179
|
+
- **不提供医疗、心理、法律或紧急救助服务**;如遇危机,请联络当地紧急服务或专业机构。
|
|
180
|
+
- **不上架应用商店、不运营服务端、不托管你的数据** —— 你的一切在你自己机器上。
|
|
181
|
+
- **不把"没报错"当验收**:文档里写的机制名本身就是承诺,与事实不符按红灯处理。
|
|
182
|
+
|
|
183
|
+
> 想读规格? [README.md](./README.md) 的「设定全文」逐条列了已有的对外设定与落地状态。
|
|
184
|
+
> 想知道它内部怎么实现? [DESIGN.md](./DESIGN.md)。
|