feat(knowledge): broaden context resolution and prompt query
- Walk knowledge group children recursively so selecting a parent group pulls snippets from its descendants too. - Filter knowledge items to status='completed' across the prompt and active-snippet queries to avoid leaking in-progress documents. - Dedupe vector candidates by point id and normalized text, then pick rerank+vector results with a per-item cap so prompts stay diverse. - Tag each rendered snippet with a [Kn] reference id for prompt citing. - Build the rule knowledge query from task params (title/keywords/etc.) before falling back to rule prompt content so retrieval matches the user's actual ask. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -3,6 +3,8 @@ package app
|
||||
import (
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/geo-platform/tenant-api/internal/shared/retrieval"
|
||||
)
|
||||
|
||||
func TestExtractKnowledgePreciseFactsKeepsAddressAndContactLines(t *testing.T) {
|
||||
@@ -43,6 +45,7 @@ func TestRenderPromptSectionIncludesPreciseFactsAndSourceURI(t *testing.T) {
|
||||
for _, expected := range []string{
|
||||
"以下为必须严格按原文保留的高精度事实",
|
||||
"- 门店信息 / 合肥门店:地址:合肥市蜀山区望江西路 123 号",
|
||||
"1. [K1] 门店信息 / 合肥门店",
|
||||
"来源链接: https://example.com/store",
|
||||
"这里是一段门店介绍。",
|
||||
} {
|
||||
@@ -93,6 +96,64 @@ func TestExtractKnowledgePreciseFactsKeepsBusinessScopeSection(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestKnowledgeSnippetsFromSearchPointsSkipsMissingTextAndDedupes(t *testing.T) {
|
||||
snippets := dedupeKnowledgeSearchCandidates(knowledgeSnippetsFromSearchPoints([]retrieval.SearchPoint{
|
||||
{
|
||||
ID: "point-1",
|
||||
Score: 0.91,
|
||||
Payload: map[string]any{
|
||||
"group_id": int64(10),
|
||||
"group_name": "产品资料",
|
||||
"item_id": int64(100),
|
||||
"item_name": "旗舰款",
|
||||
"text": " 支持全屋定制方案 ",
|
||||
},
|
||||
},
|
||||
{
|
||||
ID: "point-2",
|
||||
Payload: map[string]any{
|
||||
"text": "支持全屋定制方案",
|
||||
},
|
||||
},
|
||||
{
|
||||
ID: "point-3",
|
||||
Payload: map[string]any{},
|
||||
},
|
||||
}))
|
||||
|
||||
if len(snippets) != 1 {
|
||||
t.Fatalf("deduped snippets length = %d, want 1: %#v", len(snippets), snippets)
|
||||
}
|
||||
if snippets[0].Text != "支持全屋定制方案" {
|
||||
t.Fatalf("snippet text = %q, want trimmed text", snippets[0].Text)
|
||||
}
|
||||
}
|
||||
|
||||
func TestSelectKnowledgeRerankedSnippetsFillsAndDiversifiesResults(t *testing.T) {
|
||||
candidates := []KnowledgeSnippet{
|
||||
{PointID: "p1", KnowledgeItemID: 1, Text: "同一文档片段 1", Score: 0.90},
|
||||
{PointID: "p2", KnowledgeItemID: 1, Text: "同一文档片段 2", Score: 0.89},
|
||||
{PointID: "p3", KnowledgeItemID: 1, Text: "同一文档片段 3", Score: 0.88},
|
||||
{PointID: "p4", KnowledgeItemID: 2, Text: "第二文档片段", Score: 0.80},
|
||||
{PointID: "p5", KnowledgeItemID: 3, Text: "第三文档片段", Score: 0.70},
|
||||
}
|
||||
|
||||
selected := selectKnowledgeRerankedSnippets(candidates, []retrieval.RerankResult{
|
||||
{Index: 2, RelevanceScore: 0.99},
|
||||
{Index: 99, RelevanceScore: 1},
|
||||
}, 4)
|
||||
|
||||
if len(selected) != 4 {
|
||||
t.Fatalf("selected length = %d, want 4: %#v", len(selected), selected)
|
||||
}
|
||||
if selected[0].PointID != "p3" || selected[0].Score != 0.99 {
|
||||
t.Fatalf("selected[0] = %#v, want reranked p3 with rerank score", selected[0])
|
||||
}
|
||||
if selected[1].PointID != "p1" || selected[2].PointID != "p4" || selected[3].PointID != "p5" {
|
||||
t.Fatalf("selected order = %#v, want reranked result filled with diverse vector candidates", selected)
|
||||
}
|
||||
}
|
||||
|
||||
func containsExactString(items []string, target string) bool {
|
||||
for _, item := range items {
|
||||
if item == target {
|
||||
|
||||
Reference in New Issue
Block a user