643 lines
25 KiB
Python
643 lines
25 KiB
Python
"""
|
||
优化建议引擎 - 基于品牌数据差距提供可操作的优化建议
|
||
|
||
建议类型:
|
||
- content_optimization: 内容优化建议(如何让AI更容易引用你的内容)
|
||
- platform_targeting: 平台定向建议(哪些平台需要重点优化)
|
||
- competitor_gap: 竞品差距建议(如何缩小与竞品的差距)
|
||
- query_expansion: 查询词扩展建议(应该添加哪些查询词)
|
||
- citation_improvement: 引用改善建议(如何增加被引用的概率)
|
||
|
||
生成逻辑:
|
||
1. 基于评分V2的五维度数据,找出最弱的维度
|
||
2. 基于竞品对比数据,找出差距最大的方面
|
||
3. 基于情感分析数据,找出负面情感的来源
|
||
4. 使用DeepSeek API生成个性化建议(ENABLE_LLM控制开关)
|
||
5. 无LLM时使用基于规则的模板建议
|
||
"""
|
||
from __future__ import annotations
|
||
|
||
import asyncio
|
||
import json
|
||
import logging
|
||
import uuid
|
||
from dataclasses import dataclass, field
|
||
from typing import Any
|
||
|
||
from app.config import settings
|
||
from app.services.scoring.scoring_service import ScoringResultV2
|
||
from app.utils.json_extractor import extract_json
|
||
|
||
logger = logging.getLogger(__name__)
|
||
|
||
|
||
# ============================================================
|
||
# 建议数据结构
|
||
# ============================================================
|
||
|
||
@dataclass
|
||
class SuggestionItem:
|
||
"""单条优化建议"""
|
||
type: str # content_optimization/platform_targeting/competitor_gap/query_expansion/citation_improvement
|
||
priority: str # high/medium/low
|
||
title: str # 建议标题
|
||
description: str # 详细描述
|
||
action: str # 具体操作步骤
|
||
expected_impact: str # 预期效果
|
||
difficulty: str # easy/medium/hard
|
||
|
||
|
||
@dataclass
|
||
class BrandAnalysisContext:
|
||
"""品牌分析上下文 - 用于生成建议的输入数据"""
|
||
brand_name: str
|
||
overall_score: float
|
||
# 五维度评分
|
||
mention_rate_score: float = 0.0
|
||
mention_rate_max: float = 25.0
|
||
mention_rate_percentage: float = 0.0
|
||
rank_score: float = 0.0
|
||
rank_max: float = 25.0
|
||
rank_percentage: float = 0.0
|
||
sentiment_score: float = 0.0
|
||
sentiment_max: float = 20.0
|
||
sentiment_percentage: float = 0.0
|
||
citation_score: float = 0.0
|
||
citation_max: float = 15.0
|
||
citation_percentage: float = 0.0
|
||
competitive_score: float = 0.0
|
||
competitive_max: float = 15.0
|
||
competitive_percentage: float = 0.0
|
||
# 竞品对比数据
|
||
competitor_data: dict[str, Any] = field(default_factory=dict)
|
||
# 情感分析数据
|
||
sentiment_data: dict[str, int] = field(default_factory=dict)
|
||
# 平台评分数据
|
||
platform_scores: dict[str, float] = field(default_factory=dict)
|
||
# 查询词数据
|
||
total_queries: int = 0
|
||
mentioned_count: int = 0
|
||
|
||
|
||
# ============================================================
|
||
# 基于规则的建议生成器
|
||
# ============================================================
|
||
|
||
def _get_weakest_dimensions(ctx: BrandAnalysisContext) -> list[tuple[str, float, float]]:
|
||
"""
|
||
找出最弱的维度,按得分率升序排列。
|
||
|
||
Returns:
|
||
[(维度名, 得分率, 得分), ...]
|
||
"""
|
||
dimensions = [
|
||
("提及率", ctx.mention_rate_percentage, ctx.mention_rate_score),
|
||
("推荐排名", ctx.rank_percentage, ctx.rank_score),
|
||
("情感倾向", ctx.sentiment_percentage, ctx.sentiment_score),
|
||
("引用质量", ctx.citation_percentage, ctx.citation_score),
|
||
("竞品对比", ctx.competitive_percentage, ctx.competitive_score),
|
||
]
|
||
return sorted(dimensions, key=lambda x: x[1])
|
||
|
||
|
||
def _generate_content_optimization_suggestions(
|
||
ctx: BrandAnalysisContext,
|
||
) -> list[SuggestionItem]:
|
||
"""生成内容优化建议"""
|
||
suggestions: list[SuggestionItem] = []
|
||
|
||
# 提及率低 -> 内容优化
|
||
if ctx.mention_rate_percentage < 50:
|
||
suggestions.append(SuggestionItem(
|
||
type="content_optimization",
|
||
priority="high",
|
||
title="提升品牌内容在AI平台的可见性",
|
||
description=(
|
||
f"当前提及率仅{ctx.mention_rate_percentage:.0f}%,"
|
||
f"品牌在AI回答中被提及的频率较低。"
|
||
f"AI平台倾向于引用结构化、权威性强的内容。"
|
||
),
|
||
action=(
|
||
"1. 在官网和核心页面添加FAQ结构化数据(Schema.org FAQPage)\n"
|
||
"2. 创建行业白皮书和深度分析文章,使用清晰的标题层级\n"
|
||
"3. 确保品牌在维基百科、行业百科等权威来源有准确条目\n"
|
||
"4. 优化内容中的品牌名称一致性,避免过多别名导致AI无法识别"
|
||
),
|
||
expected_impact="预计可将提及率提升15-25个百分点",
|
||
difficulty="medium",
|
||
))
|
||
elif ctx.mention_rate_percentage < 75:
|
||
suggestions.append(SuggestionItem(
|
||
type="content_optimization",
|
||
priority="medium",
|
||
title="持续优化品牌内容结构",
|
||
description=(
|
||
f"当前提及率为{ctx.mention_rate_percentage:.0f}%,仍有提升空间。"
|
||
f"建议进一步优化内容结构以提高AI引用概率。"
|
||
),
|
||
action=(
|
||
"1. 增加品牌相关的高质量长文内容(2000字以上)\n"
|
||
"2. 在内容中增加数据支撑和案例引用\n"
|
||
"3. 定期更新内容以保持时效性"
|
||
),
|
||
expected_impact="预计可将提及率提升5-15个百分点",
|
||
difficulty="easy",
|
||
))
|
||
|
||
return suggestions
|
||
|
||
|
||
def _generate_platform_targeting_suggestions(
|
||
ctx: BrandAnalysisContext,
|
||
) -> list[SuggestionItem]:
|
||
"""生成平台定向建议"""
|
||
suggestions: list[SuggestionItem] = []
|
||
|
||
# 找出评分最低的平台
|
||
if ctx.platform_scores:
|
||
weak_platforms = sorted(
|
||
ctx.platform_scores.items(),
|
||
key=lambda x: x[1],
|
||
)[:3]
|
||
weak_platform_names = [p[0] for p in weak_platforms if p[1] < 40]
|
||
weak_platform_str = "、".join(weak_platform_names) if weak_platform_names else ""
|
||
|
||
if weak_platform_str:
|
||
suggestions.append(SuggestionItem(
|
||
type="platform_targeting",
|
||
priority="high",
|
||
title=f"重点优化{weak_platform_str}平台表现",
|
||
description=(
|
||
f"在这些平台上品牌评分低于40分,"
|
||
f"AI引用率极低。不同AI平台有不同的内容偏好,"
|
||
f"需要针对性优化。"
|
||
),
|
||
action=(
|
||
f"1. 分析{weak_platform_str}平台的内容偏好和引用模式\n"
|
||
"2. 针对各平台优化内容格式和表达方式\n"
|
||
"3. 增加在这些平台关联的内容源数量\n"
|
||
"4. 关注平台算法更新,及时调整优化策略"
|
||
),
|
||
expected_impact="预计可将弱平台评分提升20-30分",
|
||
difficulty="hard",
|
||
))
|
||
|
||
# 如果没有平台数据
|
||
if not ctx.platform_scores or all(v == 0 for v in ctx.platform_scores.values()):
|
||
suggestions.append(SuggestionItem(
|
||
type="platform_targeting",
|
||
priority="high",
|
||
title="启动全平台品牌监控",
|
||
description=(
|
||
"当前没有任何平台的引用数据。"
|
||
"需要先在各AI平台建立品牌存在感。"
|
||
),
|
||
action=(
|
||
"1. 确保已添加足够的查询词覆盖核心业务关键词\n"
|
||
"2. 等待系统完成首轮数据采集(通常需要1-2天)\n"
|
||
"3. 采集完成后查看各平台评分,确定优先优化方向"
|
||
),
|
||
expected_impact="获取全平台基准数据,为后续优化提供方向",
|
||
difficulty="easy",
|
||
))
|
||
|
||
return suggestions
|
||
|
||
|
||
def _generate_competitor_gap_suggestions(
|
||
ctx: BrandAnalysisContext,
|
||
) -> list[SuggestionItem]:
|
||
"""生成竞品差距建议"""
|
||
suggestions: list[SuggestionItem] = []
|
||
|
||
# 竞品对比维度弱
|
||
if ctx.competitive_percentage < 40:
|
||
# 找出领先的品牌
|
||
ahead_competitors = []
|
||
behind_competitors = []
|
||
if ctx.competitor_data:
|
||
brand_mentions = ctx.competitor_data.get("brand_mentions", 0)
|
||
for name, count in ctx.competitor_data.get("competitor_mentions", {}).items():
|
||
if count > brand_mentions:
|
||
ahead_competitors.append((name, count))
|
||
else:
|
||
behind_competitors.append((name, count))
|
||
|
||
ahead_str = "、".join([n for n, _ in ahead_competitors[:3]]) if ahead_competitors else "竞品"
|
||
|
||
suggestions.append(SuggestionItem(
|
||
type="competitor_gap",
|
||
priority="high",
|
||
title=f"缩小与{ahead_str}的差距",
|
||
description=(
|
||
f"当前竞品对比得分率仅{ctx.competitive_percentage:.0f}%,"
|
||
f"品牌在AI引用中落后于主要竞品。"
|
||
f"需要分析竞品的优势领域并制定追赶策略。"
|
||
),
|
||
action=(
|
||
"1. 分析竞品在AI平台被引用的内容类型和话题\n"
|
||
"2. 找出竞品有而品牌缺失的内容领域\n"
|
||
"3. 针对性创建竞品优势领域的优质内容\n"
|
||
"4. 加强品牌差异化定位,突出独特价值主张"
|
||
),
|
||
expected_impact="预计3-6个月内可将竞品对比得分率提升15-25个百分点",
|
||
difficulty="hard",
|
||
))
|
||
elif ctx.competitive_percentage < 70:
|
||
suggestions.append(SuggestionItem(
|
||
type="competitor_gap",
|
||
priority="medium",
|
||
title="持续巩固竞争优势",
|
||
description=(
|
||
f"当前竞品对比得分率为{ctx.competitive_percentage:.0f}%,"
|
||
f"品牌处于中等水平,需要持续巩固并扩大优势。"
|
||
),
|
||
action=(
|
||
"1. 定期监控竞品动态和内容更新\n"
|
||
"2. 在品牌优势领域持续输出高质量内容\n"
|
||
"3. 关注新兴话题和趋势,抢占先机"
|
||
),
|
||
expected_impact="预计可将竞品对比得分率提升10-15个百分点",
|
||
difficulty="medium",
|
||
))
|
||
|
||
return suggestions
|
||
|
||
|
||
def _generate_query_expansion_suggestions(
|
||
ctx: BrandAnalysisContext,
|
||
) -> list[SuggestionItem]:
|
||
"""生成查询词扩展建议"""
|
||
suggestions: list[SuggestionItem] = []
|
||
|
||
# 查询词数量不足
|
||
if ctx.total_queries < 10:
|
||
suggestions.append(SuggestionItem(
|
||
type="query_expansion",
|
||
priority="high" if ctx.total_queries < 3 else "medium",
|
||
title="扩展查询词覆盖范围",
|
||
description=(
|
||
f"当前仅有{ctx.total_queries}个查询词,"
|
||
f"覆盖范围不足,无法全面反映品牌在AI搜索中的表现。"
|
||
f"更多查询词意味着更全面的品牌认知度画像。"
|
||
),
|
||
action=(
|
||
"1. 添加行业核心关键词(如:'XX行业推荐'、'XX解决方案')\n"
|
||
"2. 添加品牌相关长尾词(如:'XX品牌怎么样'、'XX vs 竞品')\n"
|
||
"3. 添加场景化查询词(如:'XX场景下选什么产品')\n"
|
||
"4. 建议至少添加10-20个查询词以获得可靠的分析结果"
|
||
),
|
||
expected_impact="更多查询词可提升评分准确度,发现更多优化机会",
|
||
difficulty="easy",
|
||
))
|
||
|
||
# 提及率低且查询词不少 -> 可能需要优化查询词质量
|
||
elif ctx.total_queries >= 10 and ctx.mention_rate_percentage < 50:
|
||
suggestions.append(SuggestionItem(
|
||
type="query_expansion",
|
||
priority="medium",
|
||
title="优化查询词质量和相关性",
|
||
description=(
|
||
f"已有{ctx.total_queries}个查询词,但提及率仅{ctx.mention_rate_percentage:.0f}%。"
|
||
f"可能是查询词与品牌核心业务关联度不够。"
|
||
),
|
||
action=(
|
||
"1. 检查现有查询词是否覆盖品牌核心业务场景\n"
|
||
"2. 添加品牌擅长的专业领域相关查询词\n"
|
||
"3. 删除与品牌无关的宽泛查询词\n"
|
||
"4. 增加品牌独特卖点和差异化优势相关的查询词"
|
||
),
|
||
expected_impact="优化查询词后可提升提及率10-20个百分点",
|
||
difficulty="easy",
|
||
))
|
||
|
||
return suggestions
|
||
|
||
|
||
def _generate_citation_improvement_suggestions(
|
||
ctx: BrandAnalysisContext,
|
||
) -> list[SuggestionItem]:
|
||
"""生成引用改善建议"""
|
||
suggestions: list[SuggestionItem] = []
|
||
|
||
# 引用质量低
|
||
if ctx.citation_percentage < 40:
|
||
suggestions.append(SuggestionItem(
|
||
type="citation_improvement",
|
||
priority="high",
|
||
title="提升AI引用内容的质量和深度",
|
||
description=(
|
||
f"当前引用质量得分率仅{ctx.citation_percentage:.0f}%,"
|
||
f"AI对品牌的引用多为浅层提及,缺乏深度正面描述。"
|
||
f"高质量引用能显著影响用户决策。"
|
||
),
|
||
action=(
|
||
"1. 创建详细的产品对比页面,包含数据表格和评测结论\n"
|
||
"2. 发布客户案例和成功故事,提供具体数据支撑\n"
|
||
"3. 在内容中增加可引用的数据点和统计信息\n"
|
||
"4. 优化内容结构,使用AI易于提取的格式(列表、表格、要点)"
|
||
),
|
||
expected_impact="预计可将引用质量得分率提升15-25个百分点",
|
||
difficulty="medium",
|
||
))
|
||
|
||
# 推荐排名低
|
||
if ctx.rank_percentage < 40:
|
||
suggestions.append(SuggestionItem(
|
||
type="citation_improvement",
|
||
priority="high",
|
||
title="提升品牌在AI推荐中的排名位置",
|
||
description=(
|
||
f"当前推荐排名得分率仅{ctx.rank_percentage:.0f}%,"
|
||
f"品牌在AI推荐列表中排名靠后,用户看到概率低。"
|
||
f"排名越靠前,被用户选择的可能性越大。"
|
||
),
|
||
action=(
|
||
"1. 增加品牌在权威第三方平台的正面评价和推荐\n"
|
||
"2. 优化品牌在行业榜单和评测中的排名\n"
|
||
"3. 创建'最佳XX'、'XX推荐'类内容,增加被推荐概率\n"
|
||
"4. 提升品牌官网的SEO表现,增加AI爬取到的概率"
|
||
),
|
||
expected_impact="预计可将推荐排名提升2-3位",
|
||
difficulty="medium",
|
||
))
|
||
|
||
# 情感倾向差
|
||
negative_rate = 0.0
|
||
total_sentiment = sum(ctx.sentiment_data.values())
|
||
if total_sentiment > 0:
|
||
negative_rate = ctx.sentiment_data.get("negative", 0) / total_sentiment
|
||
|
||
if negative_rate > 0.3:
|
||
suggestions.append(SuggestionItem(
|
||
type="citation_improvement",
|
||
priority="high",
|
||
title="改善AI平台对品牌的负面评价",
|
||
description=(
|
||
f"当前负面评价占比{negative_rate:.0%},"
|
||
f"AI在引用品牌时倾向使用负面表述。"
|
||
f"负面引用会严重影响用户对品牌的印象。"
|
||
),
|
||
action=(
|
||
"1. 分析负面引用的具体内容,找出主要批评点\n"
|
||
"2. 针对性改进产品或服务,解决用户痛点\n"
|
||
"3. 主动发布正面内容,稀释负面信息的影响\n"
|
||
"4. 在官方渠道积极回应用户反馈和投诉"
|
||
),
|
||
expected_impact="减少负面引用比例10-20个百分点",
|
||
difficulty="hard",
|
||
))
|
||
|
||
return suggestions
|
||
|
||
|
||
def generate_rule_based_suggestions(
|
||
ctx: BrandAnalysisContext,
|
||
) -> list[SuggestionItem]:
|
||
"""
|
||
基于规则生成优化建议(不依赖LLM)
|
||
|
||
分析品牌数据差距,按照优先级生成可操作的建议。
|
||
"""
|
||
all_suggestions: list[SuggestionItem] = []
|
||
|
||
# 收集各类建议
|
||
all_suggestions.extend(_generate_content_optimization_suggestions(ctx))
|
||
all_suggestions.extend(_generate_platform_targeting_suggestions(ctx))
|
||
all_suggestions.extend(_generate_competitor_gap_suggestions(ctx))
|
||
all_suggestions.extend(_generate_query_expansion_suggestions(ctx))
|
||
all_suggestions.extend(_generate_citation_improvement_suggestions(ctx))
|
||
|
||
# 按优先级排序: high > medium > low
|
||
priority_order = {"high": 0, "medium": 1, "low": 2}
|
||
all_suggestions.sort(key=lambda s: priority_order.get(s.priority, 1))
|
||
|
||
# 限制最多5条建议
|
||
return all_suggestions[:5]
|
||
|
||
|
||
# ============================================================
|
||
# LLM 建议生成器
|
||
# ============================================================
|
||
|
||
OPTIMIZATION_PROMPT = """你是一个GEO(生成式引擎优化)专家。基于以下品牌数据,提供3-5条可操作的优化建议。
|
||
|
||
品牌: {brand_name}
|
||
当前评分: {overall_score}/100
|
||
评分维度:
|
||
- 提及率: {mention_rate_score}/{mention_rate_max} ({mention_rate_percentage}%)
|
||
- 推荐排名: {rank_score}/{rank_max} ({rank_percentage}%)
|
||
- 情感倾向: {sentiment_score}/{sentiment_max} ({sentiment_percentage}%)
|
||
- 引用质量: {citation_score}/{citation_max} ({citation_percentage}%)
|
||
- 竞品对比: {competitive_score}/{competitive_max} ({competitive_percentage}%)
|
||
|
||
竞品对比数据:
|
||
{competitor_data_str}
|
||
|
||
情感分析:
|
||
{sentiment_data_str}
|
||
|
||
平台评分:
|
||
{platform_data_str}
|
||
|
||
请返回JSON格式:
|
||
{{
|
||
"suggestions": [
|
||
{{
|
||
"type": "content_optimization" | "platform_targeting" | "competitor_gap" | "query_expansion" | "citation_improvement",
|
||
"priority": "high" | "medium" | "low",
|
||
"title": "建议标题",
|
||
"description": "详细描述",
|
||
"action": "具体操作步骤",
|
||
"expected_impact": "预期效果",
|
||
"difficulty": "easy" | "medium" | "hard"
|
||
}}
|
||
]
|
||
}}
|
||
|
||
要求:
|
||
1. 每条建议必须基于数据,指出具体的差距和改进方向
|
||
2. 优先关注最弱的维度
|
||
3. 建议必须可操作,包含具体步骤
|
||
4. 预期效果要量化
|
||
5. 返回3-5条建议,按优先级从高到低排列
|
||
"""
|
||
|
||
|
||
async def generate_llm_suggestions(
|
||
ctx: BrandAnalysisContext,
|
||
) -> list[SuggestionItem]:
|
||
"""
|
||
使用DeepSeek LLM生成个性化优化建议
|
||
|
||
如果LLM不可用或调用失败,回退到规则生成。
|
||
"""
|
||
if not settings.ENABLE_LLM or not settings.DEEPSEEK_API_KEY:
|
||
logger.info("LLM未启用或API Key未配置,使用规则生成建议")
|
||
return generate_rule_based_suggestions(ctx)
|
||
|
||
try:
|
||
# 构建prompt
|
||
competitor_data_str = json.dumps(ctx.competitor_data, ensure_ascii=False, indent=2)
|
||
sentiment_data_str = json.dumps(ctx.sentiment_data, ensure_ascii=False, indent=2)
|
||
platform_data_str = json.dumps(ctx.platform_scores, ensure_ascii=False, indent=2)
|
||
|
||
prompt = OPTIMIZATION_PROMPT.format(
|
||
brand_name=ctx.brand_name,
|
||
overall_score=ctx.overall_score,
|
||
mention_rate_score=round(ctx.mention_rate_score, 2),
|
||
mention_rate_max=ctx.mention_rate_max,
|
||
mention_rate_percentage=round(ctx.mention_rate_percentage, 1),
|
||
rank_score=round(ctx.rank_score, 2),
|
||
rank_max=ctx.rank_max,
|
||
rank_percentage=round(ctx.rank_percentage, 1),
|
||
sentiment_score=round(ctx.sentiment_score, 2),
|
||
sentiment_max=ctx.sentiment_max,
|
||
sentiment_percentage=round(ctx.sentiment_percentage, 1),
|
||
citation_score=round(ctx.citation_score, 2),
|
||
citation_max=ctx.citation_max,
|
||
citation_percentage=round(ctx.citation_percentage, 1),
|
||
competitive_score=round(ctx.competitive_score, 2),
|
||
competitive_max=ctx.competitive_max,
|
||
competitive_percentage=round(ctx.competitive_percentage, 1),
|
||
competitor_data_str=competitor_data_str,
|
||
sentiment_data_str=sentiment_data_str,
|
||
platform_data_str=platform_data_str,
|
||
)
|
||
|
||
# 调用DeepSeek API
|
||
from openai import OpenAI
|
||
|
||
client = OpenAI(
|
||
api_key=settings.DEEPSEEK_API_KEY,
|
||
base_url="https://api.deepseek.com",
|
||
)
|
||
|
||
response = await asyncio.to_thread(
|
||
client.chat.completions.create,
|
||
model="deepseek-chat",
|
||
messages=[{"role": "user", "content": prompt}],
|
||
temperature=0.3,
|
||
max_tokens=2000,
|
||
)
|
||
|
||
content = response.choices[0].message.content
|
||
if not content:
|
||
raise ValueError("LLM返回空响应")
|
||
|
||
# 提取JSON
|
||
json_str = extract_json(content)
|
||
result = json.loads(json_str)
|
||
|
||
# 解析建议
|
||
suggestions: list[SuggestionItem] = []
|
||
for item in result.get("suggestions", []):
|
||
# 验证type字段
|
||
valid_types = {
|
||
"content_optimization", "platform_targeting",
|
||
"competitor_gap", "query_expansion", "citation_improvement",
|
||
}
|
||
suggestion_type = item.get("type", "content_optimization")
|
||
if suggestion_type not in valid_types:
|
||
suggestion_type = "content_optimization"
|
||
|
||
# 验证priority字段
|
||
valid_priorities = {"high", "medium", "low"}
|
||
priority = item.get("priority", "medium")
|
||
if priority not in valid_priorities:
|
||
priority = "medium"
|
||
|
||
# 验证difficulty字段
|
||
valid_difficulties = {"easy", "medium", "hard"}
|
||
difficulty = item.get("difficulty", "medium")
|
||
if difficulty not in valid_difficulties:
|
||
difficulty = "medium"
|
||
|
||
suggestions.append(SuggestionItem(
|
||
type=suggestion_type,
|
||
priority=priority,
|
||
title=item.get("title", "优化建议"),
|
||
description=item.get("description", ""),
|
||
action=item.get("action", ""),
|
||
expected_impact=item.get("expected_impact", ""),
|
||
difficulty=difficulty,
|
||
))
|
||
|
||
if not suggestions:
|
||
logger.warning("LLM未返回有效建议,回退到规则生成")
|
||
return generate_rule_based_suggestions(ctx)
|
||
|
||
return suggestions[:5]
|
||
|
||
except Exception as e:
|
||
logger.error(f"LLM生成建议失败: {e},回退到规则生成")
|
||
return generate_rule_based_suggestions(ctx)
|
||
|
||
|
||
# ============================================================
|
||
# 主入口:生成优化建议
|
||
# ============================================================
|
||
|
||
async def generate_suggestions(
|
||
ctx: BrandAnalysisContext,
|
||
) -> list[SuggestionItem]:
|
||
"""
|
||
生成优化建议
|
||
|
||
如果ENABLE_LLM=True且有DeepSeek API Key,使用LLM生成个性化建议。
|
||
否则使用基于规则的模板建议。
|
||
"""
|
||
if settings.ENABLE_LLM and settings.DEEPSEEK_API_KEY:
|
||
return await generate_llm_suggestions(ctx)
|
||
return generate_rule_based_suggestions(ctx)
|
||
|
||
|
||
def build_context_from_scoring_result(
|
||
brand_name: str,
|
||
scoring_result: ScoringResultV2,
|
||
competitor_data: dict[str, Any] | None = None,
|
||
sentiment_data: dict[str, int] | None = None,
|
||
platform_scores: dict[str, float] | None = None,
|
||
total_queries: int = 0,
|
||
mentioned_count: int = 0,
|
||
) -> BrandAnalysisContext:
|
||
"""
|
||
从ScoringResultV2构建BrandAnalysisContext
|
||
|
||
Args:
|
||
brand_name: 品牌名称
|
||
scoring_result: V2评分结果
|
||
competitor_data: 竞品对比数据
|
||
sentiment_data: 情感分析数据
|
||
platform_scores: 平台评分数据
|
||
total_queries: 总查询次数
|
||
mentioned_count: 被提及次数
|
||
|
||
Returns:
|
||
BrandAnalysisContext: 品牌分析上下文
|
||
"""
|
||
return BrandAnalysisContext(
|
||
brand_name=brand_name,
|
||
overall_score=scoring_result.overall_score,
|
||
mention_rate_score=scoring_result.mention_rate.score,
|
||
mention_rate_max=scoring_result.mention_rate.max_score,
|
||
mention_rate_percentage=scoring_result.mention_rate.percentage,
|
||
rank_score=scoring_result.recommendation_rank.score,
|
||
rank_max=scoring_result.recommendation_rank.max_score,
|
||
rank_percentage=scoring_result.recommendation_rank.percentage,
|
||
sentiment_score=scoring_result.sentiment_score.score,
|
||
sentiment_max=scoring_result.sentiment_score.max_score,
|
||
sentiment_percentage=scoring_result.sentiment_score.percentage,
|
||
citation_score=scoring_result.citation_quality.score,
|
||
citation_max=scoring_result.citation_quality.max_score,
|
||
citation_percentage=scoring_result.citation_quality.percentage,
|
||
competitive_score=scoring_result.competitive_position.score,
|
||
competitive_max=scoring_result.competitive_position.max_score,
|
||
competitive_percentage=scoring_result.competitive_position.percentage,
|
||
competitor_data=competitor_data or {},
|
||
sentiment_data=sentiment_data or {},
|
||
platform_scores=platform_scores or {},
|
||
total_queries=total_queries,
|
||
mentioned_count=mentioned_count,
|
||
)
|