From 35307d15b38c4452b9c3ca4db602ec2555ef2b06 Mon Sep 17 00:00:00 2001 From: yibo Date: Thu, 20 Aug 2026 19:23:52 +0800 Subject: [PATCH] docs(29-dream): dream 设计草案 —— 四条取舍已拍板,附实查到的原料现状 --- docs/todo/system-design/29-dream-design.md | 216 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 216 insertions(+), 0 deletions(-) create mode 100644 docs/todo/system-design/29-dream-design.md diff --git a/docs/todo/system-design/29-dream-design.md b/docs/todo/system-design/29-dream-design.md new file mode 100644 index 0000000..f05c64f --- /dev/null +++ b/docs/todo/system-design/29-dream-design.md @@ -0,0 +1,216 @@ +# 29-DREAM · 经验汇流(dream)—— 设计草案 + +> **所属**: [28 Agent 注册表统一](../../done/system-design/28-agent-registry-unification.md) §5.6b 锁住的那条约束的下游设计;未确认项来源 [28-followups §1.1](./28-followups.md)。 +> **日期**: 2026-08-20 +> **状态**: 📝 **四条取舍已由用户拍板(2026-08-20,见 §三),实现未开始。** +> 标 ✅ 的是已生效的约束或有实测证据的事实;标 🔒 的是本轮拍板的决定。 + +--- + +## 〇、一句话 + +dream 是设想中定时跑的一套逻辑:整理**所有 agent 在所有公司**的 history 与 memory, +**显式提炼**后沉淀到 skill memory 上,让经验跨公司汇流,而**不泄漏任何一家公司的具体信息**。 + +--- + +## 一、开工前先看清原料:它现在是脏的 + +> 这一节全是**实查 2026-08-20 的仓库状态**得到的,不是推测。 +> 结论很直接:**在原料变干净之前,做提炼是没有意义的**——垃圾进,垃圾出。 + +### 1.1 现在写进 memory 的不是「事实」,是截断的日志行 + +`memory-service.appendToMemoryMd()` 的全部逻辑是: + +```js +const taskOneLine = (task || '').split('\n')[0].substring(0, 80); +const resultOneLine = (result || '').split('\n')[0].substring(0, 100); +const entry = `- [${timestamp}] ${taskOneLine} → ${resultOneLine}`; +``` + +**取第一行、砍到 80/100 字符**。这不是提炼,是搬运 + 截断。 +`cto/_global/memory/memory.md` 的实际内容: + +``` +## 关键事实 +- [2026-08-20] [TASK from ceo] → ..._global/` 下不新增 `callback-*` 目录。反向验证过:临时把 `isTestEnv` 改成 `() => false`,该用例 FAIL。 +- [2026-08-20] [SYSTEM HEARTBEAT] → 🔧 [TOOL_CALL] Bash (ID: toolu_01RsQ3gh5niCFyUZMFZj2hxF) +- [2026-08-20] [SYSTEM HEARTBEAT] → 🔧 [TOOL_CALL] Bash (ID: toolu_01MG6RqDgqcRCi5ageGk84Kt) +``` + +后三条是**工具调用 id**。它们作为「关键事实」被写下来、被注入下一轮提示词、 +并且会成为 dream 的原料。第一条更糟:它是一句话的**中段**(开头被截掉了), +读起来像事实,其实缺主语。 + +> §5.6b 说的错误版本是「每轮对话自动把原文写进共享 SKILL.md」。 +> **现在这套已经是那个错误版本的单公司版了** —— 只是还没跨公司共享,所以还没泄漏。 +> dream 如果直接消费它,等于把这个错误放大到跨公司。 + +### 1.2 长期记忆是一个 3000 字符的滚动窗口 + +`MAX_MEMORY_MD_SIZE = 3000`。超了就截。所以「长期记忆」实际是**最近若干条截断日志**, +没有任何按重要性保留的机制。 + +### 1.3 「公司态目录」里有不是公司的东西 + +`system/agents/{aid}/{cid}/` 是约定,不是校验。实查 96 个二级目录里混着: + +| 形态 | 例子 | 是什么 | +|---|---|---| +| 真公司 | `e2e-demo` / `mycompany` / `idearun` | ✅ dream 的原料 | +| 保留段 | `_global` | 全局 agent 的落点,算不算「一家公司」需要定 ❓ | +| ArenaSK 时代的运行态 | `train/` `workspace/` | ❌ 不是公司,是训练 / 工作目录 | +| **测试残留** | `tmp-w7-bootstrap-1786986291739-9n2xv`、`qroute-co-13/14`、`zz-*` | ❌ 夹具,且**带时间戳**,会无限增长 | + +**dream 的第一步「枚举所有公司」现在没有可靠答案。** +可靠的答案在库里:`company_agents` 表(28 批次 4 起「这家公司雇了谁」的唯一真相源)。 +目录只能用来定位文件,不能用来枚举公司。 + +### 1.4 绝大多数 memory 是空模板 + +75 份 `memory.md` 里,**只有 7 份**有非模板内容(`innovator-evolver` / `master` / +`org-innovator` / `innovator-digest` / `innovator-hunt` / `coo` / `qa-judge`), +其余全是 `_(刚入职,尚无记忆)_`。 + +> 这条对设计有直接影响:**dream 现在跑起来几乎没有原料**。 +> 它的价值随「真跑的量」增长,不是上线即有收益的东西。 + +--- + +## 二、已经生效的约束(不随设计变化) + +✅ **公司态目录是 dream 的唯一原料,不许被任何清理策略当垃圾扫掉**(§5.6b)。 +任务态(`{aid}/{cid}/{taskId}/`)可清,公司态(`{aid}/{cid}/memory/` 与 `conversations/`)不可清。 + +✅ **沉淀必须是提炼后的抽象,不是自动搬运原文。** +错误版本真的发生过(28 号第一轮验收挖出的最严重 blocker):直接灌原始对话 += 把 A 公司的机密喂给 B 公司。 + +✅ **目录布局本身就是 dream 的主查询索引**: +`某个 agent 在所有公司的全部记忆 == glob: system/agents/{aid}/*/memory/`,一个 glob 就够。 +(前提是 1.3 那个「二级目录不都是公司」的问题被解决。) + +--- + +## 三、需要定的取舍 + +### 🔒 决策 A —— 去敏的判据:**独立判定 agent + 写作要求**(2026-08-20 拍板) + +**定案 = A3 + A1 组合**:提炼 agent 按 A1 的写作要求产出,产出后由**另一个只负责判 +「这段话能不能给别家公司看」的 agent** 过闸门,判不准就拒绝沉淀。 + +理由(也是本仓库反复验证过的一条):**两种错的代价不对称时,判据要偏向贵的那一侧** +(P3B §八 关于 `FAILING_VERDICTS` 的那段)。「少沉淀一条经验」与「泄漏一次机密」显然不对称。 +失效方向因此是「少沉淀」,不是「泄漏」。 + +下面三种形态的对比留档,说明为什么选它。 + +### 决策 A 的备选(留档) + +这是**安全边界**,也是整个设计里唯一「做错了会造成真实损害」的一条。三种可选形态: + +| 方案 | 判据 | 代价 | +|---|---|---| +| **A1 白名单式**:只允许提炼成「不含任何专有名词的方法论」 | 产出里不许出现公司 id、人名、产品名、URL、文件路径 | 最安全;但也最容易把有用的经验一起滤掉(「给 X 公司做官网时发现静态站点要先定验收脚本」→ 只剩「要先定验收脚本」) | +| **A2 黑名单式**:过滤已知敏感模式后放行 | 正则 / LLM 扫一遍公司 id、密钥形态、路径 | 保留信息量更多;**漏一个模式就是一次泄漏**,而且是静默的 | +| **A3 二次判定**:提炼 agent 产出后,由**另一个** agent 只负责判「这段话能不能给别家公司看」 | 判定是一次独立的 agent 调用,双方不共享上下文 | 成本翻倍;但与本仓库既有的 validator / triager 模式同构,且**失效方向可控**(判不准就拒绝沉淀,宁可少沉淀) | + +### 🔒 决策 B —— 沉淀落点:**四个都要,按「改动的可逆性」分层**(2026-08-20 拍板) + +用户定的是:`system/agents/{aid}/memory/` **和** `SKILL.md` 都要写, +**必要时还要迭代 `soul.md` / `agent.md`**。 + +这比原来三选一的范围大得多,**因为它引入了「agent 改写自己的定义」这件事**。 +下面这一节是这个决定的直接后果,不是可选的补充说明。 + +#### B.1 四个落点不是一回事,风险差三个数量级 + +| 落点 | 是什么 | 谁受影响 | 可逆性 | +|---|---|---|---| +| `{aid}/memory/`(本体根) | **经验积累**,跨公司那一份 | 只影响这个 agent 读到的上下文 | 删掉即可,无副作用 | +| `SKILL.md` | **能力定义**:这个 agent 会什么 | 同上,但会进 Skill 市场的语义 | 覆盖前留旧版即可回滚 | +| `soul.md` | **人格**:它怎么说话、怎么取舍 | 所有雇了这个本体的公司 | 改坏了表现为「行为漂了」,**不报错** | +| `agent.md` | **行为契约**:它必须做什么、禁止做什么 | 所有雇了这个本体的公司 | 改坏了可能让整条链路静默失效 | + +**28 号的核心裁决是「本体只有一份」** —— 所以写 `agent.md` / `soul.md` 的影响面是 +**所有雇了它的公司**,而不是提炼出这条经验的那一家。这恰恰是 §5.6b 担心的那种扩散, +只不过现在是**有意为之**。因此它必须比前两个落点多两道东西: + +#### B.2 写定义文件的硬约束(这几条不是建议) + +1. ✅ **`agent.md` 有一处 2000 字符的截断,但作用点要说准**: + `graph-engine.js:915` 给**规划 agent** 看的「角色定义摘录」只取 + `agent.md` 的**前 2000 字符**(`readFileSync(f).slice(0, 2000)`), + 超出部分静默丢弃。agent 自己那条装配链(`prompt-builder`)**不截断**,读全文。 + → 后果是:dream 往 `agent.md` **末尾追加**内容,规划侧读到的画像不会变; + 但如果追加把重要约束**顶到 2000 之后**(例如重写时调整了顺序),规划侧会无声地少看到那一段。 + → 判据:写 `agent.md` 时**保证前 2000 字符仍然自足**(职能说清楚), + 新增内容往后放;改动后应当有一条检查盯住这个窗口。 +2. ✅ **这四个文件里有三个是 git 跟踪的定义**(`agent.md` / `soul.md` / `SKILL.md`), + 不是运行态。dream 改它们 = 改仓库里的文件 → 会被边界守卫看见,也会进 `git status`。 + → 需要明确:dream 的产出是**直接落盘**还是**提案 + 人工合并**。 +3. ✅ **毕业闸门读这些文件**(`missingBodyDefinition` / `promoteDraftIfComplete`)。 + 把 `agent.md` 写空 / 写坏会让本体退回「定义没填满」,**雇不进任何公司**。 +4. ✅ **必须可回滚且能分别回滚**:经验沉淀写坏了只影响上下文,`agent.md` 写坏了影响所有公司。 + 两者混在同一次提交里,回滚粒度就没了。 + +> **建议(仍需你定)**:`memory/` 与 `SKILL.md` 由 dream 直接写; +> `soul.md` / `agent.md` 只产出**提案**(写到 `{aid}/memory/proposals/` 下),由人过一眼再合并。 +> 理由是上面第 1、3 条:这两个文件写坏的失效方向都是**静默**的,而 dream 的产出目前 +> 没有任何质量评价手段(决策 D 未定)。 + +### 决策 B 的备选(留档) + +「skill memory」目前**没有对应的实体**。候选落点: + +| 落点 | 形态 | 问题 | +|---|---|---| +| **B1** `system/agents/{aid}/memory/`(本体根,跨公司那一份) | 与公司态并列,一个 agent 一份跨公司经验 | 目前只有 `zz-scaffold-readonly-fresh` 有这个目录,等于新建一层;**但它天然与 §5.6b 的 glob 对齐**,读的时候本体根 + 各公司态一起加载 | +| **B2** 本体的 `SKILL.md` | 复用现有概念(5 个本体已有 SKILL.md) | **这正是 §5.6b 点名的错误版本的落点**;SKILL.md 是「能力定义」,不是「经验积累」,混在一起之后没法分别回滚 | +| **B3** `/../data/skills/` 那 27,059 条 Skill 仓库 | 与 ArenaSK 的 Skill 市场打通 | 那是**面向分发**的仓库,写进去等于对外发布;且跨了两个 data 根 | + +(拍板结果见上:B1 与 B2 都要,另加 `soul.md` / `agent.md` 两个定义落点。) + +### 🔒 决策 C —— 触发时机:**定时,每日一次**(2026-08-20 拍板) + +备选留档如下。 + +> ⚠️ **与 §1.4 的冲突要知道**:实查 75 份 `memory.md` 里只有 7 份有非模板内容, +> 所以每日跑起来**绝大多数 agent 会是空转**。这不是反对定时,是要求实现时做到: +> **没有新原料就不起 agent 调用**(判据:上次 dream 之后有没有新的 session 摘要), +> 否则每天会有几十次「读完发现没东西」的 LLM 调用在烧钱,而且日志里全是噪声。 + +| 方案 | 说明 | +|---|---| +| **C1 定时**(如每日一次) | 简单;但没有真跑的日子会空转,有大量真跑的日子又攒得太多 | +| **C2 按量**(某 agent 新增 N 条 session 摘要后触发) | 与实际产出量对齐;需要一个计数落点 | +| **C3 手动**(控制台一个按钮 + API) | 先手动跑一段时间看提炼质量,稳定了再自动 | + +### ❓ 决策 D —— 提炼质量怎么评 / 失败怎么办 + +这一条**可以先不定**,但要知道它没定: + +- 没有评价手段时,dream 的产出**无法验证对错**——它和 FDE 那边 + 「LLM 自己汇报分数」的问题是同一个(见 [architecture_todo_list](../future/architecture_todo_list.md) §1)。 +- 一个便宜的起步:dream 每次产出都**保留原料指针**(哪几条 session 提炼出这一条), + 人抽查时能顺着回去看。**可追溯**比「可评分」便宜得多,也更早有用。 + +--- + +## 四、推进顺序 🔒 **上游与 dream 一起做**(2026-08-20 拍板) + +用户定的是两件一起做,不分先后。拆成可验收的步骤: + +| 步 | 内容 | 为什么不能省 | +|---|---|---| +| 1 | **`appendToMemoryMd` 停止截断搬运**(§1.1) | 它现在把工具调用 id 当「关键事实」写进 `memory.md` 并注入**每一轮**提示词。dream 直接消费它 = 把这个错误放大到跨公司 | +| 2 | **「有哪些公司」改查 `company_agents` 表**(§1.3) | 目录里混着 `train/` `workspace/` 和带时间戳的测试残留,拿目录枚举公司必然把它们当原料 | +| 3 | **dream v0**:枚举 → 提炼 agent → 去敏判定 agent(决策 A)→ 写 `memory/` + `SKILL.md` | 主体 | +| 4 | **定义文件走提案**(决策 B.2 建议):`soul.md` / `agent.md` 只产出提案 | 两者写坏的失效方向都是静默的,而质量评价(决策 D)还没定 | +| 5 | **可追溯**:每条沉淀记住它是从哪几条 session 提炼来的(决策 D 的便宜起步) | 没有它,dream 的产出无法验证对错 —— 与 FDE 那边「LLM 自己汇报分数」是同一个问题 | + +**空转保护**(决策 C 的直接要求):没有新原料就不起 agent 调用。 + +> 本文档仍**不是实现规格**。决策 D(质量怎么评)未定;B.2 里「定义文件直接写还是走提案」 +> 也还需要你点头 —— 我按「走提案」写在了步骤 4,但那是我的建议,不是你拍的板。 -- libgit2 0.21.4