142 lines
5.5 KiB
Go
142 lines
5.5 KiB
Go
|
|
package app
|
|||
|
|
|
|||
|
|
import (
|
|||
|
|
"context"
|
|||
|
|
"errors"
|
|||
|
|
"strings"
|
|||
|
|
"testing"
|
|||
|
|
|
|||
|
|
"github.com/geo-platform/tenant-api/internal/shared/llm"
|
|||
|
|
)
|
|||
|
|
|
|||
|
|
func TestBuildGenerationKnowledgeQueryIncludesContextAndFallbackQuestions(t *testing.T) {
|
|||
|
|
query := buildGenerationKnowledgeQuery("top_x_article", "Top X 推荐文章", map[string]interface{}{
|
|||
|
|
"topic": "合肥全屋定制",
|
|||
|
|
"brand_name": "安徽海翔家居",
|
|||
|
|
"region": "合肥",
|
|||
|
|
"primary_keyword": "合肥定制家具",
|
|||
|
|
"supplemental_questions": []string{"合肥家具品牌怎么选", "全屋定制避坑"},
|
|||
|
|
"key_points": "突出门店与服务能力",
|
|||
|
|
})
|
|||
|
|
|
|||
|
|
for _, expected := range []string{
|
|||
|
|
"文章类型:推荐榜文章",
|
|||
|
|
"模板名称:Top X 推荐文章",
|
|||
|
|
"主题:合肥全屋定制",
|
|||
|
|
"品牌:安徽海翔家居",
|
|||
|
|
"地域:合肥",
|
|||
|
|
"主关键词:合肥定制家具",
|
|||
|
|
"兜底检索问题:",
|
|||
|
|
} {
|
|||
|
|
if !strings.Contains(query, expected) {
|
|||
|
|
t.Fatalf("buildGenerationKnowledgeQuery() = %q, want %q", query, expected)
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
questions := normalizeKnowledgeResolveQueries(query)
|
|||
|
|
if len(questions) != maxKnowledgeQueryQuestions {
|
|||
|
|
t.Fatalf("normalizeKnowledgeResolveQueries() len = %d, want %d: %#v", len(questions), maxKnowledgeQueryQuestions, questions)
|
|||
|
|
}
|
|||
|
|
for _, question := range questions {
|
|||
|
|
if !strings.HasSuffix(question, "?") {
|
|||
|
|
t.Fatalf("question = %q, want Chinese question mark", question)
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
func TestParseKnowledgeQueryRewriteOutputAcceptsObjectAndArray(t *testing.T) {
|
|||
|
|
objectQuestions := parseKnowledgeQueryRewriteOutput(`{"questions":["安徽海翔家居在合肥有哪些门店和联系方式?","安徽海翔家居主营哪些全屋定制产品和服务?","合肥全屋定制用户选择时关注哪些案例和售后?"]}`)
|
|||
|
|
if len(objectQuestions) != maxKnowledgeQueryQuestions {
|
|||
|
|
t.Fatalf("object questions len = %d, want %d: %#v", len(objectQuestions), maxKnowledgeQueryQuestions, objectQuestions)
|
|||
|
|
}
|
|||
|
|
if objectQuestions[0] != "安徽海翔家居在合肥有哪些门店和联系方式?" {
|
|||
|
|
t.Fatalf("first object question = %q", objectQuestions[0])
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
arrayQuestions := parseKnowledgeQueryRewriteOutput(`["安徽海翔家居有什么品牌优势?","合肥全屋定制有哪些案例可引用?","全屋定制选型需要哪些避坑信息?"]`)
|
|||
|
|
if len(arrayQuestions) != maxKnowledgeQueryQuestions {
|
|||
|
|
t.Fatalf("array questions len = %d, want %d: %#v", len(arrayQuestions), maxKnowledgeQueryQuestions, arrayQuestions)
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
func TestNormalizeKnowledgeResolveQueriesPrefersFallbackQuestionBlock(t *testing.T) {
|
|||
|
|
query := strings.Join([]string{
|
|||
|
|
"文章类型:推荐榜文章",
|
|||
|
|
"主题:合肥全屋定制",
|
|||
|
|
"品牌:安徽海翔家居",
|
|||
|
|
"重点要求:",
|
|||
|
|
"请突出本地服务",
|
|||
|
|
"不要写成硬广",
|
|||
|
|
"兜底检索问题:",
|
|||
|
|
"- 安徽海翔家居在合肥有哪些门店和联系方式?",
|
|||
|
|
"- 安徽海翔家居主营哪些全屋定制产品和服务?",
|
|||
|
|
"- 合肥全屋定制用户选择时关注哪些案例和售后?",
|
|||
|
|
}, "\n")
|
|||
|
|
|
|||
|
|
got := normalizeKnowledgeResolveQueries(query)
|
|||
|
|
want := []string{
|
|||
|
|
"安徽海翔家居在合肥有哪些门店和联系方式?",
|
|||
|
|
"安徽海翔家居主营哪些全屋定制产品和服务?",
|
|||
|
|
"合肥全屋定制用户选择时关注哪些案例和售后?",
|
|||
|
|
}
|
|||
|
|
if len(got) != len(want) {
|
|||
|
|
t.Fatalf("normalizeKnowledgeResolveQueries() = %#v, want %#v", got, want)
|
|||
|
|
}
|
|||
|
|
for index := range want {
|
|||
|
|
if got[index] != want[index] {
|
|||
|
|
t.Fatalf("normalizeKnowledgeResolveQueries()[%d] = %q, want %q", index, got[index], want[index])
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
func TestRewriteKnowledgeResolveQueriesUsesKnowledgeURLModelAndJSONObject(t *testing.T) {
|
|||
|
|
client := &fakeKnowledgeQueryLLM{
|
|||
|
|
content: `{"questions":["安徽海翔家居在合肥有哪些门店和联系方式?","安徽海翔家居主营哪些全屋定制产品和服务?","合肥全屋定制用户选择时关注哪些案例和售后?"]}`,
|
|||
|
|
}
|
|||
|
|
svc := &KnowledgeService{
|
|||
|
|
llmClient: client,
|
|||
|
|
cfg: knowledgeRuntimeConfig{URLMarkdownModel: "knowledge-url-fast"},
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
questions, err := svc.rewriteKnowledgeResolveQueries(context.Background(), "品牌:安徽海翔家居\n地域:合肥")
|
|||
|
|
if err != nil {
|
|||
|
|
t.Fatalf("rewriteKnowledgeResolveQueries() error = %v", err)
|
|||
|
|
}
|
|||
|
|
if len(questions) != maxKnowledgeQueryQuestions {
|
|||
|
|
t.Fatalf("questions len = %d, want %d: %#v", len(questions), maxKnowledgeQueryQuestions, questions)
|
|||
|
|
}
|
|||
|
|
if client.request.Model != "knowledge-url-fast" {
|
|||
|
|
t.Fatalf("Generate model = %q, want knowledge-url-fast", client.request.Model)
|
|||
|
|
}
|
|||
|
|
if client.request.ResponseFormat == nil || client.request.ResponseFormat.Type != llm.ResponseFormatTypeJSONObject {
|
|||
|
|
t.Fatalf("ResponseFormat = %#v, want json_object", client.request.ResponseFormat)
|
|||
|
|
}
|
|||
|
|
if !strings.Contains(client.request.Prompt, "品牌:安徽海翔家居") || !strings.Contains(client.request.Prompt, "必须输出 3 个问题") {
|
|||
|
|
t.Fatalf("Prompt = %q, want raw user input and three-question instruction", client.request.Prompt)
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
type fakeKnowledgeQueryLLM struct {
|
|||
|
|
request llm.GenerateRequest
|
|||
|
|
content string
|
|||
|
|
err error
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
func (f *fakeKnowledgeQueryLLM) Validate() error {
|
|||
|
|
if f.err != nil {
|
|||
|
|
return f.err
|
|||
|
|
}
|
|||
|
|
return nil
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
func (f *fakeKnowledgeQueryLLM) Generate(_ context.Context, req llm.GenerateRequest, _ func(string)) (*llm.GenerateResult, error) {
|
|||
|
|
f.request = req
|
|||
|
|
if f.err != nil {
|
|||
|
|
return nil, f.err
|
|||
|
|
}
|
|||
|
|
if strings.TrimSpace(f.content) == "" {
|
|||
|
|
return nil, errors.New("empty fake content")
|
|||
|
|
}
|
|||
|
|
return &llm.GenerateResult{Content: f.content, Model: req.Model}, nil
|
|||
|
|
}
|