# 27-NAMING · Graph Agent 体系命名规范 > **所属**: [27 Graph Agent 架构](./27-graph-agent-architecture.md) 的跨文档命名约定。 > **版本**: v0.2(§六 OKR 正名已落地到代码层,见该节执行结果表) > **日期**: 2026-08-15 > **适用范围**: 27 系列全部文档 + 后续实现中新建的 system agent id、表名、字段名、API 路径。 > **为什么需要这份文档**: 本体系引入的概念(planner / validator / planning / benchmark)在仓库里**全部已经有同名但不同义的既有占用**。不先统一,日后 grep 和排查会持续付出代价。 --- ## 一、总规则 ``` {作用域}-{职能} ``` **任何 agent id、验收产物类型、评测体系名,都必须带作用域前缀**,不允许裸用 `planner` / `validator` / `planning` / `benchmark`。 作用域从大到小: | 作用域 | 含义 | |---|---| | `orggraph` | 公司级 workflow | | `workgraph` | org node 级 workflow | | `{domain}` | 业务领域(`code` / `content` / `ops` …),即 org node 所属领域 | | `company-goal` | **公司的目标**(建公司时写入,OKR 树的输入)| | `task-goal` | **一次用户请求的目标**(唯一,验收边界;R-12 说的就是这一层)| | `subtask-goal` | **子任务的目标**(一个 task-goal 分解出多个)| | `task` | 图中的执行单元(一个 task-goal 分解出多个,可并行)| | `agent` | 单个 system agent | | `skill` | 单个 skill | --- ## 二、规划类 agent —— `{产出物}-planning` | id | 职责 | 所在层级 | |---|---|---| | `orggraph-planning` | 根据公司目标/描述,生成**公司级 workflow**(org graph) | company,建公司时跑一次 | | `workgraph-planning` | 根据任务,为某个 org node 生成**该节点的 workflow**(work graph) | org node,每次接到任务时 | | `brd-planning` | 产出 BRD | work graph 内的节点 | | `prd-planning` | 产出 PRD | work graph 内的节点 | | `sdd-planning` | 产出 SDD | work graph 内的节点 | ⚠️ **重命名**:[27-P2](../../done/system-design/27-p2-org-graph.md) v0.2/v0.3 里叫 `org-graph-creator` 的 agent,**统一改名为 `orggraph-planning`**。v0.4 起全文使用新名。 --- ## 三、图节点 agent —— 按**所在图的层级**命名(R-15.3 定案) 命名轴是**节点在哪一层图里**,不是它属于哪个业务领域。 ### 3.1 两层节点名 | 层级 | 节点名规则 | 例 | |---|---|---| | **Org Graph 节点** | `org-{职能}` | `org-planner` / `org-coder` / `org-validator` / `org-innovator` | | **Work Graph 节点** | `{父 org 节点全名}-{职能}` | `org-coder-validator` / `org-planner-validator` | 三个 org-graph 模板因此首尾同名,只有中段职能不同——差异在于**中间做什么**,规划和验收本身不分领域: ``` dev.json org-planner → org-coder → org-validator content.json org-planner → org-writer → org-validator ops.json org-planner → org-monitor → org-validator ``` **为什么不按业务领域**(v0.2 曾定为 `code-planner` / `content-planner`,已作废):一家公司的 org graph 只有一套节点,领域信息由 company 本身承载。同一个 `org-planner` 服务开发型还是内容型公司,靠 [27-E2E](./27-e2e-task-execution-flow.md) §八bis 的**元 agent 继承 + 公司/角色记忆分区**区分行为。按领域命名会让 agent 池随「领域数 × 职能数」膨胀,同一种能力无法跨领域复用经验。 ### 3.2 节点名 ≠ 本体名(R-15.3 新增的命名轴) **这是本规范此前完全缺失的一条轴**,不区分会直接导致 agent 本体被无谓复制: | | 例 | 带前缀? | |---|---|---| | **system agent 本体** | `planner-validator` / `coder-validator` / `org-validator` | ❌ 不带 | | **org graph 节点** | `org-planner` / `org-coder` / `org-validator` | ✅ 带 `org-` | | **work graph 节点** | `org-coder-validator` / `org-planner-validator` | ✅ 带父节点全名 | 落到 schema 上就是两个字段([27-P3/P4](./27-p3-p4-work-graph-console.md) §四.A):`node_name` 存节点名(图内唯一),`system_agent_id` 存本体名(跨图复用)。 **为什么本体不带前缀**:禁止裸用 `validator` 的理由是「一家公司内两个节点不能重名」——该理由针对**节点**,不针对**本体**。本体若被迫带领域前缀,`coder-validator` 会复制成 `org-coder-validator` / `content-coder-validator` 多份,与 [27-P1](../../done/system-design/27-p1-virtual-reference.md)「同一份本体、多处引用、各自私有语境」直接冲突。 ### 3.3 三个 validator 是三个独立本体 `planner-validator` / `coder-validator` / `org-validator` 验的东西本质不同——规划产物 / 代码 / 整个任务达成,是**不同能力**,不是同一能力的不同语境。§八bis 的「不特化」原则针对的是同一能力在不同 org node 下,与本条不冲突。 两级验收的分工: | | 节点名 | 验什么 | 失败退到哪 | |---|---|---|---| | **org 层** | `org-validator` | 整个任务达成了吗 | 退回某个 org node,**整张 work graph 重跑** | | **work 层** | `org-{X}-validator` | 这个 org node 自己的产出合格吗 | 退回本张 work graph 内的某个节点 | `org-validator` 本身是一个 org node,按定义有自己的 work graph,`benchmark-validator` / `testdata-validator` 就是这张 work graph 的节点——落在已有的递归模型里,引擎不加分支。 ### 3.4 存量 system agent 的归位 按同一规则把散落的老 agent 归到它实际所处的层级: | 旧名 | 新名 | 依据 | |---|---|---| | `innovator` | **`org-innovator`** | `type='cxo'`,是 org graph 层的节点 | | `hunter` | **`innovator-hunt`** | `retrain-pipeline.js:161`,innovator work graph 第 1 阶段 | | `digester` | **`innovator-digest`** | 同上第 2 阶段(行 179,输入是 hunter 产出) | | `evolver` | **`innovator-evolver`** | 同上第 3 阶段(行 196);行 231 明写 `steps: ['re_hunt','re_digest','evolve']` | **这顺带解决了 6 个命名冲突 id**(27 号文档 §七)——重命名后 `system/agents/` 与 `system/templates/` 下不再有同名目录。剩两个单独判断: - `agent-creator`:与 `orggraph-planning` 同属**基础设施角色**,不是任何图上的节点,不加前缀,保持原名。 - `evolver-youtube-crawler`:针对单个 skill 特化的 evolver 实例,按规则应为 `innovator-evolver-youtube-crawler`,名字过长且疑似一次性产物,**待定**,暂不改名。 > ✅ §3.4 的四个重命名**已落到代码**(`scripts/migrate-agent-naming.js`,幂等可重跑): > 8 个目录(`system/agents/` + `system/templates/`)、`system_agents` 表 4 行、 > 以及 5 处代码引用点(`retrain-pipeline.js` 的 `agent:` 字段 / `agentToStep` 映射 / > `call_sub_agent` 判断、`agent-chat.js` 的 agent 列表过滤、`company.js` 的训练触发)。 > > **迁移时明确不动的东西**(改了会坏): > - skill 内部子目录名 `{skillDir}/hunter/knowhow.md` —— 那是 hunt/digest/evolve > 三阶段的**产物目录**约定,与 agent 名同形但不同义 > - skill 命名空间的白名单(`skill-dir-manager.js` 的 `CORE_SKILLS`、 > `migrate-trained-skills.js` 的 `SYSTEM_SKILLS`)—— 那里的 `innovator` 指同名 skill > - `conversations/` 与 `system/tasklog/` 下的历史记录 —— 运行态数据,不改写历史 ### 3.5 归因类节点 —— `validator-triager`(R-16 立项,R-17.2 定名) R-16 把「失败该怪谁」从 validator 手里拆出来,交给一个专职的**归因节点**:`org-validator` 自己的 work graph 里 第一个节点就是它,产出 `fail_reason` + `remediation_note`,透传回 org 层作为选边依据 (见 [27-P3/P4](./27-p3-p4-work-graph-console.md) §四.D.2.2 / §四.D.3)。 按本节的命名轴,它是**work graph 层的节点**——它活在 `org-validator` 这个 org node 的 work graph 里, 不是 org graph 上的节点,因此不带 `org-` 前缀,而是随父节点得名。 ✅ **职能词已定案为 `triager`**([27-REQ](./27-requirement.md) R-17.2)。理由:它做的是**归因**不是**判定**, `triager` 与 DevLoop 已经跑通的 BugTriager 同构,语义更准。 **`validator-reflection` 是历史名**,除 R-16 正文的原话引用(照搬原则,不改)外,一律不再使用。 ⚠️ **仍有一处未定案:前缀取父节点全名还是职能**: | # | 待定案 | 两个选项 | 现状 | |---|---|---|---| | 1 | 前缀取父节点**全名**还是**职能** | §3.1 的规则写的是 `{父 org 节点全名}-{职能}` → `org-validator-triager`;27-P3/P4 全文用的是父节点职能的简写 → `validator-triager` | 暂用简写 `validator-triager`,与 §3.1 的字面规则存在出入,定案前不要以任一方去改另一方 | | ~~2~~ | ~~职能词用 `reflection` 还是 `triager`~~ | ~~两个选项~~ | **已定案 `triager`**(R-17.2),见上 | > 同一层的其它 work graph 节点(`benchmark-validator` / `testdata-validator` 等)不受这处待定案影响, > 它们在 §3.2 里已经明确是**本体名**,本来就不带前缀。 > 🔁 **改名的影响面**(R-17.2 落地范围):`system/templates/validator-triager/`(目录 + 内容自称)、 > `system/templates/_registry.json` 条目、`system/templates/org-validator/` 的节点名引用、 > `system/work-graph-templates/validator.json` 的 `nodeName` / `systemAgentId` / 边端点、 > 本文档本节、[27-P3/P4](./27-p3-p4-work-graph-console.md) 全文,以及 `graph-engine.js` 的 > `ATTRIBUTION_NODE_NAME` 常量与 `node.reflection` timeline 事件名(引擎侧另行改)。 > **这七处必须同时改**——节点名与 `systemAgentId` 与引擎常量三者只要有一处不同步, > 归因产出就会静默落不到透传路径上(27-P3/P4 §十 的「四环漏一环」缺陷模式)。 ## 四、验收产物 —— `goal-{类型}` work graph 内动态选入的验收产出节点(见 [27-E2E](./27-e2e-task-execution-flow.md) §十一): | id | 产出 | |---|---| | `goal-benchmark` | 打分式验收标准(维度 + 权重 + 及格线) | | `goal-test-data` | 输入样本集(正常 / 边界 / 异常) | | `goal-test-script` | 可执行断言脚本 | ### 为什么是 `goal-` 而不是 `task-` **关键区分是基数**: | 概念 | 基数 | 定义 | |---|---|---| | **goal** | 一个人类需求描述 = **1 个** | 验收的边界。回答"这次交付到底算不算满足了人提的要求" | | **task** | 一个 goal 分解出 **N 个** | 图中的执行单元。可并行、可下钻 sub agent,一个 goal 在图里会有很多个 | | **okr** | 跨多个 goal 的长周期度量树 | Objective / Key Result,见 §六 | 验收标准回答的是"**人类想要的东西做到了没有**"——这是 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 末端的 `org-validator` | 任务入口带入 | | **`subtask-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` / `subtask-goal-benchmark` 这种按层级复制的 agent。 > **命名轴已统一**(R-14):三者一律按**作用域**命名 —— `company-goal` / `task-goal` / `subtask-goal`。最初提出时第三个叫 `subagent-goal`(按**执行者**命名,与另两个的轴不一致),已统一为 `subtask-goal`。原话见 [27-REQ](./27-requirement.md) R-13.1。 > **决策沿革**(完整过程见 [27-REQ](./27-requirement.md) R-10 / R-12):最初选 `task-`,理由是"`goal` 被 OKR 占用";§六 把 OKR 正名后该理由失效,改用"验收跟着一次 task 执行走"继续保留 `task-`;最终因上面的基数论证推翻,定为 `goal-`。 --- ## 五、三种 benchmark 必须分开(当前全部叫 `benchmark`) 仓库里 `benchmark` 一词同时指三件完全不同的事。**统一加前缀区分**: | 新名 | 指什么 | 现状 | |---|---|---| | **`skill-benchmark`** | 给 **Skill** 打分 + Arena ELO 排行榜 | ✅ 已实现:`server/services/benchmark-runner.js`、`benchmark-db.js`、`benchmark_runs` 表(主键 `skill_name`)、`comparisons` ELO 表、路由 `/benchmarks` `/benchmark-all` `/benchmark/status` | | **`agent-benchmark`** | 给**单个 system agent** 打能力分,用于 hire / `depositTemplate()` 的 **≥95 门槛** | ⚠️ 部分存在:`system_agents.benchmark_score` 字段(P0 已落地)、`_registry.json` 的 `benchmarkScore`(实测仅 3 条非空:ceo=97 / qa-judge=95 / base=90) | | **`goal-benchmark`** | 给**单个 goal**(一次人类需求描述)定验收标准,由 `goal-benchmark` agent 产出、validator 节点执行 | 🔴 本次设计新增,尚未实现 | ### 代码层重命名的影响面(未执行,需单独排期) 文档层的消歧已在 27 系列全部完成。**代码层的重命名是一次真实的数据迁移,不是 find-replace**,因此本轮只给出影响面清单,不动代码: | 对象 | 位置 | 风险 | |---|---|---| | `benchmark_runs` / `comparisons` / `security_scans` 表 | `data/benchmark.sqlite` | 改表名需迁移脚本 + 停机;这三张表服务于线上 Arena 排行榜 | | `benchmarkScore` 字段 | `system/templates/_registry.json`(磁盘数据) | 改字段名需同步改所有读写方 + 迁移已有数据 | | `/benchmarks` `/benchmark-all` `/benchmark/status` | `server/routes/` | 公开 API,改路径破坏外部调用方 | | `benchmark-runner.js` / `benchmark-db.js` | `server/services/` | 文件重命名成本低,但所有 require 点要同步 | **建议**:代码层保持现状(既有的就默认是 `skill-benchmark` 语义),**只对新增代码强制用新前缀**。即 `goal-benchmark` 相关的表、字段、路由从第一天就带前缀,不与既有的混在一起。等哪天真要清理历史命名时,再按上表单独排期。 --- ## 六、OKR 体系:从 `goal` 改名为 `okr` > ⚠️ **注意与 §四 的区别**:本节说的是**把 OKR 体系/树/节点从 `goal` 这个词里解放出来**。解放之后 `goal` 被重新赋予 §四 的新含义 ——「一个人类需求描述」。两者不冲突,但读的时候要分清:`okr` 是跨多个 goal 的长周期度量树,`goal` 是一次具体的人类需求。 这棵树的正式名称是 **OKR(Objective - Key Result)**,不应该叫 "goal"。当前代码里 `goal` 既是树的名字、又是树里第 1 层节点的名字,是一处自我遮蔽的命名。 ### 层级改动(已实施) 迁移前: ``` G-001 → Goal (第1层) G-001.1 → Objective (第2层) G-001.1.1 → Key Result (第3层) G-001.1.1.1 → Sub-Task (第4层+) ``` 迁移后: ``` O-001 → Objective (第1层) O-001.1 → Key Result (第2层) O-001.1.1 → Sub-Task (第3层+) ``` ### 改法 体系整体叫 **OKR**,第 1 层的 "Goal" 也一并正名 —— OKR 的标准词汇里没有 "Goal" 这一层,现在的第 1 层实际承担的是"一个大目标下挂多组 OKR"的容器角色: | 层 | 旧名 | 新名 | ID 前缀 | |---|---|---|---| | 1 | Goal | **Objective**(顶层目标) | `O-001` | | 2 | Objective | **Key Result** | `O-001.1` | | 3 | Key Result | **Sub-Task** | `O-001.1.1` | | 4+ | Sub-Task | (合并进 Sub-Task 层级) | `O-001.1.1.1` | > 这样是标准的 Objective → Key Result → Sub-Task 三层。如果确实需要保留四层,则第 1 层建议叫 **Mission**(`M-001`),下挂 Objective / Key Result / Sub-Task —— 但**不要**再叫 Goal。 ### 影响面与执行结果(✅ 已实施) > **v0.2 更正**:本节初版把磁盘数据迁移标为「高风险」,那是**高估**。实地核实后,存量数据几乎为空: > `data/goals/_index.json` 与 `data/companies/idearun/goals/_index.json` 都是 `[]`, > `data/goals/G-001/` 只剩一个孤儿 `history.log`(`goal.json` 早已丢失), > `system/tasklog/` 完全为空。真实风险是**低**的。 | 对象 | 处理 | 状态 | |---|---|---| | `server/services/goals.js` → `okr-service.js`;`GoalsService` → `OkrService` | git mv + 标识符改名 | ✅ | | `server/routes/goals.js` → `okr.js` | git mv | ✅ | | `/api/goals/*` → `/api/okr/*` | `index.js` / `agent-server.js` **同时注册新旧两条路径**,旧路径作为向后兼容别名 | ✅ | | `/api/company/:cid/goals/*` → `/okr/*`(7 条) | 用数组路径 `[`/:companyId/okr...`, `/:companyId/goals...`]` 一次挂两个 URL | ✅ | | `data/{,companies/*/}goals/` → `okr/` | `scripts/migrate-goals-to-okr.js`(`npm run okr:migrate`) | ✅ | | ID 前缀 `G-` → `O-`;`goal.json` → `objective.json` | 同上脚本,含 `nodes` 的 key、`children` 数组、`_index.json`、`progress.json`、`history.log` | ✅ | | 层级名 `['Goal','Objective','KeyResult','SubTask']` → `['Objective','KeyResult','SubTask','SubTask']` | `parseNodeId()` | ✅ | | `goalId` 请求体字段 | `agent-chat.js` 的 dispatch/report **同时接受 `objectiveId`(新)与 `goalId`(旧)**,写入时两个字段都留 | ✅ | | `OkrDashboard.vue` / `CompanyDashboard.vue` | 切到 `/api/okr` | ✅ 前后端命名终于一致 | | `bin/lemon.js` | 新增 `okr create/add-node/list/status`,保留 `goal *` 作为别名 | ✅ | **迁移脚本要点**(`scripts/migrate-goals-to-okr.js`): - `--dry-run` 预演;幂等可重复执行;迁移前自动备份为 `*.bak-goals` - 处理一个反直觉的边界:**`OkrService` 构造函数会自动 `mkdir` 目标目录**,所以「`okr/` 已存在」不等于「已迁移过」——只要服务被 require 过一次就会留下空壳。脚本用 `isEmptyOkrDir()` 区分空壳与真实数据,空壳直接移除后继续迁移。 - 容忍孤儿条目(有 `history.log` 无 `goal.json`) **验证**:`server/tests/unit/okr-service.test.js`(7 例)+ 全量 `npm run test:unit`(652 例全绿)。 ### 对 27 系列的直接影响 [27-E2E](./27-e2e-task-execution-flow.md) §三 阶段 3 指出:27 号文档 `work_graphs.goal_task_id` 这个单字段不足以定位一个 OKR 节点(需要 `companyId` + 树 ID + 节点 ID 三元组)。改名后该字段应为: ```sql okr_node_id TEXT, -- 形如 'O-001.1.1',配合 company_id 定位 task_id TEXT, -- 形如 'T-xxx',system/tasklog/{taskId}.json ``` --- ## 七、速查表 | 不要用 | 要用 | |---|---| | `planner` | `org-planner`(节点)| | `validator` | 节点:`org-validator` / `org-{X}-validator`;本体:`org-validator` / `{X}-validator` | | `planning` | `orggraph-planning` / `workgraph-planning` / `brd-planning` / `prd-planning` / `sdd-planning` | | `code-planner` / `code-coder` / `code-validator` | `org-planner` / `org-coder` / `org-validator` | | `content-planner` / `content-writer` / `content-validator` | `org-planner` / `org-writer` / `org-validator` | | `ops-planner` / `ops-monitor` / `ops-validator` | `org-planner` / `org-monitor` / `org-validator` | | `org-graph-creator` | `orggraph-planning` | | `innovator` | `org-innovator` | | `hunter` | `innovator-hunt` | | `digester` | `innovator-digest` | | `evolver` | `innovator-evolver` | | `benchmark`(裸用) | `skill-benchmark` / `agent-benchmark` / `goal-benchmark` | | `test-data` / `test-script`(裸用) | `goal-test-data` / `goal-test-script` | | `goal` / `goals`(指 OKR 体系时) | `okr` | | `Goal`(指 OKR 第 1 层节点) | `Objective`(或 `Mission`,若保留四层) | | `goalId`(OKR 树根 ID) | `objectiveId` | | `nodeId`(树内节点) | `nodeId`(不变,已由 objectiveId 限定作用域) | | `G-001.1.1` | `O-001.1.1` |