agent-registry-db-migration.md 14.2 KB

Agent Registry → PostgreSQL 迁移 — 完整规划

⚠️ 2026-02-24 更新:本文档中的 agent_launchers 和 agent_cli_backends 表已在四维配置重构中被删除。 新架构使用 agent_clients 表统一管理 AI 客户端配置(kimi / claude / gemini / codex)。 agent_definitions 表的 protocol / transport / launcher / cli_backend 字段已替换为 client / deploy / mode。 详见 docs/agent-network/04-四维正交架构.md。

1. 现状(迁移前快照)

存储分裂

数据 迁移前存储 迁移后存储
任务/执行/事件/文件 ✅ PostgreSQL (knex) ✅ PostgreSQL
网关会话 ✅ PostgreSQL (knex) ✅ PostgreSQL
Agent 定义 ❌ config/agents.json ✅ agent_definitions 表
Launcher 定义 ❌ config/backends.json 已删除 → agent_clients
CLI Backend 定义 ❌ config/backends.json 已删除 → agent_clients
Client 定义(新) — ✅ agent_clients 表
Agent Group 定义 ❌ config/agents.json ✅ agent_groups 表
系统设置 ❌ config/agents.json ✅ agent_settings 表

消费方

模块 使用的 Registry 方法
agent-manager.js getFullConfig(), getAgent()
routes/agents.js getFullConfig(), getAgent(), listAgents(), listCliBackends(), register(), unregister(), checkHealth()
agent-config-builder.js 接收 agents 对象(从 orchestrator 传入)

2. 目标架构

设计原则

  1. 单一存储源:PostgreSQL 是唯一的持久化存储
  2. 静态默认 + 用户自定义:JSON 文件降级为 seed 数据源,首次启动时导入 DB
  3. Registry 变为 DB 代理:所有读写走 DB,不再读 JSON 文件
  4. 向后兼容:对外 API 签名不变,内部实现切换

数据流变更

Before:
  启动 → 读 agents.json + backends.json → 内存 config 对象 → watchFile 热重载

After:
  首次启动 → 读 JSON → seed 到 DB → 删除/忽略 JSON
  后续启动 → 直接从 DB 读取
  UI CRUD → 直接写 DB → Registry 缓存失效 → 实时生效

3. 数据库 Schema 设计

3.1 agent_launchers 表

CREATE TABLE IF NOT EXISTS agent_launchers (
    name        VARCHAR(100) PRIMARY KEY,       -- 'kimi-local', 'claude-docker'
    type        VARCHAR(20) NOT NULL,           -- 'process', 'docker', 'none'
    command     VARCHAR(200),                   -- 'kimi', 'claude'
    args        JSONB DEFAULT '[]',             -- ['acp']
    image       VARCHAR(200),                   -- docker image
    port        INTEGER,                        -- docker port
    url         VARCHAR(500),                   -- websocket url
    env_file    VARCHAR(500),                   -- docker envFile
    volumes     JSONB DEFAULT '[]',             -- docker volumes
    is_builtin  BOOLEAN DEFAULT false,          -- 从 seed 导入的标记
    created_at  TIMESTAMP DEFAULT NOW(),
    updated_at  TIMESTAMP DEFAULT NOW()
);

3.2 agent_cli_backends 表

CREATE TABLE IF NOT EXISTS agent_cli_backends (
    name            VARCHAR(100) PRIMARY KEY,   -- 'claude-cli', 'gemini-cli'
    command         VARCHAR(200) NOT NULL,       -- 'claude', 'gemini'
    args            JSONB DEFAULT '[]',          -- ['--print', '--output-format', 'json']
    resume_args     JSONB,                       -- 恢复执行参数
    input           VARCHAR(20) DEFAULT 'arg',   -- 'arg', 'stdin'
    output          VARCHAR(20) DEFAULT 'text',  -- 'text', 'json', 'jsonl'
    resume_output   VARCHAR(20),
    model_arg       VARCHAR(50),
    session_id_fields JSONB,                     -- ['session_id', 'conversationId']
    system_prompt_arg VARCHAR(50),
    system_prompt_when VARCHAR(20),              -- 'first', 'always', 'never'
    session_mode    VARCHAR(20) DEFAULT 'none',  -- 'none', 'always', 'existing'
    image_arg       VARCHAR(50),
    image_mode      VARCHAR(20),
    reliability     JSONB DEFAULT '{}',          -- watchdog 配置
    is_builtin      BOOLEAN DEFAULT false,
    created_at      TIMESTAMP DEFAULT NOW(),
    updated_at      TIMESTAMP DEFAULT NOW()
);

3.3 agent_definitions 表

CREATE TABLE IF NOT EXISTS agent_definitions (
    name          VARCHAR(100) PRIMARY KEY,     -- 'coder', 'verifier'
    type          VARCHAR(30) NOT NULL,         -- 'executor', 'validator', 'master', 'gateway', 'orchestrator'
    description   VARCHAR(500),
    protocol      VARCHAR(30),                  -- 'acp', 'claude-stream', null
    transport     VARCHAR(30),                  -- 'pipe', 'websocket', null
    launcher      VARCHAR(100) REFERENCES agent_launchers(name),
    cli_backend   VARCHAR(100) REFERENCES agent_cli_backends(name),
    prompt_file   VARCHAR(500),                 -- './agents/coder.md'
    agent_group   VARCHAR(100),                 -- orchestrator 专用: 指向 group name
    is_builtin    BOOLEAN DEFAULT false,
    created_at    TIMESTAMP DEFAULT NOW(),
    updated_at    TIMESTAMP DEFAULT NOW(),
    CONSTRAINT agent_exec_mode CHECK (
        (launcher IS NOT NULL AND cli_backend IS NULL) OR
        (launcher IS NULL AND cli_backend IS NOT NULL) OR
        (type = 'orchestrator')
    )
);

3.4 agent_groups 表

CREATE TABLE IF NOT EXISTS agent_groups (
    name          VARCHAR(100) PRIMARY KEY,     -- 'development', 'novel-writing'
    display_name  VARCHAR(200) NOT NULL,        -- '软件开发'
    description   VARCHAR(500),
    icon          VARCHAR(50),                  -- 'code', 'book'
    is_default    BOOLEAN DEFAULT false,
    max_repairs   INTEGER,
    is_builtin    BOOLEAN DEFAULT false,
    created_at    TIMESTAMP DEFAULT NOW(),
    updated_at    TIMESTAMP DEFAULT NOW()
);

3.5 agent_group_members 表

CREATE TABLE IF NOT EXISTS agent_group_members (
    group_name  VARCHAR(100) REFERENCES agent_groups(name) ON DELETE CASCADE,
    agent_name  VARCHAR(100) REFERENCES agent_definitions(name) ON DELETE CASCADE,
    position    INTEGER DEFAULT 0,              -- 排序
    PRIMARY KEY (group_name, agent_name)
);

3.6 agent_settings 表

CREATE TABLE IF NOT EXISTS agent_settings (
    key         VARCHAR(100) PRIMARY KEY,       -- 'maxIterations', 'timeout'
    value       JSONB NOT NULL,
    updated_at  TIMESTAMP DEFAULT NOW()
);

4. 实施 TODO

Phase 1: DB Schema + Seed(后端基础)

  • [x] 1.1 创建 migration SQL:db/schema-agents.sql

    • [x] 6 张表的 CREATE TABLE
    • [x] CHECK 约束、索引、注释
    • [x] 幂等(IF NOT EXISTS)
  • [x] 1.2 创建 seed 脚本:db/seed-agents.js

    • [x] 读取 config/agents.json + config/backends.json
    • [x] 转换为 DB 行,INSERT ... ON CONFLICT DO NOTHING(幂等)
    • [x] 标记 is_builtin = true
  • [x] 1.3 启动时自动执行

    • [x] 服务启动 → 执行 schema-agents.sql → 执行 seed(如果表为空)
    • [x] 参考现有 db/schema.sql 的执行模式

Phase 2: DB 查询层 agent-queries.js

  • [x] 2.1 创建 services/agent-queries.js(对标 task-queries.js)
    • [x] getAgent(name) → SELECT agent_definitions + JOIN launcher/cli_backend
    • [x] listAgents(filter) → 按 type/launcher/cliBackend 过滤
    • [x] createAgent(name, config) → INSERT
    • [x] updateAgent(name, config) → UPDATE
    • [x] deleteAgent(name) → DELETE(允许删除)
    • [x] listCliBackends() → SELECT agent_cli_backends
    • [x] getCliBackend(name) → 单个
    • [x] listLaunchers() → SELECT agent_launchers
    • [x] listGroups() → SELECT agent_groups + JOIN members
    • [x] getGroupDetail(name) → 组 + 组内 Agent + prompt 内容
    • [x] createGroup(name, config) → INSERT group + members
    • [x] updateGroup(name, config) → UPDATE group + REPLACE members
    • [x] deleteGroup(name) → DELETE
    • [x] getSettings() → SELECT agent_settings
    • [x] getFullConfig() → 组装完整配置(兼容旧接口)

Phase 3: Registry 重写

  • [x] 3.1 AgentRegistry 切换到 DB

    • [x] 构造函数:不再读 JSON,接受可选参数保持兼容
    • [x] load() → 从 DB 加载到内存缓存(异步)
    • [x] 所有查询方法代理到 agent-queries.js
    • [x] 移除 watchFile 逻辑(startWatch/stopWatch 变为空操作)
    • [x] 写操作后清除缓存(invalidateCache() + load())
  • [x] 3.2 移除 dynamicAgents 内存存储

    • [x] register() → 写 DB
    • [x] unregister() → 从 DB 删除
  • [x] 3.3 验证消费方兼容

    • [x] agent-manager.js 不需要改(通过 Registry 接口访问)
    • [x] agent-config-builder.js 改为从 Registry 获取配置(保留 JSON fallback)
    • [x] routes/agents.js 的 getFullConfig() 返回相同结构

Phase 4: API CRUD

  • [x] 4.1 Agent CRUD

    • [x] POST /api/agents → 持久化创建(写 DB)
    • [x] PUT /api/agents/:name → 更新 Agent 配置
    • [x] DELETE /api/agents/:name → 从 DB 删除(允许删除 builtin)
    • [x] 校验:type 合法、launcher 或 cliBackend 二选一存在、prompt 文件存在
  • [x] 4.2 Backend CRUD

    • [x] POST /api/agents/backends → 创建 CLI Backend
    • [x] PUT /api/agents/backends/:name → 更新
    • [x] DELETE /api/agents/backends/:name → 删除(检查引用计数)
  • [x] 4.3 Group CRUD

    • [x] POST /api/agents/groups → 创建 Group
    • [x] PUT /api/agents/groups/:id → 更新 Group(含成员列表)
    • [x] DELETE /api/agents/groups/:id → 删除 Group
  • [x] 4.4 Backend 可用性检测

    • [x] GET /api/agents/backends/health → 对每个 CLI Backend 执行 which <command>
    • [x] 返回 { name, command, installed, version? }[]

Phase 5: 前端 CRUD UI

  • [x] 5.1 Agent 管理

    • [x] AgentRegistry 页面顶部增加 "新建 Agent" 按钮
    • [x] 新建表单(Drawer):name, type, description, prompt, backend 选择
    • [x] AgentRow 右侧增加 "编辑" / "删除" 按钮
    • [x] 编辑表单:读取当前配置 → 修改 → PUT
    • [x] 删除确认:Popconfirm → DELETE
    • [x] 操作后自动刷新列表
  • [x] 5.2 Backend 健康状态

    • [x] BackendPanel 每个卡片增加健康状态灯(绿/红/灰)
    • [x] 调用 GET /api/agents/backends/health 获取状态
    • [x] 未安装的 CLI 显示安装提示
  • [ ] 5.3 Group 管理(可选,低优先级)

    • [ ] Group 卡片增加 "编辑" / "删除" 按钮
    • [ ] 新建 Group 表单:name, description, icon, 成员多选
  • [x] 5.4 API 层

    • [x] api/index.js 新增 CRUD 函数:
    • createAgent(name, config)
    • updateAgent(name, config)
    • deleteAgent(name)
    • getBackendsHealth()
    • createAgentGroup / updateAgentGroup / deleteAgentGroup
    • createBackend / updateBackend / deleteBackend
    • resetToDefaults()

Phase 6: 清理

  • [x] 6.1 移除 JSON 依赖

    • [x] config/agents.json → 仅保留为 seed 参考文件,运行时不再读取
    • [x] config/backends.json → 同上
    • [x] Registry 构造函数移除 agentsPath / backendsPath 参数
    • [x] 移除 startWatch() / stopWatch() 方法(变为空操作)
    • [x] task-service.js 移除 config import
    • [x] AgentConfigBuilder 移除 JSON fallback,未注入 registry 时报错
  • [x] 6.2 测试

    • [x] 旧测试已适配 DB 版 Registry(registry-integration.test.js 6/6,e2e-registry-full.test.js 20/20)
    • [x] 新增 registry-db.test.js — 36 项集成测试全绿
    • [x] 新增 api-agents-crud.test.js — API CRUD 端到端测试
    • [x] 前端 vite build 通过
  • [x] 6.3 数据安全

    • [x] 内置 Agent 删除时弹出确认(Popconfirm 区分 builtin vs 自定义,isBuiltin 已传到前端)
    • [x] 恢复内置数据的 "重置为默认" 按钮 → POST /api/agents/seed/reset

5. 关键设计决策

Q1: 是否允许删除 builtin Agent?

允许,但 UI 上增加二次确认 + "重置为默认" 恢复功能。

Q2: prompt 文件如何管理?

保持文件系统。prompt 内容继续存储在 ./agents/*.md 文件中,DB 中只存路径 (prompt_file)。 理由:prompt 是长文本、频繁编辑、适合 git 版本控制,不适合存 DB。

Q3: 是否需要缓存?

需要。AgentRegistry 维持内存缓存(Map),写操作后标记缓存失效,下次读取重新从 DB 加载。 理由:Agent 配置读多写少,且 agent-manager 在任务执行中频繁查询。

Q4: 旧的 dynamicAgents 内存注册如何迁移?

废弃。所有注册操作直接写 DB。旧的 register() / unregister() API 语义不变,底层改为 DB 操作。

Q5: 执行优先级顺序

Phase 1-3 是后端基础,必须先完成。Phase 4-5 可以增量交付。Phase 6 最后清理。


6. 依赖关系图

Phase 1 (schema + seed)
    ↓
Phase 2 (agent-queries.js)
    ↓
Phase 3 (Registry 重写)
    ↓ ← 此处可验证:现有功能不回归
Phase 4 (API CRUD)
    ↓
Phase 5 (前端 CRUD UI)
    ↓
Phase 6 (清理 JSON + 测试)

7. 风险与回退

风险 缓解措施
DB 不可用时系统无法启动 保留 JSON 作为 fallback(Phase 6 之前)
现有测试依赖 JSON 加载 Phase 3 完成后立即跑全量测试
多实例并发写 PostgreSQL 行级锁天然支持
prompt 文件路径在 DB 中失效 seed 时验证路径存在性