# 粒度与认知层级 — 术语对齐

> 解决"小步 / 轮 / 任务 / 经验 / 环境认知 / 技能"几个概念之间反复混淆的根因。
> 任何对实现细节有疑问的同学**先看这一篇**，再去读对应模块的 `ALGORITHMS.md`。
>
> 配套阅读：
> - 算法规范：`docs/Reflect2Skill_算法设计核心.md`
> - 算法 ↔ 实现对照：`docs/ALGORITHM_ALIGNMENT.md`
> - 捕获管线：`core/capture/ALGORITHMS.md`
> - 检索三层：`core/retrieval/ALGORITHMS.md`

---

## 1. 三个交互粒度（必须先锁死）

| 术语 | 内容 | 实现里的对应 |
|---|---|---|
| **小步 (step)** | 一次 `(think + 动作 + 观测)` 的完整迭代。<br/>具体形态有三种：<br/>① `<think_i + tool_i + tool_output>` 一个工具调用<br/>② `<assistant 最终回复>` 一句模型对外的回复<br/>③ `<user_query + assistant_answer>` 没调工具的纯问答 | = 一条 `traces` 表行 = 文档的 `f^(1)_{k,t}` |
| **轮 (turn / round)** | `<user_query + think_1 + tool_1 + … + assistant_answer>`<br/>用户感知的"一次问答" | = 多条 `traces` 共享同一 `turn_id`<br/>= 前端 `MemoryGroup` 一张卡<br/>**算法层不存在这个概念** |
| **任务 (task / episode)** | 同一 topic 的多个轮串成的完整任务<br/>`<轮_1 + 轮_2 + …>` | = `episodes` 表一行 = 同一 `episode_id` 的所有 `traces` |

> **关键认知**：**算法所有操作的粒度只有"小步"和"任务"两种**。
> "轮"是给用户看的视觉聚合，对算法**完全不可见**——不打分、不检索、不诱导、不结晶。

代码中保证这一点的几处关键设计：
- `core/capture/step-extractor.ts` 把一个用户消息触发的所有动作拆成多个小步 sub-step，全部带同一个 `turnId` 写入 `traces.turn_id`。
- 前端 `viewer/src/views/MemoriesView.tsx::buildGroups` 按 `(episodeId, turnId)` 把多条小步聚合成一张卡片显示。
- 检索路径 `core/retrieval/tier2-trace.ts` 永远以 `traces` 行为最小单位，从不读 `turn_id`。

---

## 2. 打分粒度

每个动作有自己的"分"吗？看下表：

| 对象 | 有自己的分吗 | 字段 / 来源 |
|---|---|---|
| **小步** | **有**：反思权重 `α_t` + 步价值 `V_t` | `traces.alpha` / `traces.value`，由反思加权回溯算出 |
| **任务** | **有**：终局奖励 `R_human` | `episodes.r_task`，任务结束时 LLM 按 rubric 打分 |
| **轮** | **没有** | 前端展示时取轮内 V 平均值凑一个，仅作展示 |

### 打分的实际触发流程

按算法文档 §0.6 + `core/capture/capture.ts::runReflect`：

```
任务结束 (episode.finalized 事件)
   ↓
1 次 LLM 批量调用
   ├─ 算 R_human (任务级，1 个标量)
   └─ 给任务内每个小步打 α_t (N 个权重)
   ↓
反思加权回溯 (core/reward/backprop.ts)
   ├─ V_T = R_human                                ← 任务最后一步
   └─ V_t = α_t · R_human + (1-α_t) · γ · V_{t+1}  ← 往前回溯
   ↓
写回每条 trace 的 reflection / alpha / value 字段
```

**注意**：
- **不是每个小步发生时都跑 LLM 打分**，而是任务结束时一次性批量打。lite 阶段只写空骨架（`alpha=0, value=0, reflection=null`），等 `runReflect` 阶段填充。
- **R_human 是任务级唯一信号**，回溯到每个小步去——不是每个小步都问一次用户"这一步好不好"。
- **轮在数学公式里完全不出现**。

### 例子 — 一次性看清楚

```
任务 ep_42 (= 同一 topic 的 3 轮)
  轮 t1: 查 cpu/内存/硬盘 → 4 个小步
  轮 t2: 在 Alpine 装 lxml → 4 个小步
  轮 t3: 用户回复"好的" → 1 个小步
共 9 个小步

任务结束 → 用户最后说"好，记下来" → R_human = +0.85
                                       ↓
对 9 个小步分别打 α (反思识别"关键发现"的高，闲聊的低)
                                       ↓
反思加权回溯 → 9 个小步各拿到自己的 V
                                       ↓
关键步 step2.1 (Alpine + pip lxml → xmlsec1 not found, α=0.7)
  最终拿到 V ≈ 0.78 (高)
闲聊步 step3.1 (用户回复"好的", α=0.2)
  最终拿到 V ≈ 0.15 (低)
```

`R_human = +0.85` 是任务级，整任务唯一一个；`V` 是小步级，9 个小步 9 个值；轮 t1 / t2 / t3 没有自己的"分"。

---

## 3. 检索粒度

按算法文档 §2.6 三层。**没有任何一层是按"轮"召回的**——这是设计选择，不是疏漏。

### 3.1 第一层 · 技能召回（任务入口）

| | |
|---|---|
| 触发时机 | 新任务到来（用户给出 query 那一刻） |
| 召回粒度 | **一个完整技能对象**（trigger / procedure / verification / scope / decision_guidance / domain_model） |
| 匹配方式 | 路由器看 user query 是否命中某个**激活技能**的 trigger |
| 实现 | `core/retrieval/tier1-skill.ts` |

### 3.2 第二层 a · 单步小步召回（执行中按需）

| | |
|---|---|
| 触发时机 | 当前小步执行失败（exit code≠0、错误模式命中、反思出现失败信号） |
| 召回粒度 | **1 条小步**（一行 `traces`） |
| 匹配方式 | 三路并行：error signature 结构匹配 / state embedding 语义匹配 / 标签过滤 → 按 V 排序取 Top-K |
| 实现 | `core/retrieval/tier2-trace.ts::runTier2`（trace 通道） |

### 3.3 第二层 b · 子任务序列召回

| | |
|---|---|
| 触发时机 | 当前子任务的"目标"和某段历史子任务的"目标"语义相似 |
| 召回粒度 | **N 条连续小步**（按 episode 业务连续性拼，**不按 turn_id 拼**） |
| 匹配方式 | goal-to-goal 语义相似度 + 整段累计 V 为正 |
| 实现 | `core/retrieval/tier2-trace.ts::bucketByEpisode` + `EpisodeCandidate` |

### 3.4 第三层 · 环境认知召回（深层推理时）

| | |
|---|---|
| 触发时机 | 反思 ρ_t 里出现**结构性不确定**——不是操作性疑问 |
| 召回粒度 | **一个环境认知对象**（𝓔 空间结构 / 𝓘 行为规律 / 𝓒 约束禁忌） |
| 实现 | `core/retrieval/tier3-world.ts` |

#### "结构性不确定" vs "操作性疑问"

| 反思内容 | 类型 | 触发第三层吗 |
|---|---|---|
| "pip 装失败了，可能要换个命令" | 操作性 | ❌ 单步召回就够 |
| "命令找不到 pg_config，可能是 PATH 问题" | 操作性 | ❌ |
| **"装失败了，但我不确定是缺系统库还是 musl 不兼容——这俩走的是完全不同的修复路径"** | **结构性** | ✅ 召回"Python 三层依赖 + Alpine musl 边界" |
| **"改了配置后服务没生效，不知道这种配置是热加载还是要重启"** | **结构性** | ✅ 召回"该服务行为规律" |
| **"删了这个目录后另一组件挂了，不知道这两个之间什么依赖关系"** | **结构性** | ✅ 召回"项目空间结构" |
| **"用户让我 DROP TABLE，不确定这是不是生产环境"** | **结构性 (约束类)** | ✅ 召回"环境约束与禁忌" |

判别信号大致是：反思里出现 *"不确定 / 哪一层 / 是不是因为 / 副作用 / 影响范围"* 等结构性词汇，或遇到 embedding 在历史所有小步里都找不到相似的全新状态。

### 3.5 为什么算法故意不设计"按轮召回"

三个直接理由：

1. **一轮 ≠ 一个完整子任务**。同一 user query 可能展开多个独立子任务（"查 cpu / 装 lxml / 跑测试"塞在一句话里）；同一子任务也可能横跨多轮（用户分 3 轮逐步引导装一个东西）。**轮和子任务正交**。
2. **单步精准检索是必须的**。当前小步出 `'pg_config not found'` 错时，最想要的就是"上次哪一步遇到这个错、怎么救活的"——按轮召回会把当时整轮的所有小步连同汇报性回复一锅端，稀释精准信号。
3. **上下文连续性该由"子任务"承担**。子任务边界是"这段连续小步是不是在做同一件事"，按 episode 拼；轮边界是"这些小步是不是同一个 user query 引出的"，没有归纳价值。

### 3.6 用例子对比

未来用户在 Debian 容器执行 `pip install cryptography` 失败：

| 召回方式 | 实际拿到什么 | 评价 |
|---|---|---|
| **算法实际做的（单步）** | 仅 step2.1：Alpine + pip lxml → 'xmlsec1 not found' (V=0.78) | ✅ 精准对症 |
| **算法实际做的（子任务）** | step2.1 + step2.2 + step2.3 三条连续序列，整段动作链作为参考方案 | ✅ 给出完整救活流程 |
| 假想：按轮召回 | step2.1 + 2.2 + 2.3 + 2.4（含 "装好了" 汇报） | ❌ 多了无用汇报 |
| 假想：按轮召回 | 把不相关的轮 t1（lscpu / free / df）也一起召回 | ❌ 容易混入无关轮 |

---

## 4. 经验 / 环境认知 / 技能 的层级关系

### 4.1 全景

```
事实层                  ┌─────────────┐
                        │   小步 ←── 用户每次 query 引出的每个动作
                        │  (记忆)
                        └──────┬──────┘
                               │ 跨任务模式归纳
                               │ (每条小步写入即触发，轻量)
                               ▼
认知层                  ┌─────────────┐
                        │    经验      │ "这类子任务该怎么做"
                        │ (子任务策略) │
                        └──────┬──────┘
                               │ 多条同领域经验背后稳定原理抽象
                               │ (异步触发)
                               ▼
                        ┌─────────────┐
                        │  环境认知    │ "这个环境长什么样"
                        │ (世界模型)   │
                        └──────┬──────┘
                               │
能力层    经验 + 环境认知       ▼
          + 三阈值满足 → ┌─────────────┐
          + 双重检验    │   技能       │ "路由器可调用的成熟能力"
                        └─────────────┘

"轮" 不在这张图里 ── 它只活在前端展示层
```

### 4.2 一句话定义对照

| | 是什么 | 回答的问题 | 类比 |
|---|---|---|---|
| **小步 (记忆)** | 一次 `(think + 动作 + 观测)` 的 grounded 记录 | "这一步做了什么、发生了什么" | 病历的一条诊疗记录 |
| **经验** | 跨任务相似小步归纳出的子任务策略 `(trigger / procedure / verification / boundary)` | "在这种条件下该怎么做" | 临床路径文档 |
| **环境认知** | 多条同领域经验背后抽象的环境压缩认知 `(空间结构 / 行为规律 / 约束禁忌)` | "这个环境长什么样、有什么规律、什么不能做" | 解剖学 + 生理学 |
| **技能** | 经验 + 环境认知 打包成可调用对象 + 持续修订的可靠性 | "当前 query 该不该调用这条策略，调用后怎么走" | 一名熟练的内科医生 |

### 4.3 两两关系

#### 经验 ↔ 环境认知

**单向抽象 + 反向增强**：
- 多条经验背后的稳定组织原理 → 抽象出 1 条环境认知（**素材关系**）
- 环境认知反过来给经验提供结构先验，让经验的 trigger 更精准（**先验关系**）

数量关系：1 条环境认知背后通常有 N≥2 条经验；1 条经验**不一定**对应到环境认知（小众场景没积累出领域规律就没有）。

#### 经验 + 环境认知 → 技能

**结晶**：技能 = 1 条经验作为骨架 + 0 或 1 条环境认知作为先验 + 决策修复产出的偏好/反模式 + 用小步级 V 算出的可靠性。

```
Skill {
  trigger / procedure / verification / scope    ← 主体来自经验
  evidence_anchors                              ← 来自小步证据
  domain_model                                  ← 来自环境认知（可选）
  decision_guidance                             ← 来自决策修复
  reliability η                                 ← 用 source_steps 的 V 算出来
}
```

**不是每条经验都能变技能**——必须满足三阈值（频率 / 增益 / 稳定性）+ 双重检验。
**不是每条技能都带环境认知**——小工具型技能可以没有，复杂技能往往携带。

#### 经验 / 环境认知 / 技能 ↔ 小步

| | 与小步的关系 |
|---|---|
| **经验** | 直接消费小步：写入即触发关联检查、用小步 V 加权更新 procedure / scope / η |
| **环境认知** | 间接消费小步：经过经验中转，但 source_policies 可追溯到底层小步 |
| **技能** | 直接挂证据：`evidence_anchors` 字段指向具体小步 id；调用后产生新小步反馈又会反向修订技能 |

#### 经验 / 环境认知 / 技能 ↔ 轮

**全部无关**。算法的：
- 经验诱导：按"跨任务相似小步集合"
- 环境认知抽象：按"同领域多经验背后的原理"
- 技能结晶：按"独立任务数 + 小步级 V 增益 + 稳定性"

这三个动作里**没有任何一个用到"轮"这个边界**。

---

## 5. 完整闭环（用例子从头串到尾）

```
1. 用户在任务 ep_42 内问 3 轮 → 写入 9 条小步记忆 (带 turn_id / episode_id)
                                     ↓
2. 每条小步写入时即时触发增量经验关联检查 (轻量，无 LLM)
   - step2.1 写入时和历史 task_X 的 pg_config 小步配对 → 诱导新经验
                                     ↓
3. 用户关闭任务 (说"好的，记下来") → 触发任务级评分
   - 1 次 LLM 调用：算 R_human=+0.85 + 给 9 个小步各打 α
   - 反思加权回溯：算出 9 个小步各自的 V
   - 写回每条记忆的 reflection / alpha / value 字段
                                     ↓
4. 异步：发现"容器系统库缺失排障"经验已关联 ≥3 个独立任务
   - 满足三阈值 → 触发结晶
   - 收集证据集 (证据全是"小步"，不是"轮"也不是"任务")
   - LLM 诱导 → 双检验 → 激活
                                     ↓
5. 未来某次用户说"在 Debian 装 cryptography":
   - 任务入口     → 召回完整技能对象
   - 某步失败     → 召回 1 条小步 step2.1 给参考
   - 子任务相似 → 召回 step2.1+2.2+2.3 整段作为参考方案
   - 深层反思     → 召回环境认知"Python 三层结构"
   ★ 这一系列召回，没有任何一次是按"轮"召回的
                                     ↓
6. 这次任务结束 → 又是 1 次任务级评分 → 又一轮 V 回溯
   → 经验/技能可靠性更新
```

---

## 6. 经验 vs 环境认知 — 边界裁剪（FAQ：要不要合并？）

回答开发者反复问到的那个问题：**"经验和环境认知能不能合并成一层？"**

**结论：不合并**——这两层是两种不同种类的知识，合并会同时损伤两边。但你产生这个疑问通常源自两个真实痛点（**边界模糊** + **环境认知很少被结晶出来**），下面分别处理。

### 6.1 为什么不合并 — 7 条理由

| # | 理由 | 关键差异 |
|---|---|---|
| 1 | **回答的是不同种类的问题** | 经验 = 过程性 (procedural) "怎么做"；环境认知 = 陈述性 (declarative) "环境长啥样" |
| 2 | **泛化粒度不同** | 经验跨任务迁移；环境认知跨**领域**迁移。合并 = 塌缩成单一泛化层 |
| 3 | **更新触发频率不同** | 经验高频（每条新小步触发）；环境认知低频（多经验稳定后抽象）。合并 = 强行一种节奏 |
| 4 | **LLM 输出 schema 不同** | trigger/procedure/verification/boundary vs 𝓔/𝓘/𝒞。合并 = 8 字段膨胀，质量下降 |
| 5 | **在技能里的角色已经分清** | 技能里 `domain_model` 是**可选**字段——小工具型技能不需要环境认知 |
| 6 | **检索注入策略不同** | 经验作为技能骨架在任务入口必注入；环境认知仅在反思出现结构性不确定时才注入第三层 |
| 7 | **算法上是不同的数学对象** | $f^{(2)} = (\phi, \pi, \kappa, \Omega, \{f^{(1)}\})$ vs $f^{(3)} = (\mathcal{E}, \mathcal{I}, \mathcal{C}, \{f^{(2)}\})$ |

### 6.2 你产生这个疑问的真实痛点

| 痛点 | 真相 | 处理方式 |
|---|---|---|
| **A. 实现里环境认知很少被生成出来，世界模型表常年空着** | 这是**冷启动期**正常现象——环境认知需要"多条同领域经验背后的稳定原理"，单用户、单 agent 场景下经验本身就稀疏 | 不是"该不该合并"，是"什么时候才该投入做"。算法 §0.4 明确说"每一条 L3 知识都省掉了未来的探索步骤和 Token"——价值在长期累积**之后**显现 |
| **B. 经验 vs 环境认知的边界在实践里有时模糊** | 这是文档/prompt 没把边界划清，**不是该合并的理由** | → 看下面 6.4 节的判别表 + 同事实多框架对照表 |

### 6.3 折中方案对比（如果你确实想降低工程负担）

| 方案 | 做法 | 优点 | 缺点 |
|---|---|---|---|
| **A · 共表不共类型** | 物理上合并 `policies` + `world_model` 进 `knowledge` 表，加 `kind` 列区分；代码层仍是两个 repo + 两套 prompt | 减少 30% 重复代码 | prompt + subscriber 还是两套，收益有限 |
| **B · 让环境认知可关闭** | 加配置 `algorithm.l3Abstraction.enabled = false`，小型部署直接关掉 | 不删功能、不改算法、降低冷启动期成本 | retrieval / capture 都要加分支；后续再开需清空旧库 |
| **C · 不动代码，加文档** | 只把"经验 vs 环境认知判别表"写进文档（即下面 6.4 节） | 零工程成本 | 解决不了"看着空表碍眼"的心理负担 |

**推荐方案 C**——不要因为冷启动期数据稀疏就否定算法的分层结构。当前实现里环境认知模块代码量并不大（`core/memory/l3/` 200 行 + `tier3-world.ts` 100 行），维护成本可控。**真正的修复点是 prompt + 文档把边界划清**——一旦下游真实场景累积起来，分层带来的可解释性收益会远超合并节省的几百行代码。

### 6.4 经验 vs 环境认知 判别表（既给读者看，也给 LLM 看）

#### A. 判别速查

| 维度 | 经验 (procedural) | 环境认知 (declarative) |
|---|---|---|
| **回答** | 在条件 X 下应该做 Y | 这个环境长什么样 / 怎么响应 / 什么不能做 |
| **是否含动作** | 必含具体动作模板（apk add / pip retry） | **不含动作**——只描述结构、规律、约束 |
| **句式** | 命令式 / 条件式："当 X 时，做 Y" | 陈述式："X 是 Y" / "X 会导致 Y" / "禁止 Z" |
| **所属层** | 子任务策略 | 环境世界模型 |
| **变量化程度** | trigger 可参数化（容器类型 / 包名） | 不参数化——是环境本身的事实 |
| **可验证性** | "执行后 import 成功" 这种步骤级判据 | "拓扑事实是否被多个经验佐证" 这种存在性判据 |
| **失效条件** | 步骤跑失败 → 经验 boundary 收缩 | 环境实际变了 (升级/迁移) → 环境认知整条作废 |

#### B. 同事实多框架对照（避免混淆）

下面三组都是**同一个底层事实**用两种形态表达。生成时**不要把右边的框架塞进左边的对象**，反之亦然。

| 底层事实 | 经验形态（怎么做） | 环境认知形态（环境长啥样） |
|---|---|---|
| Alpine 容器装 Python C 扩展库会失败 | "当 Alpine 容器内 pip install 因系统库缺失编译失败时 → 解析缺失组件 → `apk add <pkg>-dev` → 重试 pip" | "Python 包依赖分三层（系统库 / C 扩展 / 纯 Python）；容器镜像默认只装第三层；C 扩展类需要补装系统库" |
| macOS 的 sed 和 GNU sed 不兼容 | "当需要在 macOS 上做正则替换时 → 优先用 Python 脚本，避免 `sed -i`" | "macOS 自带 BSD sed，与 Linux 上的 GNU sed 在 `-i` / 反向引用 / 扩展正则上语法不同" |
| 改了配置后服务没生效 | "当改了 `config.yaml` 后 → 必须 `service restart`，不能依赖热加载" | "该服务只在启动时读 config 一次，运行时无 watcher；约束：禁止假设配置是热加载的" |

**判别口诀**：
- 看到"做什么 / 步骤 / 命令 / 顺序" → 写进**经验**
- 看到"是什么 / 在哪 / 怎么响应 / 不能怎样" → 写进**环境认知**
- 同一个事实可以同时存在于两边，**但表述形态必须分开**

#### C. 反例：常见的越界写法（要在 prompt 里禁止）

**经验越界写成环境认知形态**（应该被 prompt 拒绝）：
- ❌ trigger: "Python 包依赖三层结构" ← 这是环境事实，不是触发条件
- ❌ procedure 里写 "Alpine 用 musl libc" ← 这是环境事实，不是动作步骤
- ❌ caveat 里写 "容器镜像通常只装纯 Python 层" ← 这是环境规律，不是步骤的注意事项

**环境认知越界写成经验形态**（应该被 prompt 拒绝）：
- ❌ inference 里写 "pip 失败时应该装 -dev 库" ← "应该做"是动作，属于经验
- ❌ constraint 里写 "不要用 sed，改用 Python" ← "改用"是动作偏好，属于经验/决策修复
- ❌ environment 里写 "通过 `apk add` 装系统库" ← 描述了动作，属于经验

---

## 7. 三个问题的一句话答案（便于快速回顾）

1. **打分**：每个**小步**有自己的 α 和 V；整个**任务**有一个 R_human；**轮**没有自己的分（前端展示时取轮内 V 平均凑一个）。
2. **检索**：技能 = 任务入口召回；单步 = 当前小步失败时召回；子任务 = 多个连续小步序列召回（按 episode 拼，**非**按 turn_id 拼）；环境认知 = 深层反思时召回。**没有任何一层是按"轮"召回**。
3. **生成**：小步 → 跨任务模式诱导经验 → 多个经验抽象环境认知 → 经验 + 环境认知 + 三阈值满足 → 结晶技能。**整个生成链路也没有"轮"这个概念**。

> "轮"只活在前端那张卡片里，是给你看的；算法的所有数学公式、所有触发逻辑、所有粒度，要么是"小步"要么是"任务"，从来没有"轮"。
