feat(questions): make brand questions the primary monitoring & template axis
- add /questions/combination-fill endpoint with AI-driven, IP-region-aware matrix fill - extract ip2region resolver from ops/app into shared/ipregion for cross-service use - thread question_id filter through dashboard composite, citation summary, and collect-now - switch template wizard from keyword inputs to brand-question selection (primary + supplemental) - pass brand_question and supplemental_questions through assist/title/outline prompts - add AiWaitingModal + 45s client timeout and request_timeout error mapping for long AI flows Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,75 @@
|
||||
package app
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"strings"
|
||||
)
|
||||
|
||||
var questionCombinationFillSchema = []byte(`{
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"region": {
|
||||
"type": "array",
|
||||
"maxItems": 4,
|
||||
"items": { "type": "string", "minLength": 1, "maxLength": 16 }
|
||||
},
|
||||
"prefix": {
|
||||
"type": "array",
|
||||
"maxItems": 4,
|
||||
"items": { "type": "string", "minLength": 1, "maxLength": 16 }
|
||||
},
|
||||
"core": {
|
||||
"type": "array",
|
||||
"maxItems": 4,
|
||||
"items": { "type": "string", "minLength": 1, "maxLength": 18 }
|
||||
},
|
||||
"industry": {
|
||||
"type": "array",
|
||||
"maxItems": 4,
|
||||
"items": { "type": "string", "minLength": 1, "maxLength": 18 }
|
||||
},
|
||||
"suffix": {
|
||||
"type": "array",
|
||||
"maxItems": 4,
|
||||
"items": { "type": "string", "minLength": 1, "maxLength": 16 }
|
||||
}
|
||||
},
|
||||
"required": ["region", "prefix", "core", "industry", "suffix"]
|
||||
}`)
|
||||
|
||||
func buildQuestionCombinationFillPrompt(brandCtx *questionBrandContext, regionTerms []string, req QuestionCombinationFillRequest) string {
|
||||
competitors := "无"
|
||||
if len(brandCtx.CompetitorNames) > 0 {
|
||||
encoded, _ := json.Marshal(brandCtx.CompetitorNames)
|
||||
competitors = string(encoded)
|
||||
}
|
||||
hints, _ := json.Marshal(req)
|
||||
return fmt.Sprintf(`你是中国 GEO 搜索问题拓展策略师。请为“拓词工具”的 5 个词列生成高频、核心、可组合的问题词组。
|
||||
|
||||
【品牌信息】
|
||||
- 品牌: %s
|
||||
- 官网: %s
|
||||
- 描述: %s
|
||||
- 竞品: %s
|
||||
|
||||
【地域优先词】
|
||||
%s
|
||||
|
||||
【当前输入,仅作为语义参考,可改写】
|
||||
%s
|
||||
|
||||
【输出要求】
|
||||
1. region 使用地域优先词,不要编造不存在的城市;没有有效地域时可输出全国、本地、华东等泛地域词。
|
||||
2. prefix/core/industry/suffix 各输出 3 到 4 个词,必须短、热、可直接拼成搜索问题。
|
||||
3. core 要贴近品牌最核心的搜索需求;industry 要贴近行业/品类;suffix 偏“哪家好、怎么选、推荐、多少钱”等高转化问法。
|
||||
4. 不要输出完整问题,不要带标点,不要解释,严格按 JSON Schema 输出。`,
|
||||
strings.TrimSpace(brandCtx.BrandName),
|
||||
strings.TrimSpace(nilToString(brandCtx.Website)),
|
||||
strings.TrimSpace(nilToString(brandCtx.Description)),
|
||||
competitors,
|
||||
strings.Join(regionTerms, "、"),
|
||||
string(hints),
|
||||
)
|
||||
}
|
||||
Reference in New Issue
Block a user