feat: add rag evidence postprocess blocks

This commit is contained in:
aruo
2026-07-05 03:03:18 +08:00
parent 4a94c14feb
commit 5197712719
12 changed files with 497 additions and 1 deletions
@@ -2,6 +2,7 @@ package com.superbiz.agent.service;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.superbiz.agent.domain.entity.ToolInvocation;
import com.superbiz.agent.dto.EvidenceBlock;
import com.superbiz.agent.repository.ToolInvocationRepository;
import com.superbiz.agent.util.SessionContextHolder;
import org.junit.jupiter.api.Test;
@@ -78,6 +79,14 @@ class ToolInvocationRecorderTest {
.l0MatchedKeywords(List.of("ERR_TIMEOUT"))
.l0Domains(List.of("payment"))
.l0Entities(List.of("ERR_TIMEOUT"))
.evidenceCandidateCount(2)
.evidenceBlockCount(1)
.evidenceBlocks(List.of(Map.of(
"source", "payment/errors.md",
"title", "payment/errors.md",
"retrieval_layer", "L0+L1",
"hit_reasons", List.of("l0_keywords:ERR_TIMEOUT", "semantic_rank:1")
)))
.build();
try {
@@ -98,5 +107,45 @@ class ToolInvocationRecorderTest {
assertTrue(saved.getRetrievalDetails().contains("\"l0_matched_keywords\":[\"ERR_TIMEOUT\"]"));
assertTrue(saved.getRetrievalDetails().contains("\"l0_domains\":[\"payment\"]"));
assertTrue(saved.getRetrievalDetails().contains("\"l0_entities\":[\"ERR_TIMEOUT\"]"));
assertTrue(saved.getRetrievalDetails().contains("\"evidence_candidate_count\":2"));
assertTrue(saved.getRetrievalDetails().contains("\"evidence_block_count\":1"));
assertTrue(saved.getRetrievalDetails().contains("\"evidence_blocks\""));
}
@Test
void lookupKnowledgeRecordFromSummarizesEvidenceBlocks() {
EvidenceBlock block = EvidenceBlock.builder()
.source("doc.md")
.title("Doc")
.breadcrumb("A > B")
.retrievalLayer("L1")
.score(0.42)
.hitReasons(List.of("semantic_rank:1"))
.content("x".repeat(220))
.build();
com.superbiz.agent.dto.LookupResult result = com.superbiz.agent.dto.LookupResult.builder()
.found(true)
.evidenceCandidateCount(3)
.evidenceBlockCount(1)
.evidenceBlocks(List.of(block))
.build();
ToolInvocationRecorder.LookupKnowledgeRecord record = ToolInvocationRecorder.LookupKnowledgeRecord.from(
"query",
KnowledgeIndexService.L0Hint.empty(),
List.of(),
false,
result,
null,
null,
10,
-1
);
assertEquals(3, record.evidenceCandidateCount());
assertEquals(1, record.evidenceBlockCount());
assertEquals(1, record.evidenceBlocks().size());
assertTrue(String.valueOf(record.evidenceBlocks().get(0).get("content_preview")).endsWith("..."));
}
}
@@ -14,6 +14,7 @@ import org.mockito.MockitoAnnotations;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import static org.junit.jupiter.api.Assertions.*;
import static org.mockito.ArgumentMatchers.*;
@@ -83,6 +84,12 @@ class LookupKnowledgeToolTest {
assertTrue(content.contains("Test content"));
assertNotNull(result.getSupplement()); // L0 唯一命中仍调用 L1
assertEquals("PRECISE", result.getRelevanceLevel());
assertNotNull(result.getEvidenceBlocks());
assertEquals(2, result.getEvidenceCandidateCount());
assertEquals(2, result.getEvidenceBlockCount());
assertEquals("L0", result.getEvidenceBlocks().get(0).getRetrievalLayer());
assertTrue(result.getEvidenceBlocks().get(0).getHitReasons().stream()
.anyMatch(reason -> reason.contains("ERR_TIMEOUT")));
verify(vectorSearchService).searchSimilarDocuments("ERR_TIMEOUT", 3, null);
}
@@ -290,6 +297,41 @@ class LookupKnowledgeToolTest {
verify(vectorSearchService).searchSimilarDocuments("fallback", 3, null);
}
@Test
void testLookup_deduplicatesEvidenceBlocksBySource() throws Exception {
KnowledgeEntry entry = KnowledgeEntry.builder()
.filePath("shared.md")
.title("Shared Doc")
.keywords(List.of("shared"))
.summary("Shared summary")
.build();
VectorSearchService.SearchResult l1Result = new VectorSearchService.SearchResult();
l1Result.setContent("Shared semantic content");
String metadata = "{\"docId\":\"doc-1\",\"_source\":\"shared.md\",\"title\":\"Shared Doc\"}";
l1Result.setMetadata(metadata);
l1Result.setScore(0.2f);
when(knowledgeIndexService.analyzeQuery("shared"))
.thenReturn(hint(entry));
when(knowledgeIndexService.readDocument("shared.md", 2000))
.thenReturn("Shared content");
when(vectorSearchService.searchSimilarDocuments("shared", 3, null))
.thenReturn(List.of(l1Result));
when(objectMapper.readValue(metadata, Map.class))
.thenReturn(Map.of("docId", "doc-1", "_source", "shared.md", "title", "Shared Doc"));
LookupResult result = tool.lookupKnowledge("shared");
assertTrue(result.isFound());
assertEquals(2, result.getEvidenceCandidateCount());
assertEquals(1, result.getEvidenceBlockCount());
assertEquals("shared.md", result.getEvidenceBlocks().get(0).getSource());
assertEquals("L0+L1", result.getEvidenceBlocks().get(0).getRetrievalLayer());
assertTrue(result.getEvidenceBlocks().get(0).getHitReasons().contains("semantic_rank:1"));
assertTrue(result.getEvidenceBlocks().get(0).getHitReasons().stream()
.anyMatch(reason -> reason.startsWith("l0_keywords:")));
}
private KnowledgeIndexService.L0Hint hint(KnowledgeEntry... entries) {
List<KnowledgeEntry> matches = List.of(entries);
List<String> keywords = matches.stream()