feat(agent): add executor evidence v2 contract

This commit is contained in:
aruo
2026-07-08 01:37:15 +08:00
parent a6afbfaa9d
commit 050cbc8fee
21 changed files with 2707 additions and 20 deletions
@@ -444,8 +444,12 @@ public class ChatService {
}
if ("PASS".equals(finalDecision.verdict())) {
answer = extractUserFacingAnswer(answer)
.orElse(answer == null || answer.isBlank() ? "抱歉,多 Agent 分析未能生成有效结论。" : answer);
String executorAnswer = answer;
answer = extractUserFacingAnswer(executorAnswer)
.or(() -> renderStructuredExecutorAnswer(executorAnswer))
.orElse(executorAnswer == null || executorAnswer.isBlank()
? "抱歉,多 Agent 分析未能生成有效结论。"
: executorAnswer);
persistVerifierEvaluation(session, finalDecision, round);
break;
}
@@ -816,6 +820,98 @@ public class ChatService {
return Optional.empty();
}
private Optional<String> renderStructuredExecutorAnswer(String executorAnswer) {
if (executorAnswer == null || executorAnswer.isBlank()) {
return Optional.empty();
}
try {
JsonNode root = objectMapper.readTree(sanitizeJsonPayload(executorAnswer));
if (!"executor_evidence_v2".equals(root.path("answer_version").asText(""))) {
return Optional.empty();
}
StringBuilder output = new StringBuilder();
appendTextArraySection(output, "已确认信息", root.path("claims"), "claim_text", "暂无可稳定确认的信息");
appendHypothesesSection(output, root.path("hypotheses"));
appendStringArraySection(output, "当前缺口", root.path("missing_info"), "当前缺少足够的直接证据支撑完整结论");
appendRecommendedActionsSection(output, root.path("recommended_actions"));
String rendered = output.toString().trim();
return rendered.isBlank() ? Optional.empty() : Optional.of(rendered);
} catch (Exception e) {
logger.debug("Failed to render executor_evidence_v2 answer", e);
return Optional.empty();
}
}
private void appendTextArraySection(StringBuilder output, String title, JsonNode items,
String fieldName, String emptyText) {
output.append(title).append(":");
if (!items.isArray() || items.isEmpty()) {
output.append("\n- ").append(emptyText);
return;
}
for (JsonNode item : items) {
String text = item.path(fieldName).asText("");
if (!text.isBlank()) {
output.append("\n- ").append(text);
}
}
if (output.charAt(output.length() - 1) == ':') {
output.append("\n- ").append(emptyText);
}
}
private void appendHypothesesSection(StringBuilder output, JsonNode hypotheses) {
if (!hypotheses.isArray() || hypotheses.isEmpty()) {
return;
}
output.append("\n\n可能方向:");
for (JsonNode hypothesis : hypotheses) {
String text = hypothesis.path("hypothesis_text").asText("");
if (text.isBlank()) {
continue;
}
String basis = hypothesis.path("basis").asText("");
output.append("\n- ").append(text);
if (!basis.isBlank()) {
output.append("(").append(basis).append(")");
}
}
}
private void appendStringArraySection(StringBuilder output, String title, JsonNode items, String emptyText) {
output.append("\n\n").append(title).append(":");
if (!items.isArray() || items.isEmpty()) {
output.append("\n- ").append(emptyText);
return;
}
for (JsonNode item : items) {
String text = item.asText("");
if (!text.isBlank()) {
output.append("\n- ").append(text);
}
}
}
private void appendRecommendedActionsSection(StringBuilder output, JsonNode actions) {
output.append("\n\n建议下一步:");
if (!actions.isArray() || actions.isEmpty()) {
output.append("\n- 围绕上述证据缺口补充只读查询,再由人工复核最终结论");
return;
}
for (JsonNode action : actions) {
String text = action.path("action_text").asText("");
if (text.isBlank()) {
continue;
}
String reason = action.path("reason").asText("");
output.append("\n- ").append(text);
if (!reason.isBlank()) {
output.append(":").append(reason);
}
}
}
private String buildDegradedOutput(VerifierDecision decision) {
StringBuilder output = new StringBuilder(DEGRADED_PREFIX);