From 6f0ead162efdc92ae7942a87e7e573eb47d37e23 Mon Sep 17 00:00:00 2001 From: matevip Date: Sat, 2 May 2026 19:04:19 +0800 Subject: [PATCH] feat(wiki): instrument compile / relation / retrieval with WikiMetrics --- .../vip/mate/wiki/service/HybridRetriever.java | 17 +++++++++++++++-- .../mate/wiki/service/WikiCompileService.java | 9 +++++++++ .../mate/wiki/service/WikiRelationService.java | 16 +++++++++++++++- 3 files changed, 39 insertions(+), 3 deletions(-) diff --git a/mateclaw-server/src/main/java/vip/mate/wiki/service/HybridRetriever.java b/mateclaw-server/src/main/java/vip/mate/wiki/service/HybridRetriever.java index ba9779f7..3be05b6e 100644 --- a/mateclaw-server/src/main/java/vip/mate/wiki/service/HybridRetriever.java +++ b/mateclaw-server/src/main/java/vip/mate/wiki/service/HybridRetriever.java @@ -8,11 +8,13 @@ import vip.mate.wiki.WikiProperties; import vip.mate.wiki.dto.PageSearchResult; import vip.mate.wiki.dto.RelatedPageResult; import vip.mate.wiki.dto.WikiPageLite; +import vip.mate.wiki.metrics.WikiMetrics; import vip.mate.wiki.model.WikiChunkEntity; import vip.mate.wiki.model.WikiPageEntity; import vip.mate.wiki.repository.WikiPageMapper; import vip.mate.wiki.retrieval.SnippetExtractor; +import java.time.Duration; import java.util.*; import java.util.stream.Collectors; @@ -32,6 +34,7 @@ public class HybridRetriever { private final WikiEmbeddingService embeddingService; private final WikiProperties properties; private final WikiPageMapper pageMapper; + private final WikiMetrics metrics; @Autowired(required = false) private WikiRelationService relationService; @@ -42,12 +45,14 @@ public class HybridRetriever { WikiChunkService chunkService, WikiEmbeddingService embeddingService, WikiProperties properties, - WikiPageMapper pageMapper) { + WikiPageMapper pageMapper, + WikiMetrics metrics) { this.pageService = pageService; this.chunkService = chunkService; this.embeddingService = embeddingService; this.properties = properties; this.pageMapper = pageMapper; + this.metrics = metrics; } public enum Mode { KEYWORD, SEMANTIC, HYBRID } @@ -79,6 +84,8 @@ public class HybridRetriever { public List search(Long kbId, String query, String modeStr, int topK) { Mode mode = parseMode(modeStr); + long startNanos = System.nanoTime(); + List semantic = List.of(); List keyword = List.of(); @@ -107,7 +114,11 @@ public class HybridRetriever { // Batch-fetch page info (N+1 fix) List topIds = fused.stream().limit(topK).map(ri -> ri.pageId).toList(); - if (topIds.isEmpty()) return List.of(); + if (topIds.isEmpty()) { + metrics.recordRetrieval(mode.name().toLowerCase(), + Duration.ofNanos(System.nanoTime() - startNanos), 0); + return List.of(); + } Map liteMap = pageMapper.selectBatchLite(topIds) .stream().collect(Collectors.toMap(WikiPageLite::id, p -> p)); @@ -142,6 +153,8 @@ public class HybridRetriever { snippet != null ? snippet : lite.summary(), ri.matchedBy, reason, ri.score)); } + metrics.recordRetrieval(mode.name().toLowerCase(), + Duration.ofNanos(System.nanoTime() - startNanos), results.size()); return results; } diff --git a/mateclaw-server/src/main/java/vip/mate/wiki/service/WikiCompileService.java b/mateclaw-server/src/main/java/vip/mate/wiki/service/WikiCompileService.java index f0541bea..ebfefc4a 100644 --- a/mateclaw-server/src/main/java/vip/mate/wiki/service/WikiCompileService.java +++ b/mateclaw-server/src/main/java/vip/mate/wiki/service/WikiCompileService.java @@ -14,10 +14,12 @@ import org.springframework.stereotype.Service; import vip.mate.agent.prompt.PromptLoader; import vip.mate.wiki.job.WikiJobStep; import vip.mate.wiki.job.WikiModelRoutingService; +import vip.mate.wiki.metrics.WikiMetrics; import vip.mate.wiki.model.WikiChunkEntity; import vip.mate.wiki.model.WikiPageEntity; import vip.mate.wiki.repository.WikiChunkMapper; +import java.time.Duration; import java.util.ArrayList; import java.util.List; @@ -43,6 +45,7 @@ public class WikiCompileService { private final WikiPageService pageService; private final WikiCitationService citationService; private final ObjectMapper objectMapper; + private final WikiMetrics metrics; /** * Optional. When wired we use the routing chain (stepModels[CREATE_PAGE] @@ -91,12 +94,16 @@ public class WikiCompileService { int cap = (maxEvidenceChunks == null || maxEvidenceChunks <= 0) ? 8 : Math.min(20, maxEvidenceChunks); + long startNanos = System.nanoTime(); + // 1. Retrieve evidence chunks via semantic search (hybrid retriever). List hits = hybridRetriever.searchChunks(kbId, topic, cap); if (hits.isEmpty()) { // Structured "nothing matched" result rather than throw — lets the // tool surface respond with a clean message instead of a stack trace. log.info("[WikiCompile] No evidence chunks for topic='{}' kbId={}", topic, kbId); + metrics.recordCompileStage("no_evidence", kbId, + Duration.ofNanos(System.nanoTime() - startNanos)); return CompileResult.noEvidence(); } @@ -186,6 +193,8 @@ public class WikiCompileService { } if (overviewService != null) overviewService.rebuild(kbId); + metrics.recordCompileStage("compile_page", kbId, + Duration.ofNanos(System.nanoTime() - startNanos)); return new CompileResult(persisted.getId(), resolvedSlug, title, evidenceChunkIds.size(), created); } diff --git a/mateclaw-server/src/main/java/vip/mate/wiki/service/WikiRelationService.java b/mateclaw-server/src/main/java/vip/mate/wiki/service/WikiRelationService.java index 47a5bd0d..04838d63 100644 --- a/mateclaw-server/src/main/java/vip/mate/wiki/service/WikiRelationService.java +++ b/mateclaw-server/src/main/java/vip/mate/wiki/service/WikiRelationService.java @@ -3,11 +3,13 @@ package vip.mate.wiki.service; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; import vip.mate.wiki.dto.*; +import vip.mate.wiki.metrics.WikiMetrics; import vip.mate.wiki.model.WikiPageEntity; import vip.mate.wiki.relation.RelationSignalStrategy; import vip.mate.wiki.repository.WikiPageCitationMapper; import vip.mate.wiki.repository.WikiPageMapper; +import java.time.Duration; import java.util.*; import java.util.stream.Collectors; @@ -23,15 +25,18 @@ public class WikiRelationService { private final WikiPageMapper pageMapper; private final WikiPageService pageService; private final WikiPageCitationMapper citationMapper; + private final WikiMetrics metrics; public WikiRelationService(List signals, WikiPageMapper pageMapper, WikiPageService pageService, - WikiPageCitationMapper citationMapper) { + WikiPageCitationMapper citationMapper, + WikiMetrics metrics) { this.signals = signals; this.pageMapper = pageMapper; this.pageService = pageService; this.citationMapper = citationMapper; + this.metrics = metrics; } /** @@ -41,6 +46,8 @@ public class WikiRelationService { WikiPageEntity seed = pageService.getBySlug(kbId, seedSlug); if (seed == null) return List.of(); + long startNanos = System.nanoTime(); + Map totalScores = new HashMap<>(); Map> signalHits = new HashMap<>(); @@ -62,6 +69,13 @@ public class WikiRelationService { .map(Map.Entry::getKey) .toList(); + // Reports both compute duration and the candidate-set size. + // Cache hit/miss is reported once a relation cache layer is wired up; + // until then every call is a cold compute by definition. + metrics.recordRelationCompute(kbId, totalScores.size(), + Duration.ofNanos(System.nanoTime() - startNanos)); + metrics.recordRelationCacheHit(false); + if (topIds.isEmpty()) return List.of(); Map liteMap = pageMapper.selectBatchLite(topIds) .stream().collect(Collectors.toMap(WikiPageLite::id, l -> l));