feat(report): combine focus-keyword and scenario charts per matrix group

Made-with: Cursor
This commit is contained in:
hub-gif 2026-04-14 16:25:17 +08:00
parent 5fe4ac4ffe
commit d99d9d2ab0
5 changed files with 154 additions and 76 deletions

View File

@ -877,7 +877,7 @@ def build_scenario_groups_llm_payload(
sku_header: str, sku_header: str,
title_h: str, title_h: str,
) -> dict[str, Any]: ) -> dict[str, Any]:
"""供 ``generate_scenario_group_summaries_llm``;计数与 §8.4 场景条形图一致。""" """供 ``generate_scenario_group_summaries_llm``;计数与 §8.3 图右栏(场景)一致。"""
if not feedback_groups: if not feedback_groups:
return {} return {}
sku_meta: dict[str, tuple[str, str, str]] = {} sku_meta: dict[str, tuple[str, str, str]] = {}
@ -1658,15 +1658,10 @@ def _scenario_group_asset_slug(group: str, index: int) -> str:
return f"i{index:02d}_{core}" return f"i{index:02d}_{core}"
def _scenario_group_usage_bar_filename(group: str, index: int) -> str: def _focus_scenario_combo_bar_filename(group: str, index: int) -> str:
"""场景/用途按细类条形图(横轴为占有效文本比例 %,与报告表格口径一致)。""" """关注词 + 使用场景并排条形图(与 ``report_charts.save_combo_focus_scenario_bar`` 同源)。"""
slug = _scenario_group_asset_slug(group, index) slug = _scenario_group_asset_slug(group, index)
return f"chart_usage_scenarios_bar__{slug}.png" return f"chart_focus_and_scenarios_bar__{slug}.png"
def _focus_keywords_group_bar_filename(group: str, index: int) -> str:
slug = _scenario_group_asset_slug(group, index)
return f"chart_focus_keywords_bar__{slug}.png"
def _matrix_prices_reviews_chart_filename(group: str, index: int) -> str: def _matrix_prices_reviews_chart_filename(group: str, index: int) -> str:
@ -1900,7 +1895,7 @@ def build_competitor_markdown(
"- **品牌/店铺集中度(第四章)**:有列表全量时按列表行计店铺与品牌占比;无列表导出时按深入 SKU 合并表估算。", "- **品牌/店铺集中度(第四章)**:有列表全量时按列表行计店铺与品牌占比;无列表导出时按深入 SKU 合并表估算。",
"- **评价主题词**:对评价正文做**预设词表子串计数**,非分词主题模型,适合扫方向,**需抽样人工验证**。", "- **评价主题词**:对评价正文做**预设词表子串计数**,非分词主题模型,适合扫方向,**需抽样人工验证**。",
"- **用途/场景**:对每条评价独立判断是否命中预设场景词;一条可计入多个场景,统计的是「提及该场景的评价条数」而非用户数。", "- **用途/场景**:对每条评价独立判断是否命中预设场景词;一条可计入多个场景,统计的是「提及该场景的评价条数」而非用户数。",
"- **用户画像(第八章)**:正负面粗判含**口语短语**级摘录;关注词与场景**仅按细类**以条形图展示(场景图为**占该细类有效文本比例 %**);见 §8.38.4", "- **用户画像(第八章)**:正负面粗判含**口语短语**级摘录;关注词与场景**仅按细类**以**同图左右并列**展示(左为关注词命中次数,右为场景占有效文本 **%**);见 §8.3",
"- **细类划分§5§8****仅**依据合并表 ``detail_category_path``;该列为空或无法解析出可读细类段的 SKU **不参与**竞品矩阵与按细类评价统计(相关评价条亦**不进入**按细类图表)。", "- **细类划分§5§8****仅**依据合并表 ``detail_category_path``;该列为空或无法解析出可读细类段的 SKU **不参与**竞品矩阵与按细类评价统计(相关评价条亦**不进入**按细类图表)。",
"- **检索结果规模**:来自京东 PC 搜索返回的「结果条数」类指标,表示平台侧申报的匹配数量级,**不等于**动销、库存或独立 SKU 数。", "- **检索结果规模**:来自京东 PC 搜索返回的「结果条数」类指标,表示平台侧申报的匹配数量级,**不等于**动销、库存或独立 SKU 数。",
"", "",
@ -1985,7 +1980,7 @@ def build_competitor_markdown(
) )
if multi_feedback_cat and (hits or scen_n_texts > 0): if multi_feedback_cat and (hits or scen_n_texts > 0):
exec_bullets.append( exec_bullets.append(
"评价侧写(关注词、用途/场景)已按 **§5 同款细类** 分节,见 **§8.38.4**。" "评价侧写(关注词、用途/场景)已按 **§5 同款细类** 分节,见 **§8.3**(同图并列)"
) )
elif hits: elif hits:
top3 = "".join(f"{w}」({n})" for w, n in hits.most_common(3)) top3 = "".join(f"{w}」({n})" for w, n in hits.most_common(3))
@ -2351,8 +2346,7 @@ def build_competitor_markdown(
"- **细类划分**:与 **§5 竞品矩阵** 相同,**仅**依据 ``detail_category_path`` 解析为「饼干 / 西式糕点 / …」等(规则见 §5 章首说明)。", "- **细类划分**:与 **§5 竞品矩阵** 相同,**仅**依据 ``detail_category_path`` 解析为「饼干 / 西式糕点 / …」等(规则见 §5 章首说明)。",
"- **归因**:每条评价按其 SKU 对应到深入样本,再映射到该 SKU 所属细类SKU 不在合并表中的评价单独归入说明性分组;**在合并表中但该 SKU 缺 ``detail_category_path`` 或路径无法解析为可读细类的,该评价不进入按细类统计**(与 §5 排除口径一致)。", "- **归因**:每条评价按其 SKU 对应到深入样本,再映射到该 SKU 所属细类SKU 不在合并表中的评价单独归入说明性分组;**在合并表中但该 SKU 缺 ``detail_category_path`` 或路径无法解析为可读细类的,该评价不进入按细类统计**(与 §5 排除口径一致)。",
"- **正负面粗判§8.2**:先以关键词规则与图表做粗分;若任务开启 **llm_comment_sentiment**,可附**大模型对抽样原文的主题归因**(尤其负向「用户在抱怨什么」),与词频条形图互补。", "- **正负面粗判§8.2**:先以关键词规则与图表做粗分;若任务开启 **llm_comment_sentiment**,可附**大模型对抽样原文的主题归因**(尤其负向「用户在抱怨什么」),与词频条形图互补。",
"- **关注词按细类§8.3**:对组内评价正文做子串计数并出条形图;若无逐条正文则用该细类下评价摘要列拼接兜底;与配置关注词及联想扩展同源。", "- **关注词与使用场景§8.3**:对组内评价正文做关注词子串计数(左栏条形图);对每条有效文本独立扫描**本次任务生效的场景词组**(来自报告调参或系统默认),一条可属多场景,右栏为**占该细类有效文本比例 %**(多标签下可相加 **>** 100%)。二者在 **同一张图左右并列**,与 §5 矩阵细类一一对应。",
"- **用途/场景按细类§8.4**:对组内每条有效文本独立扫描**本次任务生效的场景词组**(来自报告调参或系统默认),一条可属多场景;条形图横轴为**占该细类有效文本比例 %**(多标签下各比例可相加大于 100%)。",
"", "",
"### 8.2 评价正负面粗判(关键词规则)", "### 8.2 评价正负面粗判(关键词规则)",
"", "",
@ -2420,7 +2414,12 @@ def build_competitor_markdown(
lines.append("") lines.append("")
lines.extend( lines.extend(
[ [
"### 8.3 关注词频次(按细类)", "### 8.3 关注词与使用场景(按细类)",
"",
"每细类一张**左右并列图**(与 ``report_assets/chart_focus_and_scenarios_bar__*.png`` 同源):"
"**左**为配置关注词子串命中次数(同一评价可出现多次,为次数而非去重条数);"
"**右**为预设场景词组命中占该细类有效文本比例 %(一条可属多场景;多柱比例可相加 **>** 100%)。"
"统计均基于评价正文(或兜底预览)子串规则,**不等于**购买动机调研结论。",
"", "",
] ]
) )
@ -2437,54 +2436,33 @@ def build_competitor_markdown(
) )
lines.append("") lines.append("")
hits_g = _group_keyword_hits(cr_g, texts_g, focus_words=focus_words) hits_g = _group_keyword_hits(cr_g, texts_g, focus_words=focus_words)
if hits_g:
lines.extend(
_embed_chart(
run_dir,
_focus_keywords_group_bar_filename(gname, gi),
f"{_md_cell(gname, 24)}」细类 · 关注词子串命中次数(条形图;同一评价可出现多次,为次数而非去重条数)",
)
)
else:
lines.append("*该细类无命中或无数文本。*")
lines.append("")
lines.extend(
[
"### 8.4 用途与使用场景(按细类)",
"",
"每条评价文本(或兜底预览)独立扫描场景词组;若命中某组内**任一**关键词则该组 +1同一条可计入多组"
"**不等于**购买动机调研结论,建议结合原句抽样阅读。",
"",
]
)
if not feedback_groups:
lines.append("*无评价数据可归组。*")
lines.append("")
else:
for gi, (gname, cr_g, texts_g) in enumerate(feedback_groups):
scen_g, scen_ng = _comment_scenario_counts(texts_g, scenario_groups) scen_g, scen_ng = _comment_scenario_counts(texts_g, scenario_groups)
lines.append(f"#### {gname}") has_focus = any(n > 0 for n in hits_g.values()) if hits_g else False
lines.append("") has_scen = scen_ng > 0 and any(n > 0 for n in scen_g.values())
if scen_ng <= 0: if scen_ng <= 0:
lines.append("*该细类下无可用评价正文。*") lines.append("*该细类下无可用评价正文。*")
lines.append("") lines.append("")
continue continue
lines.append(f"- **有效评价文本条数**{scen_ng}") if has_focus or has_scen:
lines.append("") cap = (
if scen_g: f"{_md_cell(gname, 24)}」细类 · 关注词与使用场景(左:关注词命中次数;右:场景占有效文本 %"
f"有效文本 **{scen_ng}** 条)"
)
lines.extend( lines.extend(
_embed_chart( _embed_chart(
run_dir, run_dir,
_scenario_group_usage_bar_filename(gname, gi), _focus_scenario_combo_bar_filename(gname, gi),
f"{_md_cell(gname, 24)}」细类 · 场景/用途(条形图:**横轴 = 提及条数 ÷ 本细类有效文本条数**" cap,
f"柱尾标注「条数 · 占比」;有效文本 **{scen_ng}** 条)",
) )
) )
else:
lines.append("*该细类无关注词命中且未命中预设场景词组。*")
lines.append("")
if has_scen:
for para in _scenario_summary_bullets(scen_g, scen_ng): for para in _scenario_summary_bullets(scen_g, scen_ng):
lines.append(para) lines.append(para)
lines.append("") lines.append("")
else: elif scen_ng > 0:
lines.append("*未命中预设场景词组。*") lines.append("*未命中预设场景词组。*")
lines.append("") lines.append("")
@ -2493,9 +2471,9 @@ def build_competitor_markdown(
lines.extend( lines.extend(
[ [
"", "",
"#### 使用场景要点归纳(大模型,与 §8.4 图表互补)", "#### 使用场景要点归纳(大模型,与 §8.3 右栏图表互补)",
"", "",
"> **说明**:与 §8.4 **相同**的预设场景词组与子串命中规则;**各场景条数与占比以正文条形图为准**。", "> **说明**:与 §8.3 **相同**的预设场景词组与子串命中规则;**各场景条数与占比以正文图右栏为准**。",
"", "",
_llm_sg, _llm_sg,
"", "",
@ -2507,9 +2485,9 @@ def build_competitor_markdown(
lines.extend( lines.extend(
[ [
"", "",
"#### 细类评价与关注词要点归纳(大模型,与 §8.3 图表互补)", "#### 细类评价与关注词要点归纳(大模型,与 §8.3 左栏图表互补)",
"", "",
"> **说明**:归纳各细类反馈主题与配置关注词命中;**次数与 §8.3 条形图以正文为准**。", "> **说明**:归纳各细类反馈主题与配置关注词命中;**次数与 §8.3 左栏以正文为准**。",
"", "",
_llm_cg, _llm_cg,
"", "",

View File

@ -19,7 +19,7 @@ def merge_llm_supplement_with_rules_report(llm_md: str, rules_md: str) -> str:
""" """
**以规则引擎全文为正文** §5 完整竞品矩阵各章内嵌统计图与表格 **以规则引擎全文为正文** §5 完整竞品矩阵各章内嵌统计图与表格
大模型稿作为 **§8.5** 嵌入在 **第八章末第九章策略** 之前 §8.28.4 等具体分析同卷连贯 大模型稿作为 **§8.5** 嵌入在 **第八章末第九章策略** 之前 §8.28.3 等具体分析同卷连贯
**不再**插在篇首## 一、」之前。 **不再**插在篇首## 一、」之前。
API重新生成报告已不再调用本函数避免整篇 LLM 与矩阵/图表口径冲突保留供脚本或将来显式开关复用 API重新生成报告已不再调用本函数避免整篇 LLM 与矩阵/图表口径冲突保留供脚本或将来显式开关复用
@ -33,8 +33,8 @@ def merge_llm_supplement_with_rules_report(llm_md: str, rules_md: str) -> str:
marker = "\n---\n\n## 九、策略与机会提示(假设清单,待验证)" marker = "\n---\n\n## 九、策略与机会提示(假设清单,待验证)"
insert = ( insert = (
"\n\n---\n\n" "\n\n---\n\n"
"### 8.5 大模型深度补充(与 §2§8.4 定量内容互补)\n\n" "### 8.5 大模型深度补充(与 §2§8.3 定量内容互补)\n\n"
"> **说明**:本段位于**第八章末****竞品矩阵、价盘表、统计图与 §8.28.4 词频等以正文各节为准**" "> **说明**:本段位于**第八章末****竞品矩阵、价盘表、统计图与 §8.28.3 等以正文各节为准**"
"此处为跨小节语义整合,便于衔接第九章。\n\n" "此处为跨小节语义整合,便于衔接第九章。\n\n"
f"{sup.strip()}\n" f"{sup.strip()}\n"
"\n---\n\n## 九、策略与机会提示(假设清单,待验证)" "\n---\n\n## 九、策略与机会提示(假设清单,待验证)"
@ -49,7 +49,7 @@ def merge_llm_supplement_with_rules_report(llm_md: str, rules_md: str) -> str:
if alt in body and marker not in body: if alt in body and marker not in body:
return body.replace( return body.replace(
alt, alt,
"\n\n---\n\n### 8.5 大模型深度补充(与 §2§8.4 定量内容互补)\n\n" "\n\n---\n\n### 8.5 大模型深度补充(与 §2§8.3 定量内容互补)\n\n"
"> **说明**:位于第八章末;**矩阵与图表以正文为准**。\n\n" "> **说明**:位于第八章末;**矩阵与图表以正文为准**。\n\n"
f"{sup.strip()}\n" f"{sup.strip()}\n"
+ alt, + alt,
@ -58,7 +58,7 @@ def merge_llm_supplement_with_rules_report(llm_md: str, rules_md: str) -> str:
app = "\n## 附录 A数据留存说明" app = "\n## 附录 A数据留存说明"
if app in body: if app in body:
tail = ( tail = (
"\n\n---\n\n### 8.5 大模型深度补充(与 §2§8.4 定量内容互补)\n\n" "\n\n---\n\n### 8.5 大模型深度补充(与 §2§8.3 定量内容互补)\n\n"
f"{sup.strip()}\n" f"{sup.strip()}\n"
) )
return body.replace(app, tail + app, 1) return body.replace(app, tail + app, 1)

View File

@ -41,7 +41,7 @@ REPORT_SYSTEM = """你是业务与产品读者顾问。输入 JSON 含 `keyword`
`matrix_overview_for_llm`按细分类目的 SKU 数与品牌样本 `matrix_overview_for_llm`按细分类目的 SKU 数与品牌样本
你的输出将**嵌入在规则报告第八章末**作为### 8.5 …」的正文,系统已加小节标题与说明),**紧接在** 你的输出将**嵌入在规则报告第八章末**作为### 8.5 …」的正文,系统已加小节标题与说明),**紧接在**
消费者反馈 §8.18.4 **之后**第九章策略**之前**因此写的是**具体分析型补充**不是篇首速读块 消费者反馈 §8.18.3 **之后**第九章策略**之前**因此写的是**具体分析型补充**不是篇首速读块
所有数字占比条数品牌名价格区间等**必须严格来自输入 JSON**禁止编造未在输入中出现的定量结论 所有数字占比条数品牌名价格区间等**必须严格来自输入 JSON**禁止编造未在输入中出现的定量结论
@ -463,9 +463,9 @@ def generate_comment_group_summaries_llm(
SCENARIO_GROUPS_SYSTEM = """你是用户研究与品类顾问。输入为 JSON``keyword``、``scenario_lexicon``、``groups``。 SCENARIO_GROUPS_SYSTEM = """你是用户研究与品类顾问。输入为 JSON``keyword``、``scenario_lexicon``、``groups``。
``scenario_lexicon`` 列出各场景标签及示例触发子串与报告 **§8.4** 统计规则一致 ``scenario_lexicon`` 列出各场景标签及示例触发子串与报告 **§8.3** 右栏统计规则一致
``groups`` 每项含 ``group`` §5 矩阵一致的细分类目名``effective_text_count``有效评价文本条数 ``groups`` 每项含 ``group`` §5 矩阵一致的细分类目名``effective_text_count``有效评价文本条数
``scenario_distribution``各预设场景的 ``mention_rows`` ``share_of_effective_texts``**一条评价可计入多场景** §8.4 条形图同源 ``scenario_distribution``各预设场景的 ``mention_rows`` ``share_of_effective_texts``**一条评价可计入多场景** §8.3 图右栏同源
``sample_text_snippets``摘录行常含细类SKU品名店铺等前缀的短引文已截断 ``sample_text_snippets``摘录行常含细类SKU品名店铺等前缀的短引文已截断
统计为**子串命中**不是语义主题模型 统计为**子串命中**不是语义主题模型
@ -479,7 +479,7 @@ SCENARIO_GROUPS_SYSTEM = """你是用户研究与品类顾问。输入为 JSON
SCENARIO_GROUPS_USER_PREFIX = ( SCENARIO_GROUPS_USER_PREFIX = (
"请根据以下 JSON 撰写竞品报告 §8.4 用途与使用场景之后的「使用场景要点归纳」正文Markdown\n\n" "请根据以下 JSON 撰写竞品报告 §8.3右栏使用场景之后的「使用场景要点归纳」正文Markdown\n\n"
) )

View File

@ -141,6 +141,15 @@ def _cleanup_obsolete_report_assets(out_dir: Path) -> None:
fp.unlink() fp.unlink()
except OSError: except OSError:
pass pass
for pat in (
"chart_focus_keywords_bar__*.png",
"chart_usage_scenarios_bar__*.png",
):
for fp in out_dir.glob(pat):
try:
fp.unlink()
except OSError:
pass
def generate_report_charts(run_dir: Path, brief: dict[str, Any]) -> list[str]: def generate_report_charts(run_dir: Path, brief: dict[str, Any]) -> list[str]:
@ -218,6 +227,93 @@ def generate_report_charts(run_dir: Path, brief: dict[str, Any]) -> list[str]:
plt.close(fig) plt.close(fig)
created.append(fname) created.append(fname)
def save_combo_focus_scenario_bar(
*,
gname: str,
slug: str,
wl: list[str],
vl: list[float],
gl: list[str],
gv: list[float],
n_texts: int,
) -> None:
"""左:关注词命中次数;右:场景占有效文本 %(与 §8.3 正文同源)。"""
has_l = bool(wl and vl and max(vl) > 0)
has_r = bool(gl and gv and n_texts > 0 and max(gv) > 0)
if not has_l and not has_r:
return
n_l = len(wl) if has_l else 0
n_r = len(gl) if has_r else 0
n_max = max(n_l, n_r, 1)
fig_h = max(3.4, min(14.0, 0.38 * n_max + 2.6))
fig, (ax_l, ax_r) = plt.subplots(1, 2, figsize=(10.8, fig_h))
ttl = (gname or "").strip()[:22] or "细类"
fig.suptitle(
f"{ttl}」· 关注词与使用场景(与 §8.3 统计同源)",
fontsize=11,
y=1.02,
)
if has_l:
y_pos = range(n_l)
ax_l.barh(list(y_pos), vl[:n_l], color="#2563eb", height=0.62)
ax_l.set_yticks(list(y_pos))
ax_l.set_yticklabels(wl[:n_l], fontsize=8)
ax_l.invert_yaxis()
ax_l.set_xlabel("关注词子串命中次数", fontsize=9)
ax_l.set_title("关注词", fontsize=10, pad=8)
else:
ax_l.text(
0.5,
0.5,
"本细类无关注词命中\n或无数文本",
ha="center",
va="center",
transform=ax_l.transAxes,
fontsize=10,
color="#64748b",
)
ax_l.set_axis_off()
if has_r:
pcts = [100.0 * c / n_texts for c in gv[: len(gl)]]
n_b = len(gl)
y_pos = range(n_b)
bars = ax_r.barh(list(y_pos), pcts, color="#059669", height=0.62)
ax_r.set_yticks(list(y_pos))
ax_r.set_yticklabels(gl[:n_b], fontsize=8)
ax_r.invert_yaxis()
ax_r.set_xlabel("占有效评价文本比例(%", fontsize=9)
if pcts:
xmax = max(pcts) * 1.12 + 4.0
ax_r.set_xlim(0, max(xmax, max(pcts) + 10.0, 24.0))
else:
ax_r.set_xlim(0, 24.0)
for bar, c, p in zip(bars, gv[:n_b], pcts):
ax_r.text(
min(bar.get_width() + 0.6, ax_r.get_xlim()[1] * 0.97),
bar.get_y() + bar.get_height() / 2,
f"{int(c)}条 · {p:.1f}%",
va="center",
fontsize=8,
)
ax_r.set_title("使用场景 / 用途", fontsize=10, pad=8)
else:
ax_r.text(
0.5,
0.5,
"本细类无场景词命中\n或无数文本",
ha="center",
va="center",
transform=ax_r.transAxes,
fontsize=10,
color="#64748b",
)
ax_r.set_axis_off()
fig.tight_layout()
path = out_dir / f"chart_focus_and_scenarios_bar__{slug}.png"
fig.savefig(path, dpi=130, bbox_inches="tight")
plt.close(fig)
created.append(path.name)
def save_pie( def save_pie(
labels: list[str], labels: list[str],
values: list[float], values: list[float],
@ -298,6 +394,7 @@ def generate_report_charts(run_dir: Path, brief: dict[str, Any]) -> list[str]:
core = "group" core = "group"
return f"i{index:02d}_{core}" return f"i{index:02d}_{core}"
scen_by_slug: dict[str, tuple[list[str], list[float], int]] = {}
by_grp = brief.get("usage_scenarios_by_matrix_group") or [] by_grp = brief.get("usage_scenarios_by_matrix_group") or []
if isinstance(by_grp, list): if isinstance(by_grp, list):
for item in by_grp: for item in by_grp:
@ -323,15 +420,10 @@ def generate_report_charts(run_dir: Path, brief: dict[str, Any]) -> list[str]:
gpairs.append((lb, float(c))) gpairs.append((lb, float(c)))
gpairs = _merge_labeled_counts_tail(gpairs, max_items=14) gpairs = _merge_labeled_counts_tail(gpairs, max_items=14)
if gpairs and n_unit > 0: if gpairs and n_unit > 0:
gl = [p[0] for p in gpairs] scen_by_slug[slug] = (
gv = [p[1] for p in gpairs] [p[0] for p in gpairs],
title_base = f"{gname}」· 场景/用途" if gname else "细类 · 场景/用途" [p[1] for p in gpairs],
save_bar_h_share_of_text(
gl,
gv,
n_unit, n_unit,
f"{title_base}(占有效评价文本比例)",
f"chart_usage_scenarios_bar__{slug}.png",
) )
fb = brief.get("consumer_feedback_by_matrix_group") or [] fb = brief.get("consumer_feedback_by_matrix_group") or []
@ -360,10 +452,18 @@ def generate_report_charts(run_dir: Path, brief: dict[str, Any]) -> list[str]:
vl.append(float(c)) vl.append(float(c))
wl = wl[:18] wl = wl[:18]
vl = vl[:18] vl = vl[:18]
if not wl: gl, gv, n_scen = scen_by_slug.get(slug, ([], [], 0))
continue n_unit_fb = int(item.get("effective_comment_text_units") or 0)
tkw = f"{gname}」· 关注词命中次数" if gname else "细类 · 关注词命中次数" n_texts = n_scen if n_scen > 0 else n_unit_fb
save_bar_h(wl, vl, tkw, f"chart_focus_keywords_bar__{slug}.png", "命中次数") save_combo_focus_scenario_bar(
gname=gname,
slug=slug,
wl=wl,
vl=vl,
gl=gl,
gv=gv,
n_texts=n_texts,
)
sent = brief.get("comment_sentiment_lexicon") or {} sent = brief.get("comment_sentiment_lexicon") or {}
if isinstance(sent, dict): if isinstance(sent, dict):

View File

@ -5,7 +5,7 @@
- §6 ``generate_price_group_summaries_llm`` - §6 ``generate_price_group_summaries_llm``
- §8.2``generate_comment_sentiment_analysis_llm`` - §8.2``generate_comment_sentiment_analysis_llm``
- §8末细类评价``generate_comment_group_summaries_llm`` - §8末细类评价``generate_comment_group_summaries_llm``
- §8.4 后使用场景``generate_scenario_group_summaries_llm`` - §8.3 右栏后使用场景``generate_scenario_group_summaries_llm``
- §8.5 类全文补充独立长文``generate_competitor_report_markdown_llm`` - §8.5 类全文补充独立长文``generate_competitor_report_markdown_llm``
cd backend cd backend
@ -244,7 +244,7 @@ def main() -> None:
return generate_scenario_group_summaries_llm(pl_sg, keyword=keyword) return generate_scenario_group_summaries_llm(pl_sg, keyword=keyword)
_run_one( _run_one(
"§8.4 使用场景归纳scenario_groups", "§8.3 使用场景归纳scenario_groups",
_sg, _sg,
live=args.live, live=args.live,
preview_chars=args.preview_chars, preview_chars=args.preview_chars,