Files
ChouJuGEO/modules/ui/tab_config_optimizer.py
T

298 lines
16 KiB
Python
Raw Normal View History

# Tab10:配置优化助手(从 geo_tool.py 迁移,通过 render_tab_config_optimizer() 供主入口调用。)
import hashlib
import streamlit as st
from modules.config_optimizer import ConfigOptimizer
def render_tab_config_optimizer(
storage,
cfg: dict,
brand: str,
advantages: str,
competitor_list: list,
build_llm,
model_defaults,
) -> None:
"""渲染 Tab10:配置优化助手。由主入口在 with tab10 内调用。"""
# 配置优化助手(与其他Tab保持一致的标题格式)
st.markdown("### 🎯 配置优化助手")
st.caption("分析品牌名和优势是否 GEO 友好,提供优化建议。优化后可一键应用到全局配置。")
# 初始化优化结果存储
if "config_optimization_result" not in st.session_state:
st.session_state.config_optimization_result = None
# 初始化配置hash(用于检测配置变化)
if "config_hash" not in st.session_state:
st.session_state.config_hash = None
# 计算当前配置的hash(使用cfg中的最新值)
brand_for_hash = cfg.get("brand", "").strip() or brand or ""
advantages_for_hash = cfg.get("advantages", "").strip() or advantages or ""
current_config_str = f"{brand_for_hash}|{advantages_for_hash}|{cfg.get('competitors', '')}"
current_config_hash = hashlib.md5(current_config_str.encode()).hexdigest()
# 如果配置变化了,清除旧的优化结果
# 但如果是因为应用版本导致的配置变化,保留优化结果
if st.session_state.config_hash != current_config_hash:
# 检查是否是应用版本导致的配置变化
if not st.session_state.get("_applying_version", False):
st.session_state.config_optimization_result = None
st.session_state.config_hash = current_config_hash
# 清除应用版本标志
st.session_state["_applying_version"] = False
# 检查配置是否有效
if not st.session_state.cfg_valid:
st.warning("⚠️ 请先在侧边栏完成配置并点击'应用配置'")
st.info("配置优化助手需要有效的配置才能进行分析。")
else:
# 显示当前配置
with st.expander("📋 当前配置", expanded=False):
col1, col2 = st.columns(2)
with col1:
brand_display = cfg.get("brand", "") or brand or "未设置"
st.markdown(f"**品牌名**{brand_display}")
with col2:
st.markdown(f"**竞品数量**{len(competitor_list)}")
advantages_display = cfg.get("advantages", "") or advantages or "未设置"
st.markdown(f"**核心优势**{advantages_display}")
if competitor_list:
st.markdown(f"**竞品列表**{', '.join(competitor_list[:5])}{'...' if len(competitor_list) > 5 else ''}")
# 分析按钮
col1, col2 = st.columns([1, 3])
with col1:
analyze_btn = st.button("🔍 分析配置优化", type="primary", use_container_width=True, key="tab10_optimize_config")
with col2:
if st.session_state.config_optimization_result:
st.success("✅ 已有优化结果,可直接查看下方建议")
# 执行分析
if analyze_btn:
with st.spinner("正在分析配置,优化建议生成中..."):
try:
optimizer = ConfigOptimizer()
# 从配置中获取品牌名、优势描述和竞品列表(确保使用最新配置)
brand_for_optimizer = cfg.get("brand", "").strip() or brand or ""
advantages_for_optimizer = cfg.get("advantages", "").strip() or advantages or ""
competitors_str = cfg.get("competitors", "")
competitor_list_for_optimizer = [c.strip() for c in competitors_str.split("\n") if c.strip()]
# 验证必要配置
if not brand_for_optimizer:
st.error("❌ 品牌名不能为空,请在侧边栏配置主品牌名称")
st.stop()
if not advantages_for_optimizer:
st.warning("⚠️ 优势描述为空,建议在侧边栏配置核心优势/卖点")
# 临时构建LLM用于分析(使用当前配置)
temp_llm = build_llm(
cfg["gen_provider"],
cfg["gen_api_key"],
model_defaults(cfg["gen_provider"]),
float(cfg.get("temperature", 0.7))
)
result = optimizer.optimize_config(
brand=brand_for_optimizer,
advantages=advantages_for_optimizer,
competitors=competitor_list_for_optimizer,
llm_chain=temp_llm
)
st.session_state.config_optimization_result = result
st.session_state.config_hash = current_config_hash
st.success("✅ 配置分析完成!")
st.rerun()
except Exception as e:
st.error(f"❌ 配置优化分析失败:{e}")
import traceback
with st.expander("查看错误详情"):
st.code(traceback.format_exc())
st.session_state.config_optimization_result = None
# 显示优化结果
if st.session_state.config_optimization_result:
result = st.session_state.config_optimization_result
if result.get("success", False):
st.markdown("---")
st.markdown("#### 📊 优化分析结果")
# 评估总结
if result.get("summary"):
st.markdown("**📝 评估总结**")
st.info(result["summary"])
# 优化建议
if result.get("suggestions"):
st.markdown("**💡 优化建议**")
suggestions = result["suggestions"]
if suggestions.get("brand", {}).get("problem"):
st.markdown("**🔸 品牌名问题**")
# 直接使用st.markdown渲染,CSS会限制标题大小
problem_text = suggestions["brand"]["problem"]
st.markdown(problem_text)
if suggestions["brand"].get("suggestion"):
st.markdown("**✅ 建议**")
suggestion_text = suggestions["brand"]["suggestion"]
st.markdown(suggestion_text)
if suggestions.get("advantages", {}).get("problem"):
st.markdown("**🔸 优势描述问题**")
problem_text = suggestions["advantages"]["problem"]
st.markdown(problem_text)
if suggestions["advantages"].get("suggestion"):
st.markdown("**✅ 建议**")
suggestion_text = suggestions["advantages"]["suggestion"]
st.markdown(suggestion_text)
# 推荐版本
recommended_versions = result.get("recommended_versions", [])
if recommended_versions:
st.markdown("**🎯 推荐版本**")
st.caption("选择最适合的版本,点击「应用版本」按钮即可更新配置")
# 检查是否有有效的推荐版本
valid_versions = [v for v in recommended_versions if v.get("brand") or v.get("advantages")]
if not valid_versions:
st.warning("⚠️ 推荐版本数据为空,可能是解析失败。请查看完整报告或重新分析。")
if result.get("raw_result"):
with st.expander("查看原始输出中的推荐版本部分"):
raw = result["raw_result"]
if "【推荐版本】" in raw:
raw_versions = raw.split("【推荐版本】")[1].split("")[0]
st.code(raw_versions)
for i, version in enumerate(recommended_versions[:3], 1):
version_name_map = {
1: "保守优化",
2: "平衡优化",
3: "激进优化"
}
version_name = version_name_map.get(i, f"版本{i}")
with st.expander(f"版本{i}{version_name}", expanded=False): # 默认不展开,用户自行选择
# 检查版本数据是否有效
has_brand = bool(version.get("brand", "").strip())
has_advantages = bool(version.get("advantages", "").strip())
has_reason = bool(version.get("reason", "").strip())
if not has_brand and not has_advantages:
st.warning("⚠️ 该版本数据不完整,请查看完整报告或重新分析")
if result.get("raw_result"):
with st.expander("查看原始输出中的该版本"):
# 尝试从原始输出中提取
raw = result["raw_result"]
if f"版本{i}" in raw:
version_raw = raw.split(f"版本{i}")[1]
if i < 3:
next_version = f"版本{i+1}"
if next_version in version_raw:
version_raw = version_raw.split(next_version)[0]
st.code(version_raw[:500]) # 显示前500字符
else:
col1, col2 = st.columns([2, 1])
with col1:
if has_brand:
st.markdown(f"**品牌名**`{version['brand']}`")
else:
st.warning("⚠️ 品牌名为空")
if has_advantages:
st.markdown(f"**优势描述**{version['advantages']}")
else:
st.warning("⚠️ 优势描述为空")
if has_reason:
st.caption(f"💭 理由:{version['reason']}")
else:
st.caption("💭 理由:未提供")
with col2:
# 应用按钮
apply_disabled = not (has_brand and has_advantages)
if st.button(
f"✅ 应用版本{i}",
key=f"tab10_apply_version_{i}",
use_container_width=True,
type="primary",
disabled=apply_disabled
):
if has_brand and has_advantages:
# 设置标志,表示正在应用版本(防止优化结果被清除)
st.session_state["_applying_version"] = True
# 更新配置
st.session_state.cfg["brand"] = version["brand"]
st.session_state.cfg["advantages"] = version["advantages"]
# 设置标志,表示需要更新侧边栏输入框
st.session_state["_pending_brand_update"] = version["brand"]
st.session_state["_pending_advantages_update"] = version["advantages"]
st.session_state.cfg_applied = False # 需要重新应用配置
st.success(f"✅ 已应用版本{i},侧边栏已更新,请点击'应用配置'以生效")
st.info("💡 配置更新后,建议重新运行关键词蒸馏和内容创作,以获得最佳效果")
st.rerun()
if apply_disabled:
st.caption("⚠️ 数据不完整,无法应用")
# 预期效果
if result.get("expected_effects"):
st.markdown("**📈 预期效果**")
effects = result["expected_effects"]
# 使用文本而不是 metric,避免内容被截断
if effects.get("mention_rate"):
st.markdown(f"- 提及率提升预期:{effects['mention_rate']}")
if effects.get("geo_friendliness"):
st.markdown(f"- GEO友好度提升:{effects['geo_friendliness']}")
# 完整报告
if result.get("raw_result"):
with st.expander("📄 查看完整分析报告", expanded=False):
st.markdown(result["raw_result"])
# 如果推荐版本为空或解析失败,显示原始输出中的推荐版本部分
recommended_versions = result.get("recommended_versions", [])
if not recommended_versions or all(
not v.get("brand") and not v.get("advantages")
for v in recommended_versions
):
st.warning("⚠️ 推荐版本解析失败,以下是原始输出中的推荐版本部分,请检查格式:")
raw = result["raw_result"]
if "【推荐版本】" in raw:
raw_versions = raw.split("【推荐版本】")[1].split("")[0]
st.code(raw_versions, language="text")
st.info("💡 如果原始输出中包含推荐版本但解析失败,请检查格式是否符合要求")
# 调试信息(可选)
if st.checkbox("🔍 显示调试信息", key="tab10_debug"):
st.markdown("#### 调试信息")
debug_info = {
"推荐版本数量": len(result.get("recommended_versions", [])),
"版本详情": result.get("recommended_versions", []),
"配置hash": st.session_state.config_hash,
"解析错误": result.get("parse_errors", [])
}
st.json(debug_info)
# 显示原始输出的关键部分
if result.get("raw_result"):
raw = result["raw_result"]
if "【推荐版本】" in raw:
st.markdown("**原始输出中的推荐版本部分:**")
raw_versions = raw.split("【推荐版本】")[1].split("")[0]
st.code(raw_versions[:1000], language="text") # 显示前1000字符
else:
st.error(f"❌ 分析失败:{result.get('error', '未知错误')}")
if result.get("raw_result"):
with st.expander("查看原始输出"):
st.code(result["raw_result"])
else:
st.info("💡 点击上方「分析配置优化」按钮开始分析,系统会根据当前配置生成优化建议。")
st.caption("提示:当您修改品牌名、优势描述或竞品列表后,系统会自动清除旧结果,需要重新分析。")