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 fd17c30c..15a46763 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 @@ -278,6 +278,12 @@ public class HybridRetriever { List seedLites = pageMapper.selectBatchLite(List.of(seedId)); if (seedLites.isEmpty()) continue; WikiPageLite seed = seedLites.get(0); + // RFC-051 PR-5: don't expand 1-hop neighborhood from a system page seed. + // Otherwise the overview / log neighborhood — typically every page that + // shares a raw with them — leaks into search results via boost. PR-2's + // result-emit filter drops the system pages themselves; this guard ensures + // we don't even use them as expansion roots. + if (seed.isSystem()) continue; try { relationService.relatedPages(kbId, seed.slug(), 3) .forEach(r -> { diff --git a/mateclaw-server/src/main/java/vip/mate/wiki/service/WikiPageService.java b/mateclaw-server/src/main/java/vip/mate/wiki/service/WikiPageService.java index d1305c8e..9250ddb6 100644 --- a/mateclaw-server/src/main/java/vip/mate/wiki/service/WikiPageService.java +++ b/mateclaw-server/src/main/java/vip/mate/wiki/service/WikiPageService.java @@ -472,15 +472,26 @@ public class WikiPageService { } /** - * 从 Markdown 内容中提取 [[links]] 并返回 JSON 数组 + * Extract {@code [[links]]} (and {@code [[target|label]]} alias form, + * RFC-051 PR-5) from Markdown content and return them as a JSON array of + * canonical slugs. + *

+ * For aliased links the {@code label} part is purely display — only + * {@code target} feeds slug resolution. Without this split we'd canonicalize + * "Spring AI|Spring AI Alibaba" as a single slug, polluting outgoingLinks + * and breaking graph view / backlinks. */ String extractLinksAsJson(String content) { if (content == null) return "[]"; List links = new ArrayList<>(); Matcher matcher = WIKI_LINK_PATTERN.matcher(content); while (matcher.find()) { - String link = matcher.group(1).trim(); - String slug = toSlug(link); + String raw = matcher.group(1).trim(); + int pipe = raw.indexOf('|'); + String target = pipe >= 0 ? raw.substring(0, pipe).trim() : raw; + if (target.isEmpty()) continue; + String slug = toSlug(target); + if (slug.isEmpty()) continue; if (!links.contains(slug)) { links.add(slug); }