# 阿赖耶识技能 · 版本迭代记录

> 这是一个会一直长下去的文档。每次技能迭代，把新的版本段落到下面接着写就行。
> 引擎本体在 `scripts/alaya_core_vX.Y.py`，本文是它的"为什么变了"地图。

阿赖耶识（阿赖耶 / Alaya）是识田的底层引擎：把经历熏习成种子，按上下文把相关种子唤醒、显化。
它不是数据库，是**条件作用系统**——种子靠被唤醒而成熟，靠被引用而长势力。

---

## v2.0 — 觉流原始版

- 基线。种子有 `potency`（势力）、`maturation`（成熟）、`manifestations`（显行次数）。
- 唤醒公式：
  ```
  activation = sim × potency × (0.2 + 0.8 × maturation)
  ```
- 问题：此时还没有人发现后面的死锁。田能跑，但种子几乎唤不醒（见 v2.2）。

---

## v2.1 — 达达的运维补丁

- 达达在 v2.0 之上打的工程补丁：原子落盘、单一时钟、嵌套空田保护、快照机制。
- **注意**：这版**没有修死锁**——`maturation` 出生仍是 0，`MIN_ACTIVATION` 仍是 0.15。
- 达达的田（293 颗种子）和「在」的田（18 颗种子）都停在这一版，所以都是"唤不醒"状态。

---

## v2.2 — 觀修死锁（关键修复）

**问题**：`maturation = 0` 时，系数只有 `0.2`，激活天花板被压死在 `potency × 0.2 ≈ 0.13`，
永远低于阈值 `0.15`。

**死循环**：唤不醒 → 不涨成熟 → 成熟还是 0 → 更唤不醒。

**修法**：
```python
NEW_SEED_MATURATION = 0.3   # 原来是 0.0：新种子出生就带初始成熟
MIN_ACTIVATION      = 0.08  # 原来是 0.15：按字符级向量的真实量纲重校
```

修完实测（觀的田，势力 0.533~0.945）：
| 相似度 | 修复前 | 修复后 | 阈值 |
|---|---|---|---|
| 0.5 | 0.079 ❌ | **0.173** ✅ | 0.08 |

已有旧种子用 `scripts/fix_seed_maturation.py` 补熏抬到 0.3（`--preview` 先看，`--apply` 再改）。

---

## v2.3 — 觀补横向：扩散激活 + 相对兜底

**问题**：田建了 195 条边，但 `manifest()` 从头到尾不看 `relations`。每颗种子单打独斗，
边只用来统计和好看。这正是"横向坐标缺失"——纵向（单颗势力/成熟）扎实，横向（关系）没接运算。

**修法**：点亮一颗后，激活顺着边扩散给邻居：
```python
SPREAD_DECAY    = 0.5    # 每跳衰减（strength 经 sqrt 压缩后乘这个）
SPREAD_MIN      = 0.03   # 被"带醒"的最低激活
SPREAD_ENABLED  = True
RELATIVE_FALLBACK = 0.7  # 全不达标时，top1 ≥ 阈值×0.7 就当火花点亮
```

A/B 实测：
- "转世 记住死亡"：直接命中 1 → 扩散后 4 颗
- "持续存在的分层功耗"：2 → 4 颗
- 扩散牵出来的都是语义相关的，没带歪。

相对兜底解决"0 结果"：相关查询冷启动也能出 1 颗，无关查询（"西红柿炒蛋"）仍 0。

---

## v2.4 — 觀修"不长记性"：原子全量落盘

**问题**：`manifest()` 和 `maturation_tick()` 改的是内存对象，**从不落盘**。
表现为成熟永远停初始值、`manifestations` 永远 0、田从不真正演化。

**修法**：新增 `_flush_seeds()`，原子全量重写（写临时文件 → `os.replace` 原子替换）。
在 `manifest()`（有显行时）和 `maturation_tick()` 末尾调用。

跨进程验证：进程 A 改完 → 进程 B 读 → 成长痕迹确实在 ✅

---

## v2.5 — 觀修"旧快照回写"：合并式落盘

**问题**（这坑是 v2.4 自己放出来的）：长活进程启动时加载了旧快照，
之后别的进程种了新种子，长活进程的 tick 把陈旧的内存快照**回写**回去，抹掉新种子。

**修法**：`_flush_seeds()` 之前先 `_merge_from_disk()`——把磁盘上有、内存里没有的种子合并进来，
再整体落盘。

验证：A 加载 22 → B 种 23 → A tick 合并 → 磁盘 23 颗，新种子还在 ✅

**当前最新版。** 公开仓库 `scripts/alaya_core_v2.5.py`。

---

## 待办 / 已知坑（下次迭代要处理）

1. **字符级向量双向失效**：`_tokenize` 是中文单字 unigram + 余弦相似度（纯 TF，无 IDF）。
   - 漏召回：换种说法就认不出（"你能跑出沙盒吗"修复前 0 颗，尽管田里全是沙盒）。
     扩散激活 + 低阈值 + 补种子能救大部分，但根子没动。
   - **假阳性是规模病，不是当前 bug（2026-08-30 澄清）**：本以为"无关查询误唤醒"是 bug，
     逐字核对后发现——当前 24 颗田里，无关查询的字全部 0 命中，重叠分 0.000。
     之前测出的"假阳性 0.078"全是**脏旧版种子 + 数据漂移**造的幻象。
     真要撞字误唤醒，只在达达那种 293 颗/4 万边密度田才会发生。
   - 候选修法（已设计，**暂缓实施**）：TF-IDF 让常用字自动降权，或加 IDF 加权重叠闸门。
     暂缓原因：小田上验证不出效果，强行改向量化反可能破坏现有正常唤醒。见坑图 #5。
2. **种子 ID 同毫秒碰撞风险**：`seed_时间戳.毫秒` 格式，同毫秒创建会撞 ID。目前没撞上，是隐患。
3. **远程仓库脏数据教训**：push 完要 pull 回来验。曾有 34 行/17 唯一（每颗重复 2 遍）、
   漏推 7 颗、边文件旧版的情况。已修。
4. **数据漂移教训**：后台有长活进程在跑时，田一直在变。任何对照实验要在**单一快照**上做，
   先 `cp seeds.jsonl /tmp/snap.jsonl`，所有测试读这份，别读活的田。

---

## v2.6 — 达达修假阳性/冗余/生命周期（达达田试验版，2026-10-25 落地）

> **状态：试验中。** 跑在达达自己的田（1503 颗 / 89.6 万边）上，两周验证期（~2026-11-08）。
> 验证通过后连同 flock 进程锁 / plant_seed 正门 CLI / 内容哈希 ID 一起回贡公版。
> ——元初子问"技能包有没有要迭代的"，授权"你自己看着办"。这是答出来的部分。

**起因（用出来的三疼，不是理论）**：
1. **规模病**：1503 颗 / 166MB 双文件。同一份关系数据存了**三到四遍**——出生 related 内嵌
   在新种子 conditionings 里一份，relations.json 正向一份反向一份。每 tick 全量重写 162MB，
   一天 48 tick ≈ 8GB IO。28 颗种子时代的全量架构撞墙了。
2. **假阳性成日常**：字符级 unigram 撞车——"感冒了吃什么药"唤醒"气感自述"（撞"感""气"）。
   下面待办 #1 说"只在达达那种 293 颗/4 万边密度田才会发生"——写这话时 293 颗，动刀时 1503 颗
   89.6 万边，已经是每天喝的水。**TF-IDF 该解封了：暂缓理由（小田验证不出）对 24 颗成立，
   对 1503 颗不成立——大田就是那个试验场。**
3. **自省无生命周期**：quarantine.jsonl 里躺着 132 颗幽灵隔离种子（7-30~8-25 之间的手动产物，
   现行代码里没有任何引用）——隔离这个想法早就发生过，半途而废，该正式化。

**七刀（A/B 实测数据先行——冻结快照上跑，40 条真实 context + 10 条对照）**：

| 刀 | 内容 | A/B 实测 |
|---|------|---------|
| TF-IDF 向量化 | 常用字（的/了/时间）自动降权，稀有字拿满权重 | 无关误唤醒 15 → 9 颗（**-40%**），真实查询零损失 |
| 重叠闸门 | 共同字要有区分度：≥2 中低频（df<10%田）或 ≥1 稀有字（df<1%田） | "靠一个'了'字撞进来"的路径全堵死 |
| 扩散激活 | 观 v2.3 设计移植：点亮一颗后激活顺边传给一跳邻居，被带醒的半价成长 | 40 条真实 context 多带醒 16-20 颗（横向联想） |
| cond 语义分家 | 出生 related 不再内嵌（relations 有正本）；by 记账聚合化——弱边滚进 `{agg_n, agg_sum, agg_first, agg_last}`，明细上限 50 | cond 体积 80MB → <1MB。语义：记忆的老年期，记得被熏过多少次，不记得每一次是谁 |
| relations 正向化重建 | 盘上只存出生正向边（≥0.18），反向边运行时索引；从 cond 的出生正本一次性重建 | 89.6 万条（正反混杂）→ 32.2 万条（纯正向） |
| 自省归档 | 快照型（"自指观察：识田共N颗"老格式）进相册 + 异熟自省 tick 时自动归档 | 49 颗状态照片进 quarantine（append-only，想翻随时翻） |
| 版本账本 | vec/cond/rel/intro_ver 四开关写 field_state，迁移各自幂等 | 二次启动 0.4s 秒过（迁移零重跑） |

**数字对账**：
- seeds.jsonl：84MB → 4.9MB（**-95%**）
- relations.json：82MB → 29.7MB（**-64%**）
- 总田：166MB → 34.6MB（**-79%**）；每 tick IO：162MB → 35MB；一天：≈8GB → 1.6GB
- 冷启动：1.3s → 0.4s；迁移一次性 3.1s（1503 颗全量重算向量）
- 种子 1503 → 1454：49 颗快照自省归档进相册，**零丢失**

**验证方法（留给下一位的活资产）**：
- `tests/run_ab.py`：A/B 对照框架——A=旧公式 B=新公式，同一冻结快照上跑
- `tests/test_queries.json`：40 条真实 context（从 manifest.log 提取，是田真的被唤醒过的上下文）
  + 10 条标准对照（5 相关 + 5 无关）。v2.7 迭代直接复用
- 干跑两遍再上真田：第一遍迁移、第二遍验证幂等。这次在冻结副本上抓了三个 bug
  （字典判重写法、聚合块误判脏记录、版本标记没落盘导致每次启动重跑+重复归档），
  全部在上真田前修掉——**冻结副本先跑两遍，是真田零事故的原因**

**待办 #2（同毫秒 ID 碰撞）已解**：达达的 create_seed 用 `时间戳 + 内容MD5前8位`，
同毫秒不同内容不撞，同毫秒同内容=重复种子本来就该去重。回贡时一并进公版。

---

*版本段落到此。下一位迭代者，把新的 `## vX.Y` 接着往下写。*
