diff --git a/data-agent-management/src/main/java/com/alibaba/cloud/ai/dataagent/workflow/node/SemanticConsistencyNode.java b/data-agent-management/src/main/java/com/alibaba/cloud/ai/dataagent/workflow/node/SemanticConsistencyNode.java index b1acf895c..f84a0bf91 100644 --- a/data-agent-management/src/main/java/com/alibaba/cloud/ai/dataagent/workflow/node/SemanticConsistencyNode.java +++ b/data-agent-management/src/main/java/com/alibaba/cloud/ai/dataagent/workflow/node/SemanticConsistencyNode.java @@ -29,6 +29,7 @@ import com.alibaba.cloud.ai.graph.streaming.StreamingOutput; import lombok.AllArgsConstructor; import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.StringUtils; import org.springframework.ai.chat.model.ChatResponse; import org.springframework.ai.converter.BeanOutputConverter; import org.springframework.stereotype.Component; @@ -109,6 +110,11 @@ private Map buildStructuralValidationFailure(OverAllState state, /** * Build validation result + * + *

+ * The reason is rendered into the {@code sql-error-fixer} prompt on the retry path, so a + * null reason would leave the model with an empty "错误信息" section. Fall back to a + * generic notice when the model reports a failure without explaining it. */ private Map buildValidationResult(boolean passed, String validationResult) { if (passed) { @@ -116,7 +122,8 @@ private Map buildValidationResult(boolean passed, String validat } else { return Map.of(SEMANTIC_CONSISTENCY_NODE_OUTPUT, false, SQL_REGENERATE_REASON, - SqlRetryDto.semantic(validationResult)); + SqlRetryDto.semantic(StringUtils.isNotBlank(validationResult) ? validationResult + : "语义一致性校验未通过,模型未给出具体原因")); } } diff --git a/data-agent-management/src/test/java/com/alibaba/cloud/ai/dataagent/workflow/node/SemanticConsistencyNodeTest.java b/data-agent-management/src/test/java/com/alibaba/cloud/ai/dataagent/workflow/node/SemanticConsistencyNodeTest.java index 21611dfc9..8d3af5ad3 100644 --- a/data-agent-management/src/test/java/com/alibaba/cloud/ai/dataagent/workflow/node/SemanticConsistencyNodeTest.java +++ b/data-agent-management/src/test/java/com/alibaba/cloud/ai/dataagent/workflow/node/SemanticConsistencyNodeTest.java @@ -125,6 +125,24 @@ void apply_invalidSql_returnsGeneratorWithFailOutput() throws Exception { assertEquals(new SqlRetryDto("表不存在", true, false), retry); } + @Test + void apply_failedWithoutReason_doesNotStoreNullReason() throws Exception { + OverAllState state = createTestState(); + setupBasicState(state, "SELECT * FROM nonexistent"); + + // LLM may omit "reason" entirely when it only reports the verdict + when(nl2SqlService.performSemanticConsistency(any(SemanticConsistencyDTO.class))) + .thenReturn(Flux.just(ChatResponseUtil.createPureResponse("{\"passed\":false}"))); + + NodeExecution execution = execute(semanticConsistencyNode.apply(state), SEMANTIC_CONSISTENCY_NODE_OUTPUT); + + assertEquals(false, execution.finalResult().get(SEMANTIC_CONSISTENCY_NODE_OUTPUT)); + SqlRetryDto retry = (SqlRetryDto) execution.finalResult().get(SQL_REGENERATE_REASON); + assertNotNull(retry, "retry reason must still be produced so the workflow loops back"); + assertTrue(retry.semanticFail()); + assertNotNull(retry.reason(), "reason must not be null; it is rendered into the repair prompt"); + } + @Test void apply_missingEvidence_throwsException() { OverAllState state = createTestState();