From 96ed9cda27d1a6a216a87d336e17a912825282aa Mon Sep 17 00:00:00 2001 From: mateaix <7333791@qq.com> Date: Sun, 13 Sep 2026 22:47:29 +0800 Subject: [PATCH] fix: include configured goal success-check guidance --- .../java/vip/mate/goal/model/GoalEntity.java | 2 +- .../goal/service/GoalEvaluationService.java | 11 ++++++ .../service/GoalEvaluationServiceTest.java | 39 +++++++++++++++++++ 3 files changed, 51 insertions(+), 1 deletion(-) diff --git a/mateclaw-server/src/main/java/vip/mate/goal/model/GoalEntity.java b/mateclaw-server/src/main/java/vip/mate/goal/model/GoalEntity.java index 07bec384..7428428b 100644 --- a/mateclaw-server/src/main/java/vip/mate/goal/model/GoalEntity.java +++ b/mateclaw-server/src/main/java/vip/mate/goal/model/GoalEntity.java @@ -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; diff --git a/mateclaw-server/src/main/java/vip/mate/goal/service/GoalEvaluationService.java b/mateclaw-server/src/main/java/vip/mate/goal/service/GoalEvaluationService.java index 5c8061e2..224365bb 100644 --- a/mateclaw-server/src/main/java/vip/mate/goal/service/GoalEvaluationService.java +++ b/mateclaw-server/src/main/java/vip/mate/goal/service/GoalEvaluationService.java @@ -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) { diff --git a/mateclaw-server/src/test/java/vip/mate/goal/service/GoalEvaluationServiceTest.java b/mateclaw-server/src/test/java/vip/mate/goal/service/GoalEvaluationServiceTest.java index 81297355..e840f4bd 100644 --- a/mateclaw-server/src/test/java/vip/mate/goal/service/GoalEvaluationServiceTest.java +++ b/mateclaw-server/src/test/java/vip/mate/goal/service/GoalEvaluationServiceTest.java @@ -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 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 = 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 = ArgumentCaptor.forClass(Prompt.class); + verify(chatModel).call(prompt.capture()); + assertFalse(prompt.getValue().getContents().contains("Goal-specific success-check guidance")); + } + // ==================== Pre-flight guards ==================== @Test