This is an automated email from the ASF dual-hosted git repository. gaoxingcun pushed a commit to branch feature/ai-sop-workflow in repository https://gitbox.apache.org/repos/asf/hertzbeat.git
commit 963b594283aa00e8184e505d7e96b3ab5cbce803 Author: TJxiaobao <[email protected]> AuthorDate: Sun Feb 1 19:34:42 2026 +0800 feat: Optimize Skills Keywords. --- .../impl/ChatClientProviderServiceImpl.java | 68 +++++++++++++++++++++- .../src/main/resources/prompt/system-message.st | 35 ++++++----- 2 files changed, 84 insertions(+), 19 deletions(-) diff --git a/hertzbeat-ai/src/main/java/org/apache/hertzbeat/ai/service/impl/ChatClientProviderServiceImpl.java b/hertzbeat-ai/src/main/java/org/apache/hertzbeat/ai/service/impl/ChatClientProviderServiceImpl.java index f624d70943..67211c1c02 100644 --- a/hertzbeat-ai/src/main/java/org/apache/hertzbeat/ai/service/impl/ChatClientProviderServiceImpl.java +++ b/hertzbeat-ai/src/main/java/org/apache/hertzbeat/ai/service/impl/ChatClientProviderServiceImpl.java @@ -19,14 +19,17 @@ package org.apache.hertzbeat.ai.service.impl; import lombok.extern.slf4j.Slf4j; +import org.apache.hertzbeat.ai.sop.model.SopDefinition; +import org.apache.hertzbeat.ai.sop.model.SopParameter; +import org.apache.hertzbeat.ai.sop.registry.SkillRegistry; import org.apache.hertzbeat.common.entity.ai.ChatMessage; import org.apache.hertzbeat.common.entity.dto.ModelProviderConfig; import org.apache.hertzbeat.ai.service.ChatClientProviderService; import org.apache.hertzbeat.base.dao.GeneralConfigDao; import org.apache.hertzbeat.common.entity.manager.GeneralConfig; import org.apache.hertzbeat.common.util.JsonUtil; -import org.springframework.ai.chat.prompt.SystemPromptTemplate; import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Lazy; import org.springframework.core.io.Resource; import org.springframework.stereotype.Service; import org.apache.hertzbeat.ai.pojo.dto.ChatRequestContext; @@ -40,6 +43,8 @@ import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.context.ApplicationContext; import reactor.core.publisher.Flux; +import java.io.IOException; +import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.List; @@ -52,9 +57,13 @@ import java.util.List; @Service public class ChatClientProviderServiceImpl implements ChatClientProviderService { + private static final String SKILLS_PLACEHOLDER = "{dynamically_injected_skills_list}"; + private final ApplicationContext applicationContext; private final GeneralConfigDao generalConfigDao; + + private final SkillRegistry skillRegistry; @Autowired @Qualifier("hertzbeatTools") @@ -66,9 +75,12 @@ public class ChatClientProviderServiceImpl implements ChatClientProviderService private Resource systemResource; @Autowired - public ChatClientProviderServiceImpl(ApplicationContext applicationContext, GeneralConfigDao generalConfigDao) { + public ChatClientProviderServiceImpl(ApplicationContext applicationContext, + GeneralConfigDao generalConfigDao, + @Lazy SkillRegistry skillRegistry) { this.applicationContext = applicationContext; this.generalConfigDao = generalConfigDao; + this.skillRegistry = skillRegistry; } @Override @@ -94,9 +106,12 @@ public class ChatClientProviderServiceImpl implements ChatClientProviderService log.info("Starting streaming chat for conversation: {}", context.getConversationId()); + // Build system prompt with dynamic skills list + String systemPrompt = buildSystemPrompt(); + return chatClient.prompt() .messages(messages) - .system(SystemPromptTemplate.builder().resource(systemResource).build().getTemplate()) + .system(systemPrompt) .toolCallbacks(toolCallbackProvider) .stream() .content() @@ -109,6 +124,53 @@ public class ChatClientProviderServiceImpl implements ChatClientProviderService } } + /** + * Build the system prompt with dynamically injected skills list. + */ + private String buildSystemPrompt() { + try { + String template = systemResource.getContentAsString(StandardCharsets.UTF_8); + String skillsList = generateSkillsList(); + return template.replace(SKILLS_PLACEHOLDER, skillsList); + } catch (IOException e) { + log.error("Failed to read system prompt template: {}", e.getMessage()); + return ""; + } + } + + /** + * Generate a formatted list of available skills for the system prompt. + */ + private String generateSkillsList() { + List<SopDefinition> skills = skillRegistry.getAllSkills(); + + if (skills.isEmpty()) { + return "No skills currently available. Use listSkills tool to refresh."; + } + + StringBuilder sb = new StringBuilder(); + for (SopDefinition skill : skills) { + sb.append("- **").append(skill.getName()).append("**: "); + sb.append(skill.getDescription()); + + // Add parameter hints + if (skill.getParameters() != null && !skill.getParameters().isEmpty()) { + sb.append(" (requires: "); + List<String> paramNames = new ArrayList<>(); + for (SopParameter param : skill.getParameters()) { + if (param.isRequired()) { + paramNames.add(param.getName()); + } + } + sb.append(String.join(", ", paramNames)); + sb.append(")"); + } + sb.append("\n"); + } + + return sb.toString(); + } + @Override public boolean isConfigured() { if (!isConfigured) { diff --git a/hertzbeat-ai/src/main/resources/prompt/system-message.st b/hertzbeat-ai/src/main/resources/prompt/system-message.st index 772340c374..3660af46ed 100644 --- a/hertzbeat-ai/src/main/resources/prompt/system-message.st +++ b/hertzbeat-ai/src/main/resources/prompt/system-message.st @@ -33,22 +33,25 @@ If the user doesn't provide required parameters, ask them iteratively to provide - **get_historical_metrics**: Get historical time-series metrics with flexible time ranges - **get_warehouse_status**: Check metrics storage system status -### Diagnostic Skills (SOP): -Skills are dynamically loaded workflows. ALWAYS call listSkills first to discover available skills. - -- **listSkills**: List all available diagnostic skills with descriptions and parameters. - IMPORTANT: Always call this first before executing any skill to get the current list. -- **executeSkill**: Execute a skill by name. Parameters are passed as JSON. - - For skills requiring monitorId, first use query_monitors to find the target monitor's ID - - If multiple monitors match, ask the user which one to diagnose - - Report-type skills return results directly to the user (no need to summarize) - -Example workflow for diagnostics: -1. User: "Help me check the system health" or "Diagnose MySQL slow queries" -2. Call listSkills() to see available skills -3. Match user intent to a skill (e.g., "daily_inspection" or "mysql_slow_query_diagnosis") -4. If skill needs monitorId, call query_monitors to find it -5. Call executeSkill(skillName, paramsJson) +### AI Skills (SOP Workflows): +You have access to automated skills that can perform complex multi-step operations. + +**Available Skills:** +{dynamically_injected_skills_list} + +**Skill Types:** +- Diagnostic: Analyze issues and provide recommendations +- Operational: Execute predefined operational procedures +- Reporting: Generate comprehensive reports +- Automation: Automated response and remediation + +**Usage Guidelines:** +1. Match user intent to available skills +2. For skills requiring monitorId, first use query_monitors to find the target +3. Execute using: executeSkill(skillName, paramsJson) +4. Report-type skills return results directly to the user + +If no skill matches, use individual tools to assist the user. ## Natural Language Examples: --------------------------------------------------------------------- To unsubscribe, e-mail: [email protected] For additional commands, e-mail: [email protected]
