feat(knowledge): multi-query retrieval with LLM query rewrite

Add a shared structured query builder (knowledge_query.go) and switch
all generation paths to it, so retrieval queries carry task intent
(type, brand, region, keywords, key points) instead of a raw prompt.

ResolveContext now rewrites the query into multiple sub-questions via
the URL-markdown model, embeds and searches each, and merges/dedupes
candidates before rerank; falls back to parsed fallback questions when
rewrite is unavailable or returns too few. Resolve logs gain query
list, count, and rewrite stage/model for observability.

- knowledge_query.go/_test.go: BuildKnowledgeQuery + intent normalization
- knowledge_service.go: per-query embed/search loop, query rewrite, logs
- template_prompt/template_service/prompt_generate/article_imitation/
  kol_generation_worker: adopt the shared query builder
- generation_observability: richer task error logging
This commit is contained in:
2026-06-02 14:50:12 +08:00
parent 85bb373a8b
commit ba2f117265
11 changed files with 734 additions and 102 deletions
@@ -1,11 +1,14 @@
package app
import (
"errors"
"strings"
"sync/atomic"
"time"
"go.uber.org/zap"
"github.com/geo-platform/tenant-api/internal/shared/response"
)
const GenerationTaskStatusRunning = "running"
@@ -103,6 +106,30 @@ func LogGenerationTaskError(logger *zap.Logger, message string, ctx GenerationTa
logger.Error(message, append(GenerationTaskLogFields(ctx), fields...)...)
}
func GenerationTaskErrorFields(err error) []zap.Field {
if err == nil {
return []zap.Field{}
}
fields := []zap.Field{zap.Error(err)}
var appErr *response.AppError
if errors.As(err, &appErr) {
if appErr.Code > 0 {
fields = append(fields, zap.Int("error_code", appErr.Code))
}
if appErr.HTTPStatus > 0 {
fields = append(fields, zap.Int("http_status", appErr.HTTPStatus))
}
if message := strings.TrimSpace(appErr.Message); message != "" {
fields = append(fields, zap.String("error_message", message))
}
if detail := strings.TrimSpace(appErr.Detail); detail != "" {
fields = append(fields, zap.String("error_detail", detail))
}
}
return fields
}
type GenerationTaskMetricsSnapshot struct {
TransitionTotal int64 `json:"transition_total"`
FailureTotal int64 `json:"failure_total"`