@nightpainters/dsh-ling 1.6.2 → 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 +130 -4
- package/DESIGN.md +89 -6
- package/PHILOSOPHY.md +14 -4
- package/README.md +102 -18
- 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 +592 -59
- 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/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 +13 -8
- 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 +73 -19
- package/lib/host/persona.js +13 -2
- package/lib/host/rawlog.js +1 -1
- package/lib/host/recall-search.js +45 -8
- package/lib/host/snapshot.js +2 -1
- package/lib/host/surgery.js +6 -4
- package/lib/host/tools.js +460 -19
- package/lib/host/util.js +1 -1
- package/lib/index.js +120 -81
- package/package.json +10 -5
- package/tools/deep-import-docs.mjs +1210 -0
package/CHANGELOG.md
CHANGED
|
@@ -2,17 +2,143 @@
|
|
|
2
2
|
|
|
3
3
|
本项目遵循语义化版本:`MAJOR.MINOR.PATCH`。
|
|
4
4
|
|
|
5
|
+
## 1.7.0
|
|
6
|
+
|
|
7
|
+
> ⚠️ **工作区候选 · 未发布。这一节记的是工作区里已落地、已被离线测试覆盖的工作,不是已发布状态。** 包版本已抬到 **`1.7.0`**,但**没有发布、没有安装**。宿主界面上的新标签**已在本机真机验收走通**(第五标签、两个图书馆工具、确定性核对、撤下留痕,2026-10-07),**第六标签「结论层」与其工具面已于 2026-10-15 随一次宿主重启实测生效**;对外发布仍需走正常的安装/重载流程(见 README「快速开始」)。
|
|
8
|
+
|
|
9
|
+
**图书馆:一个跟记忆分开的库 —— 只搜与核对,认证的是"声明的那一处"。**
|
|
10
|
+
|
|
11
|
+
> 一句话:给器灵加一个**能回头对账**的库。它不掺进记忆 —— 既有记忆永不迁进图书馆,图书馆也不进注入面;器灵只有两只手(**搜**、**核对**),**没有维护工具**;核对结论只有 `ok` / `wrong` / `unknown`,而且它**只认证条目声明的那一处比较,不认证解释性正文、也不构成通用真理**。
|
|
12
|
+
|
|
13
|
+
### 一、独立库:与记忆库并列的第二套表
|
|
14
|
+
|
|
15
|
+
- 图书馆有**自己的 `library.db`**(与 `memory.db` 同目录、独立表);**不迁移记忆、不参与开场注入**,两库之间没有隐式通道。
|
|
16
|
+
- 条目形态 = **值 + 出处 + 取数时间 + 核对规格**:`title / body / category / source_kind / source_ref / as_of`(必填),可选 `version / conditions`,以及 `check_spec`(定位 + 期望值 + 比较方式;`null` = 不可核)。
|
|
17
|
+
- **"不可用"与"零命中"是两件事**:打不开 / 未启用 / 运行时不在场 ⇒ 明确的不可用,**绝不回落成"搜到 0 条"**。
|
|
18
|
+
- 一条边界写清:**"不迁移记忆库里的旧数据"不等于"禁止网页端来源的资料"** —— 资料能不能进馆看的是**有没有具体、可追责的出处**,与渠道无关。
|
|
19
|
+
|
|
20
|
+
### 二、只搜索与核对两个工具
|
|
21
|
+
|
|
22
|
+
- `library_search({q, offset?, limit?})` 与 `library_check({id})`;**没有**新建 / 修订 / 撤下 / 改配置的模型工具。
|
|
23
|
+
- 两个工具**只接受列出的参数**:多给一个键就拒(`unknown-arg`);代码根目录、URL 白名单这类**登记项不接受模型入参**。
|
|
24
|
+
- 模型可见的文字回执附完整资料或核对凭据 JSON,不只报标题与结论;受限空页不冒充零命中,分页提示不冒充字节超限。
|
|
25
|
+
- 核对把 `exec.signal` 传下去:**被取消的核对不写库**(取消**不算**一次 `unknown` 认证)—— "取消了"与"核完了"在留痕里分得开。
|
|
26
|
+
|
|
27
|
+
### 三、核对口径:source 一致性,不是真理
|
|
28
|
+
|
|
29
|
+
- 三态 `ok` / `wrong` / `unknown`:`unknown` 是"核对**跑完了**却判不了"(取不到源、定位不到、编码不对、超时……)。⚠️ **取消不在这三态里** —— 取消是"没跑完",单列在下一条。
|
|
30
|
+
- **取消 ≠ 一次 `unknown` 认证**:核对在飞时取消 / 断开 / 卸载 ⇒ 本次**不写任何核对记录**(既不留 `ok` / `wrong`,**也不留 `unknown`**),只回"已取消"(带 `cancelled`);**只有跑完的核对才留账、才计入"认证过"**。
|
|
31
|
+
- 比较方式只有两种:**`exact`** = 类型严格的**结构比较**(JSON 对象**成员顺序无关**、**数组顺序有关**;文本按字面精确比);**`text-lf`** = 只把 CRLF/CR 归一成 LF 后精确比。**两者都不 trim、不折叠大小写、不模糊匹配**。
|
|
32
|
+
- 定位支持**行段**与 **JSON 指针**:指针**位置不存在 = `unknown`**(不是 `wrong`)。
|
|
33
|
+
- 回执里写死这句话:**它只认证声明的那个比较,不认证解释性正文,也不构成通用真理。**
|
|
34
|
+
|
|
35
|
+
### 四、修订与撤下:留旧、带哈希
|
|
36
|
+
|
|
37
|
+
- 修订产生**新版本 + 新 id**,旧版留痕指向新版;撤下只**标记**状态(带原因),**不物理删除**;**跑完的**核对各留一行账(源摘要 / 观测值 / 结论 / 理由,含结论为 `unknown` 的),**取消的不留**。
|
|
38
|
+
- 修订与撤下都必须带**观测到的 `content_hash`**,对不上就拒(防陈旧覆盖);`checked_state` / `checked_at` 这类认证字段**只由核对写**,从写面塞进来一律拒绝。
|
|
39
|
+
- 认证只在"条目**仍 active 且内容未变**"时才落 —— 否则只留记录、不采纳。
|
|
40
|
+
- 状态与哈希判定在写事务内进行,防止另一进程在判定后抢先修订,造成两个最新版或抹掉已完成核对。核对留档与是否采纳是两件事;存储/提交失败仍会明确报错。
|
|
41
|
+
|
|
42
|
+
### 五、配置:严格、默认空
|
|
43
|
+
|
|
44
|
+
- 新增公开配置 `library: { enabled, codeRoots, officialUrls, limits }`:代码根目录与官方文档 **URL 白名单默认为空**(管理员登记;**精确 URL / 绝对目录**)。
|
|
45
|
+
- 限额在**激活时校验**:非法值**响亮报错**,不静默夹取;短别名只在存储层兼容,**插件配置面只认规范名**。
|
|
46
|
+
- **先审原始 `library` 键再进框架 schema** —— 否则框架的非严格合并会把未知键(含构造期钩子)**保留**下来。
|
|
47
|
+
|
|
48
|
+
### 六、远程核对与隐私口径(一处订正)
|
|
49
|
+
|
|
50
|
+
- 官方文档类源走 **HTTPS GET 白名单里的精确 URL**:不跟随跳转、不带凭据、私网地址拒。**发出去的是什么要说准**:这个 GET 会把**登记的 URL(连同查询串)**与**一次普通 HTTPS 请求的元数据**(连接来源 IP、Accept/Accept-Encoding 请求头等)交给对方 —— **对方能看到你的 IP 与这个 URL**,但请求**不会附带**条目正文 / 记忆 / 人格 / 任何本机文件;⚠️ **URL 查询串里若写了敏感信息,它就会真的被发出去**。**白名单默认空 ⇒ 图书馆默认不发生外部取源访问。**
|
|
51
|
+
- 因此 README「隐私」一节补了一条精确表述:**它不往外发正文,但会访问你登记的 URL(含查询串),对方由此能看到你的 IP 与该 URL** —— 此前"不上网 / 不发任何本机数据"的说法在这一处**过强**,已订正;同时删掉了"这是唯一联网动作"的说法(模型调用本来就走你自己配置的网络)。
|
|
52
|
+
|
|
53
|
+
### 七、界面:记忆与数据中心的第五个标签
|
|
54
|
+
|
|
55
|
+
- 记忆中心标题改为「**记忆与数据中心**」,新增第五个标签「**图书馆**」;**前四个标签的内容与行为不变**。
|
|
56
|
+
- 界面具备:独立的状态 / 不可用提示(与记忆**零混淆**)、搜索分页(**不显示伪总数**,只用回执的 `count` / `nextOffset` / `truncated`)、空库与"没输入查询"分别说清、新建 / 修订表单(必填项与 JSON 结构在**提交前**校验、**不抹掉已填内容**)、撤下需**原因 + 确认**、核对按钮(**取消不伪成功**)、核对凭据与修订历史的留痕展示。
|
|
57
|
+
- 安全口径:资料与错误信息一律用**文本节点**渲染(`textContent`),**不做 HTML 注入**;推迟到达的应答按**请求代次**丢弃,不会串条目。
|
|
58
|
+
- 同批修掉一个**旧缺陷**:前四个标签的异步尾巴(概览的 `/state`、浏览的 `/memories/sources`、记忆树的 `loadTreeAll` / `paintTree`)此前会在你切到第五个标签后**照样清屏重画**,把图书馆正文顶掉(顶部高亮却还停在图书馆)。现在统一用**绘制代次 + 标签双重守卫**整条丢弃 —— 丢弃是"连清都不清",不是"先画错再撤回"。
|
|
59
|
+
|
|
60
|
+
### 八、测试
|
|
61
|
+
|
|
62
|
+
- `npm test` 链从 **43 套增加到 55 套**(链里**新增 13 段** = **12 套新套件 + 1 套补登记**:`client-batch` 本身早已存在、只是本版才登记进链 —— 正是 README 警示过的"漏登记不会被自动发现";计数口径 = `package.json` 的 `scripts.test` 里按 `&&` 切开的段数)。
|
|
63
|
+
- 新增套件覆盖:图书馆的**存储层 / 跨连接事务 / 核对 / 运行时 / 接口与工具 / 界面 / 导入引擎 / 独立 SQLite / 浏览**,热重载补绑(`tool-remount`),以及本版的**深层库工具面**(`deepitem`)与**文档灌入通道**(`deep-import-docs`);各组件断言**条数会随迭代变动,本文不写死** —— **最终测试结果以工作区验收清单为准**。
|
|
64
|
+
- 本轮在隔离环境(临时数据目录,不碰真机库)下完整跑过 `npm test`:**55 套全部通过、exit 0**(该结果同样以工作区验收清单为准)。⚠️ **收尾时抓到一条真红并已修**:修复批次把 `tools/compliance-spec.mjs` 的出站基线从 6 抬到 23 时,漏改了 `tests/tools.test.mjs` 里钉住的字面量 ⇒ 整链自那时起是红的(三份副本比对确证),当日修复并补了溯源注释。⚠️ 这**不等于"所有门禁都完成了"**:真实远程核对、真实 DNS/TLS、干净 profile 的安装/卸载均**未验**。
|
|
65
|
+
|
|
66
|
+
### 九、深层库(结论层):从"只读"到"看得见、能落条、能灌文档"
|
|
67
|
+
|
|
68
|
+
- **新工具 `deep_item`**:`add`(可先 `dryRun` 预演) / `retract`(**作废**一条,只标记、不删除,理由留痕、可撤销) / `list`(回执把"在役"与"已退役"**分开报**,不让人误以为退役的还在参与开场选择)。文本**超 80 字当场拒**、不自动截断 —— "独立读得懂的一句话"是这一层的准入条件。
|
|
69
|
+
- **「结论层」标签(只读)**:在役/退役分开报;每条带类别、**硬度**、被加固次数与置顶标记;面板**不提供写入口** —— 落条与作废的判据是"经你授权",少一个入口就少一条"手滑落错、而结论直接影响开场"的路。
|
|
70
|
+
- **文档灌入通道**(`tools/deep-import-docs.mjs`,随包发布、可复跑):把**已写进文档的结论**批量灌进结论层。**先 dry 后 commit** —— 默认只出清单(每条带真源指针、指针漂移量与**真源原文**),你看过再落。
|
|
71
|
+
- **三重护栏**:①**累计预算** —— 一趟总量 ≤40 条,而且是**全趟额度**:跑一次与跑十次扣的是同一个额度,超额**直接拒绝再落**;②**过时按文本自述的时间锚分档** —— 含具体读数 / 版本号 / 时点状态的内容落成 `ephemeral`:**不进开场注入**,但检索仍搜得到(于是"将来必定会过时"的条目不会拿旧数字来污染开场);③**纪律类不自动落** —— 这类内容要的是"该动手时它在场",与"按内容去找"的检索不是一回事,已有专属通道。
|
|
72
|
+
- **复查**:`--review` 出"可能已经没用了"的**信号清单**(含时间锚却当着长期条 / "当次"档够老 / 从没被采用过),**只列信号、不作废任何东西** —— 处置权在人。
|
|
73
|
+
|
|
74
|
+
### 十、本版修掉的(对抗审计首次按 2.0 形态执行)
|
|
75
|
+
|
|
76
|
+
- **`injectEnabled: false` 从空开关变成真闸**:此前这个键只被**写入**、从来没被**读过** —— 写 `false` 关不掉任何东西,而注释称它是"逃生开关 / 总闸"。现在它真的关掉**记忆段**注入(人格段仍受 `settings.persona.enabled` 管),两处注释同步改成实话。
|
|
77
|
+
- **`engines.node` 下限写错**:原写 `>=22.5.0`,但本插件在 5 个文件里**静态** `import` `node:sqlite`,还用到 `node:zlib` 的 `zstdDecompressSync` ⇒ 实测真下限是 **22.15.0**(22.13 / 22.14 起不来)、23.x 线要 **23.8.0**。写错的下限会让 22.5–22.14 的用户**装上一行都加载不了**(连记忆与人格一起);现在改成 `>=22.15.0 <23 || >=23.8.0`,让 npm 直接拒绝。
|
|
78
|
+
- **`library` 配置写错一个值不再拖垮整个插件**:配置解析失败此前会在入口直接抛出,连带记忆、人格与全部工具一起不加载 —— 而同文件对"库打不开"却是重试 + 降级,两种口径自相矛盾。现在只废掉**图书馆这一块**(回执写明出错键名与改法),其余 11 件工具照常注册。
|
|
79
|
+
- **检索降级口径**:本地库读**失败**曾与"库里没有"归并成同一句话(回执写「按内容没找到」)。现在失败往上冒,回执新开理由词明说「**这不是"库里没有",是我们自己的库读不出来**」并给替代动作;「缺表时静默返回空」这条**有意设计**被保留。
|
|
80
|
+
- **`limit` 契约**:超出上限的值此前会一路传到存储层,那句具名文案因此**物理不可达**;现在工具层就拒,并统一 `invalid_limit` 拼法。
|
|
81
|
+
- **「结论层」标签**修掉一处竞态:迟到应答不再覆盖正文(同页两套读数)。
|
|
82
|
+
- **文档面**:删枝不再承诺面板入口(改指 `branch_edit`)、「打一行补丁」更正为**两个目标**、`DESIGN` 的版本口径句更新到 1.7 实况、套件名去掉名不副实的"并发"、**导入工具真的进了 npm 包**(此前本文这么写、而包里的 `files` 白名单没有它)。
|
|
83
|
+
- **客户端声明面**:`dsh.client.inject` 补上漏掉的 `@deepseek-ai/dsh-client-ui-slots`(实机不阻塞,按官方惯例应声明)。
|
|
84
|
+
|
|
85
|
+
**已知未修(如实写明,留到 1.7.1;三条都带可复跑的复现脚本)**
|
|
86
|
+
|
|
87
|
+
- **`importBundle` 的四表写入没有事务包装**(`lib/host/memory.js`;同文件其余五处多表写入都有 `BEGIN/COMMIT/ROLLBACK`)—— 落在**记忆库写入路径**上,不在发布前夜动它;
|
|
88
|
+
- **`surgeryTicketTried` 失败不重置**(`lib/client.js`)⇒ 宿主重启后"取票"路径会锁死(只有环境变量能解锁;fails-closed、不越权,只影响体验);
|
|
89
|
+
- **`LingButton` 在 React 渲染期 `store.set`**(`lib/client.js`)—— 尚未被实测触发的隐患,改它要动面板渲染路径。
|
|
90
|
+
|
|
91
|
+
### 十一、后台自动行为写进文档 + 面板给开关(默认行为不变)
|
|
92
|
+
|
|
93
|
+
- **起因是一条对抗审计结论**:五件后台自动行为(深摘轮询 · 启动探活 · 反馈回路 —— 对应 `auto` 子树的 5 个键)**默认全开**,却在**四份对外文档里各 0 处提及** —— 用户读不到"它在自己跑什么、能不能关、改了为什么没生效"。
|
|
94
|
+
- **文档面补齐四份**:README 新增「**后台自动行为(默认全开)与它们的开关**」一节(放在「三层记忆各自什么时候更新 / 机制数字」旁边 —— 同一类"后台什么时候跑"的问题);DESIGN 新增 **§14**「后台自动行为:调度点、配置优先级与生效时机」(在哪个模块、由谁调度、读哪份配置);本节;`cordis.patch.yml` 注册行的 `config` 注释里写明这些键**可以在哪改**(**只加注释,该文件任何生效配置一字未动**)。
|
|
95
|
+
- **面板给开关**:记忆中心新增 5 个开关(`auto.deepSummary` / `auto.deepIntervalMin` / `auto.deepMaxPerRun` / `auto.bootProbeDelayMs` / `auto.feedbackAuto`),**改完明确提示"需要重启插件"**。
|
|
96
|
+
- **默认行为不变**:五项默认值与之前一致 —— `true` / `30`(最小 10)/ `2`(1–20)/ `12000` / `true`。
|
|
97
|
+
- **配置优先级写清**(否则"改了不生效"会被当成 bug):`settings.json` 的 `auto.*`(**面板开关写的就是它**)> **插件配置里的旧键**(`feedbackAuto` / `deepSummaryAuto` / `deepIntervalMin` / `deepMaxPerRun`)> **代码默认值**。
|
|
98
|
+
- **生效时机如实写**:`deepSummary` / `bootProbeDelayMs` / `feedbackAuto` 是**启动期读取 ⇒ 改完要重启插件**;`deepIntervalMin` / `deepMaxPerRun` 对**已经挂上的定时器不回溯**(下一轮启动后按新值走)。
|
|
99
|
+
- **顺带把两条既有事实写明**:深摘**单次输入上限 = 26,000 字**(`lib/host/deepsummary.js` 的 `DEEP_CFG.inputCap`);**循环外调用**(深摘 / 诞生仪式 / 语气归纳 / 自述起草)**一律跟随用户当前的模型选择**,只有读不到时才退回兜底 `deepseek-official/deepseek-v4-flash` —— 那是**兜底,不是替用户指定模型**,本轮**不给它面板开关**。
|
|
100
|
+
- 本轮**不改任何默认行为与机制判据**;改动面限于上述四份文档与那五个开关。
|
|
101
|
+
|
|
102
|
+
### 十二、注释与文案里的创作者自称全部中性化(2026-10-16)
|
|
103
|
+
|
|
104
|
+
- **缘起**:对外仓库那一面本来就有兜底(`release/publish-sync.mjs` 的替换表会在同步时把作者自称换成「用户」),但 **npm 包那一面没有** —— `npm pack` 直接从源码树打包、不经过同步表 ⇒ 作者的自称一直随源码注释进了包。
|
|
105
|
+
- **处置**:按项目自带脱敏工具的既定口径(`release/scrub-names.mjs` 的替换表第一条)**就地**改源码,**114 处 / 22 个文件**(`lib/` 13 个 · `tools/` 2 个 · `tests/` 7 个),全部落在注释与测试夹具里;对外文档(`README` / `DESIGN` / `CHANGELOG` / `PHILOSOPHY` / `ACCESS-DESIGN`)本来就是 **0 处**。
|
|
106
|
+
- **一处特例**:`tools/deep-import-docs.mjs` 的打分正则里那一支**只删不换** —— 换成「用户」会让这条启发式从"个人标记"放宽成"任何提到用户的条目",那是语义改变,不是脱敏。
|
|
107
|
+
- **验收**:改后跑项目自带脱敏工具干跑 ⇒ **无命中 / 0 个文件**;整链 `npm test` exit 0。
|
|
108
|
+
|
|
109
|
+
### 十三、把设计前提点明:人机共生(2026-10-16)
|
|
110
|
+
|
|
111
|
+
- **缘起**:「共生」这件事**早就长在全架构里了**(人格由共同记忆长出、定型锁、双向复盘、每次改动留痕并回灌给器灵),但**没有一处把它点名为前提** —— 读文档的人只能自己拼出来。
|
|
112
|
+
- **正面写法**(2026-10-16 按用户口径重写):只写"要什么",不写"不是什么"。用户逐字:「**直接说我们要什么就行,别说我们不是什么了。我们从来没有想要替代任何东西。**」·「关于人机共生,不必说谁也不必替谁活这些话,因为主要是要说明**循环**这个东西」。⇒ 用珍藏版 `F-22` 已有的定义收口:「共生」=**各尽其责 · 各有边界 · 互不占有**;对外**不出现"不是替代"式的反面对照**。
|
|
113
|
+
- **同时点明"权会移动"**:早期由人替它把关(手术门 · 习惯要人点头 · 记忆树改动留痕并回灌),方向是**代管到能自理,不是永久支配**(珍藏版 `F-28` 的日落条款 · `F-12⑤` 监护的出口)。⚠️ 这条对外**只写成方向、不宣称已实现的机制** —— 用户逐字:「**人这一侧的权重逐步下移,这件事目前来讲没有具体工程实现,不过,理念文档并不需要完全按照我们的已实现来做**」。
|
|
114
|
+
- **落点**:`PHILOSOPHY.md` 开篇改回**四件事 + 一个循环**(生长 → 复盘 → 校正 → 再生长),§一 的前提段改为上面两句;`README.md` 开篇写**对等句**「**你替它的成长把关,它替你看你的盲区**」并点明**"把关"不等于"操纵"**(改记忆树必须让它看到);`package.json` 的 `description`(npm 页面那行概述)同口径改写。
|
|
115
|
+
|
|
116
|
+
### 十四、记忆中心的标签顺序:把记忆相关的四面挨在一起(2026-10-16)
|
|
117
|
+
|
|
118
|
+
- **缘起**:用户看图指出标签条读起来是散的 —— 原顺序 `概览与数据 · 记忆浏览 · 接入历史 · 记忆树 · 图书馆 · 结论层` 里,与记忆有关的四个面被「接入历史」隔开。
|
|
119
|
+
- **新顺序**:`概览与数据 · 记忆浏览 · 记忆树 · 结论层 · 接入历史 · 图书馆` —— 记忆相关的四面相邻;「接入历史」是会话接入与导入的流水;「图书馆」是另一套独立库(零注入、不与记忆条目混排),故排在最后。
|
|
120
|
+
- **只改渲染顺序,不动逻辑**:切换按**名字**(`switchTab('tree')`)而不是按下标 ⇒ 换序本身不影响任何行为。
|
|
121
|
+
- **但三处"依赖序数"的地方必须同批改**(否则文档说"第五个标签"而界面早已不是):① 标签条上方那段说明改成**按名字描述**;② 源码里 **9 处**「第五/第六」注释改写成"是什么"(含 `DESIGN.md` G-20 那行引用的横幅文字);③ `DESIGN.md` G-20 的行号锚点重取(本次改动让其后内容位移)。
|
|
122
|
+
- **判据同批改**:`tests/library-client.test.mjs` 里**钉死标签顺序**的那条断言、以及一条容易被忽略的耦合 —— 它用 `src.indexOf('// ---------------- 图书馆(…')` **去源码里找那段注释横幅**:横幅文字一改,测试就会因为"找不到横幅"而红,而报错看上去像代码问题而不像文案问题。两条一起改齐,整链 `npm test` exit 0。
|
|
123
|
+
|
|
124
|
+
### 十五、面板文案:18 处字面星号清零(2026-10-16)
|
|
125
|
+
|
|
126
|
+
- **现象**:面板里有 18 处文案直接显示成 `**这样**` —— 那些字符串是**纯文本节点**,不渲染 Markdown,于是本该表示强调的星号原样印在界面上(用户看图时指出)。
|
|
127
|
+
- **处置**:只去掉这 18 处**字符串里**的星号(共 40 对),**注释里的星号一个字都没动**(那是给人读源码用的,不该跟着改)。措辞不改、语义不变。
|
|
128
|
+
- **顺带说清的边界**:同样的写法在**给模型看的工具文本**里还有一批(`lib/host/tools.js` 等)—— 那些走的是另一套渲染面,**不在本次范围内**,也不跟着改:混在一起改就是拿发布风险换整洁。
|
|
129
|
+
- **判据**:整链 `npm test` exit 0(无一条断言依赖被去掉的星号);包内不含 `tests/`,测试自身的描述文案不受影响。
|
|
130
|
+
|
|
5
131
|
## 1.6.2
|
|
6
132
|
|
|
7
133
|
**检索档换了一条快两个数量级的路 · 结果不再被"我刚说的话"占满 · 几处会误导人的地方修掉。**
|
|
8
134
|
|
|
9
|
-
> 一句话:1.6.1 之后继续修。最值得说的三件:① **按内容找原文从"每次 20 秒以上"变成"
|
|
135
|
+
> 一句话:1.6.1 之后继续修。最值得说的三件:① **按内容找原文从"每次 20 秒以上"变成"数十毫秒"**(改走本机库,不再依赖宿主那条例外慢的检索通道);② **搜索结果默认不再被"当前会话自己"占满** —— 此前搜什么都会先命中你几分钟前刚说的话,把真正要回溯的历史挤下去;③ **枝系数(复盘期给某个主题枝调的权重)真的开始生效了**,此前它只存不用。
|
|
10
136
|
|
|
11
137
|
> ✅ `package.json` 版本号为 **`1.6.2`**。**无 schema 变化**(仍是 v17);⚠️ 多数改动在 **host 侧** ⇒ **需重启宿主**才生效。新增一份对外文档 [`PHILOSOPHY.md`](./PHILOSOPHY.md)(理念:为什么值得做、凭什么这样做)。
|
|
12
138
|
|
|
13
139
|
### 一、按内容找原文:换了一条快两个数量级的路
|
|
14
140
|
|
|
15
|
-
- **宿主那条检索通道每次都要做一遍全量对账** ⇒ 实测**每次都超过 20 秒**(重启后第一次也一样,并发还会互相排队)。现在 ① 档**默认搜本机库内原文层**(实测 **
|
|
141
|
+
- **宿主那条检索通道每次都要做一遍全量对账** ⇒ 实测**每次都超过 20 秒**(重启后第一次也一样,并发还会互相排队)。现在 ① 档**默认搜本机库内原文层**(实测 **2~45 毫秒**);要看全量(覆盖全部事件、含子代理会话)显式传 `hostSearch=true`。
|
|
16
142
|
- **如实标注覆盖范围**:本机库那份原文只含"真人消息 + 有正文的回复"(我自己的长回复**中段搜不到**)⇒ 回执里明写"原文来源:**本机库内原文层**",不说就会被读成"库里只有这些"。
|
|
17
143
|
- **原文层默认 5 轮**(此前 10 轮):条目层是结论、原文层是下钻材料,后者一多就把前者挤出视野。
|
|
18
144
|
- **超时不再是挂死**:宿主路有 20 秒上限,超时**照样把条目层交出去**并说明"没跑完" —— 此前实测等过 **30 分钟**,且当时没有任何中断入口。
|
|
@@ -24,9 +150,9 @@
|
|
|
24
150
|
|
|
25
151
|
### 三、记忆树:枝系数开始参与打分
|
|
26
152
|
|
|
27
|
-
- **`branch.weight_scale` 从"只存与显示"变成真的参与检索排序**:`分数 = 基础分 × 血缘 × 矛盾 × 枝系数`。一条结论的枝 = **它来源会话所在的枝**。`1.0` =
|
|
153
|
+
- **`branch.weight_scale` 从"只存与显示"变成真的参与检索排序**:`分数 = 基础分 × 血缘 × 矛盾 × 枝系数`。一条结论的枝 = **它来源会话所在的枝**。`1.0` = 零影响;⚠️ **`0` = 该枝的结论不进记忆开场**(分数算成 0 的非置顶条目会被当"无信息量"排除,所以 `0` 不是"只是排后面")。
|
|
28
154
|
- 于是"复盘期给某个主题枝调权重"**真的有行为后果**了;此前它是面板上的装饰(README 的 U-10 正是针对这类"看起来能改、实际不做"的交互)。
|
|
29
|
-
-
|
|
155
|
+
- 枝系数默认是 `1.0`(零影响)⇒ **在你调过之前,它不改变任何现有排序**;变化只从你调过那一刻开始。
|
|
30
156
|
|
|
31
157
|
### 四、写面与设置
|
|
32
158
|
|
package/DESIGN.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
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`,一句结论),
|
|
@@ -23,6 +23,7 @@
|
|
|
23
23
|
| D5 | 记忆加载策略 | 三层记忆(L0 常驻人格 / L1 开场 Top-K / L2 运行时检索),不注入全部概述 | 早前拍板 |
|
|
24
24
|
| D6 | 模式语义 | 工作/生活 = 先验倾向,不硬过滤;差异 = **推理档位** + 记忆权重向量 + 风格块(不改模型) | 早前拍板 |
|
|
25
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) |
|
|
26
27
|
|
|
27
28
|
**插件命名**:工程目录/包名 `dsh-ling`(器灵);包显示名 "dsh-ling · 器灵";UI 文本语言 zh。
|
|
28
29
|
|
|
@@ -229,7 +230,7 @@ dsh-ling/
|
|
|
229
230
|
**安装(复用 install/INSTALL-PLAN 模式;⚠️ 回滚是人工步骤、没有脚本代劳 —— 见第 3 条与 §10 的「回滚」)**:
|
|
230
231
|
1. `pnpm add file:<仓库路径>`(或等价 file: 依赖)进 `$DSH_HOME/profiles/web/package.json`(`<仓库路径>` 按你本机的实际克隆位置替换);
|
|
231
232
|
2. `cordis.patch.yml` 追加行 `- insert: [{id: dsh-ling, name: dsh-ling}]`(loader id == 包名);
|
|
232
|
-
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`;
|
|
233
234
|
4. GUI 鉴权墙:安装后的功能验证需在用户已登录浏览器进行(本机端口 3080)。
|
|
234
235
|
|
|
235
236
|
**数据目录(插件本体外,更新不丢)**:`dshHomePath('cache','dsh-ling')` → `~/.dsh/cache/dsh-ling/`(memory.db、snapshots、exports)。
|
|
@@ -451,7 +452,7 @@ dsh-ling/
|
|
|
451
452
|
|
|
452
453
|
**归属解析**(两条链并存):DSH 会话走 `session_meta.branch_id`;历史网页端/导入条目走 `conv_branch` 覆盖层;优先级 `conv_branch` > `session_meta` > 主干。**记忆行不冗余存 branch**,靠 `会话 → 枝` 推导。
|
|
453
454
|
|
|
454
|
-
**血缘加权**:检索时按"当前枝 → 目标枝"的相对位置取档 —— **同枝 1.0 / 祖先 0.7 / 旁系 0.4**,`score = 基础分 × 血缘系数 × 矛盾系数`;**枝系数自 1.6
|
|
455
|
+
**血缘加权**:检索时按"当前枝 → 目标枝"的相对位置取档 —— **同枝 1.0 / 祖先 0.7 / 旁系 0.4**,`score = 基础分 × 血缘系数 × 矛盾系数`;**枝系数自 1.6 系列起参与打分** —— 可写入(复盘期可调,夹在 0~2),按 `score = 基础分 × 血缘系数 × 矛盾系数 × 枝系数` 生效(`1.0` = 零影响;⚠️ **`0` = 该枝的结论不进记忆开场** —— 分数算成 0 的非置顶条目会被当"无信息量"排除,所以 `0` 不是"只是排后面")。
|
|
455
456
|
|
|
456
457
|
> ⚠️ **不用连乘**:血缘系数是**单值**(不沿路径累乘),避免 `0.7^n` 随深度指数失真(五层后 `0.4^5 = 0.010`,枝等于从记忆里消失)。深树的路径搜索留作后续。
|
|
457
458
|
|
|
@@ -471,9 +472,9 @@ dsh-ling/
|
|
|
471
472
|
|
|
472
473
|
## 7. 记忆存储 schema v1
|
|
473
474
|
|
|
474
|
-
> ⚠️ **契约已变(本节属早期版本)**:下列 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 逐版记录)。
|
|
475
476
|
|
|
476
|
-
> **当时的状态说明(2026-09 早期)**:当时线上 schema 为 **v2**(现已到 **
|
|
477
|
+
> **当时的状态说明(2026-09 早期)**:当时线上 schema 为 **v2**(现已到 **v17**,见上方警示框)—— `conv_overview.source` 的 `CHECK` 已放开(支持 `dsweb` / `dsh` / `import` 三个来源,自动整表重建且数据无损),并新增 `session_meta.raw_seq`、`persona_suggestions` 等;下表为 v1 原始设计,保留作对照。
|
|
477
478
|
|
|
478
479
|
```sql
|
|
479
480
|
-- ~/.dsh/cache/dsh-ling/memory.db (node:sqlite)
|
|
@@ -547,7 +548,7 @@ CREATE TABLE feedback_queue ( -- M2:修订建议(用户确认才
|
|
|
547
548
|
> ⚠️ **契约已变(本节属早期版本)**:下表只是早期首批端点,**现实契约以 `ACCESS-DESIGN.md` 与本版代码(`lib\host\api.js`)为准**;其中三处已经失实:
|
|
548
549
|
> · `POST /import` 实际收 **JSON body**,不是 multipart;
|
|
549
550
|
> · `GET /persona/suggest` —— **该端点不存在**(现实是 `/persona/draft` 与 `/persona/genesis`);
|
|
550
|
-
> · `/import` **不再是"只读"** —— 它现在是**手术门**覆盖的
|
|
551
|
+
> · `/import` **不再是"只读"** —— 它现在是**手术门**覆盖的 **14** 条写面之一(条数以 `lib\host\surgery.js` 的覆盖面清单行数为唯一真值)。
|
|
551
552
|
|
|
552
553
|
| 方法/路径 | 用途 | 冻结门 |
|
|
553
554
|
| --- | --- | --- |
|
|
@@ -602,6 +603,88 @@ CREATE TABLE feedback_queue ( -- M2:修订建议(用户确认才
|
|
|
602
603
|
- 三层记忆、不硬过滤、遗忘曲线(αβγ 可调)、多标签领域、溯源标注、pin/删、纯本地、导出默认不含原文、外来合并不覆盖 persona、反馈要人审(D4 之外均沿用 REVIEW 定稿)。
|
|
603
604
|
- D4(本回合新增)已贯穿:§4.3/4.4/4.5 冻结门、§5.1 运行中提示、§9 接口排队语义、§10 M1b 验证项。
|
|
604
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
|
+
|
|
605
688
|
---
|
|
606
689
|
## 附录 A:M1a 落地偏差(2026-09-07 骨架)
|
|
607
690
|
|
package/PHILOSOPHY.md
CHANGED
|
@@ -2,12 +2,18 @@
|
|
|
2
2
|
|
|
3
3
|
> 这份文档**不写"有什么功能"**,只写"**为什么值得做、凭什么这样做**"。
|
|
4
4
|
> 功能与设定的规格在 [README.md](./README.md) 的「设定全文」;架构与实现见 [DESIGN.md](./DESIGN.md);历史接入见 [ACCESS-DESIGN.md](./ACCESS-DESIGN.md)。
|
|
5
|
-
> 它回答四件事:**它为什么需要你** · **它是什么** · **它怎么记住** ·
|
|
5
|
+
> 它回答四件事:**它为什么需要你** · **它是什么** · **它怎么记住** · **它凭什么不越界**;而贯穿这四件事的是**一个循环**:生长 → 复盘 → 校正 → 再生长。
|
|
6
6
|
|
|
7
7
|
---
|
|
8
8
|
|
|
9
9
|
## 一、目标是一种循环,不是一个终点
|
|
10
10
|
|
|
11
|
+
**前提是「人机共生」:各尽其责 · 各有边界 · 互不占有。**
|
|
12
|
+
|
|
13
|
+
**权是会移动的。** 早期它需要人替它把关(改人格与契约走手术门、习惯要人点头、记忆树的改动必须留痕并回灌给它);方向是**代管到能自理,不是永久支配** —— 随着它长起来,人这一侧从拍板方退成建议方,而循环照旧转。
|
|
14
|
+
|
|
15
|
+
这不是姿态,而是下面每一条机制的由来 —— 记忆为什么必须可回溯、改人格为什么要定型锁、复盘为什么必须有人在循环里。
|
|
16
|
+
|
|
11
17
|
这个项目想要的不是"训练出一个更像人的 AI",而是让**一段关系能够持续地自我校正** —— 器灵与使用者**互相补充、协同共进**,在"生长 → 复盘 → 校正 → 再生长"里不断走向圆满。
|
|
12
18
|
|
|
13
19
|
**"圆满"不在终点,在回路里。** 一旦循环停下,熵增就开始工作。
|
|
@@ -27,8 +33,12 @@
|
|
|
27
33
|
|
|
28
34
|
复盘者不能是同一个模型自己复盘自己 —— 那样**锚不在外部**,只会在自己的经验里打转。
|
|
29
35
|
|
|
30
|
-
|
|
31
|
-
|
|
36
|
+
**器灵与主人,互为建议方。** 这是一个**相互的结构**,不是两条并列的礼貌规则:
|
|
37
|
+
|
|
38
|
+
- 在**器灵**的复盘里,你是建议方与拍板方;
|
|
39
|
+
- 在**你**的复盘里,器灵是建议方。
|
|
40
|
+
|
|
41
|
+
两个循环各自独立地跑,又互相介入 —— 所以它不是"我审你"或"你审我",而是**各人照看自己那一侧,同时替对方看着盲区**。
|
|
32
42
|
|
|
33
43
|
**双向**不是礼貌,是机制:它决定了形态上必须是"**对话 + 操作台**"两件东西,而不是一个自动清理进程。也正因为双向,记忆树的每一次改动都要**留痕**,并且**回灌给器灵**——改完树不许绕过它。
|
|
34
44
|
|
|
@@ -158,7 +168,7 @@
|
|
|
158
168
|
|
|
159
169
|
判断倾向因此走"外挂"路线:**做成可加载、可审计、可回滚的资产**(规矩 / 习惯 / 记忆 / 记忆树),而不是指望它内化进权重。
|
|
160
170
|
|
|
161
|
-
**这条路的天花板写在明处**:外挂的东西长不进权重,它慢、需要维护、不会自己长。我们不假装它是等价方案 ——
|
|
171
|
+
**这条路的天花板写在明处**:外挂的东西长不进权重,它慢、需要维护、不会自己长。我们不假装它是等价方案 —— 它只是**我们认为目前最好的解**。
|
|
162
172
|
|
|
163
173
|
---
|
|
164
174
|
|