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
@@ -255,6 +255,10 @@ func (s *KolGenerationService) enqueue(ctx context.Context, input kolGenerationE
if row.PublishedRevisionNo != nil && *row.PublishedRevisionNo > 0 {
promptRevisionNo = *row.PublishedRevisionNo
}
brandPromptContext, err := loadBrandPromptContext(ctx, s.pool, actor.TenantID, input.BrandID)
if err != nil {
return nil, err
}
balance, err := s.quotaRepo.GetCurrentBalance(ctx, actor.TenantID, "article_generation")
if err != nil || balance < 1 {
@@ -271,12 +275,13 @@ func (s *KolGenerationService) enqueue(ctx context.Context, input kolGenerationE
"prompt_revision_no": promptRevisionNo,
"variables": variables,
"enable_web_search": enableWebSearch,
"knowledge_group_ids": knowledgeGroupIDs,
"schema_snapshot": json.RawMessage(prompt.SchemaJSON),
"card_config_snapshot": json.RawMessage(prompt.CardConfigJSON),
"rendered_prompt_hash": renderedPromptHash,
"rendered_prompt": renderedPrompt,
}
attachKolGenerationKnowledgeScope(taskInput, input.BrandID, knowledgeGroupIDs)
taskInput = attachBrandPromptContext(taskInput, brandPromptContext)
if input.Schedule != nil {
attachKolScheduleTaskInput(taskInput, *input.Schedule)
}
@@ -446,6 +451,14 @@ func (s *KolGenerationService) enqueue(ctx context.Context, input kolGenerationE
}, nil
}
func attachKolGenerationKnowledgeScope(input map[string]any, brandID int64, knowledgeGroupIDs []int64) {
if input == nil {
return
}
input["brand_id"] = brandID
input["knowledge_group_ids"] = normalizeInt64IDs(knowledgeGroupIDs)
}
func attachKolScheduleTaskInput(input map[string]any, schedule ScheduleKolGenerationInput) {
input["generation_mode"] = "schedule"
input["schedule_task_id"] = schedule.ScheduleTaskID