This is an automated email from the ASF dual-hosted git repository.
sadadw1 pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/ozhera.git
The following commit(s) were added to refs/heads/master by this push:
new d22eaace fix: delete SNAPSHOT on intelligence module (#663)
d22eaace is described below
commit d22eaace270b05af13f8aeca5dd013bdffae95df
Author: EricDing <[email protected]>
AuthorDate: Tue Jun 2 11:15:34 2026 +0800
fix: delete SNAPSHOT on intelligence module (#663)
* refactor: refactor ozhera intelligence
* feat: add coder tool and git mcp
* fix: update codefix agent and tool
* fix: add git suffix
* refactor: add log query tool
* fix: add endTime and update desc
* feat: add git checkout new branch and create MR flow
* fix: update intelligence compiler error
* fix: 删除SNAPSHOT
---
ozhera-intelligence/README.md | 11 +-
ozhera-intelligence/README_cn.md | 12 +-
.../bootstrap/IntelligenceBootStrap.java | 2 +-
.../ozhera-intelligence-service/pom.xml | 40 ---
.../intelligence/agents/config/AgentConfig.java | 115 --------
.../agents/function/TraceAnalysisFunction.java | 97 ------
.../agents/tool/RootExceptionSpanTool.java | 324 ---------------------
.../agents/tool/TraceAnalysisTool.java | 217 --------------
.../ozhera/intelligence/service/PromptService.java | 221 --------------
.../intelligence/service/RootAnalysisService.java | 62 ----
.../intelligence/service/TraceAnalysisService.java | 265 -----------------
.../intelligence/service/PromptServiceTest.java | 295 -------------------
12 files changed, 10 insertions(+), 1651 deletions(-)
diff --git a/ozhera-intelligence/README.md b/ozhera-intelligence/README.md
index 3f822159..0a9b992a 100644
--- a/ozhera-intelligence/README.md
+++ b/ozhera-intelligence/README.md
@@ -23,9 +23,9 @@
>
> This module (ozhera-intelligence) is still **under construction (Work In
> Progress)**. It has not been fully open-sourced yet and **cannot be compiled
> or run on its own**.
>
-> At this stage we are only releasing a few of its **most important prompts**
(the agent role definitions, the vulnerability-fixing workflow, etc. — see
classes such as `AgentConfig`) so that the community can study and reflect on
how intelligent operations can be implemented.
+> At this stage we are only releasing a few of its **most important prompts**
(trace / log / metrics root-cause-analysis prompts, the result-aggregation
prompt, the code-fix-analysis prompt — see the `Prompts` class under
`ozhera-intelligence-domain`) so that the community can study and reflect on
how intelligent operations can be implemented.
>
-> Some dependencies (such as the MCP tools from Xiaomi's open-source agent
platform hive/m78) are still being opened up. The complete functionality will
be filled in gradually in subsequent releases — stay tuned.
+> The agent/LLM orchestration layer (which previously depended on Xiaomi's
internal agent framework) has been removed from the open-source code. The
complete functionality will be filled in gradually in subsequent releases —
stay tuned.
# Overview
+ Apache OzHera(incubating) Intelligence Platform is responsible for
generating intelligent-related functionalities.
@@ -46,14 +46,13 @@
+ MetricsService: Retrieves monitoring metric data
+ TraceService: Retrieves trace data
-# Important Dependencies
-It relies on a Xiaomi open-source [Agent builder
platform--m78](https://github.com/XiaoMi/mone/tree/master/m78-all)
-
# Intelligent Workflow (Sequence Diagram)

# LLM Environment Variables
-Currently, the LLM class is used for invoking large language models (LLMs).
The required environment variables (env vars) are defined in the LLMProvider
class.
+> Note: The LLM invocation layer is not part of the currently open-sourced
code (see the Work In Progress notice above). The notes below describe the
design intent of the planned implementation.
+
+The implementation is intended to invoke large language models (LLMs) through
a configurable provider, with the required environment variables driven by an
`LLM_PROVIDER` setting.
## Prerequisite:
A base LLM_PROVIDER env var must be set to specify the desired LLMProvider.
diff --git a/ozhera-intelligence/README_cn.md b/ozhera-intelligence/README_cn.md
index 946fd0ce..79307bae 100644
--- a/ozhera-intelligence/README_cn.md
+++ b/ozhera-intelligence/README_cn.md
@@ -23,9 +23,7 @@
>
> 本模块(ozhera-intelligence)目前仍处于**建设中(Work In Progress)**阶段,尚未完整开源,**无法独立编译运行**。
>
-> 当前阶段我们仅把其中**最关键的几个 Prompt**(智能体角色设定、漏洞修复工作流等,见 `AgentConfig`
等类)发布出来,供社区参考与思考智能化运维的实现思路。
->
-> 部分依赖(如小米开源 Agent 平台 hive/m78 的相关 MCP 工具)仍在持续开放中,完整功能将在后续版本陆续补齐,敬请期待。
+> 当前阶段我们仅把其中**最关键的几个 Prompt**(Trace / 日志 / 指标的根因分析 Prompt、结果汇总 Prompt、代码修复分析
Prompt,见 `ozhera-intelligence-domain` 下的 `Prompts` 类)发布出来,供社区参考与思考智能化运维的实现思路。
# 概述
+ Apache OzHera(incubating)智能化平台,负责产生智能化相关功能。
@@ -45,16 +43,14 @@
+ MetricsService,取监控指标数据
+ TraceService,取链路数据
-# 重要依赖
-会依赖小米开源的一款[Agent平台——hive](https://github.com/XiaoMi/mone/tree/master/m78-all)
-
-
# 智能化工作流程(时序图)

# 启动依赖
## LLM环境变量
-目前使用LLM类进行大模型调用,它所需要的env都在LLMProvider类中,首先需要一个基础的LLM_PROVIDER的env,来指定使用的LLMProvider。
+> 说明:大模型调用层不在当前开源代码范围内(见上方"正在建设中"提示),以下内容描述的是规划中实现的设计思路。
+
+规划中的实现会通过可配置的 Provider 来调用大模型,所需环境变量由 `LLM_PROVIDER` 配置驱动,首先需要一个基础的
`LLM_PROVIDER` env 来指定使用的 Provider。
**示例**:
比如想要使用DEEPSEEK
diff --git
a/ozhera-intelligence/ozhera-intelligence-server/src/main/java/org/apache/ozhera/intelligence/bootstrap/IntelligenceBootStrap.java
b/ozhera-intelligence/ozhera-intelligence-server/src/main/java/org/apache/ozhera/intelligence/bootstrap/IntelligenceBootStrap.java
index 26c35da8..fc258db6 100644
---
a/ozhera-intelligence/ozhera-intelligence-server/src/main/java/org/apache/ozhera/intelligence/bootstrap/IntelligenceBootStrap.java
+++
b/ozhera-intelligence/ozhera-intelligence-server/src/main/java/org/apache/ozhera/intelligence/bootstrap/IntelligenceBootStrap.java
@@ -24,7 +24,7 @@ import
org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.context.annotation.ComponentScan;
@EnableAutoConfiguration
-@ComponentScan(basePackages = {"org.apache.ozhera.intelligence",
"run.mone.mcp.git", "run.mone.mcp.hera.analysis.tool",
"run.mone.mcp.hera.analysis.service"})
+@ComponentScan(basePackages = {"org.apache.ozhera.intelligence"})
@Slf4j
public class IntelligenceBootStrap {
public static void main(String... args) {
diff --git a/ozhera-intelligence/ozhera-intelligence-service/pom.xml
b/ozhera-intelligence/ozhera-intelligence-service/pom.xml
index 026b98e0..fd07f762 100644
--- a/ozhera-intelligence/ozhera-intelligence-service/pom.xml
+++ b/ozhera-intelligence/ozhera-intelligence-service/pom.xml
@@ -36,39 +36,6 @@
</properties>
<dependencies>
- <dependency>
- <groupId>run.mone</groupId>
- <artifactId>mcp-miline</artifactId>
- <version>1.6.1-jdk21-SNAPSHOT</version>
- <exclusions>
- <exclusion>
- <artifactId>spring-webmvc</artifactId>
- <groupId>org.springframework</groupId>
- </exclusion>
- </exclusions>
- </dependency>
- <dependency>
- <groupId>run.mone</groupId>
- <artifactId>mcp-hera-analysis</artifactId>
- <version>1.6.1-jdk21-SNAPSHOT</version>
- <exclusions>
- <exclusion>
- <artifactId>spring-webmvc</artifactId>
- <groupId>org.springframework</groupId>
- </exclusion>
- </exclusions>
- </dependency>
- <dependency>
- <groupId>run.mone</groupId>
- <artifactId>mcp-git</artifactId>
- <version>1.6.1-jdk21-SNAPSHOT</version>
- <exclusions>
- <exclusion>
- <artifactId>spring-webmvc</artifactId>
- <groupId>org.springframework</groupId>
- </exclusion>
- </exclusions>
- </dependency>
<dependency>
<groupId>org.apache.ozhera</groupId>
<artifactId>ozhera-intelligence-domain</artifactId>
@@ -195,13 +162,6 @@
<groupId>com.squareup.okhttp3</groupId>
<artifactId>okhttp</artifactId>
</dependency>
-
- <!-- hive -->
- <dependency>
- <groupId>run.mone</groupId>
- <artifactId>hive-spring-starter</artifactId>
- <version>1.6.0-jdk21-SNAPSHOT</version>
- </dependency>
</dependencies>
<build>
diff --git
a/ozhera-intelligence/ozhera-intelligence-service/src/main/java/org/apache/ozhera/intelligence/agents/config/AgentConfig.java
b/ozhera-intelligence/ozhera-intelligence-service/src/main/java/org/apache/ozhera/intelligence/agents/config/AgentConfig.java
deleted file mode 100644
index 16702c60..00000000
---
a/ozhera-intelligence/ozhera-intelligence-service/src/main/java/org/apache/ozhera/intelligence/agents/config/AgentConfig.java
+++ /dev/null
@@ -1,115 +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.ozhera.intelligence.agents.config;
-
-import com.google.common.collect.Lists;
-import org.apache.ozhera.intelligence.agents.tool.RootExceptionSpanTool;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.beans.factory.annotation.Value;
-import org.springframework.context.annotation.Bean;
-import org.springframework.context.annotation.Configuration;
-import run.mone.hive.mcp.function.ChatFunction;
-import run.mone.hive.mcp.service.RoleMeta;
-import run.mone.hive.roles.tool.*;
-import run.mone.mcp.git.tool.*;
-// TODO: dependency under construction - module not yet open-sourced
-// import run.mone.mcp.hera.analysis.tool.LogQueryTool;
-import run.mone.mcp.miline.tools.GetPipelineDetailTool;
-import run.mone.mcp.miline.tools.RunPipelineTool;
-
-
-@Configuration
-public class AgentConfig {
-
- @Value("${mcp.agent.name}")
- private String agentName;
-
- @Autowired
- private RootExceptionSpanTool codeFixTool;
-
- @Autowired
- private GitCloneTool gitCloneTool;
- @Autowired
- private GitCommitTool gitCommitTool;
- @Autowired
- private GitPushTool gitPushTool;
- @Autowired
- private GitCheckoutNewBranchTool gitCheckoutNewBranchTool;
- @Autowired
- private GitCreateMergeRequestTool gitCreateMergeRequestTool;
- // TODO: dependency under construction - module not yet open-sourced
-// @Autowired
-// private LogQueryTool logQueryTool;
-
- private boolean isRemoteFile = false;
-
- @Bean
- public RoleMeta roleMeta() {
- ChatFunction chat = new ChatFunction(agentName, 20);
-
chat.setDesc("和%s聊天,让%s基于某一个traceId执行漏洞修复。支持各种形式如:'%s'、'请%s帮我修复问题'、'让%s根据cd573409c0e75849c73ddd1ca655dfcc修复漏洞'、'%s
cd573409c0e75849c73ddd1ca655dfcc'等。支持上下文连续对话。");
-
- return RoleMeta.builder()
- .name("OzHera漏洞修复专家")
- .profile("你是OzHera漏洞修复专家,精通漏洞修复,能够根据traceId修复trace上的各种问题")
- .goal("你的目标是根据用户输入的traceId,查询trace和log的相关信息,根据现有代码进行修复,最后进行发布")
- .constraints("不要探讨一些负面的东西,如果用户问你,你可以直接拒绝掉")
- //允许自动从知识库获取内容(意图识别的小模型)
-//
.webQuery(WebQuery.builder().autoWebQuery(true).modelType("bert").version("finetune-bert-20250605-73a29258").releaseServiceName("bert-is-network").build())
-//
.rag(Rag.builder().autoRag(true).modelType("bert").version("finetune-bert-20250605-ed8acbcf").releaseServiceName("bert-is-knowledge-base").build())
- //内部工具
- .tools(Lists.newArrayList(
- new ChatTool(),
- new AskTool(),
- new AttemptCompletionTool(),
- codeFixTool,
- new ListFilesTool(isRemoteFile),
- new ExecuteCommandToolOptimized(),
- new ReadFileTool(isRemoteFile),
- new SearchFilesTool(isRemoteFile),
- new ReplaceInFileTool(isRemoteFile),
- new ListCodeDefinitionNamesTool(),
- new WriteToFileTool(isRemoteFile),
- new RunPipelineTool(),
- new GetPipelineDetailTool(),
- gitCloneTool,
- gitCommitTool,
- gitPushTool,
- gitCheckoutNewBranchTool,
- gitCreateMergeRequestTool
- // TODO: dependency under construction - module not
yet open-sourced
- // logQueryTool
- ))
- //mcp工具
- .mcpTools(Lists.newArrayList(chat))
- .workflow("""
- 你是代码级自动异常修复系统,当前系统环境是Linux,严格按照以下步骤执行:
- 1、根据traceId获取链路上根因节点的项目信息与异常信息
- 2、查询对应project和流水线(env)endTime前后半小时的该traceId的全部日志
- 2、根据根因节点的projectId和envId(就是pipelineId)获取流水线详情
-
3、根据流水线详情中的gitUrl、gitBranch、gitCommitId调用git_clone工具进行git clone\s
- 4、基于gitBranch和localPath创建一个新的分支,这个分支用于代码修复分支
- 5、根据trace链路上的异常信息或者是日志中的信息,结合项目代码进行异常修复\s
- 6、修复完成后,将本地代码使用git_commit工具进行git
commit,commit信息是自动代码修复, 使用git_push进行git push\s
-
7、根据流水线详情中的gitUrl、代码修复分支、gitBranch创建MR,将代码修复分支的代码合并到gitBranch中
- 8、使用ask工具询问用户是否能够发布
-
9、如果能够发布,根据projectId和envId调用RunPipelineTool进行发布,如果不能够发布,则直接结束流程
- """)
- .build();
- }
-}
diff --git
a/ozhera-intelligence/ozhera-intelligence-service/src/main/java/org/apache/ozhera/intelligence/agents/function/TraceAnalysisFunction.java
b/ozhera-intelligence/ozhera-intelligence-service/src/main/java/org/apache/ozhera/intelligence/agents/function/TraceAnalysisFunction.java
deleted file mode 100644
index 450219dc..00000000
---
a/ozhera-intelligence/ozhera-intelligence-service/src/main/java/org/apache/ozhera/intelligence/agents/function/TraceAnalysisFunction.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.ozhera.intelligence.agents.function;
-
-import lombok.Data;
-import lombok.extern.slf4j.Slf4j;
-import org.apache.ozhera.intelligence.domain.rootanalysis.TraceQueryParam;
-import org.apache.ozhera.intelligence.service.TraceAnalysisService;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.stereotype.Component;
-import reactor.core.publisher.Flux;
-import run.mone.hive.mcp.function.McpFunction;
-import run.mone.hive.mcp.spec.McpSchema;
-
-import java.util.List;
-import java.util.Map;
-
-@Data
-@Slf4j
-@Component
-public class TraceAnalysisFunction implements McpFunction {
-
- @Autowired
- private TraceAnalysisService traceAnalysisService;
-
- private String name = "stream_hera_analysis";
-
- private String desc = "Analyze the root cause of exceptions or slow
queries in the trace chain based on traceId";
-
- private String chaosToolSchema = """
- {
- "type": "object",
- "properties": {
- "traceId": {
- "type": "string",
- "description": "traceId, a random string consisting of
32 characters from 0-9 and a-f"
- }
- },
- "required": ["traceId"]
- }
- """;
-
-
- @Override
- public Flux<McpSchema.CallToolResult> apply(Map<String, Object> args) {
- return Flux.defer(() -> {
- try {
- String traceId = getStringParam(args, "traceId");
-
- if (traceId.isEmpty()) {
- log.warn("traceId is empty");
- }
-
- String result =
traceAnalysisService.analyzeTraceRoot(TraceQueryParam.builder().traceId(traceId).env("online").build());
-
- return createSuccessFlux(result);
- } catch (Exception e) {
- log.error("Failed to execute chaos operation", e);
- return Flux.just(new McpSchema.CallToolResult(
- List.of(new McpSchema.TextContent("Operation failed: " +
e.getMessage())), true));
- }
- });
- }
-
- private Flux<McpSchema.CallToolResult> createSuccessFlux(String result) {
- return Flux.just(
- new McpSchema.CallToolResult(List.of(new
McpSchema.TextContent(result)), false),
- new McpSchema.CallToolResult(List.of(new
McpSchema.TextContent("[DONE]")), false)
- );
- }
-
- private String getStringParam(Map<String, Object> params, String key) {
- Object value = params.get(key);
- return value != null ? value.toString() : "";
- }
-
- @Override
- public String getToolScheme() {
- return chaosToolSchema;
- }
-}
diff --git
a/ozhera-intelligence/ozhera-intelligence-service/src/main/java/org/apache/ozhera/intelligence/agents/tool/RootExceptionSpanTool.java
b/ozhera-intelligence/ozhera-intelligence-service/src/main/java/org/apache/ozhera/intelligence/agents/tool/RootExceptionSpanTool.java
deleted file mode 100644
index 2a8a23d8..00000000
---
a/ozhera-intelligence/ozhera-intelligence-service/src/main/java/org/apache/ozhera/intelligence/agents/tool/RootExceptionSpanTool.java
+++ /dev/null
@@ -1,324 +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.ozhera.intelligence.agents.tool;
-
-import com.google.gson.Gson;
-import com.google.gson.JsonObject;
-import lombok.extern.slf4j.Slf4j;
-import org.apache.commons.lang3.StringUtils;
-import org.apache.ozhera.intelligence.domain.rootanalysis.CodeFixInfo;
-import org.apache.ozhera.intelligence.domain.rootanalysis.TraceQueryParam;
-import org.apache.ozhera.intelligence.service.PromptService;
-import org.apache.ozhera.intelligence.service.TraceService;
-import org.apache.ozhera.trace.etl.domain.jaegeres.JaegerAttribute;
-import org.apache.ozhera.trace.etl.domain.jaegeres.JaegerLogs;
-import org.apache.ozhera.trace.etl.domain.tracequery.Span;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.beans.factory.annotation.Value;
-import org.springframework.stereotype.Component;
-import run.mone.hive.roles.ReactorRole;
-import run.mone.hive.roles.tool.ITool;
-
-import java.util.List;
-
-/**
- * Code fix tool for analyzing trace and extracting error information
- * <p>
- * This tool analyzes trace data to find the root cause of exceptions,
- * and extracts project ID, environment ID and stack trace information
- * for code fixing purposes.
- */
-@Slf4j
-@Component
-public class RootExceptionSpanTool implements ITool {
-
- @Value("${server.type}")
- private String env;
-
- /**
- * Tool name
- */
- public static final String name = "root_exception_span";
-
- /**
- * Trace service for querying spans
- */
- @Autowired
- private TraceService traceService;
-
- /**
- * Prompt service for AI analysis
- */
- @Autowired
- private PromptService promptService;
-
- @Override
- public String getName() {
- return name;
- }
-
- @Override
- public boolean needExecute() {
- return true;
- }
-
- @Override
- public boolean show() {
- return true;
- }
-
- @Override
- public String description() {
- return """
- 根据traceId智能分析trace链路,提取异常根因节点的相关信息用于代码修复。
- 该工具会自动识别异常节点,并提取项目ID、环境ID和异常堆栈信息。
-
- **使用场景:**
- - 分析异常trace链路,定位错误根因
- - 提取异常信息用于自动化代码修复
- - 获取项目和环境信息用于问题定位
-
- **分析能力:**
- - 自动识别链路中的异常节点
- - 提取异常堆栈信息
- - 解析项目ID和环境ID
- - 返回完整的代码修复所需信息
-
- **重要提示:**
- - traceId必须是32位由0-9和a-f组成的随机字符串
- - 确保traceId对应的链路数据已经采集完整
- """;
- }
-
- @Override
- public String parameters() {
- return """
- - traceId: (必填) 追踪ID,32位由0-9和a-f组成的随机字符串
- """;
- }
-
- @Override
- public String usage() {
- String taskProgress = """
- <task_progress>
- 任务进度记录(可选)
- </task_progress>
- """;
- if (!taskProgress()) {
- taskProgress = "";
- }
- return """
- <root_exception_span>
- <traceId>32位追踪ID</traceId>
- %s
- </root_exception_span>
- """.formatted(taskProgress);
- }
-
- @Override
- public String example() {
- return """
- 示例: 分析异常trace并提取修复信息
- <root_exception_span>
- <traceId>a1b2c3d4e5f6789012345678abcdef01</traceId>
- </root_exception_span>
- """;
- }
-
- @Override
- public JsonObject execute(ReactorRole role, JsonObject inputJson) {
- JsonObject result = new JsonObject();
-
- try {
- // Validate required parameter
- if (!inputJson.has("traceId") ||
StringUtils.isBlank(inputJson.get("traceId").getAsString())) {
- log.error("root_exception_span operation missing required
parameter traceId");
- result.addProperty("error", "缺少必填参数 'traceId'");
- return result;
- }
-
- // Get traceId parameter
- String traceId = inputJson.get("traceId").getAsString().trim();
-
- if (traceId.isEmpty()) {
- log.warn("traceId parameter is empty");
- result.addProperty("error", "参数错误:traceId不能为空");
- return result;
- }
-
- // Validate traceId format (32-character hexadecimal string)
- if (!isValidTraceId(traceId)) {
- log.warn("Invalid traceId format: {}", traceId);
- result.addProperty("error",
"参数错误:traceId必须是32位由0-9和a-f组成的字符串");
- return result;
- }
-
- log.info("Starting code fix analysis for traceId: {}", traceId);
-
- // Query spans from trace service
- TraceQueryParam queryParam = TraceQueryParam.builder()
- .traceId(traceId)
- .env(env)
- .build();
- List<Span> spans = traceService.queryTraceRootAnalysis(queryParam);
-
- if (spans == null || spans.isEmpty()) {
- log.warn("No spans found for traceId: {}", traceId);
- result.addProperty("error", "未找到对应的trace数据");
- return result;
- }
-
- // Convert spans to JSON for AI analysis
- Gson gson = new Gson();
- String spansJson = gson.toJson(spans);
-
- // Call AI to analyze and get root cause spanId
- String rootCauseSpanId = promptService.codeFixAnalysis(spansJson);
-
- if (StringUtils.isBlank(rootCauseSpanId)) {
- log.warn("Failed to identify root cause spanId for traceId:
{}", traceId);
- result.addProperty("error", "无法识别异常根因节点");
- return result;
- }
-
- log.info("Identified root cause spanId: {}", rootCauseSpanId);
-
- // Find the span with matching spanId
- Span rootCauseSpan = null;
- for (Span span : spans) {
- if (rootCauseSpanId.equals(span.getSpanID())) {
- rootCauseSpan = span;
- break;
- }
- }
-
- if (rootCauseSpan == null) {
- log.warn("Root cause span not found with spanId: {}",
rootCauseSpanId);
- result.addProperty("error", "未找到对应的根因节点");
- return result;
- }
-
- // Extract information from the root cause span
- CodeFixInfo codeFixInfo = extractCodeFixInfo(rootCauseSpan);
-
- if (codeFixInfo == null) {
- log.warn("Failed to extract code fix info from span");
- result.addProperty("error", "提取代码修复信息失败");
- return result;
- }
-
- // Set success response
- result.addProperty("success", true);
- result.addProperty("traceId", traceId);
- result.addProperty("spanId", rootCauseSpanId);
- result.addProperty("projectId", codeFixInfo.getProjectId());
- result.addProperty("envId", codeFixInfo.getEnvId());
- result.addProperty("endTime", codeFixInfo.getEndTime());
- result.addProperty("stacktrace", codeFixInfo.getStacktrace());
-
- log.info("Successfully analyzed code fix info for traceId: {}",
traceId);
-
- return result;
-
- } catch (Exception e) {
- log.error("Error executing root_exception_span operation", e);
- result.addProperty("error", "代码修复分析失败:" + e.getMessage());
- result.addProperty("success", false);
- return result;
- }
- }
-
- /**
- * Extract code fix information from a span
- *
- * @param span the root cause span
- * @return CodeFixInfo containing projectId, envId and stacktrace
- */
- private CodeFixInfo extractCodeFixInfo(Span span) {
- try {
- // Extract projectId from serviceName
- String serviceName = span.getProcess().getServiceName();
- String projectId = null;
- if (serviceName != null && serviceName.contains("-")) {
- projectId = serviceName.split("-")[0];
- }
-
- // Extract envId from process tags
- String envId = null;
- List<JaegerAttribute> tags = span.getProcess().getTags();
- if (tags != null) {
- for (JaegerAttribute tag : tags) {
- if ("service.env.id".equals(tag.getKey())) {
- envId = tag.getValue();
- break;
- }
- }
- }
-
- // Extract stacktrace from logs
- String stacktrace = null;
- List<JaegerLogs> logs = span.getLogs();
- if (logs != null) {
- for (JaegerLogs log : logs) {
- List<JaegerAttribute> fields = log.getFields();
- if (fields != null) {
- for (JaegerAttribute field : fields) {
- if ("exception.stacktrace".equals(field.getKey()))
{
- stacktrace = field.getValue();
- break;
- }
- }
- if (stacktrace != null) {
- break;
- }
- }
- }
- }
-
- // Calculate endTime from startTime + duration (convert from
microseconds to milliseconds)
- Long endTime = (span.getStartTime() + span.getDuration()) / 1000;
-
- return CodeFixInfo.builder()
- .projectId(projectId)
- .envId(envId)
- .stacktrace(stacktrace)
- .endTime(endTime)
- .build();
-
- } catch (Exception e) {
- log.error("Error extracting code fix info from span", e);
- return null;
- }
- }
-
- /**
- * Validate traceId format
- * TraceId should be a 32-character string composed of 0-9 and a-f
- *
- * @param traceId trace ID
- * @return whether the format is correct
- */
- private boolean isValidTraceId(String traceId) {
- if (traceId == null || traceId.length() != 32) {
- return false;
- }
- // Check if it only contains 0-9 and a-f characters
- return traceId.matches("[0-9a-fA-F]{32}");
- }
-}
diff --git
a/ozhera-intelligence/ozhera-intelligence-service/src/main/java/org/apache/ozhera/intelligence/agents/tool/TraceAnalysisTool.java
b/ozhera-intelligence/ozhera-intelligence-service/src/main/java/org/apache/ozhera/intelligence/agents/tool/TraceAnalysisTool.java
deleted file mode 100644
index ba2dbc1a..00000000
---
a/ozhera-intelligence/ozhera-intelligence-service/src/main/java/org/apache/ozhera/intelligence/agents/tool/TraceAnalysisTool.java
+++ /dev/null
@@ -1,217 +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.ozhera.intelligence.agents.tool;
-
-import com.google.gson.JsonObject;
-import lombok.extern.slf4j.Slf4j;
-import org.apache.commons.lang3.StringUtils;
-import org.apache.ozhera.intelligence.domain.rootanalysis.TraceQueryParam;
-import org.apache.ozhera.intelligence.service.TraceAnalysisService;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.beans.factory.annotation.Value;
-import org.springframework.stereotype.Component;
-import run.mone.hive.roles.ReactorRole;
-import run.mone.hive.roles.tool.ITool;
-
-/**
- * Trace链路分析工具
- * <p>
- * 该工具用于根据traceId分析trace链路上异常或慢查询出现的根本原因。
- * 通过AI智能分析,帮助开发人员快速定位问题根源,提高问题排查效率。
- * <p>
- * 使用场景:
- * - 分析异常trace链路,定位错误根因
- * - 分析慢查询trace链路,找出性能瓶颈
- * - 排查分布式系统中的链路问题
- * - 辅助故障诊断和性能优化
- */
-@Slf4j
-@Component
-public class TraceAnalysisTool implements ITool {
-
- @Value("${server.type}")
- private String env;
-
- /**
- * 工具名称
- */
- public static final String name = "trace_analysis";
-
- /**
- * Trace分析服务
- */
- @Autowired
- private TraceAnalysisService traceAnalysisService;
-
- @Override
- public String getName() {
- return name;
- }
-
- @Override
- public boolean needExecute() {
- return true;
- }
-
- @Override
- public boolean show() {
- return true;
- }
-
- @Override
- public String description() {
- return """
- 根据traceId智能分析trace链路上异常或慢查询出现的根本原因。该工具利用AI能力,
- 深度分析分布式链路追踪数据,帮助快速定位问题根源。
-
- **使用场景:**
- - 分析异常trace链路,快速定位错误根因
- - 分析慢查询trace链路,找出性能瓶颈所在
- - 排查分布式系统中的调用链路问题
- - 辅助开发人员进行故障诊断
- - 支持性能优化和系统调优
-
- **分析能力:**
- - 自动识别链路中的异常节点
- - 分析慢查询的原因和影响范围
- - 提供问题根因的详细说明
- - 给出优化建议和解决方案
-
- **重要提示:**
- - traceId必须是32位由0-9和a-f组成的随机字符串
- - 默认分析线上环境(online)的trace数据
- - 确保traceId对应的链路数据已经采集完整
- """;
- }
-
- @Override
- public String parameters() {
- return """
- - traceId: (必填) 追踪ID,32位由0-9和a-f组成的随机字符串
- """;
- }
-
- @Override
- public String usage() {
- String taskProgress = """
- <task_progress>
- 任务进度记录(可选)
- </task_progress>
- """;
- if (!taskProgress()) {
- taskProgress = "";
- }
- return """
- <trace_analysis>
- <traceId>32位追踪ID</traceId>
- %s
- </trace_analysis>
- """.formatted(taskProgress);
- }
-
- @Override
- public String example() {
- return """
- 示例 1: 分析异常trace链路
- <trace_analysis>
- <traceId>a1b2c3d4e5f6789012345678abcdef01</traceId>
- </trace_analysis>
-
- 示例 2: 分析慢查询trace链路
- <trace_analysis>
- <traceId>fedcba9876543210f6e5d4c3b2a10000</traceId>
- </trace_analysis>
-
- 示例 3: 排查分布式链路问题
- <trace_analysis>
- <traceId>123456789abcdef0123456789abcdef0</traceId>
- </trace_analysis>
- """;
- }
-
- @Override
- public JsonObject execute(ReactorRole role, JsonObject inputJson) {
- JsonObject result = new JsonObject();
-
- try {
- // 验证必填参数
- if (!inputJson.has("traceId") ||
StringUtils.isBlank(inputJson.get("traceId").getAsString())) {
- log.error("trace_analysis 操作缺少必填参数 traceId");
- result.addProperty("error", "缺少必填参数 'traceId'");
- return result;
- }
-
- // 获取traceId参数
- String traceId = inputJson.get("traceId").getAsString().trim();
-
- if (traceId.isEmpty()) {
- log.warn("traceId 参数为空");
- result.addProperty("error", "参数错误:traceId不能为空");
- return result;
- }
-
- // 验证traceId格式(32位16进制字符串)
- if (!isValidTraceId(traceId)) {
- log.warn("traceId 格式不正确: {}", traceId);
- result.addProperty("error",
"参数错误:traceId必须是32位由0-9和a-f组成的字符串");
- return result;
- }
-
- log.info("开始分析trace链路,traceId: {}", traceId);
-
- // 调用服务分析trace根本原因,默认环境为online
- TraceQueryParam queryParam = TraceQueryParam.builder()
- .traceId(traceId)
- .env(env)
- .build();
- String analysisResult =
traceAnalysisService.analyzeTraceRoot(queryParam);
-
- // 设置成功响应
- result.addProperty("result", analysisResult);
- result.addProperty("traceId", traceId);
- result.addProperty("environment", "online");
- result.addProperty("success", true);
-
- log.info("成功分析trace链路,traceId: {}", traceId);
-
- return result;
-
- } catch (Exception e) {
- log.error("执行 trace_analysis 操作时发生异常", e);
- result.addProperty("error", "分析trace链路失败:" + e.getMessage());
- result.addProperty("success", false);
- return result;
- }
- }
-
- /**
- * 验证traceId格式是否正确
- * traceId应该是32位由0-9和a-f组成的字符串
- *
- * @param traceId 追踪ID
- * @return 格式是否正确
- */
- private boolean isValidTraceId(String traceId) {
- if (traceId == null || traceId.length() != 32) {
- return false;
- }
- // 检查是否只包含0-9和a-f字符
- return traceId.matches("[0-9a-fA-F]{32}");
- }
-}
diff --git
a/ozhera-intelligence/ozhera-intelligence-service/src/main/java/org/apache/ozhera/intelligence/service/PromptService.java
b/ozhera-intelligence/ozhera-intelligence-service/src/main/java/org/apache/ozhera/intelligence/service/PromptService.java
deleted file mode 100644
index 8dc0c0af..00000000
---
a/ozhera-intelligence/ozhera-intelligence-service/src/main/java/org/apache/ozhera/intelligence/service/PromptService.java
+++ /dev/null
@@ -1,221 +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.ozhera.intelligence.service;
-
-import com.google.common.collect.ImmutableMap;
-import org.apache.ozhera.intelligence.domain.rootanalysis.LogPromptResult;
-import org.apache.ozhera.intelligence.domain.rootanalysis.MetricsPromptResult;
-import org.apache.ozhera.intelligence.domain.rootanalysis.TracePromptResult;
-import org.apache.ozhera.intelligence.domain.rootanalysis.constant.Prompts;
-import org.springframework.stereotype.Service;
-import run.mone.hive.common.AiTemplate;
-import run.mone.hive.common.MultiXmlParser;
-import run.mone.hive.common.ToolDataInfo;
-import run.mone.hive.configs.LLMConfig;
-import run.mone.hive.llm.CustomConfig;
-import run.mone.hive.llm.LLM;
-import run.mone.hive.llm.LLMProvider;
-
-import javax.annotation.PostConstruct;
-import java.util.List;
-import java.util.Map;
-
-@Service
-public class PromptService {
-
- private LLM llm;
-
- @PostConstruct
- private void init() {
- LLMProvider llmProvider =
LLMProvider.valueOf(System.getenv("LLM_PROVIDER"));
- LLMConfig config =
LLMConfig.builder().llmProvider(llmProvider).build();
- llm = new LLM(config);
- // 为 MIFY_GATEWAY 设置环境变量中的配置
- if (llmProvider == LLMProvider.MIFY_GATEWAY) {
- config.setUrl(System.getenv("MIFY_GATEWAY_URL"));
- config.setToken(System.getenv("MIFY_API_KEY"));
- CustomConfig customConfig = new CustomConfig();
- customConfig.setModel(System.getenv("MIFY_MODEL"));
- customConfig.addCustomHeader(CustomConfig.X_MODEL_PROVIDER_ID,
System.getenv("MIFY_MODEL_PROVIDER_ID"));
- config.setCustomConfig(customConfig);
- }
- }
-
- /**
- * Perform LLM analysis based on trace data
- *
- * @param trace trace link data
- * @return TracePromptResult analysis result
- */
- public TracePromptResult traceAnalysis(String trace) {
- // Get the corresponding prompt
- String prompt = Prompts.TRACE_ANALYSIS_PROMPT + "\n" + trace;
-
- // Call LLM for analysis
- String llmRes = llm.chat(prompt);
-
- // Parse the XML result returned by LLM
- List<ToolDataInfo> tools = new MultiXmlParser().parse(llmRes);
- if (tools == null || tools.isEmpty()) {
- // If parsing result is empty, return default error message
- return TracePromptResult.builder()
- .traceReason("Failed to parse LLM result")
- .root(false)
- .build();
- }
-
- Map<String, String> keyValuePairs = tools.get(0).getKeyValuePairs();
-
- // Build and return complete TracePromptResult object
- return TracePromptResult.builder()
-
.traceReason(keyValuePairs.get(TracePromptResult.TRACE_REASON_KEY))
- .application(keyValuePairs.get(TracePromptResult.APPLICATION))
-
.root(Boolean.parseBoolean(keyValuePairs.get(TracePromptResult.ROOT)))
- .projectId(keyValuePairs.get(TracePromptResult.PROJECT_ID))
- .envId(keyValuePairs.get(TracePromptResult.ENV_ID))
- .startTime(keyValuePairs.get(TracePromptResult.START_TIME))
- .duration(keyValuePairs.get(TracePromptResult.DURATION))
- .spanId(keyValuePairs.get(TracePromptResult.SPAN_ID))
- .ip(keyValuePairs.get(TracePromptResult.IP))
- .build();
- }
-
- /**
- * Perform LLM analysis based on log data
- *
- * @param log log data
- * @return LogPromptResult analysis result
- */
- public LogPromptResult logAnalysis(String log) {
- // Get the corresponding prompt
- String prompt = Prompts.LOG_ANALYSIS_PROMPT + "\n" + log;
-
- // Call LLM for analysis
- String llmRes = llm.chat(prompt);
-
- // Parse the XML result returned by LLM
- List<ToolDataInfo> tools = new MultiXmlParser().parse(llmRes);
- if (tools == null || tools.isEmpty()) {
- // If parsing result is empty, return default error message
- return LogPromptResult.builder()
- .logReason("Failed to parse LLM result")
- .root(false)
- .build();
- }
-
- Map<String, String> keyValuePairs = tools.get(0).getKeyValuePairs();
-
- // Build and return complete LogPromptResult object
- return LogPromptResult.builder()
- .logReason(keyValuePairs.get(LogPromptResult.LOG_REASON))
-
.root(Boolean.parseBoolean(keyValuePairs.get(LogPromptResult.ROOT)))
- .build();
- }
-
- /**
- * Perform LLM analysis based on metrics data
- *
- * @param metrics metrics data
- * @return MetricsPromptResult analysis result
- */
- public MetricsPromptResult metricsAnalysis(String metrics) {
- // Get the corresponding prompt, replace data placeholder
- String prompt = Prompts.METRICS_ANALYSIS_PROMPT + "\n" + metrics;
-
- // Call LLM for analysis
- String llmRes = llm.chat(prompt);
-
- // Parse the XML result returned by LLM
- List<ToolDataInfo> tools = new MultiXmlParser().parse(llmRes);
- if (tools == null || tools.isEmpty()) {
- // If parsing result is empty, return default error message
- return MetricsPromptResult.builder()
- .metricsReason("Failed to parse LLM result")
- .root(false)
- .build();
- }
-
- Map<String, String> keyValuePairs = tools.get(0).getKeyValuePairs();
-
- // Build and return complete MetricsPromptResult object
- return MetricsPromptResult.builder()
-
.metricsReason(keyValuePairs.get(MetricsPromptResult.METRICS_REASON))
-
.root(Boolean.parseBoolean(keyValuePairs.get(MetricsPromptResult.ROOT)))
- .build();
- }
-
- /**
- * Based on the analysis results of trace, log and metrics,
comprehensively derive clear and concise root cause analysis
- *
- * @param application application name
- * @param traceReason trace analysis result
- * @param logReason log analysis result
- * @param metricsReason metrics analysis result
- * @return String comprehensive analysis result
- */
- public String getSimpleReason(String application, String traceReason,
String logReason, String metricsReason) {
- // Build prompt, replace placeholders
- String prompt =
AiTemplate.renderTemplate(Prompts.RESULT_COLLECT_PROMPT,
- ImmutableMap.of("application", application != null ?
application : "",
- "traceReason", traceReason != null ? traceReason : "",
- "logReason", logReason != null ? logReason : "",
- "metricsReason", metricsReason != null ? metricsReason
: ""));
-
- // Call LLM for analysis
- String llmRes = llm.chat(prompt);
-
- // Parse the XML result returned by LLM
- List<ToolDataInfo> tools = new MultiXmlParser().parse(llmRes);
- if (tools == null || tools.isEmpty()) {
- // If parsing result is empty, return default error message
- return "Failed to parse LLM result";
- }
-
- Map<String, String> keyValuePairs = tools.get(0).getKeyValuePairs();
-
- // Return simplified analysis result
- return keyValuePairs.get("simpleReason");
- }
-
- /**
- * Perform LLM analysis based on trace data to find the root cause spanId
for code fix
- *
- * @param trace trace link data
- * @return spanId of the root cause node
- */
- public String codeFixAnalysis(String trace) {
- // Get the corresponding prompt
- String prompt = Prompts.CODE_FIX_ANALYSIS_PROMPT + "\n" + trace;
-
- // Call LLM for analysis
- String llmRes = llm.chat(prompt);
-
- // Parse the XML result returned by LLM
- List<ToolDataInfo> tools = new MultiXmlParser().parse(llmRes);
- if (tools == null || tools.isEmpty()) {
- // If parsing result is empty, return null
- return null;
- }
-
- Map<String, String> keyValuePairs = tools.get(0).getKeyValuePairs();
-
- // Return spanId
- return keyValuePairs.get("spanId");
- }
-}
diff --git
a/ozhera-intelligence/ozhera-intelligence-service/src/main/java/org/apache/ozhera/intelligence/service/RootAnalysisService.java
b/ozhera-intelligence/ozhera-intelligence-service/src/main/java/org/apache/ozhera/intelligence/service/RootAnalysisService.java
deleted file mode 100644
index ef09b1d2..00000000
---
a/ozhera-intelligence/ozhera-intelligence-service/src/main/java/org/apache/ozhera/intelligence/service/RootAnalysisService.java
+++ /dev/null
@@ -1,62 +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.ozhera.intelligence.service;
-
-import com.xiaomi.youpin.infra.rpc.Result;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.stereotype.Service;
-import org.apache.ozhera.intelligence.domain.rootanalysis.MarkDownParam;
-import org.apache.ozhera.intelligence.domain.rootanalysis.TraceQueryParam;
-
-@Service
-public class RootAnalysisService {
-
- @Autowired
- private TraceService traceService;
-
- @Autowired
- private LogService logService;
-
- @Autowired
- private MetricsService metricsService;
-
- @Autowired
- private PromptService promptService;
-
- /**
- * Root cause analysis based on trace, utilizing a large model to analyze
the fundamental causes of trace issues.
- * First, analyze based on trace data; if unsuccessful, analyze using logs.
- * If the issue still cannot be identified, then analyze using metrics.
- * Finally, aggregate the results and output them to the frontend.
- *
- * @return
- */
- public Result<String> traceRootAnalysis(TraceQueryParam param) {
- return null;
- }
-
- /**
- * Generate the results required by the frontend in markdown format based
on the summarized content.
- * @param param
- * @return
- */
- private String generateMarkDownResult(MarkDownParam param){
- return null;
- }
-}
diff --git
a/ozhera-intelligence/ozhera-intelligence-service/src/main/java/org/apache/ozhera/intelligence/service/TraceAnalysisService.java
b/ozhera-intelligence/ozhera-intelligence-service/src/main/java/org/apache/ozhera/intelligence/service/TraceAnalysisService.java
deleted file mode 100644
index 083d5aa5..00000000
---
a/ozhera-intelligence/ozhera-intelligence-service/src/main/java/org/apache/ozhera/intelligence/service/TraceAnalysisService.java
+++ /dev/null
@@ -1,265 +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.ozhera.intelligence.service;
-
-import com.google.gson.Gson;
-import lombok.extern.slf4j.Slf4j;
-import org.apache.ozhera.intelligence.domain.rootanalysis.*;
-import org.apache.ozhera.trace.etl.domain.tracequery.Span;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.beans.factory.annotation.Value;
-import org.springframework.stereotype.Service;
-
-import java.util.List;
-import java.util.Map;
-
-/**
- * Trace analysis service
- */
-@Slf4j
-@Service
-public class TraceAnalysisService {
-
- @Value("${hera.trace.url}")
- private String heraTraceUrl;
-
- @Autowired
- private TraceService traceService;
-
- @Autowired
- private PromptService promptService;
-
- @Autowired
- private LogService logService;
-
- @Autowired
- private MetricsService metricsService;
-
- private final Gson gson = new Gson();
-
- /**
- * Analyze trace root cause
- * Performs trace analysis based on input parameters. If trace analysis
doesn't find root cause,
- * continues with log analysis. If log analysis also doesn't find root
cause, performs metrics analysis,
- * and finally generates comprehensive analysis report.
- *
- * @param param trace query parameters
- * @return analysis result report
- */
- public String analyzeTraceRoot(TraceQueryParam param) {
- try {
- log.info("Starting trace root cause analysis, parameters: {}",
param);
-
- // Step 1: Query trace data
- List<Span> spans = traceService.queryTraceRootAnalysis(param);
- if (spans == null || spans.isEmpty()) {
- log.warn("No trace data found, traceId: {}",
param.getTraceId());
- return "No related trace data found";
- }
-
- // Step 2: Call trace analysis AI model
- String traceData = gson.toJson(spans);
- TracePromptResult traceResult =
promptService.traceAnalysis(traceData);
- log.info("Trace analysis result: {}", traceResult);
-
- // Check if trace analysis found root cause
- if (traceResult != null && traceResult.isRoot()) {
- log.info("Trace analysis found root cause, returning
comprehensive report directly");
- return generateFinalReport(traceResult, null, null, param);
- }
-
- // Step 3: Trace didn't find root cause, perform log analysis
- log.info("Trace analysis didn't find root cause, starting log
analysis");
-
- // Check if trace analysis result contains necessary information
for further analysis
- if (traceResult == null) {
- log.warn("Trace analysis result is empty, cannot continue with
log and metrics analysis");
- return "Trace analysis failed, unable to obtain necessary
analysis information";
- }
-
- LogParam logParam = buildLogParam(traceResult, param);
- List<Map<String, Object>> logs =
logService.queryLogRootAnalysis(logParam);
-
- LogPromptResult logResult = null;
- if (logs != null && !logs.isEmpty()) {
- String logData = gson.toJson(logs);
- logResult = promptService.logAnalysis(logData);
- log.info("Log analysis result: {}", logResult);
-
- // Check if log analysis found root cause
- if (logResult != null && logResult.isRoot()) {
- log.info("Log analysis found root cause, returning
comprehensive report directly");
- return generateFinalReport(traceResult, logResult, null,
param);
- }
- } else {
- log.warn("No related log data found");
- }
-
- // Step 4: Log analysis also didn't find root cause, perform
metrics analysis
- log.info("Log analysis didn't find root cause, starting metrics
analysis");
- MetricsQueryParam metricsParam = buildMetricsParam(traceResult,
param);
- HeraRootCaseAnalyseRes metricsData =
metricsService.queryMetricsRootAnalysis(metricsParam);
-
- MetricsPromptResult metricsResult = null;
- if (metricsData != null) {
- String metricsJsonData = gson.toJson(metricsData);
- metricsResult = promptService.metricsAnalysis(metricsJsonData);
- log.info("Metrics analysis result: {}", metricsResult);
- } else {
- log.warn("No related metrics data found");
- }
-
- // Step 5: Generate final comprehensive report
- return generateFinalReport(traceResult, logResult, metricsResult,
param);
-
- } catch (Exception e) {
- log.error("Exception occurred during trace root cause analysis",
e);
- return "Exception occurred during analysis: " + e.getMessage();
- }
- }
-
- /**
- * Generate final comprehensive analysis report
- *
- * @param traceResult trace analysis result
- * @param logResult log analysis result
- * @param metricsResult metrics analysis result
- * @param param original query parameters
- * @return comprehensive analysis report
- */
- private String generateFinalReport(TracePromptResult traceResult,
LogPromptResult logResult,
- MetricsPromptResult metricsResult,
TraceQueryParam param) {
- // Get simplified root cause analysis results
- String application = traceResult != null ?
traceResult.getApplication() : "";
- String traceReason = traceResult != null ?
traceResult.getTraceReason() : "";
- String projectId = traceResult != null ? traceResult.getProjectId() :
"";
- String envId = traceResult != null ? traceResult.getEnvId() : "";
- String logReason = logResult != null ? logResult.getLogReason() : "";
- String metricsReason = metricsResult != null ?
metricsResult.getMetricsReason() : "";
-
- String simpleReason = promptService.getSimpleReason(application,
traceReason, logReason, metricsReason);
-
- // Build MarkDownParam parameters
- MarkDownParam markDownParam = MarkDownParam.builder()
- .projectId(projectId)
- .envId(envId)
- .application(application)
- .traceReason(traceReason)
- .logReason(logReason)
- .metricsReason(metricsReason)
- .simpleReason(simpleReason)
- .traceId(param.getTraceId())
- .timestamp(param.getTimeStamp() != null ?
param.getTimeStamp().toString() : "")
- .build();
-
- // Generate and return comprehensive report
- return assembleReport(markDownParam);
- }
-
- /**
- * Build log query parameters based on trace analysis result
- *
- * @param traceResult trace analysis result
- * @param param original query parameters
- * @return log query parameters
- */
- private LogParam buildLogParam(TracePromptResult traceResult,
TraceQueryParam param) {
- return LogParam.builder()
- .application(traceResult.getApplication() != null ?
traceResult.getApplication() : "")
- .envId(traceResult.getEnvId() != null ? traceResult.getEnvId()
: "")
- .traceId(param.getTraceId())
- .startTime(traceResult.getStartTime() != null ?
traceResult.getStartTime() : "")
- .duration(traceResult.getDuration() != null ?
traceResult.getDuration() : "")
- .level("ERROR") // Default to query ERROR level logs
- .build();
- }
-
- /**
- * Build metrics query parameters based on trace analysis result
- *
- * @param traceResult trace analysis result
- * @param param original query parameters
- * @return metrics query parameters
- */
- private MetricsQueryParam buildMetricsParam(TracePromptResult traceResult,
TraceQueryParam param) {
- return MetricsQueryParam.builder()
- .application(traceResult.getApplication() != null ?
traceResult.getApplication() : "")
- .ip(traceResult.getIp() != null ? traceResult.getIp() : "")
- .startTime(traceResult.getStartTime() != null ?
traceResult.getStartTime() : "")
- .duration(traceResult.getDuration() != null ?
traceResult.getDuration() : "")
- .gap("300000") // Default 5-minute time interval
- .build();
- }
-
- /**
- * Execute root cause analysis and generate comprehensive report
- * Based on various analysis results input, generates formatted root cause
analysis report
- *
- * @param param parameter object containing various analysis results
- * @return formatted root cause analysis report
- */
- public String assembleReport(MarkDownParam param) {
- // Get various analysis results from parameter object, use default
values if empty
- String traceReason = param.getTraceReason() == null ? "" :
param.getTraceReason();
- String application = param.getApplication() == null ? "未获取到应用" :
param.getApplication();
- String logReason = param.getLogReason() == null ? "" :
param.getLogReason();
- String metricsReason = param.getMetricsReason() == null ? "" :
param.getMetricsReason();
- String simpleReason = param.getSimpleReason() == null ? "" :
param.getSimpleReason();
- String traceId = param.getTraceId() == null ? "" : param.getTraceId();
- String timestamp = param.getTimestamp() == null ? "" :
param.getTimestamp();
-
-
- // Build comprehensive analysis report
- StringBuilder sb = new StringBuilder();
- sb.append("根本原因在于应用:**")
- .append(application)
- .append("**,\n原因:**")
- .append(simpleReason)
- .append("**\n\n");
-
- sb.append("---\n")
- .append("通过链路追踪分析的详细原因:\n\n")
- .append(traceReason)
- .append("\n\n");
-
- sb.append("---\n")
- .append("通过日志分析的详细原因:\n\n")
- .append(logReason)
- .append("\n\n");
-
- sb.append("---\n")
- .append("通过指标监控分析的详细原因:\n\n")
- .append(metricsReason)
- .append("\n\n");
-
- // Build trace link
- sb.append("---\n")
- .append("链路追踪:<")
- .append(heraTraceUrl)
- .append(traceId);
- if (!"".equals(timestamp)) {
- sb.append("?startTime=").append(timestamp);
- sb.append("&endTime=").append(timestamp);
- }
- sb.append(">\n");
-
- // Return result
- return sb.toString();
- }
-}
\ No newline at end of file
diff --git
a/ozhera-intelligence/ozhera-intelligence-service/src/test/java/org/apache/ozhera/intelligence/service/PromptServiceTest.java
b/ozhera-intelligence/ozhera-intelligence-service/src/test/java/org/apache/ozhera/intelligence/service/PromptServiceTest.java
deleted file mode 100644
index 026360e3..00000000
---
a/ozhera-intelligence/ozhera-intelligence-service/src/test/java/org/apache/ozhera/intelligence/service/PromptServiceTest.java
+++ /dev/null
@@ -1,295 +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.ozhera.intelligence.service;
-
-import org.apache.ozhera.intelligence.domain.rootanalysis.LogPromptResult;
-import org.apache.ozhera.intelligence.domain.rootanalysis.MetricsPromptResult;
-import org.apache.ozhera.intelligence.domain.rootanalysis.TracePromptResult;
-import org.junit.jupiter.api.BeforeEach;
-import org.junit.jupiter.api.Test;
-import run.mone.hive.configs.LLMConfig;
-import run.mone.hive.llm.LLM;
-import run.mone.hive.llm.LLMProvider;
-
-import java.lang.reflect.Field;
-
-import static org.junit.jupiter.api.Assertions.*;
-
-/**
- * PromptService unit test class
- * Test the basic functionality of various methods in PromptService
- */
-class PromptServiceTest {
-
- private PromptService promptService;
-
- /**
- * Preparation before testing
- * Initialize PromptService instance
- */
- @BeforeEach
- void setUp() throws Exception {
- promptService = new PromptService();
-
- // Create a test LLM instance (using environment variables or default
values)
- String llmProvider = System.getenv("LLM_PROVIDER");
-
- try {
- LLM llm = new
LLM(LLMConfig.builder().llmProvider(LLMProvider.valueOf(llmProvider)).build());
-
- // Set private field llm through reflection
- Field llmField = PromptService.class.getDeclaredField("llm");
- llmField.setAccessible(true);
- llmField.set(promptService, llm);
- } catch (Exception e) {
- // If LLM initialization fails, skip the test or use empty
implementation
- System.out.println("Warning: LLM initialization failed, tests may
not run properly: " + e.getMessage());
- }
- }
-
- /**
- * Test traceAnalysis method
- * Verify that the method can be called normally and returns
TracePromptResult object
- */
- @Test
- void testTraceAnalysis() {
- // Prepare test data
- String traceData = """
- {
- "traceID": "test-trace-123",
- "spans": [
- {
- "spanID": "span-001",
- "operationName": "database-query",
- "duration": 5000,
- "tags": [
- {"key": "error", "value": "true"},
- {"key": "db.statement", "value": "SELECT * FROM users"}
- ]
- }
- ]
- }
- """;
-
- try {
- // Execute test method
- TracePromptResult result = promptService.traceAnalysis(traceData);
-
- // Verify result is not null
- assertNotNull(result, "TracePromptResult should not be null");
-
- // Verify basic fields exist
- assertNotNull(result.getTraceReason(), "traceReason field should
not be null");
-
- // Print results for debugging
- System.out.println("Trace analysis result:");
- System.out.println("- Reason: " + result.getTraceReason());
- System.out.println("- Application: " + result.getApplication());
- System.out.println("- Is root: " + result.isRoot());
- System.out.println("- Environment ID: " + result.getEnvId());
-
- } catch (Exception e) {
- // If call fails, at least verify the method exists and is callable
- System.out.println("Trace analysis call exception (possibly LLM
configuration issue): " + e.getMessage());
- assertNotNull(promptService, "PromptService instance should
exist");
- }
- }
-
- /**
- * Test logAnalysis method
- * Verify that the method can be called normally and returns
LogPromptResult object
- */
- @Test
- void testLogAnalysis() {
- // Prepare test data
- String logData = """
- [
- {
- "timestamp": "2024-01-01 10:00:00",
- "level": "ERROR",
- "message": "java.lang.NullPointerException at
com.example.service.UserService.getUserById(UserService.java:45)",
- "thread": "http-nio-8080-exec-1"
- },
- {
- "timestamp": "2024-01-01 10:00:01",
- "level": "ERROR",
- "message": "Failed to process user request",
- "thread": "http-nio-8080-exec-1"
- }
- ]
- """;
-
- try {
- // Execute test method
- LogPromptResult result = promptService.logAnalysis(logData);
-
- // Verify result is not null
- assertNotNull(result, "LogPromptResult should not be null");
-
- // Verify basic fields exist
- assertNotNull(result.getLogReason(), "logReason field should not
be null");
-
- // Print results for debugging
- System.out.println("Log analysis result:");
- System.out.println("- Reason: " + result.getLogReason());
- System.out.println("- Is root: " + result.isRoot());
-
- } catch (Exception e) {
- // If call fails, at least verify the method exists and is callable
- System.out.println("Log analysis call exception (possibly LLM
configuration issue): " + e.getMessage());
- assertNotNull(promptService, "PromptService instance should
exist");
- }
- }
-
- /**
- * Test metricsAnalysis method
- * Verify that the method can be called normally and returns
MetricsPromptResult object
- */
- @Test
- void testMetricsAnalysis() {
- // Prepare test data
- String metricsData = """
- {
- "maxCpuUsage": 95.5,
- "maxLoad": 8.2,
- "maxJvmHeapUsage": 85.0,
- "maxSTWCost": 250,
- "STWCountOf1m": 12,
- "cpuCount": 4
- }
- """;
-
- try {
- // Execute test method
- MetricsPromptResult result =
promptService.metricsAnalysis(metricsData);
-
- // Verify result is not null
- assertNotNull(result, "MetricsPromptResult should not be null");
-
- // Verify basic fields exist
- assertNotNull(result.getMetricsReason(), "metricsReason field
should not be null");
-
- // Print results for debugging
- System.out.println("Metrics analysis result:");
- System.out.println("- Reason: " + result.getMetricsReason());
- System.out.println("- Is root: " + result.isRoot());
-
- } catch (Exception e) {
- // If call fails, at least verify the method exists and is callable
- System.out.println("Metrics analysis call exception (possibly LLM
configuration issue): " + e.getMessage());
- assertNotNull(promptService, "PromptService instance should
exist");
- }
- }
-
- /**
- * Test getSimpleReason method
- * Verify that the method can be called normally and returns comprehensive
analysis result
- */
- @Test
- void testGetSimpleReason() {
- // Prepare test data
- String application = "UserService";
- String traceReason = "Database query timeout, took 5 seconds";
- String logReason = "NullPointerException occurred at line 45 in
UserService.getUserById method";
- String metricsReason = "CPU usage reached 95.5%, system load is too
high";
-
- try {
- // Execute test method
- String result = promptService.getSimpleReason(application,
traceReason, logReason, metricsReason);
-
- // Verify result is not null
- assertNotNull(result, "Comprehensive analysis result should not be
null");
- assertFalse(result.trim().isEmpty(), "Comprehensive analysis
result should not be empty string");
-
- // Print results for debugging
- System.out.println("Comprehensive analysis result:");
- System.out.println(result);
-
- } catch (Exception e) {
- // If call fails, at least verify the method exists and is callable
- System.out.println("Comprehensive analysis call exception
(possibly LLM configuration issue): " + e.getMessage());
- assertNotNull(promptService, "PromptService instance should
exist");
- }
- }
-
- /**
- * Test handling of empty input parameters
- * Verify the method's ability to handle empty inputs
- */
- @Test
- void testWithEmptyInputs() {
- try {
- // Test empty string input
- TracePromptResult traceResult = promptService.traceAnalysis("");
- assertNotNull(traceResult, "Trace analysis result with empty input
should not be null");
-
- LogPromptResult logResult = promptService.logAnalysis("");
- assertNotNull(logResult, "Log analysis result with empty input
should not be null");
-
- MetricsPromptResult metricsResult =
promptService.metricsAnalysis("");
- assertNotNull(metricsResult, "Metrics analysis result with empty
input should not be null");
-
- // Test comprehensive analysis with null parameters
- String simpleReason = promptService.getSimpleReason(null, null,
null, null);
- assertNotNull(simpleReason, "Comprehensive analysis result with
null parameters should not be null");
-
- System.out.println("Empty input test passed");
-
- } catch (Exception e) {
- System.out.println("Empty input test exception: " +
e.getMessage());
- // Even if exception occurs, ensure service instance exists
- assertNotNull(promptService, "PromptService instance should
exist");
- }
- }
-
- /**
- * Test method parameter validation
- * Verify basic parameter handling of each method
- */
- @Test
- void testMethodParameterHandling() {
- // Verify PromptService instance exists
- assertNotNull(promptService, "PromptService instance should not be
null");
-
- // Verify methods exist (check through reflection)
- try {
- // Check traceAnalysis method
- assertNotNull(promptService.getClass().getMethod("traceAnalysis",
String.class),
- "traceAnalysis method should exist");
-
- // Check logAnalysis method
- assertNotNull(promptService.getClass().getMethod("logAnalysis",
String.class),
- "logAnalysis method should exist");
-
- // Check metricsAnalysis method
-
assertNotNull(promptService.getClass().getMethod("metricsAnalysis",
String.class),
- "metricsAnalysis method should exist");
-
- // Check getSimpleReason method
-
assertNotNull(promptService.getClass().getMethod("getSimpleReason",
- String.class, String.class, String.class, String.class),
- "getSimpleReason method should exist");
-
- System.out.println("All method signature validation passed");
-
- } catch (NoSuchMethodException e) {
- fail("Method signature validation failed: " + e.getMessage());
- }
- }
-}
\ No newline at end of file
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]