diff --git a/client/faq.go b/client/faq.go index eca2dffa..5757dbf4 100644 --- a/client/faq.go +++ b/client/faq.go @@ -22,6 +22,7 @@ type FAQEntry struct { SimilarQuestions []string `json:"similar_questions"` NegativeQuestions []string `json:"negative_questions"` Answers []string `json:"answers"` + AnswerStrategy string `json:"answer_strategy"` IndexMode string `json:"index_mode"` UpdatedAt time.Time `json:"updated_at"` CreatedAt time.Time `json:"created_at"` @@ -36,6 +37,7 @@ type FAQEntryPayload struct { SimilarQuestions []string `json:"similar_questions,omitempty"` NegativeQuestions []string `json:"negative_questions,omitempty"` Answers []string `json:"answers"` + AnswerStrategy *string `json:"answer_strategy,omitempty"` TagID string `json:"tag_id,omitempty"` TagName string `json:"tag_name,omitempty"` IsEnabled *bool `json:"is_enabled,omitempty"` diff --git a/internal/application/service/knowledge.go b/internal/application/service/knowledge.go index 088861cd..472d8b81 100644 --- a/internal/application/service/knowledge.go +++ b/internal/application/service/knowledge.go @@ -3990,6 +3990,11 @@ func (s *knowledgeService) chunkToFAQEntry(chunk *types.Chunk, kb *types.Knowled if meta == nil { meta = &types.FAQChunkMetadata{StandardQuestion: chunk.Content} } + // 默认使用 all 策略 + answerStrategy := meta.AnswerStrategy + if answerStrategy == "" { + answerStrategy = types.AnswerStrategyAll + } entry := &types.FAQEntry{ ID: chunk.ID, ChunkID: chunk.ID, @@ -4002,6 +4007,7 @@ func (s *knowledgeService) chunkToFAQEntry(chunk *types.Chunk, kb *types.Knowled SimilarQuestions: meta.SimilarQuestions, NegativeQuestions: meta.NegativeQuestions, Answers: meta.Answers, + AnswerStrategy: answerStrategy, IndexMode: kb.FAQConfig.IndexMode, UpdatedAt: chunk.UpdatedAt, CreatedAt: chunk.CreatedAt, @@ -4110,11 +4116,22 @@ func (s *knowledgeService) resolveTagID(ctx context.Context, kbID string, payloa } func sanitizeFAQEntryPayload(payload *types.FAQEntryPayload) (*types.FAQChunkMetadata, error) { + // 处理 AnswerStrategy,默认为 all + answerStrategy := types.AnswerStrategyAll + if payload.AnswerStrategy != nil && *payload.AnswerStrategy != "" { + switch *payload.AnswerStrategy { + case types.AnswerStrategyAll, types.AnswerStrategyRandom: + answerStrategy = *payload.AnswerStrategy + default: + return nil, werrors.NewBadRequestError("answer_strategy 必须是 'all' 或 'random'") + } + } meta := &types.FAQChunkMetadata{ StandardQuestion: strings.TrimSpace(payload.StandardQuestion), SimilarQuestions: payload.SimilarQuestions, NegativeQuestions: payload.NegativeQuestions, Answers: payload.Answers, + AnswerStrategy: answerStrategy, Version: 1, Source: "faq", } diff --git a/internal/types/faq.go b/internal/types/faq.go index 0c134fe9..b1915fb3 100644 --- a/internal/types/faq.go +++ b/internal/types/faq.go @@ -11,12 +11,13 @@ import ( // FAQChunkMetadata 定义 FAQ 条目在 Chunk.Metadata 中的结构 type FAQChunkMetadata struct { - StandardQuestion string `json:"standard_question"` - SimilarQuestions []string `json:"similar_questions,omitempty"` - NegativeQuestions []string `json:"negative_questions,omitempty"` - Answers []string `json:"answers,omitempty"` - Version int `json:"version,omitempty"` - Source string `json:"source,omitempty"` + StandardQuestion string `json:"standard_question"` + SimilarQuestions []string `json:"similar_questions,omitempty"` + NegativeQuestions []string `json:"negative_questions,omitempty"` + Answers []string `json:"answers,omitempty"` + AnswerStrategy AnswerStrategy `json:"answer_strategy,omitempty"` + Version int `json:"version,omitempty"` + Source string `json:"source,omitempty"` } // GeneratedQuestion 表示AI生成的单个问题 @@ -162,37 +163,49 @@ func CalculateFAQContentHash(meta *FAQChunkMetadata) string { return hex.EncodeToString(hash[:]) } +// AnswerStrategy 定义答案返回策略 +type AnswerStrategy string + +const ( + // AnswerStrategyAll 返回所有答案 + AnswerStrategyAll AnswerStrategy = "all" + // AnswerStrategyRandom 随机返回一个答案 + AnswerStrategyRandom AnswerStrategy = "random" +) + // FAQEntry 表示返回给前端的 FAQ 条目 type FAQEntry struct { - ID string `json:"id"` - ChunkID string `json:"chunk_id"` - KnowledgeID string `json:"knowledge_id"` - KnowledgeBaseID string `json:"knowledge_base_id"` - TagID string `json:"tag_id"` - IsEnabled bool `json:"is_enabled"` - IsRecommended bool `json:"is_recommended"` - StandardQuestion string `json:"standard_question"` - SimilarQuestions []string `json:"similar_questions"` - NegativeQuestions []string `json:"negative_questions"` - Answers []string `json:"answers"` - IndexMode FAQIndexMode `json:"index_mode"` - UpdatedAt time.Time `json:"updated_at"` - CreatedAt time.Time `json:"created_at"` - Score float64 `json:"score,omitempty"` - MatchType MatchType `json:"match_type,omitempty"` - ChunkType ChunkType `json:"chunk_type"` + ID string `json:"id"` + ChunkID string `json:"chunk_id"` + KnowledgeID string `json:"knowledge_id"` + KnowledgeBaseID string `json:"knowledge_base_id"` + TagID string `json:"tag_id"` + IsEnabled bool `json:"is_enabled"` + IsRecommended bool `json:"is_recommended"` + StandardQuestion string `json:"standard_question"` + SimilarQuestions []string `json:"similar_questions"` + NegativeQuestions []string `json:"negative_questions"` + Answers []string `json:"answers"` + AnswerStrategy AnswerStrategy `json:"answer_strategy"` + IndexMode FAQIndexMode `json:"index_mode"` + UpdatedAt time.Time `json:"updated_at"` + CreatedAt time.Time `json:"created_at"` + Score float64 `json:"score,omitempty"` + MatchType MatchType `json:"match_type,omitempty"` + ChunkType ChunkType `json:"chunk_type"` } // FAQEntryPayload 用于创建/更新 FAQ 条目的 payload type FAQEntryPayload struct { - StandardQuestion string `json:"standard_question" binding:"required"` - SimilarQuestions []string `json:"similar_questions"` - NegativeQuestions []string `json:"negative_questions"` - Answers []string `json:"answers" binding:"required"` - TagID string `json:"tag_id"` - TagName string `json:"tag_name"` - IsEnabled *bool `json:"is_enabled,omitempty"` - IsRecommended *bool `json:"is_recommended,omitempty"` + StandardQuestion string `json:"standard_question" binding:"required"` + SimilarQuestions []string `json:"similar_questions"` + NegativeQuestions []string `json:"negative_questions"` + Answers []string `json:"answers" binding:"required"` + AnswerStrategy *AnswerStrategy `json:"answer_strategy,omitempty"` + TagID string `json:"tag_id"` + TagName string `json:"tag_name"` + IsEnabled *bool `json:"is_enabled,omitempty"` + IsRecommended *bool `json:"is_recommended,omitempty"` } const (