Add diagnosis playbook skills

This commit is contained in:
aruo
2026-07-06 08:35:54 +08:00
parent 88e0a6c944
commit 6ccfd33ec5
23 changed files with 1002 additions and 75 deletions
@@ -56,17 +56,17 @@ class AiOpsServiceTest {
request.setSeverity("P1");
request.setTimeRange("last_15m");
request.setDescription("P95 latency is high");
request.setUserRequest("结合日志和指标排查支付超时");
request.setUserRequest("check logs and metrics for payment timeout");
String summary = service.buildQuerySummary(request);
assertTrue(summary.contains("AI Ops 告警分析"));
assertTrue(summary.contains("告警: payment-service-latency-high"));
assertTrue(summary.contains("服务: payment-service"));
assertTrue(summary.contains("等级: P1"));
assertTrue(summary.contains("时间范围: last_15m"));
assertTrue(summary.contains("描述: P95 latency is high"));
assertTrue(summary.contains("请求: 结合日志和指标排查支付超时"));
assertTrue(summary.contains("AI Ops alert analysis"));
assertTrue(summary.contains("alert: payment-service-latency-high"));
assertTrue(summary.contains("service: payment-service"));
assertTrue(summary.contains("severity: P1"));
assertTrue(summary.contains("timeRange: last_15m"));
assertTrue(summary.contains("description: P95 latency is high"));
assertTrue(summary.contains("request: "));
}
@Test
@@ -99,8 +99,8 @@ class AiOpsServiceTest {
assertTrue(prompt.contains("Related Risk"));
assertTrue(prompt.contains("Recommended lookup_knowledge query: HighCPUUsage payment-service P1 CPU usage is above 80% last_15m"));
assertTrue(prompt.contains("preserves alertName and service"));
assertTrue(prompt.contains("告警: HighCPUUsage"));
assertTrue(prompt.contains("服务: payment-service"));
assertTrue(prompt.contains("alert: HighCPUUsage"));
assertTrue(prompt.contains("service: payment-service"));
assertFalse(prompt.contains("AIOps scope mode: AUTO_DISCOVERY"));
}
@@ -112,11 +112,11 @@ class AiOpsServiceTest {
request.setSeverity(" ");
request.setDescription("P95 latency above threshold");
request.setTimeRange("last_10m");
request.setUserRequest("结合日志和指标排查");
request.setUserRequest("check logs and metrics");
String query = service.buildKnowledgeRetrievalQuery(request);
assertEquals("HighLatency payment-service P95 latency above threshold last_10m 结合日志和指标排查", query);
assertEquals("HighLatency payment-service P95 latency above threshold last_10m check logs and metrics", query);
}
@Test
@@ -142,16 +142,16 @@ class AiOpsServiceTest {
void persistFinalReportUpdatesDiagnosisSessionAnswer() {
DiagnosisSession session = DiagnosisSession.builder()
.sessionId("aiops-session-001")
.query("AI Ops 告警分析")
.query("AI Ops alert analysis")
.status("SUCCESS")
.agentFlow("AI_OPS")
.build();
when(diagnosisSessionRepository.findBySessionId("aiops-session-001")).thenReturn(Optional.of(session));
when(toolInvocationRepository.findBySessionIdOrderByIdAsc("aiops-session-001")).thenReturn(List.of());
service.persistFinalReport("aiops-session-001", "# 告警分析报告\nHighCPUUsage payment-service analysis with evidence summary.");
service.persistFinalReport("aiops-session-001", "# 闁告稑锕ㄩ鐔煎礆閸℃鈧粙骞庨妷銉﹀暈\nHighCPUUsage payment-service analysis with evidence summary.");
assertEquals("# 告警分析报告\nHighCPUUsage payment-service analysis with evidence summary.", session.getAnswer());
assertEquals("# 闁告稑锕ㄩ鐔煎礆閸℃鈧粙骞庨妷銉﹀暈\nHighCPUUsage payment-service analysis with evidence summary.", session.getAnswer());
assertTrue(session.getSelfEvaluation().contains("aiops_rule_evaluation"));
verify(diagnosisSessionRepository).save(session);
}
@@ -1,5 +1,8 @@
package com.superbiz.agent.service;
import com.alibaba.cloud.ai.graph.agent.ReactAgent;
import com.alibaba.cloud.ai.graph.skills.registry.SkillRegistry;
import com.alibaba.cloud.ai.graph.skills.registry.classpath.ClasspathSkillRegistry;
import com.superbiz.agent.agent.tool.DateTimeTools;
import com.superbiz.agent.agent.tool.QueryLogsTools;
import com.superbiz.agent.agent.tool.QueryMetricsTools;
@@ -194,6 +197,56 @@ class ChatServiceSequentialAgentTest {
assertSame(queryMetricsTools, methodTools[3]);
}
@Test
void createReactAgentInjectsSkillCatalogThroughAlibabaHook() throws Exception {
ChatService chatService = createChatService();
ScriptedChatModel chatModel = new ScriptedChatModel();
SkillRegistry skillRegistry = ClasspathSkillRegistry.builder()
.classpathPath("skills")
.basePath("target/test-skills-cache")
.build();
ReflectionTestUtils.setField(chatService, "skillRegistry", skillRegistry);
ReactAgent agent = chatService.createReactAgent(chatModel, "BASE_TEST_PROMPT");
agent.call("diagnose mysql connection pool exhaustion");
assertTrue(chatModel.promptText.contains("BASE_TEST_PROMPT"));
assertTrue(chatModel.promptText.contains("## Skills System"));
assertTrue(chatModel.promptText.contains("diagnose-mysql-connection-pool"));
assertTrue(chatModel.promptText.contains("read_skill"));
}
@Test
void plannerGetsSkillMetadataAndExecutorGetsReadSkillTool() throws Exception {
ChatService chatService = createChatService();
ScriptedChatModel chatModel = new ScriptedChatModel();
SkillRegistry skillRegistry = ClasspathSkillRegistry.builder()
.classpathPath("skills")
.basePath("target/test-skills-cache")
.build();
ReflectionTestUtils.setField(chatService, "skillRegistry", skillRegistry);
chatService.executeChatComplex(
chatModel,
new ToolCallback[0],
"diagnose mysql connection pool exhaustion",
List.of(),
"planner-skill-metadata-session"
);
assertTrue(chatModel.plannerPromptText.contains("\"skill_catalog\""));
assertTrue(chatModel.plannerPromptText.contains("diagnose-mysql-connection-pool"));
assertTrue(chatModel.plannerPromptText.contains("\"selected_skill\""));
assertFalse(chatModel.plannerPromptText.contains("## Skills System"));
assertFalse(chatModel.plannerPromptText.contains("read_skill"));
assertTrue(chatModel.executorPromptText.contains("## Skills System"));
assertTrue(chatModel.executorPromptText.contains("diagnose-mysql-connection-pool"));
assertTrue(chatModel.executorPromptText.contains("read_skill"));
assertFalse(chatModel.verifierPromptText.contains("diagnose-mysql-connection-pool"));
assertFalse(chatModel.verifierPromptText.contains("read_skill"));
}
private ChatService createChatService() {
ChatService chatService = new ChatService();
@@ -245,6 +298,9 @@ class ChatServiceSequentialAgentTest {
private static final class ScriptedChatModel implements ChatModel {
private final java.util.ArrayList<String> agentCalls = new java.util.ArrayList<>();
private String promptText = "";
private String plannerPromptText = "";
private String executorPromptText = "";
private String verifierPromptText = "";
private boolean sawVerifierPrompt;
private final java.util.List<String> verifierOutputs;
private int verifierOutputIndex;
@@ -283,12 +339,15 @@ class ChatServiceSequentialAgentTest {
String text;
if (promptText.contains("PLANNER_TEST_PROMPT")) {
agentCalls.add("chat_planner");
plannerPromptText = promptText;
text = "PLANNER_PLAN";
} else if (promptText.contains("EXECUTOR_TEST_PROMPT")) {
agentCalls.add("chat_executor");
executorPromptText = promptText;
text = "EXECUTOR_FINAL_ANSWER";
} else if (promptText.contains("VERIFIER_TEST_PROMPT")) {
agentCalls.add("chat_verifier");
verifierPromptText = promptText;
sawVerifierPrompt = true;
int index = Math.min(verifierOutputIndex, verifierOutputs.size() - 1);
text = verifierOutputs.get(index);
@@ -0,0 +1,44 @@
package com.superbiz.agent.service;
import com.alibaba.cloud.ai.graph.agent.hook.skills.ReadSkillTool;
import com.alibaba.cloud.ai.graph.skills.registry.SkillRegistry;
import com.superbiz.agent.config.SkillConfig;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertTrue;
class SkillCatalogServiceTest {
@Test
void loadsDiagnosisSkillsFromClasspathRegistry() {
SkillRegistry registry = newRegistry();
assertEquals(1, registry.size());
assertTrue(registry.contains("diagnose-mysql-connection-pool"));
}
@Test
void readSkillReturnsFullInstructionsFromOfficialTool() {
ReadSkillTool tool = new ReadSkillTool(newRegistry());
String skill = tool.apply(new ReadSkillTool.ReadSkillRequest("diagnose-mysql-connection-pool"), null);
assertTrue(skill.contains("## Workflow"));
assertTrue(skill.contains("query_logs"));
assertTrue(skill.contains("Fixed diagnosis case: `mysql-pool-exhausted`"));
}
@Test
void readSkillToolReturnsUnknownSkillError() {
ReadSkillTool tool = new ReadSkillTool(newRegistry());
String result = tool.apply(new ReadSkillTool.ReadSkillRequest("missing-skill"), null);
assertTrue(result.contains("Skill not found: missing-skill"));
}
private SkillRegistry newRegistry() {
return new SkillConfig().skillRegistry();
}
}