fix(knowledge): focus formal citation snippets
This commit is contained in:
+43
-5
@@ -74,12 +74,50 @@ public final class AihrFormalPolicyClassifier {
|
||||
if (query.isEmpty() || evidence.isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
for (EvidenceProfile profile : EVIDENCE_PROFILES) {
|
||||
if (containsAny(query, profile.questionSignals())) {
|
||||
return containsAny(evidence, profile.evidenceSignals());
|
||||
}
|
||||
EvidenceProfile profile = matchingProfile(query);
|
||||
return profile == null || containsAny(evidence, profile.evidenceSignals());
|
||||
}
|
||||
|
||||
/**
|
||||
* Keep the visible citation centered on the same deterministic anchor used
|
||||
* by the formal-evidence gate instead of returning an unrelated document prefix.
|
||||
*/
|
||||
public static String relevantEvidenceSnippet(String question, String evidenceText, int maxLength) {
|
||||
String evidence = evidenceText == null ? "" : evidenceText.trim();
|
||||
if (evidence.isEmpty() || maxLength <= 0) {
|
||||
return "";
|
||||
}
|
||||
return true;
|
||||
if (evidence.length() <= maxLength) {
|
||||
return evidence;
|
||||
}
|
||||
EvidenceProfile profile = matchingProfile(question == null ? "" : question.trim());
|
||||
if (profile == null || maxLength < 3) {
|
||||
return evidence.substring(0, maxLength);
|
||||
}
|
||||
int anchorIndex = profile.evidenceSignals().stream()
|
||||
.mapToInt(evidence::indexOf)
|
||||
.filter(index -> index >= 0)
|
||||
.min()
|
||||
.orElse(-1);
|
||||
if (anchorIndex < 0) {
|
||||
return evidence.substring(0, maxLength);
|
||||
}
|
||||
int windowLength = maxLength - 2;
|
||||
int start = Math.max(0, anchorIndex - Math.min(120, windowLength / 3));
|
||||
int end = Math.min(evidence.length(), start + windowLength);
|
||||
if (end == evidence.length()) {
|
||||
start = Math.max(0, end - windowLength);
|
||||
}
|
||||
return (start > 0 ? "…" : "")
|
||||
+ evidence.substring(start, end).trim()
|
||||
+ (end < evidence.length() ? "…" : "");
|
||||
}
|
||||
|
||||
private static EvidenceProfile matchingProfile(String question) {
|
||||
return EVIDENCE_PROFILES.stream()
|
||||
.filter(profile -> containsAny(question, profile.questionSignals()))
|
||||
.findFirst()
|
||||
.orElse(null);
|
||||
}
|
||||
|
||||
private static EvidenceProfile evidence(List<String> questionSignals, List<String> evidenceSignals) {
|
||||
|
||||
+4
-1
@@ -650,7 +650,10 @@ public class AihrKnowledgeQueryService {
|
||||
|| AihrFormalPolicyClassifier.isRelevantFormalEvidence(queryText, row.snippet()))
|
||||
.map(row -> new Citation(
|
||||
row.spaceCode(), row.sourceType(), row.docId(), row.title(),
|
||||
truncate(row.snippet(), 500), row.fragmentId()
|
||||
formalPolicyOnly
|
||||
? AihrFormalPolicyClassifier.relevantEvidenceSnippet(queryText, row.snippet(), 500)
|
||||
: truncate(row.snippet(), 500),
|
||||
row.fragmentId()
|
||||
))
|
||||
.toList();
|
||||
}
|
||||
|
||||
+16
@@ -72,4 +72,20 @@ class AihrFormalPolicyClassifierTest {
|
||||
assertFalse(AihrFormalPolicyClassifier.isRelevantFormalEvidence(
|
||||
installmentQuestion, " "));
|
||||
}
|
||||
|
||||
@Test
|
||||
void formalCitationSnippetKeepsTheMatchedAnchorVisible() {
|
||||
String question = "装修产生的废料该找谁、怎么约清运、能不能收费?";
|
||||
String evidence = "无关前言。".repeat(120)
|
||||
+ "装修垃圾应登记后预约清运,收费标准须公示。"
|
||||
+ "无关附录。".repeat(120);
|
||||
|
||||
String snippet = AihrFormalPolicyClassifier.relevantEvidenceSnippet(question, evidence, 500);
|
||||
|
||||
assertTrue(snippet.length() <= 500);
|
||||
assertTrue(snippet.contains("装修垃圾"));
|
||||
assertTrue(snippet.contains("清运"));
|
||||
assertTrue(snippet.startsWith("…"));
|
||||
assertTrue(snippet.endsWith("…"));
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user