Files
ChouJuGEO/modules/ui/tab_config_optimizer.py
T
2026-04-30 18:37:46 +08:00

298 lines
16 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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("提示:当您修改品牌名、优势描述或竞品列表后,系统会自动清除旧结果,需要重新分析。")