mirror of
https://gitee.com/mateos/mateclaw.git
synced 2026-09-15 03:55:09 +08:00
feat(wiki): instrument compile / relation / retrieval with WikiMetrics
This commit is contained in:
parent
4f96a46900
commit
6f0ead162e
@ -8,11 +8,13 @@ import vip.mate.wiki.WikiProperties;
|
|||||||
import vip.mate.wiki.dto.PageSearchResult;
|
import vip.mate.wiki.dto.PageSearchResult;
|
||||||
import vip.mate.wiki.dto.RelatedPageResult;
|
import vip.mate.wiki.dto.RelatedPageResult;
|
||||||
import vip.mate.wiki.dto.WikiPageLite;
|
import vip.mate.wiki.dto.WikiPageLite;
|
||||||
|
import vip.mate.wiki.metrics.WikiMetrics;
|
||||||
import vip.mate.wiki.model.WikiChunkEntity;
|
import vip.mate.wiki.model.WikiChunkEntity;
|
||||||
import vip.mate.wiki.model.WikiPageEntity;
|
import vip.mate.wiki.model.WikiPageEntity;
|
||||||
import vip.mate.wiki.repository.WikiPageMapper;
|
import vip.mate.wiki.repository.WikiPageMapper;
|
||||||
import vip.mate.wiki.retrieval.SnippetExtractor;
|
import vip.mate.wiki.retrieval.SnippetExtractor;
|
||||||
|
|
||||||
|
import java.time.Duration;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
@ -32,6 +34,7 @@ public class HybridRetriever {
|
|||||||
private final WikiEmbeddingService embeddingService;
|
private final WikiEmbeddingService embeddingService;
|
||||||
private final WikiProperties properties;
|
private final WikiProperties properties;
|
||||||
private final WikiPageMapper pageMapper;
|
private final WikiPageMapper pageMapper;
|
||||||
|
private final WikiMetrics metrics;
|
||||||
|
|
||||||
@Autowired(required = false)
|
@Autowired(required = false)
|
||||||
private WikiRelationService relationService;
|
private WikiRelationService relationService;
|
||||||
@ -42,12 +45,14 @@ public class HybridRetriever {
|
|||||||
WikiChunkService chunkService,
|
WikiChunkService chunkService,
|
||||||
WikiEmbeddingService embeddingService,
|
WikiEmbeddingService embeddingService,
|
||||||
WikiProperties properties,
|
WikiProperties properties,
|
||||||
WikiPageMapper pageMapper) {
|
WikiPageMapper pageMapper,
|
||||||
|
WikiMetrics metrics) {
|
||||||
this.pageService = pageService;
|
this.pageService = pageService;
|
||||||
this.chunkService = chunkService;
|
this.chunkService = chunkService;
|
||||||
this.embeddingService = embeddingService;
|
this.embeddingService = embeddingService;
|
||||||
this.properties = properties;
|
this.properties = properties;
|
||||||
this.pageMapper = pageMapper;
|
this.pageMapper = pageMapper;
|
||||||
|
this.metrics = metrics;
|
||||||
}
|
}
|
||||||
|
|
||||||
public enum Mode { KEYWORD, SEMANTIC, HYBRID }
|
public enum Mode { KEYWORD, SEMANTIC, HYBRID }
|
||||||
@ -79,6 +84,8 @@ public class HybridRetriever {
|
|||||||
public List<PageSearchResult> search(Long kbId, String query, String modeStr, int topK) {
|
public List<PageSearchResult> search(Long kbId, String query, String modeStr, int topK) {
|
||||||
Mode mode = parseMode(modeStr);
|
Mode mode = parseMode(modeStr);
|
||||||
|
|
||||||
|
long startNanos = System.nanoTime();
|
||||||
|
|
||||||
List<RankedItem> semantic = List.of();
|
List<RankedItem> semantic = List.of();
|
||||||
List<RankedItem> keyword = List.of();
|
List<RankedItem> keyword = List.of();
|
||||||
|
|
||||||
@ -107,7 +114,11 @@ public class HybridRetriever {
|
|||||||
|
|
||||||
// Batch-fetch page info (N+1 fix)
|
// Batch-fetch page info (N+1 fix)
|
||||||
List<Long> topIds = fused.stream().limit(topK).map(ri -> ri.pageId).toList();
|
List<Long> 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<Long, WikiPageLite> liteMap = pageMapper.selectBatchLite(topIds)
|
Map<Long, WikiPageLite> liteMap = pageMapper.selectBatchLite(topIds)
|
||||||
.stream().collect(Collectors.toMap(WikiPageLite::id, p -> p));
|
.stream().collect(Collectors.toMap(WikiPageLite::id, p -> p));
|
||||||
@ -142,6 +153,8 @@ public class HybridRetriever {
|
|||||||
snippet != null ? snippet : lite.summary(),
|
snippet != null ? snippet : lite.summary(),
|
||||||
ri.matchedBy, reason, ri.score));
|
ri.matchedBy, reason, ri.score));
|
||||||
}
|
}
|
||||||
|
metrics.recordRetrieval(mode.name().toLowerCase(),
|
||||||
|
Duration.ofNanos(System.nanoTime() - startNanos), results.size());
|
||||||
return results;
|
return results;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -14,10 +14,12 @@ import org.springframework.stereotype.Service;
|
|||||||
import vip.mate.agent.prompt.PromptLoader;
|
import vip.mate.agent.prompt.PromptLoader;
|
||||||
import vip.mate.wiki.job.WikiJobStep;
|
import vip.mate.wiki.job.WikiJobStep;
|
||||||
import vip.mate.wiki.job.WikiModelRoutingService;
|
import vip.mate.wiki.job.WikiModelRoutingService;
|
||||||
|
import vip.mate.wiki.metrics.WikiMetrics;
|
||||||
import vip.mate.wiki.model.WikiChunkEntity;
|
import vip.mate.wiki.model.WikiChunkEntity;
|
||||||
import vip.mate.wiki.model.WikiPageEntity;
|
import vip.mate.wiki.model.WikiPageEntity;
|
||||||
import vip.mate.wiki.repository.WikiChunkMapper;
|
import vip.mate.wiki.repository.WikiChunkMapper;
|
||||||
|
|
||||||
|
import java.time.Duration;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
@ -43,6 +45,7 @@ public class WikiCompileService {
|
|||||||
private final WikiPageService pageService;
|
private final WikiPageService pageService;
|
||||||
private final WikiCitationService citationService;
|
private final WikiCitationService citationService;
|
||||||
private final ObjectMapper objectMapper;
|
private final ObjectMapper objectMapper;
|
||||||
|
private final WikiMetrics metrics;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Optional. When wired we use the routing chain (stepModels[CREATE_PAGE]
|
* Optional. When wired we use the routing chain (stepModels[CREATE_PAGE]
|
||||||
@ -91,12 +94,16 @@ public class WikiCompileService {
|
|||||||
int cap = (maxEvidenceChunks == null || maxEvidenceChunks <= 0)
|
int cap = (maxEvidenceChunks == null || maxEvidenceChunks <= 0)
|
||||||
? 8 : Math.min(20, maxEvidenceChunks);
|
? 8 : Math.min(20, maxEvidenceChunks);
|
||||||
|
|
||||||
|
long startNanos = System.nanoTime();
|
||||||
|
|
||||||
// 1. Retrieve evidence chunks via semantic search (hybrid retriever).
|
// 1. Retrieve evidence chunks via semantic search (hybrid retriever).
|
||||||
List<HybridRetriever.ChunkHit> hits = hybridRetriever.searchChunks(kbId, topic, cap);
|
List<HybridRetriever.ChunkHit> hits = hybridRetriever.searchChunks(kbId, topic, cap);
|
||||||
if (hits.isEmpty()) {
|
if (hits.isEmpty()) {
|
||||||
// Structured "nothing matched" result rather than throw — lets the
|
// Structured "nothing matched" result rather than throw — lets the
|
||||||
// tool surface respond with a clean message instead of a stack trace.
|
// tool surface respond with a clean message instead of a stack trace.
|
||||||
log.info("[WikiCompile] No evidence chunks for topic='{}' kbId={}", topic, kbId);
|
log.info("[WikiCompile] No evidence chunks for topic='{}' kbId={}", topic, kbId);
|
||||||
|
metrics.recordCompileStage("no_evidence", kbId,
|
||||||
|
Duration.ofNanos(System.nanoTime() - startNanos));
|
||||||
return CompileResult.noEvidence();
|
return CompileResult.noEvidence();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -186,6 +193,8 @@ public class WikiCompileService {
|
|||||||
}
|
}
|
||||||
if (overviewService != null) overviewService.rebuild(kbId);
|
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);
|
return new CompileResult(persisted.getId(), resolvedSlug, title, evidenceChunkIds.size(), created);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -3,11 +3,13 @@ package vip.mate.wiki.service;
|
|||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
import vip.mate.wiki.dto.*;
|
import vip.mate.wiki.dto.*;
|
||||||
|
import vip.mate.wiki.metrics.WikiMetrics;
|
||||||
import vip.mate.wiki.model.WikiPageEntity;
|
import vip.mate.wiki.model.WikiPageEntity;
|
||||||
import vip.mate.wiki.relation.RelationSignalStrategy;
|
import vip.mate.wiki.relation.RelationSignalStrategy;
|
||||||
import vip.mate.wiki.repository.WikiPageCitationMapper;
|
import vip.mate.wiki.repository.WikiPageCitationMapper;
|
||||||
import vip.mate.wiki.repository.WikiPageMapper;
|
import vip.mate.wiki.repository.WikiPageMapper;
|
||||||
|
|
||||||
|
import java.time.Duration;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
@ -23,15 +25,18 @@ public class WikiRelationService {
|
|||||||
private final WikiPageMapper pageMapper;
|
private final WikiPageMapper pageMapper;
|
||||||
private final WikiPageService pageService;
|
private final WikiPageService pageService;
|
||||||
private final WikiPageCitationMapper citationMapper;
|
private final WikiPageCitationMapper citationMapper;
|
||||||
|
private final WikiMetrics metrics;
|
||||||
|
|
||||||
public WikiRelationService(List<RelationSignalStrategy> signals,
|
public WikiRelationService(List<RelationSignalStrategy> signals,
|
||||||
WikiPageMapper pageMapper,
|
WikiPageMapper pageMapper,
|
||||||
WikiPageService pageService,
|
WikiPageService pageService,
|
||||||
WikiPageCitationMapper citationMapper) {
|
WikiPageCitationMapper citationMapper,
|
||||||
|
WikiMetrics metrics) {
|
||||||
this.signals = signals;
|
this.signals = signals;
|
||||||
this.pageMapper = pageMapper;
|
this.pageMapper = pageMapper;
|
||||||
this.pageService = pageService;
|
this.pageService = pageService;
|
||||||
this.citationMapper = citationMapper;
|
this.citationMapper = citationMapper;
|
||||||
|
this.metrics = metrics;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -41,6 +46,8 @@ public class WikiRelationService {
|
|||||||
WikiPageEntity seed = pageService.getBySlug(kbId, seedSlug);
|
WikiPageEntity seed = pageService.getBySlug(kbId, seedSlug);
|
||||||
if (seed == null) return List.of();
|
if (seed == null) return List.of();
|
||||||
|
|
||||||
|
long startNanos = System.nanoTime();
|
||||||
|
|
||||||
Map<Long, Double> totalScores = new HashMap<>();
|
Map<Long, Double> totalScores = new HashMap<>();
|
||||||
Map<Long, List<String>> signalHits = new HashMap<>();
|
Map<Long, List<String>> signalHits = new HashMap<>();
|
||||||
|
|
||||||
@ -62,6 +69,13 @@ public class WikiRelationService {
|
|||||||
.map(Map.Entry::getKey)
|
.map(Map.Entry::getKey)
|
||||||
.toList();
|
.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();
|
if (topIds.isEmpty()) return List.of();
|
||||||
Map<Long, WikiPageLite> liteMap = pageMapper.selectBatchLite(topIds)
|
Map<Long, WikiPageLite> liteMap = pageMapper.selectBatchLite(topIds)
|
||||||
.stream().collect(Collectors.toMap(WikiPageLite::id, l -> l));
|
.stream().collect(Collectors.toMap(WikiPageLite::id, l -> l));
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user