diff --git a/docs/todo/system-design/27-naming-conventions.md b/docs/todo/system-design/27-naming-conventions.md index 06826ce..bfce6ae 100644 --- a/docs/todo/system-design/27-naming-conventions.md +++ b/docs/todo/system-design/27-naming-conventions.md @@ -23,8 +23,10 @@ | `orggraph` | 公司级 workflow | | `workgraph` | org node 级 workflow | | `{domain}` | 业务领域(`code` / `content` / `ops` …),即 org node 所属领域 | -| `goal` | **一个人类需求描述**(唯一,验收边界)| -| `task` | 图中的执行单元(一个 goal 分解出多个,可并行)| +| `company-goal` | **公司的目标**(建公司时写入,OKR 树的输入)| +| `task-goal` | **一次用户请求的目标**(唯一,验收边界;R-12 说的就是这一层)| +| `subagent-goal` | **子任务的目标**(一个 task-goal 分解出多个)| +| `task` | 图中的执行单元(一个 task-goal 分解出多个,可并行)| | `agent` | 单个 system agent | | `skill` | 单个 skill | @@ -81,6 +83,22 @@ work graph 内动态选入的验收产出节点(见 [27-E2E](./27-e2e-task-exe 验收标准回答的是"**人类想要的东西做到了没有**"——这是 goal 级的判定。task 只是达成它的执行分支,一个 goal 下有很多个;把验收挂在 task 上会导致"到底以哪个 task 的验收为准"根本无法回答。 +### goal 是三层递归的(R-13) + +「goal」本身在三个层级上各有一个,与 [27-E2E](./27-e2e-task-execution-flow.md) §11.3「验收逐层递归」严格对应——每一层的 goal 就是那一层的验收边界: + +| goal | 归属 | 谁验收它 | 落在哪 | +|---|---|---|---| +| **`company-goal`** | 公司 | OKR 树(长周期度量) | `company.json.goal`,建公司时写入 | +| **`task-goal`** | 一次用户请求 | org graph 末端的 `{domain}-validator` | 任务入口带入 | +| **`subagent-goal`** | 子任务 / 并行分支 | 该 work graph 内的 validator 节点 | 由 `workgraph-planning` 拆分时产生 | + +**`company-goal` 与 OKR 的关系**:`company-goal` 是建公司时的一句话**定性**目标,是 **OKR 树的输入**——OKR 把它拆成可度量的 Objective → Key Result。两者不是替代关系,不要用 OKR 节点去存 `company-goal`。 + +**三个验收 agent(`goal-benchmark` / `goal-test-data` / `goal-test-script`)是通用的,不按层级拆分**:它们在哪一层被调用,就为那一层的 goal 产出验收标准。这与 §八bis 的元 agent 继承(全局通用本体 + 记忆分区)一致——不需要 `task-goal-benchmark` / `subagent-goal-benchmark` 这种按层级复制的 agent。 + +> **一处命名轴不一致(待定,不阻塞)**:`company-goal` / `task-goal` 是按**作用域**命名,`subagent-goal` 是按**执行者**命名。按作用域应为 `subtask-goal`。当前按提出时的原词 `subagent-goal` 记录,若要统一改为 `subtask-goal`,全局换一个词即可。 + > **决策沿革**(完整过程见 [27-REQ](./27-requirement.md) R-10 / R-12):最初选 `task-`,理由是"`goal` 被 OKR 占用";§六 把 OKR 正名后该理由失效,改用"验收跟着一次 task 执行走"继续保留 `task-`;最终因上面的基数论证推翻,定为 `goal-`。 --- diff --git a/docs/todo/system-design/27-p2-org-graph.md b/docs/todo/system-design/27-p2-org-graph.md index f646a05..461c927 100644 --- a/docs/todo/system-design/27-p2-org-graph.md +++ b/docs/todo/system-design/27-p2-org-graph.md @@ -98,7 +98,7 @@ CREATE UNIQUE INDEX IF NOT EXISTS idx_org_graph_edges_dedupe_active 与 work graph 的执行前校验同一套规则([27-P3/P4](./27-p3-p4-work-graph-console.md) §四.A): 1. **无环**(仅针对 `edge_kind='forward'`)—— 三色 DFS,复用 DevLoop `doc-store.js:167` 的 `hasCycle()` 实现。 -2. **单一入口** —— 必须恰好有一个入度为 0 的 forward 节点(任务从这里进入)。多入口会让"任务从哪开始"无法确定。 +2. **单一入口**(✅ 已定案,见 [27-REQ](./27-requirement.md) R-13)—— 必须恰好有一个入度为 0 的 forward 节点,任务从这里进入。多入口会让"任务从哪开始"无法确定,且需要任务入口多带一个参数。 3. **可达性** —— 每个节点都必须从入口可达,孤立节点直接拒绝。 4. **回退边必须显式标注** —— 任何指向"拓扑序更早节点"的边,`edge_kind` 必须是 `backward`;引擎独立复核,不信任标注(27 号文档 §3.4 第 5 点的同款兜底)。 @@ -110,13 +110,18 @@ CREATE UNIQUE INDEX IF NOT EXISTS idx_org_graph_edges_dedupe_active 新增 system agent 模板 `system/templates/orggraph-planning/`(`config.json.type` 取 `meta-bootstrap`,与业务角色区分)。工作流: -1. **读输入**:公司 `id` / `name` / `description`(由 §4.4 的首轮消息带入)。 +1. **读输入**:公司 `id` / `name` / **`goal`(company-goal)** / `description`(由 §4.4 的首轮消息带入)。 + `company-goal` 是「这家公司要达成什么」,`description` 是「这家公司是干什么的」——前者是规划流水线的主依据,后者是背景。见 [27-NAMING](./27-naming-conventions.md) §四。 2. **读候选骨架**:`ls system/org-graph-templates/`,逐个读取,理解各自的 `whenToUse`。 3. **读可招角色**:`node bin/lemon.js company templates`(复用现成 CLI,`listTemplates()` 返回 id/name/type/description)。 4. **规划**:综合三者,产出一张 workflow —— 节点列表 + 执行流边。允许裁剪、替换、组合多个模板,也允许判断需要一个模板库里没有的新角色。 5. **落盘规划记录**:`workspace/orggraph-plan.md`(图 + 为什么这么设计的理由),供事后审计。 6. **提交图**:调用 §4.3 的整体写入 API,图通过 §3.3 校验后落库。 -7. **执行招聘**:按拓扑序对每个节点调 `hire()`。模板不存在的角色走既有 `needs_training` 分支(自动触发 agent-creator + innovator,`company.js:917-940` 已实现)。 +7. **执行招聘**:按拓扑序对每个节点调 `hire()`。 + - **模板已存在** → 走 P1 虚拟引用寻址,不拷贝 `agent.md`。 + - **模板不存在** → **从 `system/templates/base/` 元 agent 继承出骨架**(自动带上 base 的 `RESEARCH → PLAN → EXECUTE → VERIFY` 四段式生命周期),再由 `agent-creator` 填充该角色特有的定义。这是 [27-E2E](./27-e2e-task-execution-flow.md) §八bis「所有 node agent 由元 agent 继承生成」在 P2 的落地点——**不是**直接走现有的 `needs_training` 全量培养分支。 + - **新建 agent 的 `scope`**:一律默认 `scope='company'`、`owner_company_id` 为本公司(27 号文档 §3.2)。升级为 `public` 走既有的 benchmark ≥95 门槛,不在 P2 范围内。 + - **训练是可选的后置步骤**:建成后可手动触发 `innovator` 培养,也可按公司需求配置为自动触发。P2 只保证「角色能被建出来且带四段式骨架」,不强制等训练完成。 8. **收尾**:关闭自己的心跳转入休眠。后续增量调整归 HR。 `orggraph-planning` 自己**不出现在它规划的 org graph 里** —— 它是基础设施角色,类比 `agent-creator` / `innovator`。 @@ -134,6 +139,7 @@ v0.2/v0.3 设计的 `hire(companyId, roleName, {reportsTo})` **不再需要**。 | `POST /api/company/:companyId/org-graph` | **整体写入**:接收 `{nodes:[], edges:[]}`,过 §3.3 四项校验,事务性落库。校验失败返回 400 + 具体哪条规则不满足(供 `orggraph-planning` 读错误后自行修正重试) | | `PATCH /api/company/:companyId/org-graph` | **增量修改**:加/删节点、加/删边(后续 HR 调整用)。同样过校验——改完之后整张图仍须合法 | | `GET /api/company/:companyId/org-graph` | **只读**:返回 `{nodes, edges}`,只含 `status='active'` 的行 | +| `POST /api/company/:companyId/org-graph/replan` | **重跑规划**:对已有公司手动再触发一次 `orggraph-planning`(§六 存量公司补图的入口)。异步,立即返回;agent 跑完后通过上面的 POST 写入新图 | `GET` 返回形状: @@ -164,7 +170,7 @@ if (bootstrap.status === 'hired_from_template') { fetch(`http://127.0.0.1:${PORT}/api/agent/orggraph-planning/chat`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ - message: `公司 ${id}(${name})已创建。描述:${description || '(未填写)'}\n请按 orggraph-planning 工作流规划并建立这家公司的 workflow。`, + message: `公司 ${id}(${name})已创建。\n公司目标(company-goal):${goal || '(未填写)'}\n公司描述:${description || '(未填写)'}\n请按 orggraph-planning 工作流规划并建立这家公司的 workflow。`, conversationId: `orggraph-bootstrap-${id}-${Date.now().toString(36)}`, }), }).catch(e => console.error('[Create] orggraph-planning 触发失败:', e.message)); @@ -215,17 +221,19 @@ if (bootstrap.status === 'hired_from_template') { | # | 任务 | 文件 | 依赖 | |---|---|---|---| +| **T0** | `company.json` 新增 `goal`(company-goal)字段;`create({id,name,goal,description})`;`POST /create` 与前端建公司表单同步 | `server/services/company-service.js`、`server/routes/company.js`、前端建公司表单 | 无 | | **T1** | schema:节点加 `status` / `reports_to_node_id`;DROP 全量唯一索引改建部分索引;**DROP 重建 `org_graph_edges` 为执行流边** | `server/services/graph-db.js` | 无 | | **T2** | `org-graph-service.js`:图的整体写入(含 §3.3 四项校验)、增量改、只读查询、节点软删除级联失效边 | `server/services/org-graph-service.js`(新增) | T1 | -| **T3** | 三个 API 端点(POST / PATCH / GET) | `server/routes/company.js` | T2 | +| **T3** | 四个 API 端点(POST / PATCH / GET / POST …/replan) | `server/routes/company.js` | T2 | | **T4** | `system/org-graph-templates/{dev,content,ops}.json` | 新增 | 无,可并行 | | **T5** | `system/templates/orggraph-planning/` agent 本体(含 §4.1 八步工作流) | 新增 | T3, T4 | +| **T5b** | 「从 base 元 agent 继承 + agent-creator 填充」的招聘分支(§4.1 第 7 步),含新建 agent 的 `scope='company'` 落库 | `server/services/company-service.js`、`server/services/graph-service.js` | T1 | | **T6** | `/create` 路由接入自动招聘 + 异步触发 | `server/routes/company.js` | T5 | | **T7** | `/fire` 路由接入节点软删除(改路由 handler 本体,**不是** `CompanyService.fire()`——后者只有测试脚本在用,是死代码路径) | `server/routes/company.js` | T2 | | **T8** | 前端:复用 P3 画布组件渲染 org graph(或先上纯文本拓扑序占位) | `web/src/views/CompanyDashboard.vue` | T3(+ P3 的 T3 若要画布) | | **T9** | 测试:`org-graph-service.test.js`(四项校验各一条负例、幂等写入、软删除级联)+ e2e 扩展 | `server/tests/unit/org-graph-service.test.js` | T2, T3 | -**顺序**:T1→T2→T3 是主干。T4 可最早并行。T5 依赖 T3/T4。T7 独立于 T5/T6,可并行。T8 视是否等 P3 画布决定。 +**顺序**:T0 独立可先做(它是 `orggraph-planning` 的输入前提)。T1→T2→T3 是主干。T4 可最早并行。T5 依赖 T3/T4。T7 独立于 T5/T6,可并行。T8 视是否等 P3 画布决定。 --- diff --git a/docs/todo/system-design/27-requirement.md b/docs/todo/system-design/27-requirement.md index ac50ad1..50d7243 100644 --- a/docs/todo/system-design/27-requirement.md +++ b/docs/todo/system-design/27-requirement.md @@ -148,7 +148,7 @@ **落点**:[27-NAMING](./27-naming-conventions.md) §六 -**要点**:体系整体从 `goal` 改为 `okr`;且当前第 1 层节点也叫 "Goal" 是一处自我遮蔽,建议正名为 `Objective`(`O-001`),成为标准的 Objective → Key Result → Sub-Task 三层。代码层是真实数据迁移(9 条 API 路径 + `data/companies/{cid}/goals/` 磁盘目录 + `G-` ID 前缀写在已有 JSON 里),已给影响面清单,未执行。 +**要点**:体系整体从 `goal` 改为 `okr`;且当前第 1 层节点也叫 "Goal" 是一处自我遮蔽,建议正名为 `Objective`(`O-001`),成为标准的 Objective → Key Result → Sub-Task 三层。代码层已于 commit `74ea1ff` **实施完毕**(`okr-service.js`/`routes/okr.js`、`/api/okr` + 旧路径别名、`scripts/migrate-goals-to-okr.js` 迁移脚本、7 例单测)。 --- @@ -170,6 +170,44 @@ --- +## R-13 · P2 需求审计中的四个定案 + +针对「P2 是否兑现原话」的逐条比对,四个分叉的定案回答,逐条照搬。 + +### R-13.1 · goal 分三层 + +> 公司的目标是公司的 goal 叫做 company-goal ,任务的目标叫做 task-goal ,还有每一个子任务的目标叫做 subagent-goal,应该这么叫吧? + +**落点**:[27-NAMING](./27-naming-conventions.md) §四「goal 是三层递归的」、[27-P2](./27-p2-org-graph.md) §4.1 第 1 步 + +**澄清了什么**:R-12 说的「一个人类需求描述只有一个 goal」,指的其实是三层里的 **`task-goal`**。三个验收 agent(`goal-benchmark` / `goal-test-data` / `goal-test-script`)保持通用,不按层级拆分——在哪一层调用就为那一层的 goal 产出标准,与 R-9.2 元 agent 继承一致。 + +**待定(不阻塞)**:`subagent-goal` 是按执行者命名,另两个按作用域命名,轴不一致;按作用域应为 `subtask-goal`。当前按原词记录。 + +### R-13.2 · 公司类型由 agent 推断 + +> 完全由 orggraph-planning 推断 + +**落点**:[27-P2](./27-p2-org-graph.md) §4.6(模板是起点集合,不是代码里的 `switch(type)`) + +**推翻了什么**:审计时我把「不给 company.json 加枚举 type 字段」记为「我擅自偏离了 R-3 原话(原话把类型列为输入)」。本条确认 LLM 推断正是所需,**当前设计无需修改**,那条自我记过的缺陷不成立。 + +### R-13.3 · 新 agent 从元 agent 继承 + +> 从 base 元 agent 继承 + agent-creator 填内容,建成后可以手动或者根据 company 的需求设置自动innovator 培养训练 + +**落点**:[27-P2](./27-p2-org-graph.md) §4.1 第 7 步、§五 T5b + +**这是 R-9.2 在 P2 的落地点**:模板不存在的角色,先从 `system/templates/base/` 继承骨架(自动带上 `RESEARCH → PLAN → EXECUTE → VERIFY` 四段式),再由 `agent-creator` 填充角色定义——**不是**直接走现有的 `needs_training` 全量培养分支。训练是可选后置步骤,可手动触发或按公司需求配置自动。新建 agent 一律 `scope='company'`。 + +### R-13.4 · org graph 强制单入口 + +> 强制单入口(推荐) + +**落点**:[27-P2](./27-p2-org-graph.md) §3.3 第 2 条(原为 open question,现已定案) + +--- + ## 附 · 需求 → 设计落点 速查 | 需求 | 一句话 | 主要落点 | @@ -186,3 +224,4 @@ | R-10 | 统一命名前缀 | NAMING 全文 | | R-11 | OKR 正名,不叫 goal | NAMING §六 | | R-12 | **验收产物定名 `goal-`**;goal 唯一、task 多个 | NAMING §四 | +| R-13 | goal 三层(company/task/subagent);类型由 agent 推断;新 agent 从 base 继承;强制单入口 | NAMING §四 / P2 §3.3 §4.1 §五 | diff --git a/server/routes/company.js b/server/routes/company.js index 7e12554..5a729fa 100644 --- a/server/routes/company.js +++ b/server/routes/company.js @@ -21,7 +21,7 @@ const scheduleService = new ScheduleService(); // POST /api/company/create router.post('/create', async (req, res) => { try { - const { id, name, description } = req.body; + const { id, name, goal, description } = req.body; if (!id || !name) return res.status(400).json({ error: 'id 和 name 为必填项' }); // 校验 id 格式 (小写字母、数字、-) @@ -29,7 +29,7 @@ router.post('/create', async (req, res) => { return res.status(400).json({ error: 'id 格式无效 (只允许小写字母、数字、-)' }); } - const company = companyService.create({ id, name, description }); + const company = companyService.create({ id, name, goal, description }); res.json({ success: true, company, message: '公司已创建,当前为没有员工的空壳公司。请通知 MasterBot 从 Template 库中雇佣 Agent 到你的团队中。' }); } catch (err) { diff --git a/server/services/company-service.js b/server/services/company-service.js index 5c0f9da..53724b8 100644 --- a/server/services/company-service.js +++ b/server/services/company-service.js @@ -33,9 +33,12 @@ class CompanyService { /** * 创建一家新公司 - * @param {object} opts - { id, name, description } + * @param {object} opts - { id, name, goal, description } + * goal company-goal:这家公司要达成什么(定性,OKR 树的输入,orggraph-planning 规划流水线的主依据) + * description 这家公司是干什么的(背景) + * 两者的区分见 docs/todo/system-design/27-naming-conventions.md §四 */ - create({ id, name, description = '' }) { + create({ id, name, goal = '', description = '' }) { const companyDir = path.join(DATA_DIR, id); if (fs.existsSync(companyDir)) { throw new Error(`公司 ${id} 已存在`); @@ -51,6 +54,7 @@ class CompanyService { const company = { id, name, + goal, description, status: 'creating', // creating → running → paused → shutdown createdAt: now, @@ -120,6 +124,7 @@ class CompanyService { if (!company) return null; if (updates.name !== undefined) company.name = updates.name; + if (updates.goal !== undefined) company.goal = updates.goal; if (updates.description !== undefined) company.description = updates.description; if (updates.status !== undefined) { company.status = updates.status;