Skip to content

Commit da1a383

Browse files
committed
fix(semantic-consistency): keep a usable reason when the model omits one
SemanticConsistencyNode#buildValidationResult passed the model's reason straight into SqlRetryDto.semantic(). A response of {"passed":false} without a "reason" field therefore stored a null reason, which then reaches SqlGenerationDTO.exceptionMessage and is rendered into the {error_message} section of the sql-error-fixer prompt — leaving the repair model with an empty "错误信息" block and nothing to act on. Fall back to a generic notice when the reason is blank so the retry prompt always carries an explanation. Behaviour is unchanged whenever the model supplies a reason. Add a regression test asserting the stored reason is never null while the failure verdict and retry routing are preserved.
1 parent 27e7baa commit da1a383

2 files changed

Lines changed: 26 additions & 1 deletion

File tree

‎data-agent-management/src/main/java/com/alibaba/cloud/ai/dataagent/workflow/node/SemanticConsistencyNode.java‎

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
import com.alibaba.cloud.ai.graph.streaming.StreamingOutput;
3030
import lombok.AllArgsConstructor;
3131
import lombok.extern.slf4j.Slf4j;
32+
import org.apache.commons.lang3.StringUtils;
3233
import org.springframework.ai.chat.model.ChatResponse;
3334
import org.springframework.ai.converter.BeanOutputConverter;
3435
import org.springframework.stereotype.Component;
@@ -109,14 +110,20 @@ private Map<String, Object> buildStructuralValidationFailure(OverAllState state,
109110

110111
/**
111112
* Build validation result
113+
*
114+
* <p>
115+
* The reason is rendered into the {@code sql-error-fixer} prompt on the retry path, so a
116+
* null reason would leave the model with an empty "错误信息" section. Fall back to a
117+
* generic notice when the model reports a failure without explaining it.
112118
*/
113119
private Map<String, Object> buildValidationResult(boolean passed, String validationResult) {
114120
if (passed) {
115121
return Map.of(SEMANTIC_CONSISTENCY_NODE_OUTPUT, true);
116122
}
117123
else {
118124
return Map.of(SEMANTIC_CONSISTENCY_NODE_OUTPUT, false, SQL_REGENERATE_REASON,
119-
SqlRetryDto.semantic(validationResult));
125+
SqlRetryDto.semantic(StringUtils.isNotBlank(validationResult) ? validationResult
126+
: "语义一致性校验未通过,模型未给出具体原因"));
120127
}
121128
}
122129

‎data-agent-management/src/test/java/com/alibaba/cloud/ai/dataagent/workflow/node/SemanticConsistencyNodeTest.java‎

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,24 @@ void apply_invalidSql_returnsGeneratorWithFailOutput() throws Exception {
125125
assertEquals(new SqlRetryDto("表不存在", true, false), retry);
126126
}
127127

128+
@Test
129+
void apply_failedWithoutReason_doesNotStoreNullReason() throws Exception {
130+
OverAllState state = createTestState();
131+
setupBasicState(state, "SELECT * FROM nonexistent");
132+
133+
// LLM may omit "reason" entirely when it only reports the verdict
134+
when(nl2SqlService.performSemanticConsistency(any(SemanticConsistencyDTO.class)))
135+
.thenReturn(Flux.just(ChatResponseUtil.createPureResponse("{\"passed\":false}")));
136+
137+
NodeExecution execution = execute(semanticConsistencyNode.apply(state), SEMANTIC_CONSISTENCY_NODE_OUTPUT);
138+
139+
assertEquals(false, execution.finalResult().get(SEMANTIC_CONSISTENCY_NODE_OUTPUT));
140+
SqlRetryDto retry = (SqlRetryDto) execution.finalResult().get(SQL_REGENERATE_REASON);
141+
assertNotNull(retry, "retry reason must still be produced so the workflow loops back");
142+
assertTrue(retry.semanticFail());
143+
assertNotNull(retry.reason(), "reason must not be null; it is rendered into the repair prompt");
144+
}
145+
128146
@Test
129147
void apply_missingEvidence_throwsException() {
130148
OverAllState state = createTestState();

0 commit comments

Comments
 (0)