Update Klassifizierung - Teil 1

This commit is contained in:
2026-08-01 21:53:16 +02:00
parent bca5c1335c
commit a5cc4f881c
119 changed files with 3710 additions and 194 deletions
+28
View File
@@ -1194,6 +1194,34 @@ func (s *Store) Search(ctx context.Context, text string, topK int, categorySets
return hits, nil
}
// FilterHitsBySources keeps the existing relevance order while applying a
// purpose-specific source allowlist. This lets one shared index serve normal
// retrieval and category-only knowledge without exposing category-only entries
// as reply candidates.
func FilterHitsBySources(hits []model.KnowledgeHit, sources []string, topK int) []model.KnowledgeHit {
allowed := make(map[string]struct{}, len(sources))
for _, source := range sources {
normalized := strings.ToLower(strings.TrimSpace(source))
if normalized != "" {
allowed[normalized] = struct{}{}
}
}
if len(allowed) == 0 || len(hits) == 0 {
return nil
}
out := make([]model.KnowledgeHit, 0, len(hits))
for _, hit := range hits {
if _, ok := allowed[strings.ToLower(strings.TrimSpace(hit.Doc.Source))]; !ok {
continue
}
out = append(out, hit)
if topK > 0 && len(out) >= topK {
break
}
}
return out
}
// RerankForCategory applies a deterministic post-classification boost when a
// knowledge document is explicitly mapped to the category selected by the
// classifier. This happens after the model decision, so the dashboard and
+15
View File
@@ -610,3 +610,18 @@ func TestCategoryMappingEditorReappliesWithoutReembedding(t *testing.T) {
t.Fatalf("unexpected mapping state after save: %+v", state)
}
}
func TestFilterHitsBySourcesSeparatesCategoryOnlyKnowledge(t *testing.T) {
hits := []model.KnowledgeHit{
{Doc: model.KnowledgeDoc{ID: "CAT", Source: "internal-category"}, Score: .9},
{Doc: model.KnowledgeDoc{ID: "REPLY", Source: "internal-kb"}, Score: .8},
}
category := FilterHitsBySources(hits, []string{"internal-category"}, 0)
reply := FilterHitsBySources(hits, []string{"internal-kb"}, 0)
if len(category) != 1 || category[0].Doc.ID != "CAT" {
t.Fatalf("category hits=%+v", category)
}
if len(reply) != 1 || reply[0].Doc.ID != "REPLY" {
t.Fatalf("reply hits=%+v", reply)
}
}