第二十三章 二次开发与功能扩展
作者
谭策 — 独立开发者 | AIOps 领域探索者
- 🌐 项目官网:ITOpsAgentinfo
- 📝 博客:zjzwfw.cloud
- 📧 邮箱:huawei_network@foxmail.com
- 💬 微信公众号:IT Online

许可证
MPL-2.0 © 谭策
本章导读
ITOps Agent Platform 采用模块化架构设计,前后端分离、路由与业务逻辑分层、数据库迁移体系完善,为二次开发提供了良好的基础。本章将以实际操作为导向,手把手教你如何在现有代码基础上添加新功能,涵盖从前端页面到后端接口、从数据库表到自定义 Agent 的完整开发流程。
学习目标
- 掌握前端新增页面的完整流程(路由 + 组件 + API 调用)
- 掌握后端新增 API 端点的完整流程(路由 + Service + 数据库操作)
- 掌握数据库表的新增与迁移机制
- 学会创建自定义 Agent 和自定义工作流
- 学会扩展通知渠道和 LLM Provider
- 学会添加告警源适配器
- 理解项目的扩展点与插件化设计思路
核心内容
23.1 添加前端新页面
以添加一个 "系统状态监控" 页面为例。
步骤 1:创建页面组件
// frontend/src/pages/SystemStatus.tsx
import { useQuery } from '@tanstack/react-query';
import { Activity, Server, Database, Shield } from 'lucide-react';
import api from '../lib/api';
interface HealthStatus {
status: string;
uptime: number;
memory: NodeJS.MemoryUsage;
database: string;
}
export default function SystemStatus() {
const { data, isLoading, error } = useQuery({
queryKey: ['systemStatus'],
queryFn: () => api.get('/api/health/summary').then(r => r.data.data),
refetchInterval: 30000,
});
if (isLoading) return <div>加载中...</div>;
if (error) return <div>加载失败</div>;
return (
<div className="p-6">
<h1 className="text-2xl font-bold mb-6">系统状态监控</h1>
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-4">
<StatusCard
icon={<Activity />}
title="系统状态"
value={data.status}
color={data.status === 'healthy' ? 'green' : 'red'}
/>
<StatusCard
icon={<Server />}
title="运行时间"
value={formatUptime(data.uptime)}
color="blue"
/>
<StatusCard
icon={<Database />}
title="数据库"
value={data.database}
color={data.database === 'connected' ? 'green' : 'red'}
/>
<StatusCard
icon={<Shield />}
title="内存使用"
value={formatBytes(data.memory.rss)}
color="purple"
/>
</div>
</div>
);
}
function StatusCard({ icon, title, value, color }: any) {
const colorMap: Record<string, string> = {
green: 'text-green-500',
red: 'text-red-500',
blue: 'text-blue-500',
purple: 'text-purple-500',
};
return (
<div className="bg-card rounded-lg p-4 border border-border">
<div className={`w-8 h-8 mb-2 ${colorMap[color]}`}>{icon}</div>
<p className="text-sm text-text-secondary">{title}</p>
<p className="text-xl font-semibold text-text-primary">{value}</p>
</div>
);
}
function formatUptime(seconds: number): string {
const days = Math.floor(seconds / 86400);
const hours = Math.floor((seconds % 86400) / 3600);
return `${days}天 ${hours}小时`;
}
function formatBytes(bytes: number): string {
if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`;
return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
}步骤 2:添加路由
// frontend/src/App.tsx — 在现有路由中添加
import SystemStatus from './pages/SystemStatus';
function App() {
return (
<QueryClientProvider client={queryClient}>
<BrowserRouter>
<AuthProvider>
<ErrorBoundary>
<Routes>
<Route path="/login" element={<Login />} />
<Route element={<ProtectedRoute />}>
<Route element={<Layout />}>
<Route path="/dashboard" element={<Dashboard />} />
<Route path="/system-status" element={<SystemStatus />} /> {/* 新增 */}
{/* ... 其他路由 ... */}
</Route>
</Route>
</Routes>
</ErrorBoundary>
</AuthProvider>
</BrowserRouter>
</QueryClientProvider>
);
}步骤 3:添加到导航菜单
// frontend/src/components/layout/Layout.tsx — 在 navigation 数组中添加
import { Activity } from 'lucide-react';
const navigation = [
{ name: '仪表盘', href: '/dashboard', icon: LayoutDashboard },
{ name: '系统状态', href: '/system-status', icon: Activity }, // 新增
{ name: '服务器管理', href: '/servers', icon: Server },
// ...
];前端页面开发流程总结:
1. 创建页面组件 (src/pages/xxx.tsx)
│
2. 添加路由 (App.tsx)
│
3. 添加到导航菜单 (Layout.tsx)
│
4. 调用后端 API (api.get/post/...)
│
5. 使用 React Query 管理数据状态
│
6. 测试页面功能23.2 添加后端 API 端点
以添加一个 "系统信息" 接口为例。
步骤 1:创建路由文件
// backend/src/routes/systemStatusRoutes.ts
import { Router, Request, Response } from 'express';
import { requireRole } from '../middleware/auth';
import { db } from '../models/database';
import { logger } from '../utils/logger';
const router = Router();
// 获取系统信息
router.get('/info', requireRole('admin'), async (_req: Request, res: Response) => {
try {
const memUsage = process.memoryUsage();
const uptime = process.uptime();
res.json({
success: true,
data: {
nodeVersion: process.version,
platform: process.platform,
pid: process.pid,
uptime,
memory: {
rss: memUsage.rss,
heapUsed: memUsage.heapUsed,
heapTotal: memUsage.heapTotal,
},
timestamp: new Date().toISOString(),
},
});
} catch (error) {
logger.error('获取系统信息失败', error as Error);
res.status(500).json({ success: false, error: '获取系统信息失败' });
}
});
// 获取数据库统计
router.get('/database-stats', requireRole('admin'), async (_req: Request, res: Response) => {
try {
const stats = db.prepare(`
SELECT name,
(SELECT COUNT(*) FROM sqlite_master WHERE type='table') as tableCount
`).get();
const tables = db.prepare(
"SELECT name, (SELECT COUNT(*) FROM sqlite_master WHERE name = ?) as existsFlag FROM sqlite_master WHERE type='table'"
).all();
const tableSizes: Array<{ name: string; count: number }> = [];
for (const t of tables as Array<{ name: string }>) {
const count = db.prepare(`SELECT COUNT(*) as count FROM ${t.name}`).get() as { count: number };
tableSizes.push({ name: t.name, count: count.count });
}
res.json({ success: true, data: { tables: tableSizes } });
} catch (error) {
logger.error('获取数据库统计失败', error as Error);
res.status(500).json({ success: false, error: '获取数据库统计失败' });
}
});
export default router;步骤 2:在 app.ts 中注册路由
// backend/src/app.ts — 添加 import 和路由注册
import systemStatusRoutes from './routes/systemStatusRoutes';
// 在 authenticateToken 之后添加
app.use(authenticateToken);
app.use('/api/system-status', rateLimiter, systemStatusRoutes); // 新增后端 API 开发流程总结:
1. 创建路由文件 (src/routes/xxxRoutes.ts)
│
2. 定义路由处理函数 (router.get/post/put/delete)
│
3. 添加权限控制 (requireRole)
│
4. 实现业务逻辑 (Service 层或直接 db 操作)
│
5. 错误处理 (try-catch + logger)
│
6. 在 app.ts 注册路由
│
7. 测试 API (curl 或 Postman)23.3 添加数据库表与迁移
方式一:在 initializeDatabase 中直接创建(新项目或开发环境)
// backend/src/models/database.ts — 在 initializeDatabase 函数中添加
export function initializeDatabase() {
db.exec(`
-- ... 现有表定义 ...
-- 新增: 自定义监控指标表
CREATE TABLE IF NOT EXISTS custom_metrics (
id TEXT PRIMARY KEY,
name TEXT NOT NULL,
metric_type TEXT NOT NULL, -- 'gauge', 'counter', 'histogram'
value REAL NOT NULL,
labels TEXT, -- JSON 格式的标签
source TEXT, -- 数据来源
collected_at DATETIME DEFAULT CURRENT_TIMESTAMP,
created_at DATETIME DEFAULT CURRENT_TIMESTAMP
);
CREATE INDEX IF NOT EXISTS idx_metrics_name ON custom_metrics(name);
CREATE INDEX IF NOT EXISTS idx_metrics_collected ON custom_metrics(collected_at);
CREATE INDEX IF NOT EXISTS idx_metrics_source ON custom_metrics(source);
`);
runMigrations();
// ...
}方式二:通过迁移系统(生产环境推荐)
// backend/src/models/migrations.ts
interface Migration {
id: string;
up: (db: Database.Database) => void;
}
const migrations: Migration[] = [
// ... 已有迁移 ...
{
id: '2024-01-15-add-custom-metrics',
up: (db) => {
db.exec(`
CREATE TABLE IF NOT EXISTS custom_metrics (
id TEXT PRIMARY KEY,
name TEXT NOT NULL,
metric_type TEXT NOT NULL,
value REAL NOT NULL,
labels TEXT,
source TEXT,
collected_at DATETIME DEFAULT CURRENT_TIMESTAMP,
created_at DATETIME DEFAULT CURRENT_TIMESTAMP
);
CREATE INDEX IF NOT EXISTS idx_metrics_name ON custom_metrics(name);
CREATE INDEX IF NOT EXISTS idx_metrics_collected ON custom_metrics(collected_at);
`);
},
},
];
export function runMigrations(): void {
db.exec(`
CREATE TABLE IF NOT EXISTS migrations (
id TEXT PRIMARY KEY,
applied_at DATETIME DEFAULT CURRENT_TIMESTAMP
);
`);
for (const migration of migrations) {
const existing = db.prepare('SELECT 1 FROM migrations WHERE id = ?').get(migration.id);
if (!existing) {
logger.info(`Applying migration: ${migration.id}`);
migration.up(db);
db.prepare('INSERT INTO migrations (id) VALUES (?)').run(migration.id);
}
}
}数据库迁移流程:
应用启动
│
▼
initializeDatabase()
│
├── 创建/更新所有表 (CREATE TABLE IF NOT EXISTS)
│
└── runMigrations()
│
├── 创建 migrations 跟踪表
│
└── 遍历迁移列表
│
├── 已执行 → 跳过
│
└── 未执行 → 执行 up() + 记录到 migrations 表23.4 创建自定义 Agent
项目已内置多个预设 Agent(如告警分析 Agent、合规检查 Agent)。创建自定义 Agent 有两种方式:通过 UI 界面或通过 API 直接创建。
通过 API 创建自定义 Agent:
// 调用现有 API 接口
POST /api/agents
// 请求体
{
"name": "日志分析 Agent",
"avatar": "📊",
"role": "日志分析专家",
"category": "日志分析",
"description": "分析系统日志,识别异常模式和潜在问题",
"system_prompt": "你是一个专业的日志分析专家。你的任务是:\n1. 解析给定的日志内容\n2. 识别错误、警告和异常模式\n3. 分析可能的根本原因\n4. 提供处理建议\n\n请按照以下格式输出:\n## 日志摘要\n## 发现的问题\n## 根本原因分析\n## 处理建议",
"model": "doubao-4o",
"temperature": 0.5,
"enabled": 1
}Agent 执行流程:
用户输入 Prompt
│
▼
┌──────────────────────────────────┐
│ Agent 执行引擎 │
│ │
│ 1. 加载 Agent 配置 │
│ - system_prompt │
│ - model │
│ - temperature │
│ │
│ 2. 构建 LLM 请求 │
│ - system: system_prompt │
│ - user: 用户输入 │
│ - 参数: temperature, etc. │
│ │
│ 3. 调用 LLM API │
│ - Doubao / OpenAI / LocalAI │
│ │
│ 4. 解析 LLM 响应 │
│ │
│ 5. 记录执行结果 │
│ - agent_executions 表 │
│ - 执行时间、Token 数 │
│ │
│ 6. 返回结果 │
└──────────────────────────────────┘
│
▼
输出 Markdown 格式的分析结果自定义 Agent 最佳实践:
| 要点 | 说明 | 示例 |
|---|---|---|
| System Prompt | 明确角色、任务、输出格式 | "你是一个...你的任务是...请按以下格式输出..." |
| Temperature | 控制创造性:低=精确,高=灵活 | 分析类 0.3-0.5,创作类 0.7-0.9 |
| Model 选择 | 根据任务复杂度选择模型 | 简单任务用小模型,复杂分析用大模型 |
| Category | 合理分类,方便用户查找 | "日志分析"、"安全审计"、"性能诊断" |
23.5 创建自定义工作流
工作流(Workflow)由节点(Nodes)和边(Edges)组成,支持多个 Agent 协作执行复杂任务。
工作流节点类型:
| 节点类型 | 说明 | 适用场景 |
|---|---|---|
| Agent 节点 | 调用 LLM Agent 执行任务 | 分析、生成、判断 |
| 条件节点 | 根据条件分支执行 | if-else 逻辑 |
| 并行节点 | 多个分支同时执行 | 并发检查 |
| 服务器命令节点 | 在服务器上执行命令 | 巡检、修复操作 |
通过 API 创建自定义工作流:
POST /api/workflows
{
"name": "服务器异常诊断工作流",
"description": "当服务器出现异常时,自动执行诊断流程",
"nodes": [
{
"id": "node-1",
"type": "agent",
"agentId": "告警分析 Agent ID",
"label": "分析告警内容",
"position": { "x": 100, "y": 100 }
},
{
"id": "node-2",
"type": "server-command",
"command": "top -bn1 | head -20",
"label": "检查 CPU 使用率",
"position": { "x": 400, "y": 100 }
},
{
"id": "node-3",
"type": "condition",
"condition": "cpu_usage > 90",
"label": "CPU 是否过载?",
"position": { "x": 700, "y": 100 }
},
{
"id": "node-4",
"type": "agent",
"agentId": "修复建议 Agent ID",
"label": "生成修复建议",
"position": { "x": 1000, "y": 100 }
}
],
"edges": [
{ "from": "node-1", "to": "node-2" },
{ "from": "node-2", "to": "node-3" },
{ "from": "node-3", "to": "node-4", "condition": "true" }
]
}工作流执行流程:
触发工作流
│
▼
┌────────────────────────────────────┐
│ Task 引擎 │
│ │
│ 1. 加载工作流定义 (nodes + edges) │
│ │
│ 2. 按拓扑排序确定执行顺序 │
│ │
│ 3. 逐个执行节点: │
│ ├── Agent 节点 → 调用 LLM API │
│ ├── 命令节点 → SSH 执行命令 │
│ ├── 条件节点 → 评估条件表达式 │
│ └── 并行节点 → 并发执行分支 │
│ │
│ 4. 节点间通过 context 传递数据 │
│ │
│ 5. 实时更新任务状态 (tasks 表) │
│ │
│ 6. 通过 WebSocket 推送进度 │
│ │
│ 7. 生成执行报告 │
└────────────────────────────────────┘
│
▼
任务完成/失败23.6 添加通知渠道
项目已支持 Webhook、邮件、企业微信、钉钉等通知渠道。添加新的通知渠道需要以下步骤:
步骤 1:扩展通知配置
// backend/src/services/notificationService.ts
// 在通知服务中添加新的渠道
interface NotificationConfig {
// 现有渠道
webhook_enabled?: boolean;
email_enabled?: boolean;
wechat_enabled?: boolean;
dingtalk_enabled?: boolean;
// 新增:飞书
feishu_enabled?: boolean;
feishu_config?: {
webhook_url: string;
secret?: string;
};
}步骤 2:实现通知发送逻辑
// backend/src/services/notificationService.ts
// 在 sendNotification 方法中添加飞书渠道
async sendFeishuNotification(
config: { webhook_url: string; secret?: string },
title: string,
content: string
): Promise<void> {
const payload = {
msg_type: 'interactive',
card: {
header: {
title: { tag: 'plain_text', content: title },
},
elements: [
{
tag: 'markdown',
content: content,
},
],
},
};
if (config.secret) {
// 添加签名验证
const timestamp = Date.now();
const sign = this.generateFeishuSign(timestamp, config.secret);
(payload as any).timestamp = timestamp;
(payload as any).sign = sign;
}
const response = await fetch(config.webhook_url, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(payload),
});
if (!response.ok) {
throw new Error(`飞书通知发送失败: ${response.status}`);
}
}步骤 3:更新数据库和 API
// database.ts - 更新 notification_config 表
CREATE TABLE IF NOT EXISTS notification_config (
id INTEGER PRIMARY KEY AUTOINCREMENT,
webhook_enabled INTEGER DEFAULT 1,
feishu_enabled INTEGER DEFAULT 0, -- 新增
feishu_config TEXT, -- 新增,JSON 格式
-- ... 其他现有字段 ...
);通知渠道架构:
┌──────────────────────────────────────┐
│ NotificationService │
│ │
│ sendNotification(notification) │
│ │ │
│ ┌────┼────┬────┬────┐ │
│ ▼ ▼ ▼ ▼ ▼ │
│ Webhook 邮件 企微 钉钉 飞书(新) │
│ │ │ │ │ │ │
│ ▼ ▼ ▼ ▼ ▼ │
│ HTTP SMTP API API API │
└──────────────────────────────────────┘23.7 集成新的 LLM Provider
项目已支持 Doubao、OpenAI 和本地 AI(Ollama 等)三种 Provider。集成新的 LLM Provider(如 Anthropic Claude)的步骤:
步骤 1:扩展环境变量配置
// backend/src/utils/env.ts
interface EnvConfig {
// 新增 Claude 配置
ANTHROPIC_API_KEY?: string;
ANTHROPIC_API_BASE?: string;
ANTHROPIC_MODEL?: string;
// ...
}
// 在 validateEnv 函数中读取
const env = validateEnv();
// ...
ANTHROPIC_API_KEY: process.env.ANTHROPIC_API_KEY,
ANTHROPIC_API_BASE: process.env.ANTHROPIC_API_BASE || 'https://api.anthropic.com/v1',
ANTHROPIC_MODEL: process.env.ANTHROPIC_MODEL || 'claude-3-sonnet-20240229',步骤 2:在 LLM 服务中添加 Provider
// backend/src/services/llmService.ts
interface LLMProviderConfig {
providerName: string;
apiKeySetting: string;
apiKeyEnv: string;
apiBaseSetting: string;
apiBaseEnv: string;
defaultApiBase: string;
modelSetting: string;
modelEnv: string;
defaultModel: string;
placeholderKey: string;
}
// 新增 Anthropic 配置
const ANTHROPIC_CONFIG: LLMProviderConfig = {
providerName: 'Anthropic',
apiKeySetting: 'ANTHROPIC_API_KEY',
apiKeyEnv: 'ANTHROPIC_API_KEY',
apiBaseSetting: 'ANTHROPIC_API_BASE',
apiBaseEnv: 'ANTHROPIC_API_BASE',
defaultApiBase: 'https://api.anthropic.com/v1',
modelSetting: 'ANTHROPIC_MODEL',
modelEnv: 'ANTHROPIC_MODEL',
defaultModel: 'claude-3-sonnet-20240229',
placeholderKey: 'your-anthropic-api-key-here'
};
// 在 sendMessage 函数中添加 Anthropic 分支
async function sendMessage(provider: string, messages: Array<{role: string, content: string}>, options: any): Promise<string> {
switch (provider) {
case 'doubao':
return callOpenAICompatible(DOUBAO_CONFIG, messages, options);
case 'openai':
return callOpenAICompatible(OPENAI_CONFIG, messages, options);
case 'local':
return callOpenAICompatible(LOCAL_AI_CONFIG, messages, options);
case 'anthropic':
return callAnthropic(messages, options); // 新增
default:
throw new Error(`Unsupported provider: ${provider}`);
}
}
// 实现 Anthropic API 调用
async function callAnthropic(messages: Array<{role: string, content: string}>, options: any): Promise<string> {
const apiKey = env.ANTHROPIC_API_KEY;
const apiBase = env.ANTHROPIC_API_BASE;
const model = env.ANTHROPIC_MODEL;
// Anthropic 使用不同的消息格式
const systemMessage = messages.find(m => m.role === 'system');
const userMessages = messages.filter(m => m.role !== 'system');
const response = await fetch(`${apiBase}/messages`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'x-api-key': apiKey,
'anthropic-version': '2023-06-01',
},
body: JSON.stringify({
model,
system: systemMessage?.content,
messages: userMessages,
max_tokens: options.maxTokens || 4096,
temperature: options.temperature || 0.7,
}),
});
if (!response.ok) {
throw new Error(`Anthropic API error: ${response.status}`);
}
const data = await response.json();
return data.content[0].text;
}LLM Provider 扩展架构:
┌─────────────────────────────────────────────┐
│ LLM Service │
│ │
│ sendMessage(provider, messages, options) │
│ │ │
│ ┌────┼────┬────┬──────────┐ │
│ ▼ ▼ ▼ ▼ ▼ │
│ Doubao OpenAI LocalAI Anthropic(新) │
│ │ │ │ │ │
│ ▼ ▼ ▼ ▼ │
│ OpenAI OpenAI OpenAI Anthropic │
│ Compatible Compatible Compatible API │
└─────────────────────────────────────────────┘步骤 3:在设置页面添加配置选项
// frontend/src/pages/Settings.tsx — 在 LLM 配置区域添加
<div className="space-y-4">
<h4 className="font-medium">Anthropic Claude</h4>
<div>
<label>API Key</label>
<input type="password" value={anthropicApiKey} onChange={...} />
</div>
<div>
<label>API Base URL</label>
<input value={anthropicApiBase} onChange={...} />
</div>
<div>
<label>Model</label>
<select value={anthropicModel} onChange={...}>
<option value="claude-3-sonnet-20240229">Claude 3 Sonnet</option>
<option value="claude-3-opus-20240229">Claude 3 Opus</option>
<option value="claude-3-haiku-20240307">Claude 3 Haiku</option>
</select>
</div>
</div>23.8 添加告警源适配器
项目已支持 Zabbix、Prometheus 等告警源通过 Webhook 推送告警。添加新的告警源(如 Grafana Alerting):
步骤 1:扩展 Webhook 路由
// backend/src/routes/webhookRoutes.ts
// Grafana Webhook
router.post('/grafana', async (req, res) => {
// 验证签名
if (!verifyWebhookSignature(req, 'grafana')) {
return res.status(401).json({ success: false, error: 'Invalid signature' });
}
try {
const grafanaAlert = req.body;
// 转换为平台内部告警格式
const alert = {
source: 'grafana',
severity: mapGrafanaSeverity(grafanaAlert.status),
title: grafanaAlert.labels?.alertname || grafanaAlert.message,
content: grafanaAlert.annotations?.description || '',
metadata: JSON.stringify({
instance: grafanaAlert.labels?.instance,
job: grafanaAlert.labels?.job,
grafanaUrl: grafanaAlert.ruleUrl,
...grafanaAlert.labels,
}),
};
// 插入告警表
db.prepare(`
INSERT INTO alerts (id, source, severity, title, content, metadata, status)
VALUES (?, ?, ?, ?, ?, ?, 'new')
`).run(
randomUUID(),
alert.source,
alert.severity,
alert.title,
alert.content,
alert.metadata
);
// 触发告警处理服务
alertService.processNewAlert(alert);
res.json({ success: true });
} catch (error) {
logger.error('处理 Grafana 告警失败', error as Error);
res.status(500).json({ success: false, error: '处理失败' });
}
});
function mapGrafanaSeverity(status: string): string {
switch (status) {
case 'Alerting': return 'critical';
case 'Resolved': return 'info';
case 'Pending': return 'warning';
default: return 'warning';
}
}步骤 2:添加告警源配置
// 在告警工作流映射中添加 Grafana 源
POST /api/alert-mappings
{
"alert_source": "grafana",
"alert_severity": "critical",
"alert_title_pattern": ".*CPU.*",
"workflow_id": "CPU异常处理工作流ID",
"enabled": true
}告警源适配器架构:
外部告警源 平台内部
┌──────────┐
│ Zabbix │ ─── Webhook ───▶ /api/webhooks/zabbix
│ │ │
│ Prometheus│── Webhook ───▶ /api/webhooks/prometheus
│ │ │
│ Grafana │ ─── Webhook ───▶ /api/webhooks/grafana (新)
│ │ │
│ Custom │ ─── Webhook ───▶ /api/webhooks/custom
└──────────┘ │
▼
┌─────────────────┐
│ 告警格式转换器 │
│ → 统一告警格式 │
└────────┬────────┘
│
▼
┌─────────────────┐
│ AlertService │
│ - 插入 alerts 表 │
│ - 匹配工作流映射 │
│ - 触发自动处理 │
└─────────────────┘本章小结
本章以实际操作为导向,系统讲解了 ITOps Agent Platform 的二次开发流程:
- 前端扩展:页面组件 → 路由注册 → 导航菜单 → API 调用
- 后端扩展:路由文件 → 业务逻辑 → 权限控制 → app.ts 注册
- 数据库扩展:initializeDatabase 直接创建 + 迁移系统
- Agent 扩展:通过 API 创建自定义 Agent,配置 System Prompt
- 工作流扩展:节点 + 边定义,多 Agent 协作
- 通知渠道扩展:配置 → 实现 → 数据库 → API
- LLM Provider 扩展:环境变量 → 服务实现 → 设置页面
- 告警源扩展:Webhook 路由 → 格式转换 → 告警服务
掌握这些扩展技能,你将能够根据实际业务需求,灵活地定制和增强平台功能。
本章练习
基础练习
添加前端页面:创建一个 "脚本库" 详情页面,展示单个脚本的内容、执行历史和关联的服务器。使用 React Query 获取数据,Markdown 组件渲染脚本内容。
添加后端 API:实现一个
GET /api/servers/:id/metrics接口,返回指定服务器的 CPU、内存、磁盘使用率的最近 24 小时数据(模拟数据即可)。创建自定义 Agent:通过 API 创建一个 "SQL 审核 Agent",System Prompt 要求它检查 SQL 语句的性能问题、安全风险,并给出优化建议。测试该 Agent 的执行效果。
进阶练习
实现告警降噪规则:添加一个告警降噪功能,当同一告警在 1 小时内出现超过 10 次时,自动合并为一条告警并标记为 "噪声告警"。实现对应的数据库表、API 和前端展示。
集成第三方 LLM:选择一个当前项目未支持的 LLM Provider(如 Google Gemini、Mistral 等),完成从环境变量配置到 API 调用的完整集成。
实现定时任务持久化:当前调度器(schedulerService)的定时任务仅保存在内存中。将其改造为基于数据库的持久化调度,支持服务重启后任务不丢失。
思考题
项目的路由注册方式是硬编码在
app.ts中的(逐个 import +app.use())。在大型项目中,这种方式的维护成本会越来越高。请设计一种自动注册路由的方案(如基于目录扫描或装饰器模式),并分析其优缺点。当前 LLM Provider 的集成方式是 Switch-Case 分支。如果要实现 "插件化" 的 LLM Provider 架构(支持热插拔、动态加载),应该如何设计?请画出架构图并说明关键技术点。
延伸阅读
- Express.js 路由最佳实践: https://expressjs.com/en/guide/routing.html
- React Router 文档: https://reactrouter.com/
- SQLite 数据库迁移: https://www.sqlite.org/lang_altertable.html
- OpenAI API 兼容实现: https://platform.openai.com/docs/api-reference
- 书籍推荐:《Node.js 设计模式》第 3 版 - 系统学习 Node.js 架构设计
