From 3353b17492604fc8c67ee9f4aaf5eed3af60b9a1 Mon Sep 17 00:00:00 2001 From: yibo Date: Thu, 20 Aug 2026 10:29:40 +0800 Subject: [PATCH] docs(27-P3C): §十六 批 5 收尾 —— 五批全部实施完毕 --- docs/todo/system-design/27-p3c-goal-task-model.md | 85 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 79 insertions(+), 6 deletions(-) diff --git a/docs/todo/system-design/27-p3c-goal-task-model.md b/docs/todo/system-design/27-p3c-goal-task-model.md index f55102d..d1df014 100644 --- a/docs/todo/system-design/27-p3c-goal-task-model.md +++ b/docs/todo/system-design/27-p3c-goal-task-model.md @@ -2,7 +2,7 @@ > **所属**: [27 Graph Agent 架构](./27-graph-agent-architecture.md) 的 **P3 收尾**(P3C),直接续 [27-P3/P4 执行控制台](./27-p3-p4-work-graph-console.md) §四.B 与 [27-P3B 真跑跑道](./27-p3b-real-run-runway.md)。 > **日期**: 2026-08-18 -> **状态**: **批 1-4 已实施并全绿**(§十三 / §十四 / §十五)。**批 5 进行中**(§十六:第 1-2 步已落地,第 3-6 步未做)。开放问题已全部关闭。 +> **状态**: **五批全部实施完毕并全绿**(§十三 ~ §十六)。132 文件 / 1257 测试,`web:build` 通过。开放问题已全部关闭;后续项见 §15.5 与 §16.8。 > **⚠️ 批次与 phase 的对应不是一一对齐的**,判断「这份能不能搬进 done」时按批看,别按文档名看: > · **批 1-3 = 真正的 P3 收尾**(goal/task 两级模型 + `task-planning` + 排队),本文档的主体; > · **批 4(并行)归 [27-P4](./27-p4-parallel-and-artifacts.md) §一.3**,那边是设计权威,本文档只服从(见 §五.2 的警示块); @@ -1078,12 +1078,12 @@ manual,授权早就给过了,让它自己接回来是**兑现人刚才那次 |---|---|---| | 1 | `tasks` 表吸收 `executor` / `assignee_agent_id` / `parent_task_id` | ✅ `5cd91c3` | | 2 | `system/tasklog/*.json` 回填进 `tasks` 表 | ✅ `514ec3d` | -| 3 | `/task/dispatch` 改写 `tasks` 表(**URL 与请求体一个字不动**) | ⏳ 未做 | -| 4 | 30 分钟去重从「扫目录」改成一条 SQL | ⏳ 未做 | -| 5 | `GET /api/tasks` 改读表;删 `TaskService.getAggregatedTasks()` 的会话反推 | ⏳ 未做 | -| 6 | 调度器只管 `executor='graph'` | ⏳ 未做 | +| 3 | `/task/dispatch` 改写 `tasks` 表(**URL 与请求体一个字不动**) | ✅ `12880b8` | +| 4 | 30 分钟去重从「扫目录」改成一条 SQL | ✅ 同上 | +| 5 | `GET /api/tasks` 改读表;删 `TaskService.getAggregatedTasks()` 的会话反推 | ✅ `71cf45e` | +| 6 | 调度器只管 `executor='graph'` | ✅ 同上 | -全量:**131 文件 / 1251 测试全绿**。 +**批 5 完成,P3C 五批全部实施完毕。** 全量:**132 文件 / 1257 测试全绿**。 ### 16.2 第 1 步踩到的:一个会让**所有存量库开不了**的顺序问题 @@ -1126,3 +1126,76 @@ manual,授权早就给过了,让它自己接回来是**兑现人刚才那次 > 顺带记一处过期:`tools-base.md` 里印的是 `http://localhost:3000`,而实际端口是 **3010** > (`server/config.js`)。已经真咬过人 —— 回填出来的派单正文里有人手写了「端口 3010,不是 3000」 > 来纠正它。这一处该在第 3 步一起改(改的是端口不是契约形态)。 + +### 16.5 第 3 步(最危险的一步)怎么做到不碰契约 + +`/task/dispatch` 的副作用有九步,**只有第 ⑧ 步是「存储」**: + +``` +① 校验 from/to/task + 两个本体目录存在 +② 去重(30 分钟内同接收方 + 正文互相包含) +③ 推断 parentTaskId +④ 正文净化(\n →
,| → |) +⑤ 写目标 agent 的 todo.md +⑥ 写发送者的 plan.md +⑦ 更新 OKR 节点 → in_progress +⑧ saveTaskMeta(taskId, {...}) ← 只换这一步的内脏 +⑨ autoExecute → POST /api/agent/{to}/chat +``` + +**把 `saveTaskMeta` / `loadTaskMeta` 变成 tasks 表的适配器**,其余八步一行不动 —— +URL 与请求体形态天然不可能变。②③ 顺带从「扫目录」改成带索引的 SQL,**判据一个字没改**, +换的只是从哪儿把候选捞出来。 + +字段落点的三个判断: +- **`objectiveId` 不单独存**:它恒等于 okr node id 的首段(`OkrService.parseNodeId` 与 + `reportRunOutcome` 同一口径),且两处读取方都是 `if (objId && meta.nodeId)` —— 从不单独用。 +- **`sourceConvId` 删掉**:全仓零读取方。正是本文档 §九.4quater 立过案的「实现在、零调用方」, + 不往新表里再搬一份。 +- **`summary` / `execution_conv_id` 加列**(用户定案 A):前者当前只写不读但语义清晰、界面迟早要它; + 后者有真读取方。**没有选「加一个 legacy meta 的 JSON blob 列」** —— §十二 引 R-19.2 明确否过 + 「建表而不是 JSON」,那会把一个已被否掉的形态重新引进来。 + +验证方式是**真起服务打一遍**,不是只看测试绿:照 `tools-base.md` 印的请求体原样 POST → 200 + +老形态响应;再打一次 → 409 且 `existingTaskId` 正确;`/task/report` 之后 `status→completed`、 +`summary` 与 `finished_at` 都在;`loadTaskMeta` 往返出来的形状与老 JSON 逐字一致。 + +### 16.6 第 5 步:删掉的是「猜」,不是「记录」 + +原 `getAggregatedTasks` 是 122 行的启发式:扫全部 agent 的 `conversations/`,按会话 id 里的 +`T-xxx` 归组,再用三条策略(meta 的 `parentTaskId` / from agent 的活跃会话时间包含 / +master conv 的标题相似)**猜**父子关系。猜出来的树没法验证对错,而且每次请求都要遍历所有 +agent 的会话目录 —— 路由那 10 秒缓存就是为它加的。 + +现在父子关系是 `tasks.parent_task_id`,派单时就显式写下来了。`task-service.js` **391 行 → 236 行**。 + +**`getTaskTimeline` 保留**,但来源拆清楚: +- 任务树(有哪些子任务)→ `tasks` 表,显式、可验证; +- 时间线正文 → agent 的 `conversations/`,那是**一手记录,没有替代品**。 + +两处口径变化(已写进代码注释):`agents` 从「会话里出现过的 agent 集合」变成「谁派的 + 派给谁」; +`conversationCount` 从「扫出来的文件数」变成「有没有起过执行会话」(0/1)—— 为一个列表字段 +遍历所有 agent 的会话目录,正是这次要去掉的那笔开销。 + +### 16.7 两次「测试全绿 ≠ 没坏」 + +这一批里有两处只有**真跑**才会暴露的问题,测试一条都没红: + +1. **`routes/tasks.js` 漏改**:它自己还在算 `t.conversations.length`,而 `conversations` 是 + 会话反推那套的产物、已随之删除 → `GET /api/tasks` 直接 500。没有任何测试走那条路。 +2. **控制台 org graph 栏宽**(§十五 之外,`81629b7`):320px 减去 padding 只剩 292px, + 而两个节点加一条边要 380px+ —— 节点名被从中间切断。构建、单测、探针全都不会报。 + +> 与之对照,§十六.2 那个「存量库一启动就炸」是**测试能覆盖但没人写**的类型。 +> 三者合起来是一条经验:**新建库的路径、纯逻辑的路径由单测天然覆盖; +> 「存量数据」「真实接口」「肉眼可见」这三类必须另外安排人去走一遍。** + +### 16.8 留给下一轮 + +1. `system/tasklog/` 目录**还在**(4 个历史文件,已回填进表)。按 §十二.5 应改名为 + `system/tasklog.bak-unify/` 而不是直接删,跑顺一个版本再清。本轮未做。 +2. `tools-base.md` 里印的端口仍是 `localhost:3000`,实际是 **3010**。已经真咬过人 —— + 回填出来的派单正文里有人手写「端口 3010,不是 3000」在纠正它。**改端口不改契约形态**, + 但它是所有 agent 的工具说明,值得单独一次改动 + 通告。 +3. `TaskTimeline.vue` 未动。它按字段名取值,本轮保住了全部 12 个字段,但 `agents` / + `conversationCount` 的语义变了(见 §16.6),界面上的措辞可能要跟着调。 -- libgit2 0.21.4