mirror of
https://gitee.com/mateos/mateclaw.git
synced 2026-09-13 03:13:41 +08:00
feat(wiki): knowledge-layer filter on wiki search
This commit is contained in:
parent
ad1f5b4a15
commit
cc8c9cf951
@ -275,6 +275,7 @@ public class WikiTool {
|
||||
@ToolParam(description = "Search query") String query,
|
||||
@ToolParam(description = "Mode: keyword|semantic|hybrid (default: hybrid)", required = false) String mode,
|
||||
@ToolParam(description = "Max results (default 5, max 20)", required = false) Integer topK,
|
||||
@ToolParam(description = "Knowledge layer filter: fact | experience | all (default all). 'fact' = factual pages (and unlayered pages); 'experience' = synthesis/analysis pages.", required = false) String layer,
|
||||
@ToolParam(description = "Target knowledge base name (from wiki_list_kbs). Omit to use the agent's primary KB; switch to `kbId` when two KBs share the name.", required = false) String kbName,
|
||||
@ToolParam(description = "Numeric KB id from wiki_list_kbs. Use when `kbName` returns an ambiguous-name error.", required = false) Long kbId) {
|
||||
|
||||
@ -289,15 +290,17 @@ public class WikiTool {
|
||||
int k = (topK != null && topK > 0) ? Math.min(topK, 20) : 5;
|
||||
List<PageSearchResult> results = hybridRetriever.search(kbId, query, mode, k);
|
||||
|
||||
// Drop hits whose page type this agent may not read, so search cannot
|
||||
// surface pages a direct read would refuse.
|
||||
// Drop hits this agent may not read (by page type) or that fall outside
|
||||
// the requested knowledge layer. Both need the page, so fetch it once.
|
||||
WikiPageTypePermissionService.Access access = pageTypeAccess(agentId, kbId);
|
||||
if (access != null) {
|
||||
boolean layerFilter = layer != null && !layer.isBlank() && !"all".equalsIgnoreCase(layer.trim());
|
||||
if (access != null || layerFilter) {
|
||||
final Long resolvedKbId = kbId;
|
||||
final String layerWanted = layer;
|
||||
results = results.stream()
|
||||
.filter(r -> {
|
||||
WikiPageEntity p = pageService.getBySlug(resolvedKbId, r.slug());
|
||||
return canRead(access, p);
|
||||
return canRead(access, p) && matchesLayer(p == null ? null : p.getKnowledgeLayer(), layerWanted);
|
||||
})
|
||||
.toList();
|
||||
}
|
||||
@ -1069,6 +1072,20 @@ public class WikiTool {
|
||||
return access == null || access.canRead(pageType);
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether a page's knowledge layer matches a retrieval filter. A null/blank
|
||||
* or {@code all} filter matches everything; an unlayered page counts as
|
||||
* {@code fact} (the RFC default), so {@code layer=fact} includes legacy
|
||||
* pages while {@code layer=experience} returns only experience pages.
|
||||
*/
|
||||
static boolean matchesLayer(String pageLayer, String filter) {
|
||||
if (filter == null || filter.isBlank() || "all".equalsIgnoreCase(filter.trim())) {
|
||||
return true;
|
||||
}
|
||||
String effective = (pageLayer == null || pageLayer.isBlank()) ? "fact" : pageLayer.trim().toLowerCase();
|
||||
return effective.equals(filter.trim().toLowerCase());
|
||||
}
|
||||
|
||||
/**
|
||||
* Gate a write/mutate operation by pageType permission. Returns an error
|
||||
* JSON string to short-circuit the tool when the write is not permitted, or
|
||||
|
||||
@ -0,0 +1,41 @@
|
||||
package vip.mate.wiki.tool;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
/**
|
||||
* Unit tests for the retrieval knowledge-layer filter ({@link WikiTool#matchesLayer}).
|
||||
*/
|
||||
class WikiToolLayerFilterTest {
|
||||
|
||||
@Test
|
||||
void noFilterOrAll_matchesEverything() {
|
||||
assertTrue(WikiTool.matchesLayer("experience", null));
|
||||
assertTrue(WikiTool.matchesLayer("experience", ""));
|
||||
assertTrue(WikiTool.matchesLayer("experience", "all"));
|
||||
assertTrue(WikiTool.matchesLayer(null, "all"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void factFilter_includesUnlayeredPages() {
|
||||
assertTrue(WikiTool.matchesLayer(null, "fact")); // legacy / unlayered counts as fact
|
||||
assertTrue(WikiTool.matchesLayer("", "fact"));
|
||||
assertTrue(WikiTool.matchesLayer("fact", "fact"));
|
||||
assertFalse(WikiTool.matchesLayer("experience", "fact"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void experienceFilter_excludesFactAndUnlayered() {
|
||||
assertTrue(WikiTool.matchesLayer("experience", "experience"));
|
||||
assertFalse(WikiTool.matchesLayer("fact", "experience"));
|
||||
assertFalse(WikiTool.matchesLayer(null, "experience"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void caseInsensitive() {
|
||||
assertTrue(WikiTool.matchesLayer("Experience", "EXPERIENCE"));
|
||||
assertTrue(WikiTool.matchesLayer("FACT", "fact"));
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user