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 表 |
❌ config/backends.json |
agent_clients |
|
❌ 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. 目标架构
设计原则
- 单一存储源:PostgreSQL 是唯一的持久化存储
- 静态默认 + 用户自定义:JSON 文件降级为 seed 数据源,首次启动时导入 DB
- Registry 变为 DB 代理:所有读写走 DB,不再读 JSON 文件
- 向后兼容:对外 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] 读取
[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()→ 组装完整配置(兼容旧接口)
- [x]
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]
[x] 3.3 验证消费方兼容
- [x]
agent-manager.js不需要改(通过 Registry 接口访问) - [x]
agent-config-builder.js改为从 Registry 获取配置(保留 JSON fallback) - [x]
routes/agents.js的getFullConfig()返回相同结构
- [x]
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]
[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]
[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]
[x] 4.4 Backend 可用性检测
- [x]
GET /api/agents/backends/health→ 对每个 CLI Backend 执行which <command> - [x] 返回
{ name, command, installed, version? }[]
- [x]
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 / deleteAgentGroupcreateBackend / updateBackend / deleteBackendresetToDefaults()
- [x]
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移除configimport - [x]
AgentConfigBuilder移除 JSON fallback,未注入 registry 时报错
- [x]
[x] 6.2 测试
- [x] 旧测试已适配 DB 版 Registry(
registry-integration.test.js6/6,e2e-registry-full.test.js20/20) - [x] 新增
registry-db.test.js— 36 项集成测试全绿 - [x] 新增
api-agents-crud.test.js— API CRUD 端到端测试 - [x] 前端 vite build 通过
- [x] 旧测试已适配 DB 版 Registry(
[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 时验证路径存在性 |