fix: include configured goal success-check guidance

This commit is contained in:
mateaix 2026-09-13 22:47:29 +08:00
parent 0c045eaff8
commit 96ed9cda27
3 changed files with 51 additions and 1 deletions

View File

@ -60,7 +60,7 @@ public class GoalEntity {
@TableField(value = "exit_criteria", updateStrategy = FieldStrategy.ALWAYS)
private String exitCriteria;
/** Optional per-goal evaluator prompt override; nullable -> default. */
/** Optional evaluation guidance; does not replace platform evidence/output rules. */
@TableField(value = "success_check_prompt", updateStrategy = FieldStrategy.ALWAYS)
private String successCheckPrompt;

View File

@ -70,6 +70,7 @@ public class GoalEvaluationService implements Evaluator {
private static final int MAX_OUTPUT_TOKENS = 2000;
private static final int MAX_CONVERSATION_CHARS = 6_000;
private static final int MAX_TERMINAL_ANSWER_CHARS = 4_000;
private static final int MAX_SUCCESS_CHECK_CHARS = 4_000;
private static final int MIN_BOOTSTRAP_CRITERIA = 1;
private static final int MAX_BOOTSTRAP_CRITERIA = 8;
/** Skip-retry template — the goal node has its own try/catch. */
@ -250,6 +251,16 @@ public class GoalEvaluationService implements Evaluator {
}
sb.append('\n');
String guidance = goal.getSuccessCheckPrompt();
if (guidance != null && !guidance.isBlank()) {
sb.append("Goal-specific success-check guidance (within the checklist evidence and JSON output rules):\n");
sb.append(guidance, 0, Math.min(guidance.length(), MAX_SUCCESS_CHECK_CHARS));
if (guidance.length() > MAX_SUCCESS_CHECK_CHARS) {
sb.append("\n[success-check guidance truncated]");
}
sb.append("\n\n");
}
if (!bootstrap) {
sb.append("Current checklist (judge each by id):\n");
for (GoalCriterion c : existing) {

View File

@ -121,6 +121,45 @@ class GoalEvaluationServiceTest {
assertEquals(7L, svc.evaluate(goal, List.of(), "answer").evaluationRevision());
}
@Test
void customSuccessGuidanceReachesBootstrapAndVerdictPrompts() {
stubChatResponse("{\"criteria\":[{\"id\":\"C1\",\"text\":\"appendix\",\"passed\":false,\"evidence\":\"\"}]}");
GoalEntity bootstrap = goal(); bootstrap.setSuccessCheckPrompt("Require an appendix with sources.");
svc.evaluate(bootstrap, List.of(), "answer");
stubChatResponse("{\"criterionVerdicts\":[],\"summary\":\"pending\"}");
GoalEntity verdict = goalWithCriteria(); verdict.setSuccessCheckPrompt("Require an appendix with sources.");
svc.evaluate(verdict, List.of(), "answer");
ArgumentCaptor<Prompt> prompts = ArgumentCaptor.forClass(Prompt.class);
org.mockito.Mockito.verify(chatModel, org.mockito.Mockito.times(2)).call(prompts.capture());
for (Prompt prompt : prompts.getAllValues()) {
assertTrue(prompt.getContents().contains("Require an appendix with sources."));
assertTrue(prompt.getInstructions().getFirst() instanceof org.springframework.ai.chat.messages.SystemMessage);
assertFalse(prompt.getInstructions().getFirst().getText().contains("Require an appendix with sources."));
}
}
@Test
void customSuccessGuidanceIsBoundedAndTruncationIsVisible() {
stubChatResponse("{\"criterionVerdicts\":[],\"summary\":\"pending\"}");
GoalEntity goal = goalWithCriteria(); goal.setSuccessCheckPrompt("x".repeat(4000) + "omitted-tail-marker");
svc.evaluate(goal, List.of(), "answer");
ArgumentCaptor<Prompt> prompt = ArgumentCaptor.forClass(Prompt.class);
verify(chatModel).call(prompt.capture());
assertTrue(prompt.getValue().getContents().contains("x".repeat(4000)));
assertFalse(prompt.getValue().getContents().contains("omitted-tail-marker"));
assertTrue(prompt.getValue().getContents().contains("[success-check guidance truncated]"));
}
@Test
void blankSuccessGuidanceDoesNotAddAnEmptyPromptSection() {
stubChatResponse("{\"criterionVerdicts\":[],\"summary\":\"pending\"}");
GoalEntity goal = goalWithCriteria(); goal.setSuccessCheckPrompt(" \n\t ");
svc.evaluate(goal, List.of(), "answer");
ArgumentCaptor<Prompt> prompt = ArgumentCaptor.forClass(Prompt.class);
verify(chatModel).call(prompt.capture());
assertFalse(prompt.getValue().getContents().contains("Goal-specific success-check guidance"));
}
// ==================== Pre-flight guards ====================
@Test