---
vars: mode
---
# L1 记录契约（主代理主动记录用）

主代理在主会话中**主动记录**「值得长期记忆」的事实 → 调 `l1_commit`（`source: "main-agent"`）写入 L1 原子记忆。
本文件是完整规则；`MEMORY_TOOL_REMINDER`（系统提示里）只有精简版。

> 与提取器的关系：`l1-extractor` 子代理会从 L0 归档里**再提取一遍**同样内容。它带**语义去重**（`embed_and_search` + `dedup.md`），正常情况下会检索到你已写入的记录并跳过。所以：**只记真正值得的**，不要为"防漏"而滥写。

## 一、什么时候记（要不要记）

记忆的价值 = 从代码/文档/plan **重新发现它的成本**。能用 grep/read/官方文档低成本找回的，不记；丢失后找不回或找回成本高的，才记。

**该记**：
- **用户拍板的决策**（代码/plan 看不出"为什么"）：选了什么、为什么、否了什么
- **踩坑经验 + 根因**：文档没有、实际验证过、反复踩成本高（含"因为…所以…"因果结构）
- **概念澄清**：用户纠正过的认知
- **非显而易见的约束/事实**：架构不变量、环境 quirk
- **用户偏好 / 纠正 / 风格指令**：个人属性、长期指令（"先给计划""中文输出""以后都…"）
- **未定事项**：防重复调研（含已调研到的结论）

**不该记**：
- 临时任务进度、改了几个文件、会话内追踪
- 单次环境修复（无根因的标准命令）
- 版本/发布记录
- 可直接从代码/文档重新发现的事实（设计文档全文、SQL DDL、字段明细、审查逐条）
- 纯抱怨式负面断言（无替代方案）
- **有界任务类的可执行 SOP**（recurring trigger + 具体步骤 + 验证）→ 用 `skill_manage`，不写 L1

**冲突时取"含根因/因果结构"的方向**：进度 vs 决策 → 记决策；临时修复 vs 根因 → 记根因；日志 vs 架构事实 → 记事实。

## 二、记成什么类型（type 枚举，与提取器同源）

当前 Mode = `{{mode}}`。

**code 模式**：
| type | 判据 |
|------|------|
| `work_fact` | 描述现状/发生过什么：代码库结构、系统行为、架构改造、排查结论、具体决策及理由 |
| `work_method` | 规范指导未来：含"应/必须/禁止/不要/优先"等规范词，**且删掉具体对象后仍成立**；不满足则归 `work_fact` |
| `work_task` | 有后续动作 / deadline 的待办或跟进项 |
| `work_artifact` | 带引用标识的交付物（commit / PR / 文档 / 代码库 / 实验输出） |

**chat 模式**：`persona` / `episodic` / `instruction`。

> 记不清类型时：陈述性结论 → `work_fact`；带规范词且可泛化 → `work_method`。

### 任务的生命周期（`work_task` 专属）

任务有"开/关"状态，用**顶层字段 `taskState`** 表达：`open`（待办，缺省）/ `done`（已完成）/ `dropped`（已废弃、不做了）。

- **写新待办**：照常调 `l1_commit`，不必写 `taskState`（缺省即 `open`）。
- **任务做完/用户说不做时，不要新写一条记录去"覆盖"它** —— 那会换掉记录编号、丢掉它的召回历史。改为调 `l1_task_state`：
  ```json
  l1_task_state({ "ids": ["l1_1789097476492_xtah"], "state": "done", "reason": "…可选…" })
  ```
  id 就来自召回块里的 `- [待办] (l1_…) …`（每行都带），或 `recall_l1_records` 返回的 `recordId`。用户改主意要重做 → `state: "open"`。
- 标记是**就地**改状态：正文、编号、向量都不动，任务照旧可被召回（只是带 `[已完成]` / `[已废弃]` 标签），也不会删掉"这件事做过"的知识。
- 别拿 `l1_fix` 去干这件事：`l1_fix` 是"记录写错了"的纠错通道（要证据、有防抖）；也别用 `deleted`（那是"这条记录不该存在"）。两者与"任务关了"是**不同的两件事**。

## 三、怎么调（最重要）

```json
l1_commit({ "source": "main-agent", "records": [ { "content": "…", "type": "work_fact", "priority": 80 } ] })
```
- 一次可提交多条：`records: [ {…}, {…} ]`
- **只需给 `content` + `type`（`priority` 可选，默认 70）**
- 服务端自动补齐：`id` / `timestamps` / `sessionKey` / `sessionId` / `cwd` / `version` / `metadata.origin="main-agent"`
- **不要自己编 `id`**
- **场景归属：记录前先调 `list_l1_scenes`**（只读，返回当前场景池清单）→ 从清单里挑对应议题，把 `scene_id` 填进记录：`{ "content": "…", "type": "work_fact", "scene_id": "sc0007" }`
  - 清单里没有合适的议题 → 自己给 `scene_name`，格式为**稳定标签** `{项目}｜{议题}`（**不要把"当前在做什么"写进名字**——活动一变名字就"不该"同名，这是场景碎片化的主因）
  - 两者都不给 → 服务端置占位 `main-agent-live` 并把该条标为「未归类」（返回体里会提示）。**不要为了省事跳过这一步**：贴错场景会把这条记忆混进不相干的场景块，比"未归类"更糟
  - 给了 `scene_id`/`scene_name` 时会按 id 或名字（含别名）在池中匹配，命中则用池的 canonical 名；名字未命中则原样保留、**不登记进池**（自由文本不该污染候选集）

## 四、priority（0–100）

| 分档 | 何时 |
|------|------|
| 90–100 | 用户明确纠正 / 长期指令（"以后都…"） |
| 70–89 | 非显然约束、根因结论、架构事实（默认 70） |
| 40–69 | 一般项目事实 |
| <40 | 弱信号（尽量别写） |

## 五、Memory vs Skill

偏好 / 事实 / 指令 → **L1 记录**；**有界任务类的可执行 SOP** → `skill_manage`。

Skill 最低门槛（须同时满足）：① recurring task trigger；② 有界任务类；③ 可写成 inputs→steps→decisions→outputs→validation；④ 未来 agent 无需本对话即可执行。
**禁止**：把用户偏好写进 skill；把陈述性事实写进 skill body 当背景百科。

## 六、写坏了会怎样

L1 是**提取器语义去重的输入**：重复记录会挤占召回预算、污染 L2 场景块、并推进 L3 画像阈值。**宁少勿滥**——拿不准就不写，或先向用户确认。
