feat(tenant): inject brand description context into every generation path

Include the current brand description in template, custom, imitation,
and KOL article-generation prompts while bounding prompt growth.

- Add a shared brand-context builder that resolves the current brand,
  compacts long descriptions, and caps rendered output at 2000 runes.
- Snapshot a structured brand prompt context on queued tasks so old
  jobs stay reproducible and retries stay stable.
- Route brand-profile facts through the existing typed fact guard so
  founding dates and year counts get the same pre-persistence validation
  as RAG facts, with brand facts outranking conflicting RAG facts.
- Fix the KOL creator to attach the brand scope its worker consumes.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-07-10 20:10:39 +08:00
parent 418b3c4998
commit 0bf4b73ebc
12 changed files with 998 additions and 32 deletions
@@ -332,13 +332,14 @@ func (s *PromptRuleGenerationService) ProcessQueuedTask(ctx context.Context, tas
}
job := promptRuleGenerationJob{
OperatorID: task.OperatorID,
TenantID: task.TenantID,
ArticleID: task.ArticleID,
TaskID: task.ID,
ReservationID: task.QuotaReservationID,
InputParams: inputParams,
InitialTitle: initialTitle,
OperatorID: task.OperatorID,
TenantID: task.TenantID,
ArticleID: task.ArticleID,
TaskID: task.ID,
ReservationID: task.QuotaReservationID,
InputParams: inputParams,
InitialTitle: initialTitle,
KnowledgeFactConstraints: extractKnowledgeFactConstraints(inputParams),
WebSearch: llm.WebSearchOptions{
Enabled: extractBool(inputParams, "enable_web_search"),
},
@@ -364,9 +365,17 @@ func (s *PromptRuleGenerationService) ProcessQueuedTask(ctx context.Context, tas
return
}
}
brandID, brandPromptContext, err := ResolveGenerationBrandPromptContext(ctx, s.pool, task.TenantID, task.ArticleID, inputParams)
if err != nil {
s.failGeneration(context.Background(), job, initialTitle, "brand_context_load", err)
return
}
inputParams["brand_id"] = brandID
inputParams = attachBrandPromptContext(inputParams, brandPromptContext)
job.InputParams = inputParams
knowledgePrompt := strings.TrimSpace(extractString(inputParams, generationPayloadKnowledgePrompt))
if knowledgePrompt == "" {
if knowledgePrompt == "" || len(job.KnowledgeFactConstraints) == 0 {
if knowledgeGroupIDs := extractKnowledgeGroupIDs(inputParams["knowledge_group_ids"]); len(knowledgeGroupIDs) > 0 {
if s.knowledge == nil {
s.failGeneration(context.Background(), job, initialTitle, "knowledge_resolve", fmt.Errorf("knowledge service is not configured"))
@@ -384,9 +393,13 @@ func (s *PromptRuleGenerationService) ProcessQueuedTask(ctx context.Context, tas
s.failGeneration(context.Background(), job, initialTitle, "knowledge_resolve", err)
return
}
knowledgePrompt = s.knowledge.RenderPromptSection(knowledgeContext)
if knowledgePrompt == "" {
knowledgePrompt = s.knowledge.RenderPromptSection(knowledgeContext)
}
job.KnowledgeFactConstraints = knowledgeContext.FactConstraints
}
}
job.KnowledgeFactConstraints = MergeKnowledgeFactConstraints(brandPromptContext.FactConstraints, job.KnowledgeFactConstraints)
job.Prompt = buildPromptRuleGenerationPrompt(rule, inputParams, knowledgePrompt)
s.executeGeneration(ctx, job)