# P0 地基 — Graph Agent 架构任务拆分 > 对应 `docs/system-design/27-graph-agent-architecture.md` 的 Phase P0。目标:只加不改——新建三张表,把现有数据原样导入作为种子数据并校验行数,完全不动 `company-service.js` 现有的 hire/copy 执行路径。这是 P1(虚拟引用改造)能安全推进的前提:先有一份可信、经过校验的镜像数据在 DB 里,再去改代码。 ## Context 两轮只读调研确认了关键事实: - 现有三个 sqlite 库(`skills.sqlite`/`benchmark_logs.sqlite`/`users.sqlite`)都遵循同一套手写约定:`server/services/*-db.js` 里 `new Database()` + WAL + `CREATE TABLE IF NOT EXISTS`(无迁移文件、无版本追踪),snake_case 列名。**没有 `:memory:` 测试,测试用临时文件 + env 变量覆盖 DB 路径 + 清 require 缓存**(`server/tests/unit/skill-db.test.js` 是标准范式)。`benchmark-db.js` 声明了 env override 但实际没读,是个已知的潜在 bug,新代码要避免重蹈。 - `system/agents/` 有 11 个目录,其中 `system/agents/templates/` 是遗留杂物、不是真 agent,必须过滤掉(真实 agent 数 = 10)。 - `system/templates/` 有 34 个目录,但 `_registry.json`(数组)只覆盖 9 个(`ceo`/`qa-judge`/`base`/6 个 devloop-*)——目录扫描才是数据源头,registry 只用来补充可选的 benchmark 元数据,其余 25 个模板要容忍没有 `config.json`(如 `ceo/` 只有 `plan.md`+`todo.md`)。 - `data/companies/` 目前只有一家真实公司 `idearun`,`company.json.agents` 是 6 个角色名字符串的扁平数组:`youtube-crawler`/`evolver-youtube-crawler` 能对上 `system/agents/` 里的全局 agent,另外 4 个(`claude-youtube-skills`/`trend-scout-global`/`content-selector`/`idearuncreate`)在 global/template 里都找不到对应目录——这些是公司专属训练出来的实例,是"company-scoped system agent"这个概念在真实数据里的第一批样本。 - `company-service.js` 的 `hire`(189)/`scaffoldNewAgent`(241)/`depositTemplate`(294)/`hireWithTraining`(381)/`_copyTemplate`(621)/`_injectCompanyContext`(667) 六个函数行号已核实无漂移,P0 全程不动这个文件。 ## 新增文件 - `server/services/graph-db.js` —— 新 sqlite 库 `data/graph.sqlite`,照抄 `skill-db.js`/`user-db.js` 的样板:`fs.mkdirSync` 保证目录存在 → `new Database(DB_PATH)` → `journal_mode=WAL` + `foreign_keys=ON` → lazy singleton `getDb()`。**支持 `GRAPH_DB_PATH` env 覆盖,且要确保代码真的读了这个变量**。 - `server/services/graph-seed-service.js` —— 导出 `seedGlobalAgents()` / `seedTemplates()` / `seedCompanyAgents()` / `verifyCounts()`,供脚本和测试复用。 - `scripts/graph-seed.js` —— 薄 CLI 入口,依次调用上面四个函数并打印汇总报告,风格参照现有 `scripts/sync-skills.js`。 - `package.json` 加一条 `"graph:seed": "node scripts/graph-seed.js"`。 - `server/tests/unit/graph-db.test.js` —— 照抄 `skill-db.test.js` 的临时文件 + env 覆盖 + 清 require 缓存范式。 ## Schema ```sql CREATE TABLE IF NOT EXISTS system_agents ( id TEXT PRIMARY KEY, -- 就是目录名,如 'master'/'ceo'/'claude-youtube-skills' name TEXT NOT NULL, scope TEXT NOT NULL CHECK (scope IN ('public','company')), owner_company_id TEXT, source_kind TEXT NOT NULL CHECK (source_kind IN ('global_agent','template','company_hired')), legacy_path TEXT NOT NULL, -- 当前真实所在目录,如 'system/agents/master' status TEXT NOT NULL DEFAULT 'active', benchmark_score REAL, created_at TEXT NOT NULL, updated_at TEXT NOT NULL ); CREATE TABLE IF NOT EXISTS org_graph_nodes ( id INTEGER PRIMARY KEY AUTOINCREMENT, company_id TEXT NOT NULL, system_agent_id TEXT NOT NULL REFERENCES system_agents(id), role_name TEXT NOT NULL, created_at TEXT NOT NULL ); CREATE TABLE IF NOT EXISTS org_graph_edges ( id INTEGER PRIMARY KEY AUTOINCREMENT, company_id TEXT NOT NULL, from_node_id INTEGER NOT NULL REFERENCES org_graph_nodes(id), to_node_id INTEGER NOT NULL REFERENCES org_graph_nodes(id), relation_type TEXT NOT NULL ); ``` 相对设计文档(27 号)草稿的两处调整: 1. `system_agents.id` 用 **TEXT PRIMARY KEY = 目录名**,不用自增代理键——后续阶段要靠这个 id 反查文件系统路径,代理键只会多一层无意义的间接。 2. 新增 `source_kind` + `legacy_path` 两列——P0 明确不做目录搬迁(三种来源现在物理上分散在三个不同地方),没有这两列种子数据就"没有指针",没法在 P1 真正切换执行路径时定位到原文件。 `org_graph_nodes`/`org_graph_edges` 沿用仓库现有的自增整数主键惯例,暂不需要对外暴露的字符串 ID(要到 P2/P3 才会被外部引用)。 ## 验证方式 1. `npm run test:unit` —— 新增的 `graph-db.test.js` 通过,现有测试无回归 2. `npm run graph:seed` —— 在真实仓库数据上跑,人工核对输出的行数汇总(10 / 34 / 6,见下方任务列表) 3. `sqlite3 data/graph.sqlite ".schema"` —— 人工确认三张表结构与上面 SQL 一致 4. `git diff --stat` —— 确认这一阶段的改动只新增文件,没有改动 `company-service.js`/`agent-manager.js`/`prompt-builder.js` 任何一行 任务清单见 [todo.md](./todo.md)。