This is an automated email from the ASF dual-hosted git repository. gongchao pushed a commit to branch update-ai-with in repository https://gitbox.apache.org/repos/asf/hertzbeat.git
commit a3cfaf2be6244e091d8480a28cb514cfd297df17 Author: tomsun28 <[email protected]> AuthorDate: Fri Oct 31 00:48:37 2025 +0800 refactor Signed-off-by: tomsun28 <[email protected]> --- .../hertzbeat/manager/config/AiProperties.java | 60 ---- .../hertzbeat/manager/controller/AiController.java | 68 ---- .../hertzbeat/manager/service/ai/AiService.java | 44 --- .../manager/service/ai/AlibabaAiServiceImpl.java | 118 ------- .../manager/service/ai/KimiAiServiceImpl.java | 96 ------ .../manager/service/ai/OllamaAiService.java | 95 ------ .../manager/service/ai/OpenRouterServiceImpl.java | 97 ------ .../manager/service/ai/SparkDeskAiServiceImpl.java | 104 ------ .../manager/service/ai/ZhiPuServiceImpl.java | 100 ------ .../service/ai/factory/AiServiceFactoryImpl.java | 64 ---- .../manager/controller/AiControllerTest.java | 88 ----- .../manager/service/AiServiceFactoryTest.java | 125 -------- home/docs/help/ai_agent_chat.md | 15 +- .../current/help/ai_agent_chat.md | 356 +++++++++++++++++++++ home/sidebars.json | 1 - script/application.yml | 68 +++- .../hertzbeat-mysql-iotdb/conf/application.yml | 7 - .../hertzbeat-mysql-tdengine/conf/application.yml | 7 - .../conf/application.yml | 7 - .../conf/application.yml | 7 - 20 files changed, 423 insertions(+), 1104 deletions(-) diff --git a/hertzbeat-manager/src/main/java/org/apache/hertzbeat/manager/config/AiProperties.java b/hertzbeat-manager/src/main/java/org/apache/hertzbeat/manager/config/AiProperties.java deleted file mode 100644 index f2528769d..000000000 --- a/hertzbeat-manager/src/main/java/org/apache/hertzbeat/manager/config/AiProperties.java +++ /dev/null @@ -1,60 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.apache.hertzbeat.manager.config; - -import lombok.Data; -import org.apache.hertzbeat.common.constants.ConfigConstants; -import org.springframework.boot.context.properties.ConfigurationProperties; -import org.springframework.context.annotation.Configuration; - -/** - * AiProperties - */ - -@Data -@Configuration -@ConfigurationProperties(prefix = - ConfigConstants.FunctionModuleConstants.AI) -public class AiProperties { - - /** - * AI Type: zhiPu, alibabaAi, kimiAi, sparkDesk - */ - private String type; - - /** - * Model name: glm-4, qwen-turboo, moonshot-v1-8k, generalv3.5 - */ - private String model; - - /** - * API key - */ - private String apiKey; - - /** - * At present, only IFLYTEK large model needs to be filled in - */ - private String apiSecret; - - /** - * API URL for the Ollama AI service. - */ - private String apiUrl; - -} diff --git a/hertzbeat-manager/src/main/java/org/apache/hertzbeat/manager/controller/AiController.java b/hertzbeat-manager/src/main/java/org/apache/hertzbeat/manager/controller/AiController.java deleted file mode 100644 index fb1a192b0..000000000 --- a/hertzbeat-manager/src/main/java/org/apache/hertzbeat/manager/controller/AiController.java +++ /dev/null @@ -1,68 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.apache.hertzbeat.manager.controller; - -import static org.springframework.http.MediaType.TEXT_EVENT_STREAM_VALUE; -import io.swagger.v3.oas.annotations.Operation; -import io.swagger.v3.oas.annotations.Parameter; -import io.swagger.v3.oas.annotations.tags.Tag; -import org.apache.hertzbeat.manager.config.AiProperties; -import org.apache.hertzbeat.manager.pojo.dto.AiControllerRequestParam; -import org.apache.hertzbeat.manager.service.ai.AiService; -import org.apache.hertzbeat.manager.service.ai.factory.AiServiceFactoryImpl; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.http.codec.ServerSentEvent; -import org.springframework.util.Assert; -import org.springframework.web.bind.annotation.PostMapping; -import org.springframework.web.bind.annotation.RequestBody; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RestController; -import reactor.core.publisher.Flux; - -/** - * AI Management API - */ -@Tag(name = "AI Manage API") -@RestController -@RequestMapping(value = "/api/ai") -public class AiController { - - /** - * AI beanFactory - */ - @Autowired(required = false) - private AiServiceFactoryImpl aiServiceFactory; - - @Autowired - private AiProperties aiProperties; - - /** - * request AI - * @param requestParam request text - * @return AI response - */ - @PostMapping(path = "/get", produces = {TEXT_EVENT_STREAM_VALUE}) - @Operation(summary = "Artificial intelligence questions and Answers", - description = "Artificial intelligence questions and Answers") - public Flux<ServerSentEvent<String>> requestAi(@Parameter(description = "Request text", example = "Who are you") - @RequestBody AiControllerRequestParam requestParam) { - Assert.notNull(aiServiceFactory, "please check that your type value is consistent with the documentation on the website"); - AiService aiServiceImplBean = aiServiceFactory.getAiServiceImplBean(aiProperties.getType()); - return aiServiceImplBean.requestAi(requestParam.getText()); - } -} diff --git a/hertzbeat-manager/src/main/java/org/apache/hertzbeat/manager/service/ai/AiService.java b/hertzbeat-manager/src/main/java/org/apache/hertzbeat/manager/service/ai/AiService.java deleted file mode 100644 index de8e683ed..000000000 --- a/hertzbeat-manager/src/main/java/org/apache/hertzbeat/manager/service/ai/AiService.java +++ /dev/null @@ -1,44 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.apache.hertzbeat.manager.service.ai; - - -import org.apache.hertzbeat.common.constants.AiTypeEnum; -import org.springframework.http.codec.ServerSentEvent; -import reactor.core.publisher.Flux; - - -/** - * AI Service - */ -public interface AiService { - - /** - * get AI type - * @return type - */ - AiTypeEnum getType(); - - /** - * AI response - * @param text text - * @return AI response - */ - Flux<ServerSentEvent<String>> requestAi(String text); - -} diff --git a/hertzbeat-manager/src/main/java/org/apache/hertzbeat/manager/service/ai/AlibabaAiServiceImpl.java b/hertzbeat-manager/src/main/java/org/apache/hertzbeat/manager/service/ai/AlibabaAiServiceImpl.java deleted file mode 100644 index f9c1236ab..000000000 --- a/hertzbeat-manager/src/main/java/org/apache/hertzbeat/manager/service/ai/AlibabaAiServiceImpl.java +++ /dev/null @@ -1,118 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.apache.hertzbeat.manager.service.ai; - -import java.util.List; -import java.util.Objects; -import javax.annotation.PostConstruct; -import lombok.extern.slf4j.Slf4j; -import org.apache.hertzbeat.common.constants.AiConstants; -import org.apache.hertzbeat.common.constants.AiTypeEnum; -import org.apache.hertzbeat.manager.config.AiProperties; -import org.apache.hertzbeat.manager.pojo.dto.AiMessage; -import org.apache.hertzbeat.manager.pojo.dto.AliAiRequestParamDTO; -import org.apache.hertzbeat.manager.pojo.dto.AliAiResponse; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; -import org.springframework.http.HttpHeaders; -import org.springframework.http.MediaType; -import org.springframework.http.codec.ServerSentEvent; -import org.springframework.stereotype.Service; -import org.springframework.util.Assert; -import org.springframework.util.CollectionUtils; -import org.springframework.web.reactive.function.BodyInserters; -import org.springframework.web.reactive.function.client.ExchangeStrategies; -import org.springframework.web.reactive.function.client.WebClient; -import reactor.core.publisher.Flux; - - -/** - * alibaba Ai - */ -@Service("AlibabaAiServiceImpl") -@ConditionalOnProperty(prefix = "ai", name = "type", havingValue = "alibabaAi") -@Slf4j -public class AlibabaAiServiceImpl implements AiService { - - @Autowired - private AiProperties aiProperties; - - private WebClient webClient; - - @PostConstruct - private void init() { - this.webClient = WebClient.builder() - .baseUrl(AiConstants.AliAiConstants.URL) - .defaultHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE) - .defaultHeader(HttpHeaders.AUTHORIZATION, "Bearer " + aiProperties.getApiKey()) - //sse - .defaultHeader(HttpHeaders.ACCEPT, MediaType.TEXT_EVENT_STREAM_VALUE) - .exchangeStrategies(ExchangeStrategies.builder() - .codecs(item -> item.defaultCodecs().maxInMemorySize(16 * 1024 * 1024)) - .build()) - .build(); - } - - @Override - public AiTypeEnum getType() { - return AiTypeEnum.alibabaAi; - } - - @Override - public Flux<ServerSentEvent<String>> requestAi(String text) { - checkParam(text, aiProperties.getModel(), aiProperties.getApiKey()); - AliAiRequestParamDTO aliAiRequestParamDTO = AliAiRequestParamDTO.builder() - .model(aiProperties.getModel()) - .input(AliAiRequestParamDTO.Input.builder() - .messages(List.of(new AiMessage(AiConstants.AliAiConstants.REQUEST_ROLE, text))) - .build()) - .parameters(AliAiRequestParamDTO.Parameters.builder() - .maxTokens(AiConstants.AliAiConstants.MAX_TOKENS) - .temperature(AiConstants.AliAiConstants.TEMPERATURE) - .enableSearch(true) - .resultFormat("message") - .incrementalOutput(true) - .build()) - .build(); - - return webClient.post() - .body(BodyInserters.fromValue(aliAiRequestParamDTO)) - .retrieve() - .bodyToFlux(AliAiResponse.class) - .map(aliAiResponse -> { - if (Objects.nonNull(aliAiResponse)) { - List<AliAiResponse.Choice> choices = aliAiResponse.getOutput().getChoices(); - if (CollectionUtils.isEmpty(choices)) { - return ServerSentEvent.<String>builder().build(); - } - String content = choices.get(0).getMessage().getContent(); - return ServerSentEvent.<String>builder() - .data(content) - .build(); - } - return ServerSentEvent.<String>builder().build(); - }) - .doOnError(error -> log.info("AlibabaAiServiceImpl.requestAi exception:{}", error.getMessage())); - } - - private void checkParam(String param, String apiKey, String model) { - Assert.notNull(param, "text is null"); - Assert.notNull(model, "model is null"); - Assert.notNull(apiKey, "ai.api-key is null"); - } -} diff --git a/hertzbeat-manager/src/main/java/org/apache/hertzbeat/manager/service/ai/KimiAiServiceImpl.java b/hertzbeat-manager/src/main/java/org/apache/hertzbeat/manager/service/ai/KimiAiServiceImpl.java deleted file mode 100644 index 506394915..000000000 --- a/hertzbeat-manager/src/main/java/org/apache/hertzbeat/manager/service/ai/KimiAiServiceImpl.java +++ /dev/null @@ -1,96 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.apache.hertzbeat.manager.service.ai; - -import java.util.List; -import javax.annotation.PostConstruct; -import lombok.extern.slf4j.Slf4j; -import org.apache.hertzbeat.common.constants.AiConstants; -import org.apache.hertzbeat.common.constants.AiTypeEnum; -import org.apache.hertzbeat.manager.config.AiProperties; -import org.apache.hertzbeat.manager.pojo.dto.AiMessage; -import org.apache.hertzbeat.manager.pojo.dto.OpenAiRequestParamDTO; -import org.apache.hertzbeat.manager.pojo.dto.OpenAiResponse; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; -import org.springframework.http.HttpHeaders; -import org.springframework.http.MediaType; -import org.springframework.http.codec.ServerSentEvent; -import org.springframework.stereotype.Service; -import org.springframework.util.Assert; -import org.springframework.web.reactive.function.BodyInserters; -import org.springframework.web.reactive.function.client.ExchangeStrategies; -import org.springframework.web.reactive.function.client.WebClient; -import reactor.core.publisher.Flux; - -/** - * Kimi Ai - */ -@Service("KimiAiServiceImpl") -@ConditionalOnProperty(prefix = "ai", name = "type", havingValue = "kimiAi") -@Slf4j -public class KimiAiServiceImpl implements AiService { - - @Autowired - private AiProperties aiProperties; - - private WebClient webClient; - - @PostConstruct - private void init() { - this.webClient = WebClient.builder() - .baseUrl(AiConstants.KimiAiConstants.URL) - .defaultHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE) - .defaultHeader(HttpHeaders.AUTHORIZATION, "Bearer " + aiProperties.getApiKey()) - .exchangeStrategies(ExchangeStrategies.builder() - .codecs(item -> item.defaultCodecs().maxInMemorySize(16 * 1024 * 1024)) - .build()) - .build(); - } - - @Override - public AiTypeEnum getType() { - return AiTypeEnum.kimiAi; - } - - @Override - public Flux<ServerSentEvent<String>> requestAi(String text) { - checkParam(text, aiProperties.getModel(), aiProperties.getApiKey()); - OpenAiRequestParamDTO zhiPuRequestParamDTO = OpenAiRequestParamDTO.builder() - .model(aiProperties.getModel()) - .stream(Boolean.TRUE) - .maxTokens(AiConstants.KimiAiConstants.MAX_TOKENS) - .temperature(AiConstants.KimiAiConstants.TEMPERATURE) - .messages(List.of(new AiMessage(AiConstants.KimiAiConstants.REQUEST_ROLE, text))) - .build(); - - return webClient.post() - .body(BodyInserters.fromValue(zhiPuRequestParamDTO)) - .retrieve() - .bodyToFlux(String.class) - .filter(aiResponse -> !"[DONE]".equals(aiResponse)) - .map(OpenAiResponse::convertToResponse) - .doOnError(error -> log.info("KimiAiServiceImpl.requestAi exception:{}", error.getMessage())); - } - - private void checkParam(String param, String model, String apiKey) { - Assert.notNull(param, "text is null"); - Assert.notNull(model, "model is null"); - Assert.notNull(apiKey, "ai.api-key is null"); - } -} diff --git a/hertzbeat-manager/src/main/java/org/apache/hertzbeat/manager/service/ai/OllamaAiService.java b/hertzbeat-manager/src/main/java/org/apache/hertzbeat/manager/service/ai/OllamaAiService.java deleted file mode 100644 index 8d3a7b313..000000000 --- a/hertzbeat-manager/src/main/java/org/apache/hertzbeat/manager/service/ai/OllamaAiService.java +++ /dev/null @@ -1,95 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.apache.hertzbeat.manager.service.ai; - -import io.jsonwebtoken.lang.Assert; -import lombok.extern.slf4j.Slf4j; -import org.apache.hertzbeat.common.constants.AiConstants; -import org.apache.hertzbeat.common.constants.AiTypeEnum; -import org.apache.hertzbeat.manager.config.AiProperties; -import org.apache.hertzbeat.manager.pojo.dto.AiMessage; -import org.apache.hertzbeat.manager.pojo.dto.OpenAiRequestParamDTO; -import org.apache.hertzbeat.manager.pojo.dto.OpenAiResponse; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; -import org.springframework.http.HttpHeaders; -import org.springframework.http.MediaType; -import org.springframework.http.codec.ServerSentEvent; -import org.springframework.stereotype.Service; -import org.springframework.web.reactive.function.BodyInserters; -import org.springframework.web.reactive.function.client.ExchangeStrategies; -import org.springframework.web.reactive.function.client.WebClient; -import reactor.core.publisher.Flux; - -import javax.annotation.PostConstruct; -import java.util.List; - -/** - * Ollama service - */ -@Service("OllamaServiceImpl") -@ConditionalOnProperty(prefix = "ai", name = "type", havingValue = "ollama") -@Slf4j -public class OllamaAiService implements AiService{ - @Autowired - private AiProperties aiProperties; - - private WebClient webClient; - - @PostConstruct - private void init() { - Assert.notNull(aiProperties.getApiUrl(), "Ollama API URL is null"); - this.webClient = WebClient.builder() - .baseUrl(aiProperties.getApiUrl()) - .defaultHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE) - .exchangeStrategies(ExchangeStrategies.builder() - .codecs(item -> item.defaultCodecs().maxInMemorySize(16 * 1024 * 1024)) - .build()) - .build(); - } - - @Override - public AiTypeEnum getType() { - return AiTypeEnum.ollama; - } - - @Override - public Flux<ServerSentEvent<String>> requestAi(String text) { - checkParam(text, aiProperties.getModel()); - OpenAiRequestParamDTO ollamaParam = OpenAiRequestParamDTO.builder() - .model(aiProperties.getModel()) - .stream(Boolean.TRUE) - .maxTokens(AiConstants.OllamaConstants.MAX_TOKENS) - .temperature(AiConstants.OllamaConstants.TEMPERATURE) - .messages(List.of(new AiMessage(AiConstants.OllamaConstants.REQUEST_ROLE, text))) - .build(); - - return webClient.post() - .body(BodyInserters.fromValue(ollamaParam)) - .retrieve() - .bodyToFlux(String.class) - .filter(aiResponse -> !"[DONE]".equals(aiResponse)) - .map(OpenAiResponse::convertToResponse) - .doOnError(error -> log.info("OllamaAiService.requestAi exception:{}", error.getMessage())); - } - - private void checkParam(String param, String model) { - Assert.notNull(param, "text is null"); - Assert.notNull(model, "model is null"); - } -} diff --git a/hertzbeat-manager/src/main/java/org/apache/hertzbeat/manager/service/ai/OpenRouterServiceImpl.java b/hertzbeat-manager/src/main/java/org/apache/hertzbeat/manager/service/ai/OpenRouterServiceImpl.java deleted file mode 100644 index 417c92d25..000000000 --- a/hertzbeat-manager/src/main/java/org/apache/hertzbeat/manager/service/ai/OpenRouterServiceImpl.java +++ /dev/null @@ -1,97 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.apache.hertzbeat.manager.service.ai; - -import lombok.extern.slf4j.Slf4j; -import org.apache.hertzbeat.common.constants.AiConstants; -import org.apache.hertzbeat.common.constants.AiTypeEnum; -import org.apache.hertzbeat.manager.config.AiProperties; -import org.apache.hertzbeat.manager.pojo.dto.AiMessage; -import org.apache.hertzbeat.manager.pojo.dto.OpenAiRequestParamDTO; -import org.apache.hertzbeat.manager.pojo.dto.OpenAiResponse; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; -import org.springframework.http.HttpHeaders; -import org.springframework.http.MediaType; -import org.springframework.http.codec.ServerSentEvent; -import org.springframework.stereotype.Service; -import org.springframework.util.Assert; -import org.springframework.web.reactive.function.BodyInserters; -import org.springframework.web.reactive.function.client.ExchangeStrategies; -import org.springframework.web.reactive.function.client.WebClient; -import reactor.core.publisher.Flux; - -import javax.annotation.PostConstruct; -import java.util.List; - -/** - * OpenRouter service - */ -@Service("OpenRouterServiceImpl") -@ConditionalOnProperty(prefix = "ai", name = "type", havingValue = "openRouter") -@Slf4j -public class OpenRouterServiceImpl implements AiService { - - @Autowired - private AiProperties aiProperties; - - private WebClient webClient; - - @PostConstruct - private void init() { - this.webClient = WebClient.builder() - .baseUrl(AiConstants.OpenRouterConstants.URL) - .defaultHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE) - .defaultHeader(HttpHeaders.AUTHORIZATION, "Bearer " + aiProperties.getApiKey()) - .exchangeStrategies(ExchangeStrategies.builder() - .codecs(item -> item.defaultCodecs().maxInMemorySize(16 * 1024 * 1024)) - .build()) - .build(); - } - - @Override - public AiTypeEnum getType() { - return AiTypeEnum.openRouter; - } - - @Override - public Flux<ServerSentEvent<String>> requestAi(String text) { - checkParam(text, aiProperties.getModel(), aiProperties.getApiKey()); - OpenAiRequestParamDTO openRouterParam = OpenAiRequestParamDTO.builder() - .model(aiProperties.getModel()) - .stream(Boolean.TRUE) - .maxTokens(AiConstants.OpenRouterConstants.MAX_TOKENS) - .temperature(AiConstants.OpenRouterConstants.TEMPERATURE) - .messages(List.of(new AiMessage(AiConstants.OpenRouterConstants.REQUEST_ROLE, text))) - .build(); - - return webClient.post() - .body(BodyInserters.fromValue(openRouterParam)) - .retrieve() - .bodyToFlux(String.class) - .filter(aiResponse -> !"[DONE]".equals(aiResponse)) - .map(OpenAiResponse::convertToResponse) - .doOnError(error -> log.info("OpenRouterAiServiceImpl.requestAi exception:{}", error.getMessage())); - } - - private void checkParam(String param, String model, String apiKey) { - Assert.notNull(param, "text is null"); - Assert.notNull(model, "model is null"); - Assert.notNull(apiKey, "ai.api-key is null"); - } -} diff --git a/hertzbeat-manager/src/main/java/org/apache/hertzbeat/manager/service/ai/SparkDeskAiServiceImpl.java b/hertzbeat-manager/src/main/java/org/apache/hertzbeat/manager/service/ai/SparkDeskAiServiceImpl.java deleted file mode 100644 index c35b1f664..000000000 --- a/hertzbeat-manager/src/main/java/org/apache/hertzbeat/manager/service/ai/SparkDeskAiServiceImpl.java +++ /dev/null @@ -1,104 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.apache.hertzbeat.manager.service.ai; - -import java.util.List; -import javax.annotation.PostConstruct; -import lombok.extern.slf4j.Slf4j; -import org.apache.hertzbeat.common.constants.AiConstants; -import org.apache.hertzbeat.common.constants.AiTypeEnum; -import org.apache.hertzbeat.manager.config.AiProperties; -import org.apache.hertzbeat.manager.pojo.dto.AiMessage; -import org.apache.hertzbeat.manager.pojo.dto.OpenAiRequestParamDTO; -import org.apache.hertzbeat.manager.pojo.dto.OpenAiResponse; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; -import org.springframework.http.HttpHeaders; -import org.springframework.http.MediaType; -import org.springframework.http.codec.ServerSentEvent; -import org.springframework.stereotype.Service; -import org.springframework.util.Assert; -import org.springframework.web.reactive.function.BodyInserters; -import org.springframework.web.reactive.function.client.ExchangeStrategies; -import org.springframework.web.reactive.function.client.WebClient; -import reactor.core.publisher.Flux; - - -/** - * sparkDesk AI - */ -@Service("SparkDeskAiServiceImpl") -@ConditionalOnProperty(prefix = "ai", name = "type", havingValue = "sparkDesk") -@Slf4j -public class SparkDeskAiServiceImpl implements AiService { - - @Autowired - private AiProperties aiProperties; - - private WebClient webClient; - - @PostConstruct - private void init() { - StringBuilder sb = new StringBuilder(); - String bearer = sb.append("Bearer ") - .append(aiProperties.getApiKey()) - .append(":").append(aiProperties.getApiSecret()).toString(); - - this.webClient = WebClient.builder() - .baseUrl(AiConstants.SparkDeskConstants.SPARK_ULTRA_URL) - .defaultHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE) - .defaultHeader(HttpHeaders.AUTHORIZATION, bearer) - .exchangeStrategies(ExchangeStrategies.builder() - .codecs(item -> item.defaultCodecs().maxInMemorySize(16 * 1024 * 1024)) - .build()) - .build(); - } - - @Override - public AiTypeEnum getType() { - return AiTypeEnum.sparkDesk; - } - - @Override - public Flux<ServerSentEvent<String>> requestAi(String text) { - checkParam(text, aiProperties.getApiKey(), aiProperties.getModel()); - OpenAiRequestParamDTO zhiPuRequestParamDTO = OpenAiRequestParamDTO.builder() - .model(aiProperties.getModel()) - //sse - .stream(Boolean.TRUE) - .maxTokens(AiConstants.SparkDeskConstants.MAX_TOKENS) - .temperature(AiConstants.SparkDeskConstants.TEMPERATURE) - .messages(List.of(new AiMessage(AiConstants.SparkDeskConstants.REQUEST_ROLE, text))) - .build(); - - return webClient.post() - .body(BodyInserters.fromValue(zhiPuRequestParamDTO)) - .retrieve() - .bodyToFlux(String.class) - .filter(aiResponse -> !"[DONE]".equals(aiResponse)) - .map(OpenAiResponse::convertToResponse) - .doOnError(error -> log.info("SparkDeskAiServiceImpl.requestAi exception:{}", error.getMessage())); - - } - - private void checkParam(String param, String apiKey, String model) { - Assert.notNull(param, "text is null"); - Assert.notNull(model, "model is null"); - Assert.notNull(apiKey, "ai.api-key is null"); - } -} diff --git a/hertzbeat-manager/src/main/java/org/apache/hertzbeat/manager/service/ai/ZhiPuServiceImpl.java b/hertzbeat-manager/src/main/java/org/apache/hertzbeat/manager/service/ai/ZhiPuServiceImpl.java deleted file mode 100644 index 1a8ef2d93..000000000 --- a/hertzbeat-manager/src/main/java/org/apache/hertzbeat/manager/service/ai/ZhiPuServiceImpl.java +++ /dev/null @@ -1,100 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.apache.hertzbeat.manager.service.ai; - - -import java.util.List; -import javax.annotation.PostConstruct; -import lombok.extern.slf4j.Slf4j; -import org.apache.hertzbeat.common.constants.AiConstants; -import org.apache.hertzbeat.common.constants.AiTypeEnum; -import org.apache.hertzbeat.manager.config.AiProperties; -import org.apache.hertzbeat.manager.pojo.dto.AiMessage; -import org.apache.hertzbeat.manager.pojo.dto.OpenAiRequestParamDTO; -import org.apache.hertzbeat.manager.pojo.dto.OpenAiResponse; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; -import org.springframework.http.HttpHeaders; -import org.springframework.http.MediaType; -import org.springframework.http.codec.ServerSentEvent; -import org.springframework.stereotype.Service; -import org.springframework.util.Assert; -import org.springframework.web.reactive.function.BodyInserters; -import org.springframework.web.reactive.function.client.ExchangeStrategies; -import org.springframework.web.reactive.function.client.WebClient; -import reactor.core.publisher.Flux; - - -/** - * ZhiPu AI - */ -@Service("ZhiPuServiceImpl") -@ConditionalOnProperty(prefix = "ai", name = "type", havingValue = "zhiPu") -@Slf4j -public class ZhiPuServiceImpl implements AiService { - - @Autowired - private AiProperties aiProperties; - - private WebClient webClient; - - @PostConstruct - private void init() { - this.webClient = WebClient.builder() - .baseUrl(AiConstants.ZhiPuConstants.URL) - .defaultHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE) - .defaultHeader(HttpHeaders.AUTHORIZATION, "Bearer " + aiProperties.getApiKey()) - .exchangeStrategies(ExchangeStrategies.builder() - .codecs(item -> item.defaultCodecs().maxInMemorySize(16 * 1024 * 1024)) - .build()) - .build(); - } - - @Override - public AiTypeEnum getType() { - return AiTypeEnum.zhiPu; - } - - @Override - public Flux<ServerSentEvent<String>> requestAi(String text) { - checkParam(text, aiProperties.getModel(), aiProperties.getApiKey()); - OpenAiRequestParamDTO zhiPuRequestParamDTO = OpenAiRequestParamDTO.builder() - .model(aiProperties.getModel()) - //sse - .stream(Boolean.TRUE) - .maxTokens(AiConstants.ZhiPuConstants.MAX_TOKENS) - .temperature(AiConstants.ZhiPuConstants.TEMPERATURE) - .messages(List.of(new AiMessage(AiConstants.ZhiPuConstants.REQUEST_ROLE, text))) - .build(); - - return webClient.post() - .body(BodyInserters.fromValue(zhiPuRequestParamDTO)) - .retrieve() - .bodyToFlux(String.class) - .filter(aiResponse -> !"[DONE]".equals(aiResponse)) - .map(OpenAiResponse::convertToResponse) - .doOnError(error -> log.info("ZhiPuServiceImpl.requestAi exception:{}", error.getMessage())); - } - - private void checkParam(String param, String model, String apiKey) { - Assert.notNull(param, "text is null"); - Assert.notNull(model, "model is null"); - Assert.notNull(apiKey, "ai.api-key is null"); - } - -} diff --git a/hertzbeat-manager/src/main/java/org/apache/hertzbeat/manager/service/ai/factory/AiServiceFactoryImpl.java b/hertzbeat-manager/src/main/java/org/apache/hertzbeat/manager/service/ai/factory/AiServiceFactoryImpl.java deleted file mode 100644 index 8e0954c7a..000000000 --- a/hertzbeat-manager/src/main/java/org/apache/hertzbeat/manager/service/ai/factory/AiServiceFactoryImpl.java +++ /dev/null @@ -1,64 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.apache.hertzbeat.manager.service.ai.factory; - -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.function.Function; -import java.util.stream.Collectors; -import javax.annotation.PostConstruct; -import org.apache.hertzbeat.common.constants.AiTypeEnum; -import org.apache.hertzbeat.manager.service.ai.AiService; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; -import org.springframework.stereotype.Component; -import org.springframework.util.Assert; -import org.springframework.util.CollectionUtils; - -/** - * AI bean factory - */ -@Component -@ConditionalOnProperty(prefix = "ai", name = "type") -public class AiServiceFactoryImpl { - - @Autowired(required = false) - private List<AiService> aiService; - - private Map<AiTypeEnum, AiService> aiServiceFactoryMap = new HashMap<>(); - - @PostConstruct - public void init() { - if (CollectionUtils.isEmpty(aiService)) { - return; - } - aiServiceFactoryMap = aiService.stream() - .collect(Collectors.toMap(AiService::getType, Function.identity())); - } - - public AiService getAiServiceImplBean(String type) { - Assert.notNull(type, "type is null"); - AiTypeEnum typeByName = AiTypeEnum.getTypeByName(type); - Assert.notNull(typeByName, "The current type is not supported,please check that your type value is consistent with the documentation on the website"); - AiService aiServiceImpl = aiServiceFactoryMap.get(typeByName); - Assert.notNull(aiServiceImpl, "No bean for current type found"); - return aiServiceImpl; - } - -} diff --git a/hertzbeat-manager/src/test/java/org/apache/hertzbeat/manager/controller/AiControllerTest.java b/hertzbeat-manager/src/test/java/org/apache/hertzbeat/manager/controller/AiControllerTest.java deleted file mode 100644 index e61595324..000000000 --- a/hertzbeat-manager/src/test/java/org/apache/hertzbeat/manager/controller/AiControllerTest.java +++ /dev/null @@ -1,88 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.apache.hertzbeat.manager.controller; - -import static org.mockito.ArgumentMatchers.anyString; -import static org.mockito.Mockito.when; -import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content; -import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; -import org.apache.hertzbeat.manager.config.AiProperties; -import org.apache.hertzbeat.manager.service.ai.AiService; -import org.apache.hertzbeat.manager.service.ai.factory.AiServiceFactoryImpl; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ExtendWith; -import org.mockito.InjectMocks; -import org.mockito.Mock; -import org.mockito.junit.jupiter.MockitoExtension; -import org.springframework.http.MediaType; -import org.springframework.http.codec.ServerSentEvent; -import org.springframework.test.web.servlet.MockMvc; -import org.springframework.test.web.servlet.request.MockMvcRequestBuilders; -import org.springframework.test.web.servlet.setup.MockMvcBuilders; -import reactor.core.publisher.Flux; - -/** - * test case for {@link AiController} - */ - -@ExtendWith(MockitoExtension.class) -class AiControllerTest { - - private MockMvc mockMvc; - - @Mock - private AiServiceFactoryImpl aiServiceFactory; - - @Mock - private AiProperties aiProperties; - - @Mock - private AiService aiService; - - @InjectMocks - private AiController aiController; - - @BeforeEach - public void setup() { - - mockMvc = MockMvcBuilders.standaloneSetup(aiController).build(); - } - - @Test - public void testRequestAi() throws Exception { - - String responseText = "response"; - Flux<ServerSentEvent<String>> responseFlux = Flux.just(ServerSentEvent.builder(responseText).build()); - - when(aiServiceFactory.getAiServiceImplBean(anyString())).thenReturn(aiService); - when(aiService.requestAi(anyString())).thenReturn(responseFlux); - when(aiProperties.getType()).thenReturn("alibabaAi"); - - String requestBody = "{\"text\":\"Who are you\"}"; - - mockMvc.perform((MockMvcRequestBuilders.post("/api/ai/get") - .content(requestBody) - .contentType(MediaType.APPLICATION_JSON) - .accept(MediaType.TEXT_EVENT_STREAM))) - .andExpect(status().isOk()) - .andExpect(content().contentType(MediaType.TEXT_EVENT_STREAM_VALUE)) - .andExpect(content().string("data:response\n\n")); - } - -} diff --git a/hertzbeat-manager/src/test/java/org/apache/hertzbeat/manager/service/AiServiceFactoryTest.java b/hertzbeat-manager/src/test/java/org/apache/hertzbeat/manager/service/AiServiceFactoryTest.java deleted file mode 100644 index 60f5e627d..000000000 --- a/hertzbeat-manager/src/test/java/org/apache/hertzbeat/manager/service/AiServiceFactoryTest.java +++ /dev/null @@ -1,125 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.apache.hertzbeat.manager.service; - -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertNotNull; -import static org.junit.jupiter.api.Assertions.assertThrows; -import static org.junit.jupiter.api.Assertions.assertTrue; -import static org.mockito.Mockito.when; -import java.util.Arrays; -import java.util.Collections; -import java.util.List; -import java.util.Map; -import java.util.function.Function; -import java.util.stream.Collectors; -import org.apache.hertzbeat.common.constants.AiTypeEnum; -import org.apache.hertzbeat.manager.service.ai.AiService; -import org.apache.hertzbeat.manager.service.ai.factory.AiServiceFactoryImpl; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ExtendWith; -import org.mockito.InjectMocks; -import org.mockito.Mock; -import org.mockito.junit.jupiter.MockitoExtension; -import org.springframework.test.util.ReflectionTestUtils; - -/** - * test case for {@link AiServiceFactoryImpl} - */ - -@ExtendWith(MockitoExtension.class) -class AiServiceFactoryTest { - - @Mock - private List<AiService> aiService; - - @Mock - private AiService aiService1; - - @Mock - private AiService aiService2; - - @InjectMocks - private AiServiceFactoryImpl aiServiceFactory; - - @BeforeEach - public void setup() { - - when(aiService1.getType()).thenReturn(AiTypeEnum.alibabaAi); - when(aiService2.getType()).thenReturn(AiTypeEnum.zhiPu); - - aiService = Arrays.asList(aiService1, aiService2); - ReflectionTestUtils.setField(aiServiceFactory, "aiService", aiService); - - aiServiceFactory.init(); - } - - @Test - public void testInit() { - - Map<AiTypeEnum, AiService> expectedMap = aiService.stream() - .collect(Collectors.toMap(AiService::getType, Function.identity())); - - Map<AiTypeEnum, AiService> actualMap = (Map<AiTypeEnum, AiService>) ReflectionTestUtils.getField(aiServiceFactory, "aiServiceFactoryMap"); - - assertEquals(expectedMap, actualMap); - } - - @Test - public void testGetAiServiceImplBean_Success() { - - AiService service = aiServiceFactory.getAiServiceImplBean(AiTypeEnum.alibabaAi + ""); - assertNotNull(service); - assertEquals(aiService1, service); - - service = aiServiceFactory.getAiServiceImplBean(AiTypeEnum.zhiPu + ""); - assertNotNull(service); - assertEquals(aiService2, service); - } - - @Test - public void testGetAiServiceImplBeanTypeNotFound() { - - Exception exception = assertThrows( - IllegalArgumentException.class, - () -> aiServiceFactory.getAiServiceImplBean("InvalidType") - ); - - assertTrue(exception.getMessage().contains("The current type is not supported")); - } - - @Test - public void testGetAiServiceImplBeanNoBean() { - - aiServiceFactory.init(); - - when(aiService1.getType()).thenReturn(AiTypeEnum.kimiAi); - List<AiService> singleServiceList = Collections.singletonList(aiService1); - ReflectionTestUtils.setField(aiServiceFactory, "aiService", singleServiceList); - aiServiceFactory.init(); - - Exception exception = assertThrows( - IllegalArgumentException.class, - () -> aiServiceFactory.getAiServiceImplBean(AiTypeEnum.sparkDesk + "") - ); - - assertTrue(exception.getMessage().contains("No bean for current type found")); - } - -} diff --git a/home/docs/help/ai_agent_chat.md b/home/docs/help/ai_agent_chat.md index 66e6413a6..e817960c3 100644 --- a/home/docs/help/ai_agent_chat.md +++ b/home/docs/help/ai_agent_chat.md @@ -49,26 +49,15 @@ Before using the AI Agent Chat, ensure: ### OpenAI API Key Setup -The AI Agent Chat uses OpenAI's GPT models. You need to configure an OpenAI API key in one of two ways: +The AI Agent Chat uses OpenAI's GPT models. You need to configure an OpenAI API key in this ways: -#### Method 1: Database Configuration via UI (Recommended) +#### Database Configuration via UI 1. Navigate to the AI Agent Chat interface 2. If no API key is configured, you'll see a configuration dialog 3. Enter your OpenAI API key 4. Click **Save Configuration** -#### Method 2: YAML Configuration - -Add to your `application.yml`: - -```yaml -spring: - ai: - openai: - api-key: sk-your-openai-api-key-here -``` - ### Configuration Priority - **Database configuration** takes priority over YAML configuration diff --git a/home/i18n/zh-cn/docusaurus-plugin-content-docs/current/help/ai_agent_chat.md b/home/i18n/zh-cn/docusaurus-plugin-content-docs/current/help/ai_agent_chat.md new file mode 100644 index 000000000..6c6de6291 --- /dev/null +++ b/home/i18n/zh-cn/docusaurus-plugin-content-docs/current/help/ai_agent_chat.md @@ -0,0 +1,356 @@ +--- +id: ai_agent_chat +title: AI 智能助手聊天用户指南 +sidebar_label: AI 智能助手聊天 +keywords: [AI, 聊天, 智能助手, 监控, AI Agent, OpenAI] +--- + +> HertzBeat AI 智能助手聊天是一个智能监控 AI 助手,通过自然语言对话帮助您管理监控器、配置告警并优化基础设施监控。 + +## 概述 + +AI 智能助手聊天功能提供了一个交互式聊天界面,您可以: + +**监控器管理:** + +- 🔍 按状态、类型、主机和标签查询和过滤现有监控器 +- ➕ 为网站、API、数据库和服务添加新的监控器 +- 📊 获取可用监控器类型及其参数的详细信息 +- ⚡ 检查监控器状态并排除监控问题 + +**告警管理:** + +- 🚨 通过综合过滤(类型、状态、搜索)查询活跃告警 +- 📈 获取告警摘要统计和分布 +- 🔔 查看单个和分组告警 +- 📋 分析告警模式和趋势 + +**指标分析:** + +- 📊 检索任何监控器的实时指标数据 +- 📈 访问可自定义时间范围的历史指标 +- 💾 检查仓库存储系统状态 +- 🔍 查询特定指标字段和标签 + +**告警规则管理:** + +- ⚙️ 配置告警规则和阈值 +- 📝 管理不同监控器类型的告警定义 +- 🎯 设置自定义告警条件 + +## 先决条件 + +在使用 AI 智能助手聊天之前,请确保: + +1. **OpenAI 配置**:必须配置有效的 OpenAI API 密钥。可选地,您可以在 `application.yml` 中的 `spring.ai.openai.chat.options.model` 下更改模型(默认为 `gpt-4.1-nano`)。 +2. **数据库连接**:HertzBeat 数据库必须可访问以进行监控器操作 + +## 配置 + +### OpenAI API 密钥设置 + +AI 智能助手聊天使用 OpenAI 的 GPT 模型。您需要通过以下方式配置 OpenAI API 密钥: + +#### 通过 UI 进行数据库配置 + +1. 导航到 AI 智能助手聊天界面 +2. 如果未配置 API 密钥,您将看到配置对话框 +3. 输入您的 OpenAI API 密钥 +4. 点击 **保存配置** + +### 配置优先级 + +- **数据库配置** 优先于 YAML 配置 +- 配置更改立即生效,无需重启 +- 保存前会自动验证无效的 API 密钥 + +## 使用 AI 智能助手聊天 + +### 访问聊天界面 + +1. 在 HertzBeat Web 界面中导航到 **AI 智能助手聊天** 部分 +2. 您将看到 HertzBeat 监控 AI 助手欢迎屏幕 +3. 在聊天输入框中开始输入您的问题 + +### 示例对话 + +#### 添加新监控器 + +```text +您:为我的网站 https://example.com 添加一个新的 HTTP 监控器 +AI 助手:我将帮助您为 https://example.com 添加一个 HTTP 监控器。 +让我使用以下配置创建该监控器: +- 名称:example.com 网站监控器 +- 类型:HTTP +- 主机:example.com +- 端口:443 (HTTPS) +- 路径:/ +- 间隔:60 秒 + +监控器创建成功,ID:12345 +``` + +#### 列出现有监控器 + +```text +您:显示我所有当前的监控器 +AI 助手:以下是您当前的监控器: + +1. **Linux 服务器** (ID: 1001) + - 状态:✅ 在线 + - 主机:192.168.1.100 + - 最后检查:2 分钟前 + +2. **MySQL 数据库** (ID: 1002) + - 状态:⚠️ 警告 + - 主机:db.example.com + - 最后检查:30 秒前 + +3. **网站监控器** (ID: 1003) + - 状态:✅ 在线 + - 主机:https://example.com + - 最后检查:1 分钟前 + +您想了解任何特定监控器的详细信息吗? +``` + +#### 获取监控器类型信息 + +```text +您:监控 PostgreSQL 数据库需要哪些参数? +AI 助手:要监控 PostgreSQL 数据库,您需要这些参数: + +**必需参数:** +- **主机**:数据库服务器 IP/主机名 +- **端口**:PostgreSQL 端口(默认:5432) +- **数据库**:要连接的数据库名称 +- **用户名**:数据库用户名 +- **密码**:数据库密码 + +**可选参数:** +- **超时**:连接超时(默认:6000ms) +- **查询超时**:SQL 查询超时(默认:3000ms) +- **SSL**:启用 SSL 连接(默认:false) + +您想让我帮助您添加一个 PostgreSQL 监控器吗? +``` + +#### 告警管理 + +```text +您:显示过去 24 小时内的所有严重告警 +AI 助手:以下是过去 24 小时内的严重告警: + +🚨 **活跃告警(3 个触发中)** +1. **高 CPU 使用率** - Linux 服务器 (ID: 1001) + - 状态:🔥 触发中 + - 触发时间:2 小时前 + - 严重程度:严重 + - 消息:CPU 使用率 95% > 阈值 90% + +2. **数据库连接失败** - MySQL 数据库 (ID: 1002) + - 状态:🔥 触发中 + - 触发时间:45 分钟前 + - 严重程度:严重 + - 消息:10 秒后连接超时 + +3. **网站响应时间** - 网站监控器 (ID: 1003) + - 状态:⚠️ 警告 + - 触发时间:15 分钟前 + - 严重程度:警告 + - 消息:响应时间 5.2s > 阈值 3s + +**告警摘要:** +- 总告警数:12 +- 触发中:3(2 个严重,1 个警告) +- 已解决:9 + +您想让我帮助调查任何特定的告警吗? +``` + +#### 指标分析 + +```text +您:显示我的 Linux 服务器过去 6 小时的 CPU 使用率 +AI 助手:以下是您的 Linux 服务器(监控器 ID:1001)过去 6 小时的 CPU 使用率数据: + +📊 **CPU 使用率指标 - 过去 6 小时** + +时间 | 使用率 (%) | 负载平均 | 进程数 +14:00-15:00 | 45.2% | 1.2 | 156 +15:00-16:00 | 67.8% | 2.1 | 178 +16:00-17:00 | 89.4% | 3.8 | 195 +17:00-18:00 | 92.1% | 4.2 | 203 +18:00-19:00 | 94.7% | 4.8 | 210 +19:00-20:00 | 88.3% | 3.5 | 188 + +**分析:** +- 峰值使用率:19:00 时 94.7% +- 平均使用率:79.6% +- 趋势:高使用率持续 3+ 小时 +- 当前状态:下降中(88.3%) + +**实时指标:** +- 当前 CPU:76.2% +- 内存使用率:82.1% +- 磁盘 I/O:正常 + +您想让我检查历史数据进行比较或调查高使用率的原因吗? +``` + +#### 告警规则配置 + +```text +您:当我的网站响应时间超过 5 秒时设置告警规则 +AI 助手:我将帮助您创建网站响应时间监控的告警规则。 + +**告警规则配置:** +- **监控器类型**:HTTP/网站 +- **指标**:响应时间 +- **条件**:大于 5 秒 +- **严重程度**:警告 +- **评估**:每 60 秒检查一次 +- **通知**:邮件 + 仪表板 + +**建议规则:** +```yaml +name: "网站响应时间告警" +condition: "response_time > 5000ms" +severity: "warning" +description: "当网站响应时间超过 5 秒时告警" +evaluation_interval: 60s +for: 2m # 条件持续 2 分钟后告警 +``` + +此规则将: + +1. 每分钟监控响应时间 +2. 如果响应时间 > 5s 持续 2+ 分钟则触发告警 +3. 通过配置的渠道发送通知 +4. 在仪表板中显示警告状态 + +您想让我应用此配置还是修改任何设置? +``` + +### 聊天功能 + +#### 消息历史 + +- 所有对话都会自动保存 +- 从侧边栏访问以前的对话 +- 搜索对话历史 +- 导出对话日志 + +#### 实时响应 + +- 流式响应提供即时反馈 +- 输入指示器显示助手正在处理时 +- 如需要可取消正在进行的请求 + +#### 对话管理 + +- 为不同主题创建新对话 +- 重命名对话以便更好地组织 +- 删除旧对话以保持整洁 +注意:对话不会保存在数据库中 + +## 故障排除 + +### 常见问题 + +#### 聊天界面无法加载 + +**症状**:聊天界面无限显示加载旋转器 +**解决方案**: + +1. 检查浏览器控制台中的 JavaScript 错误 +2. 确保与 HertzBeat 服务器的网络连接 + +#### "服务不可用"消息 + +**症状**:聊天显示"HertzBeat AI 监控服务不可用" +**解决方案**: +1. 验证 OpenAI API 密钥配置 +2. 检查应用程序日志中的错误 +3. 确保数据库连接 + +#### 无效 API 密钥错误 + +**症状**:配置对话框显示"无效 API 密钥"错误 +**解决方案**: + +1. 验证您的 OpenAI API 密钥以 `sk-` 开头 +2. 检查 API 密钥是否有足够的积分/配额 +3. 直接使用 OpenAI API 测试 API 密钥 +4. 确保 API 密钥中没有多余的空格 + +#### 监控器创建失败 + +**症状**:AI 建议监控器配置但创建失败 +**解决方案**: + +1. 验证您有创建监控器的权限 +2. 检查是否已存在同名监控器 +3. 确保目标主机/服务可访问 +4. 查看监控器参数验证错误 + +### 调试模式 + +通过设置日志级别为 DEBUG 来启用调试日志: + +```yaml +logging: + level: + org.apache.hertzbeat.ai.agent: DEBUG +``` + +## 最佳实践 + +### 有效的聊天使用 + +1. **具体明确**:"为 api.example.com 端口 8080 添加 HTTP 监控器" vs "添加一个监控器" +2. **提供上下文**:说明您想要生产环境还是测试环境监控器 +3. **询问后续问题**:如需要请求配置详细信息 +4. **使用自然语言**:AI 理解对话式请求 + +### 监控器管理 + +1. **命名约定**:使用描述性的监控器名称 +2. **文档记录**:要求 AI 记录复杂配置 + +### 安全考虑 + +1. **API 密钥安全**:安全存储 OpenAI API 密钥 +2. **访问控制**:限制 AI 聊天访问给授权用户 +3. **数据隐私**:注意聊天日志中的敏感信息 +4. **网络安全**:确保与 OpenAI API 的安全连接 + +## 高级功能 + +### 批量操作 + +```text +您:为我的预发布环境中的所有服务添加 HTTP 监控器: +- api-staging.example.com:8080 +- web-staging.example.com:80 +- admin-staging.example.com:3000 +``` + +## 限制 + +- 需要活跃的互联网连接访问 OpenAI API +- OpenAI API 使用会根据令牌消耗产生费用 +- 复杂的多步骤操作可能需要多次交互 +- 某些高级配置可能需要手动设置 +- 可能会根据 OpenAI 计划应用速率限制 + +## 支持 + +对于 AI 智能助手聊天的问题: + +1. 首先查看此文档 +2. 查看应用程序日志中的错误 +3. 独立测试 OpenAI API 连接 +4. 联系 HertzBeat 支持并提供具体错误消息 + +--- diff --git a/home/sidebars.json b/home/sidebars.json index e5ab4b430..0674eff5e 100755 --- a/home/sidebars.json +++ b/home/sidebars.json @@ -290,7 +290,6 @@ "help/mcp_sse_server", "help/collector", "help/ai_agent_chat", - "help/ai_config", "help/risc-v", "help/issue" ] diff --git a/script/application.yml b/script/application.yml index e776cb983..c4f463601 100644 --- a/script/application.yml +++ b/script/application.yml @@ -19,6 +19,25 @@ spring: name: ${HOSTNAME:@hertzbeat@}${PID} profiles: active: prod + ai: + mcp: + server: + enabled: true + stdio: false + name: sse-mcp-server + version: 1.0.0 + resource-change-notification: true + tool-change-notification: true + prompt-change-notification: true + sse-endpoint: /api/sse + sse-message-endpoint: /api/mcp/message + type: SYNC + capabilities: + tool: true + resource: true + prompt: true + completion: true + mvc: static-path-pattern: /** jackson: @@ -38,6 +57,7 @@ spring: max-file-size: 100MB max-request-size: 100MB + management: health: mail: @@ -124,13 +144,26 @@ spring: common: queue: - # memory or kafka + # memory, kafka, redis type: memory # properties when queue type is kafka kafka: servers: 127.0.0.1:9092 metrics-data-topic: async-metrics-data + service-discovery-data-topic: service-discovery-data alerts-data-topic: async-alerts-data + log-entry-data-topic: async-log-entry-data + log-entry-data-to-storage-topic: log-entry-data-to-storage-topic + redis: + redis-host: 127.0.0.1 + redis-port: 6379 + metrics-data-queue-name-for-service-discovery: service_discovery + metrics-data-queue-name-to-persistent-storage: metrics:to_persistent_storage + metrics-data-queue-name-to-alerter: metrics:to_alerter + metrics-data-queue-name-to-real-time-storage: metrics:to_realtime_storage + alerts-data-queue-name: alerts + log-entry-queue-name: log:to_alerter + log-entry-to-storage-queue-name: log:to_storage warehouse: store: @@ -143,10 +176,28 @@ warehouse: # (please set this configuration reasonably as history records can affect performance when it is large) max-history-record-num: 6000 victoria-metrics: + # Standalone mode toggle — must be set to false when using cluster mode enabled: false url: http://localhost:8428 username: root password: root + insert: + buffer-size: 100 + flush-interval: 3 + compression: + enabled: false + cluster: + enabled: false + select: + url: http://localhost:8481 + username: root + password: root + insert: + url: http://localhost:8480 + username: root + password: root + buffer-size: 1000 + flush-interval: 3 td-engine: enabled: false driver-class-name: com.taosdata.jdbc.rs.RestfulDriver @@ -161,6 +212,11 @@ warehouse: database: public username: greptime password: greptime + questdb: + enabled: false + url: localhost:9000 + username: admin + password: quest iot-db: enabled: false host: 127.0.0.1 @@ -244,10 +300,16 @@ alerter: region: AWS_REGION_FOR_END_USER_MESSAGING twilio: account-sid: YOUR_ACCOUNT_SID - auth-token: YOUR_AUTH_TOKEN + auth-token: YOUR_AUTH_TOKEN twilio-phone-number: YOUR_TWILIO_PHONE_NUMBER scheduler: server: enabled: true port: 1158 - + +grafana: + enabled: false + url: http://127.0.0.1:3000 + expose-url: http://127.0.0.1:3000 + username: admin + password: admin diff --git a/script/docker-compose/hertzbeat-mysql-iotdb/conf/application.yml b/script/docker-compose/hertzbeat-mysql-iotdb/conf/application.yml index d98df3fe9..1e847ec97 100644 --- a/script/docker-compose/hertzbeat-mysql-iotdb/conf/application.yml +++ b/script/docker-compose/hertzbeat-mysql-iotdb/conf/application.yml @@ -240,10 +240,3 @@ grafana: url: http://127.0.0.1:3000 username: admin password: admin - -ai: - type: - model: - api-key: - api-secret: - api-url: diff --git a/script/docker-compose/hertzbeat-mysql-tdengine/conf/application.yml b/script/docker-compose/hertzbeat-mysql-tdengine/conf/application.yml index b2f0e77af..8f55b0399 100644 --- a/script/docker-compose/hertzbeat-mysql-tdengine/conf/application.yml +++ b/script/docker-compose/hertzbeat-mysql-tdengine/conf/application.yml @@ -238,10 +238,3 @@ grafana: username: admin password: admin -ai: - type: - model: - api-key: - api-secret: - api-url: - diff --git a/script/docker-compose/hertzbeat-mysql-victoria-metrics/conf/application.yml b/script/docker-compose/hertzbeat-mysql-victoria-metrics/conf/application.yml index c48aa31d8..b85eadbce 100644 --- a/script/docker-compose/hertzbeat-mysql-victoria-metrics/conf/application.yml +++ b/script/docker-compose/hertzbeat-mysql-victoria-metrics/conf/application.yml @@ -240,10 +240,3 @@ grafana: url: http://127.0.0.1:3000 username: admin password: admin - -ai: - type: - model: - api-key: - api-secret: - api-url: diff --git a/script/docker-compose/hertzbeat-postgresql-victoria-metrics/conf/application.yml b/script/docker-compose/hertzbeat-postgresql-victoria-metrics/conf/application.yml index 15f3660b5..6d3ad2306 100644 --- a/script/docker-compose/hertzbeat-postgresql-victoria-metrics/conf/application.yml +++ b/script/docker-compose/hertzbeat-postgresql-victoria-metrics/conf/application.yml @@ -239,10 +239,3 @@ grafana: url: http://127.0.0.1:3000 username: admin password: admin - -ai: - type: - model: - api-key: - api-secret: - api-url: --------------------------------------------------------------------- To unsubscribe, e-mail: [email protected] For additional commands, e-mail: [email protected]
