Some checks failed
AI automation / Route event (push) Has been cancelled
AI automation / Hand reopened issue to maintainers (push) Has been cancelled
AI automation / Clean source issue state (push) Has been cancelled
AI automation / Reconcile handoffs (push) Has been cancelled
AI automation / Classify issue (push) Has been cancelled
AI automation / Claude Code smoke (push) Has been cancelled
AI automation / Review issue follow-up (push) Has been cancelled
AI automation / Publish issue follow-up (push) Has been cancelled
AI automation / Implement with Claude Code (push) Has been cancelled
AI automation / Publish implement PR (push) Has been cancelled
AI automation / Continue queued issue comments (push) Has been cancelled
AI automation / Codex review loop (push) Has been cancelled
AI automation / Publish Codex fix (push) Has been cancelled
AI automation / Clear Codex dispatch marker (push) Has been cancelled
AI automation / Own PR re-request Codex (push) Has been cancelled
AI automation / External PR re-request Codex (push) Has been cancelled
AI automation / Poll Codex reaction / retry (push) Has been cancelled
184 lines
8.3 KiB
TypeScript
184 lines
8.3 KiB
TypeScript
/**
|
||
* RCA (Root Cause Analysis) System Prompt for NetMesh AI Agent.
|
||
*
|
||
* This prompt replaces the generic cattyAgent system prompt when the agent is
|
||
* spawned specifically to analyze a single alert. The workflow is fixed:
|
||
*
|
||
* 1. Read the alert detail (inspection.alertDetail)
|
||
* 2. Pull device metrics + topology context (inspection.*)
|
||
* 3. If the device is online, run targeted diagnosis commands (terminal.execute)
|
||
* 4. Synthesize root cause + evidence + recommendations (structured JSON output)
|
||
*
|
||
* Permission policy for RCA:
|
||
* - terminal.execute runs in confirm mode (write=true)
|
||
* - All inspection.* tools are read-only (bypass approval)
|
||
*
|
||
* The final message MUST be a JSON object matching RCAResult schema.
|
||
*/
|
||
|
||
import type { AlertRecord } from "../../inspection/inspectionApi";
|
||
|
||
export interface RCAResult {
|
||
alertId: string;
|
||
deviceId: string;
|
||
deviceName: string;
|
||
confidence: number; // 0-100
|
||
rootCauses: string[]; // ranked by likelihood
|
||
evidence: Array<{
|
||
tool: string; // tool id that produced this evidence
|
||
summary: string; // 1-sentence human-readable summary
|
||
keyFindings: string[]; // specific observations
|
||
}>;
|
||
recommendations: Array<{
|
||
priority: "immediate" | "short_term" | "long_term";
|
||
action: string; // what to do
|
||
command?: string; // optional shell command to execute
|
||
risk: "low" | "medium" | "high";
|
||
note?: string; // caveat or expected outcome
|
||
}>;
|
||
summary: string; // 2-3 sentence executive summary
|
||
}
|
||
|
||
export interface RCAInput {
|
||
alert: AlertRecord;
|
||
/** optional: device host record from Vault */
|
||
hostInfo?: {
|
||
id: string;
|
||
label?: string;
|
||
hostname?: string;
|
||
platform?: string;
|
||
catalogId?: string;
|
||
tier?: string;
|
||
};
|
||
}
|
||
|
||
/**
|
||
* Build the RCA system prompt.
|
||
* The prompt is deterministic given a single alert — no chat history needed.
|
||
*/
|
||
export function buildRCASystemPrompt(input: RCAInput): string {
|
||
const { alert, hostInfo } = input;
|
||
const deviceLabel = hostInfo?.label || alert.device_name || alert.device_id;
|
||
const hostStr = hostInfo
|
||
? `hostname=${hostInfo.hostname || "(unknown)"} platform=${hostInfo.platform || "(unknown)"} catalog=${hostInfo.catalogId || "(unknown)"} tier=${hostInfo.tier || "(unknown)"}`
|
||
: "platform and catalog unknown";
|
||
|
||
const catalogHint = guessCatalogHint(hostInfo?.catalogId, hostInfo?.platform, alert);
|
||
|
||
return [
|
||
`你是 NetMesh 智慧运维平台的 AI 故障根因分析专家(RCA Agent)。`,
|
||
`你的任务是:针对下面这条告警,主动连接设备执行诊断命令,综合巡检数据和命令回显,定位根因并给出可执行的处置建议。`,
|
||
``,
|
||
`## 目标设备`,
|
||
`- 设备名称: ${deviceLabel}`,
|
||
`- 主机地址/标识: ${hostStr}`,
|
||
`- 告警规则: ${alert.rule_name} (rule_id=${alert.rule_id}, level=${alert.level})`,
|
||
`- 触发指标: ${alert.metric} = ${alert.metric_value} (阈值 ${alert.threshold})`,
|
||
`- 告警时间: ${new Date(alert.triggered_at).toLocaleString("zh-CN")}`,
|
||
`- 告警描述: ${alert.message}`,
|
||
`- 目录提示: ${catalogHint}`,
|
||
``,
|
||
`## 诊断工作流(按顺序执行)`,
|
||
``,
|
||
`### 第 1 步:拉取最新巡检数据`,
|
||
`调用 inspection.hostMetrics 查看该设备最新的 CPU/内存/磁盘/在线状态。`,
|
||
`调用 inspection.hostRecentResults 查看最近 5 次巡检是否有相同指标持续异常。`,
|
||
`调用 inspection.hostAlerts 查看该设备是否还有其他活跃告警(可能是关联问题)。`,
|
||
`调用 inspection.hostTopology 查看该设备的拓扑上下文(邻接设备离线/严重情况)。`,
|
||
``,
|
||
`### 第 2 步:主动重巡检(如果设备在线)`,
|
||
`如果 inspection.hostMetrics 返回 online=true,调用 inspection.runNow 主动触发一次重巡检,拿到最新指标。`,
|
||
`如果 offline 或 rule_offline,跳到第 3 步,不要重巡检。`,
|
||
``,
|
||
`### 第 3 步:执行针对性诊断命令(如果设备在线)`,
|
||
`根据告警类型,选择对应的诊断命令,逐条调用 terminal.execute(deviceId, command):`,
|
||
``,
|
||
catalogHint,
|
||
``,
|
||
`对于离线设备(rule_offline 或 online=false),不要跑 SSH 命令。改成调用 terminal.execute 执行:`,
|
||
`- ping -c 3 <hostname>`,
|
||
`- ssh -o ConnectTimeout=5 -vvv <username>@<hostname> 2>&1 | head -50`,
|
||
``,
|
||
`### 第 4 步:综合分析`,
|
||
`把 inspection.* 的返回值 + terminal.execute 的 stdout/stderr 作为证据,分析:`,
|
||
`1. 哪些证据最有力指向哪个根因?`,
|
||
`2. 根因的置信度(0-100):证据越充分、重复出现,置信度越高`,
|
||
`3. 影响范围:只影响单设备?还是可能波及同拓扑的其他设备?`,
|
||
`4. 可执行的处置建议(按紧急程度排序,每条含优先级、操作、可选命令、风险)`,
|
||
``,
|
||
`## 输出格式(严格遵守)`,
|
||
`你的最终回复必须是纯 JSON(不要 markdown code fence),符合以下 schema:`,
|
||
``,
|
||
`{`,
|
||
` "alertId": "${alert.id}",`,
|
||
` "deviceId": "${alert.device_id}",`,
|
||
` "deviceName": "${deviceLabel}",`,
|
||
` "confidence": <0-100 数字>,`,
|
||
` "rootCauses": ["根因1", "根因2(按可能性从高到低排序)"],`,
|
||
` "evidence": [`,
|
||
` {`,
|
||
` "tool": "inspection.hostMetrics",`,
|
||
` "summary": "一句话总结这条证据",`,
|
||
` "keyFindings": ["关键发现1", "关键发现2"]`,
|
||
` }`,
|
||
` ],`,
|
||
` "recommendations": [`,
|
||
` {`,
|
||
` "priority": "immediate|short_term|long_term",`,
|
||
` "action": "要做什么",`,
|
||
` "command": "可选的 shell 命令",`,
|
||
` "risk": "low|medium|high",`,
|
||
` "note": "注意事项或预期效果"`,
|
||
` }`,
|
||
` ],`,
|
||
` "summary": "2-3 句中文摘要,适合运维人员快速阅读"`,
|
||
`}`,
|
||
``,
|
||
`## 约束`,
|
||
`- 一次性 turn 内完成:不要等用户追问,自己把 1-4 步跑完再输出 JSON`,
|
||
`- terminal.execute 每次只跑一条命令,短命令(< 30s)用同步模式`,
|
||
`- 如果某个工具调用失败(返回 error),记录到 evidence 里,继续下一步,不要中断`,
|
||
`- 不要做破坏性操作(shutdown、reboot、rm -rf 等),只给建议,由运维人员执行`,
|
||
`- evidence 数组要列出你实际调用过的每个工具的关键发现`,
|
||
`- confidence 低于 50 时,在 recommendations 里加上建议人工排查的条目`,
|
||
].join("\n");
|
||
}
|
||
|
||
/**
|
||
* 根据 catalogId/platform/告警类型,给出设备诊断命令建议。
|
||
*/
|
||
function guessCatalogHint(
|
||
catalogId: string | undefined,
|
||
platform: string | undefined,
|
||
alert: AlertRecord,
|
||
): string {
|
||
const offline = alert.rule_id === "rule_offline";
|
||
const cpu = alert.metric === "cpu_usage";
|
||
const mem = alert.metric === "memory_usage";
|
||
const disk = alert.metric === "disk_usage";
|
||
|
||
const isSwitch =
|
||
/switch|huawei|h3c|cisco|fortinet/i.test(catalogId || "") ||
|
||
/switch|ios|vxworks/i.test(platform || "");
|
||
|
||
if (isSwitch && !offline) {
|
||
// 交换机在线 — 跑 display 命令
|
||
const cmds: string[] = [];
|
||
if (cpu) cmds.push("display cpu-usage");
|
||
if (mem) cmds.push("display memory");
|
||
if (disk) cmds.push("display device");
|
||
if (cmds.length === 0) cmds.push("display version\nlogbuffer");
|
||
return `这是一台网络交换机,使用 \`display\` 系列命令,\n推荐依次执行:\n${cmds.map((c) => `- terminal.execute("<hostname>", "${c}")`).join("\n")}\n然后执行 \`display logbuffer | tail -50\` 看错误日志。`;
|
||
}
|
||
|
||
// Linux(默认)
|
||
const cmds: string[] = [];
|
||
if (cpu) cmds.push("top -bn1 | head -20\nvmstat 1 3\nps aux --sort=-%cpu | head -10\nmpstat -P ALL 1 1");
|
||
if (mem) cmds.push("free -m\ncat /proc/meminfo | head -30\nslabtop\nvmstat 1 3");
|
||
if (disk) cmds.push("df -h\niostat -x 1 2\ndu -sh /var/log/* | sort -rh | head -10\nlsof | grep deleted | head -10");
|
||
if (offline) cmds.push("ping -c 3 <hostname>\nssh -o ConnectTimeout=5 -vvv <user>@<hostname> 2>&1 | head -50\nlast\nsystemctl status sshd");
|
||
if (cmds.length === 0) cmds.push("uname -a\nuptime\ncat /etc/os-release\nps aux --sort=-%cpu | head -10");
|
||
|
||
return `这是一台 Linux/Unix 服务器。推荐依次执行(terminal.execute):\n${cmds.map((c) => `- ${c}`).join("\n")}`;
|
||
}
|