feat: add rag evidence postprocess blocks
This commit is contained in:
@@ -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()
|
||||
|
||||
Reference in New Issue
Block a user