feat(report): remove §5 competitor matrix table, keep per-group charts

Made-with: Cursor
This commit is contained in:
hub-gif 2026-04-14 13:18:40 +08:00
parent a1d0fa6686
commit c45ec66d4c
2 changed files with 19 additions and 61 deletions

View File

@ -3,7 +3,7 @@
关键词 调用 ``jd_keyword_pipeline`` 全链路采集 生成 **标准化竞品分析报告**Markdown
报告结构对齐常见竞品分析框架研究范围与方法执行摘要**整体市场观察列表可见度 proxy**
市场与竞争结构**按细分类目分组的竞品对比矩阵**价格分析产品与宣称**按细分类目的消费者反馈与用户画像**策略提示与附录并明确数据边界
市场与竞争结构**按细分类目分组的竞品结构统计图 + 合并表外置**价格分析产品与宣称**按细分类目的消费者反馈与用户画像**策略提示与附录并明确数据边界
若运行配置中提供了外部市场规模摘录``EXTERNAL_MARKET_TABLE_ROWS``则追加对应表格小节否则不输出占位行
依赖全量抓取时与 ``jd_keyword_pipeline.py`` 相同Nodeh5stPlaywright``common/jd_cookie.txt``
@ -1655,7 +1655,7 @@ def _lines_4_reading_category(
"",
f"- 列表侧可读类目/简称共 **{len(cm_structure)}** 种取值,合计 **{total}** 行;"
f"其中「{_md_cell(top_lbl, 40)}」行数最多,约占 **{100 * share:.1f}%**。",
"- 若头部类目占比极高,说明当前关键词下货架被少数品类定义;跨品类机会需结合商详矩阵§5再核对。",
"- 若头部类目占比极高,说明当前关键词下货架被少数品类定义;跨品类机会需结合 §5 细类结构与合并表再核对。",
"",
"| 类目/简称Top 5 | 列表行数 | 占本章结构样本 |",
"| --- | ---: | ---: |",
@ -1800,7 +1800,7 @@ def build_competitor_markdown(
"- **用途/场景**:对每条评价独立判断是否命中预设场景词;一条可计入多个场景,统计的是「提及该场景的评价条数」而非用户数。",
"- **用户画像(第八章)**:正负面粗判含**口语短语**级摘录;关注词与场景**仅按细类**以条形图展示(场景图为**占该细类有效文本比例 %**);见 §8.38.4。",
"- **各章衔接(可选)**:若任务配置 ``llm_section_bridges``(或部署侧环境变量启用),则在「## 一」至「## 九」各章二级标题后插入大模型撰写的**衔接分析**段落,便于阅读过渡;**定量结论仍以正文表格与摘要 JSON 为准**。",
"- **细类大模型归纳(可选)**:若任务配置 ``llm_matrix_group_summaries`` / ``llm_price_group_summaries`` / ``llm_comment_group_summaries``(或对应 ``MA_ENABLE_LLM_*`` 环境变量),则在 **§5.1、§6.2、§8.6** 分别插入卖点/价盘/评论关注词的细类归纳;**仍以同章表格与 CSV 为准**",
"- **细类大模型归纳(可选)**:若任务配置 ``llm_price_group_summaries`` / ``llm_comment_group_summaries``(或对应 ``MA_ENABLE_LLM_*`` 环境变量),则在 **§6.2、§8.6** 插入价盘/评论关注词的细类归纳;**§5 不再输出矩阵表**,卖点/配料等仍以 ``keyword_pipeline_merged.csv`` 与 JSON 为准",
"- **检索结果规模**:来自京东 PC 搜索返回的「结果条数」类指标,表示平台侧申报的匹配数量级,**不等于**动销、库存或独立 SKU 数。",
"",
"### 1.4 主要局限",
@ -1863,7 +1863,7 @@ def build_competitor_markdown(
)
elif list_export and cr1_deep is not None and top_brand_deep and not brands_s:
exec_bullets.append(
f"列表导出缺少品牌标题字段,**深入 {n_sku} SKU** 商详品牌第一大品牌份额 ≈ **{100 * cr1_deep:.1f}%**(「{top_brand_deep}」),供与 §5 矩阵对照。"
f"列表导出缺少品牌标题字段,**深入 {n_sku} SKU** 商详品牌第一大品牌份额 ≈ **{100 * cr1_deep:.1f}%**(「{top_brand_deep}」),供与 §5 细类结构对照。"
)
if pst:
price_src_short = (
@ -2053,7 +2053,7 @@ def build_competitor_markdown(
lines.append(
f"*列表导出中店铺/品牌标题有效 **{brand_rows_n}** 条,"
f"低于建议阈值(≥{min_brand_rows}),品牌集中度未展开。**店铺结构见 §4.2**"
f"商详品牌在 **§5**。*"
f"商详品牌在 **§5 细类结构**。*"
)
else:
lines.append("*深入子样本无可用品牌字段。*")
@ -2116,24 +2116,17 @@ def build_competitor_markdown(
[
"---",
"",
"## 五、竞品对比矩阵(按细分类目分组)",
"## 五、竞品结构(按细分类目分组)",
"",
"优先按商详**类目路径**列分组**三级路径**取中间一段(如 … > **饼干** > 粗粮饼干),"
"**四级及以上**取倒数第二段(如 … > **面条** > 挂面)。若该列为空,退化为搜索列表中的类目或规格属性;仍无则「未归类」。全量合并模式下另有更多商详字段可供核对",
"SKU 依商详**类目路径**聚合为细类**三级路径**取中间一段(如 … > **饼干** > 粗粮饼干),"
"**四级及以上**取倒数第二段(如 … > **面条** > 挂面);类目列为空时退化为列表类目或规格,仍无则「未归类」",
"",
"维度说明:**产品**(标题/规格)、**价格**(列表展示)、**渠道**(京东店铺)、**推广**(卖点/榜单文案)、"
"**类目**、**配料表**(见下)、**声量**(评价量与摘要)。",
"",
"**配料表**:优先使用配料正文列(开启配料视觉解析时为识别出的文字);"
"仅有详情长图链接时列内会提示;若商详参数含「配料/配料表:」则摘录该段。"
"均为页面信息摘录,**以包装实物与法规标签为准**。",
"**本版不再在正文打印细类矩阵表**(宽表影响阅读);各 SKU 的标题、品牌、店铺、标价/详情价、卖点、配料、评价量与摘要等,"
"请直接查看本批次目录下 ``keyword_pipeline_merged.csv``,或调用 API 结构化字段 ``matrix_by_group``。",
"下列按细类给出 **SKU 款数** 及 **展示价 / 评价量** 统计图(与合并表、``matrix_by_group`` 同源)。",
"",
]
)
matrix_header = [
"| SKU | 产品(标题) | 品牌 | 标价 | 详情价 | 渠道(店铺) | 推广(卖点) | 榜单/标签 | 类目 | 配料表 | 评价量(搜索) | 消费者反馈摘要 |",
"| --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- |",
]
grouped_matrix = _merged_rows_grouped_for_matrix(merged_rows)
if not grouped_matrix:
lines.append("*无合并表 SKU。*")
@ -2141,39 +2134,20 @@ def build_competitor_markdown(
for gi, (gname, grows) in enumerate(grouped_matrix):
lines.append(f"### {gname}**{len(grows)}** 款)")
lines.append("")
lines.extend(matrix_header)
grows_sorted = sorted(grows, key=lambda r: _cell(r, sku_header) or "")
for row in grows_sorted:
lines.append(
_competitor_matrix_md_line(
row, sku_header=sku_header, title_h=title_h
)
)
lines.append(
"*SKU 级字段明细见 ``keyword_pipeline_merged.csv``(本细类 SKU 与上节分组规则一致)。*"
)
lines.append("")
slug_mx = _scenario_group_asset_slug(gname, gi)
lines.extend(
_embed_chart(
run_dir,
f"chart_matrix_prices_reviews__{slug_mx}.png",
f"{_md_cell(gname, 24)}」细类 · **展示价与评价量**(条形图;与上表同源:"
f"{_md_cell(gname, 24)}」细类 · **展示价与评价量**(条形图;与合并表及 ``matrix_by_group`` 同源:"
f"价取 detail_price_final→标价→券后 优先可解析数值;评价量为搜索侧「评价量」字段摘录)",
)
)
_mx_llm = (llm_matrix_section_md or "").strip()
if _mx_llm:
lines.extend(
[
"",
"### 5.1 细类要点归纳(大模型)",
"",
"> **说明**:模型按 §5 同细类拆分,仅基于上表矩阵摘录(标题/卖点/配料等)与每细类 ``price_stats``(与 §6 分位数同源对象)归纳;**不含**医学或功效结论。配料与宣称以页面为准。",
"",
_mx_llm,
"",
]
)
ch6_price_title = (
"## 六、价格分析PC 搜索列表全量)"
if list_export and pst_list.get("n", 0) > 0
@ -2237,7 +2211,7 @@ def build_competitor_markdown(
"### 6.2 细类价盘要点归纳(大模型)",
"",
"> **说明**:以下为模型按 §5 同细类拆分、仅基于上文本节价统计与价字段摘录的归纳(价带与价差);"
"不含配料/宣称/场景关键词分析——见 §5「细类要点归纳大模型」。具体数值仍以正文表格及批次 CSV 为准。",
"不含配料/宣称/场景关键词分析。具体数值仍以 **§6** 表格及 ``keyword_pipeline_merged.csv`` 为准。",
"",
_pr_llm,
"",
@ -2265,7 +2239,7 @@ def build_competitor_markdown(
"",
"### 8.1 方法",
"",
"- **细类划分**:与 **§5 竞品矩阵** 相同,依据商详类目路径解析为「饼干 / 西式糕点 / …」等(规则见 §5 章首说明)。",
"- **细类划分**:与 **§5** 相同的商详类目路径规则(见 §5 章首)。",
"- **归因**:每条评价按其 SKU 对应到深入样本,再映射到该 SKU 所属细类SKU 不在合并表中的评价单独归入说明性分组。",
"- **正负面粗判§8.2**:先以关键词规则与图表做粗分;若任务开启 **llm_comment_sentiment**,可附**大模型对抽样原文的主题归因**(尤其负向「用户在抱怨什么」),与词频条形图互补。",
"- **关注词按细类§8.3**:对组内评价正文做子串计数并出条形图;若无逐条正文则用该细类下评价摘要列拼接兜底;与配置关注词及联想扩展同源。",

View File

@ -431,24 +431,8 @@ def write_competitor_analysis_for_run_dir(
)
if want_mx and not skip_mx and merged_rows:
pl_mx = jcr.build_matrix_groups_llm_payload(
merged_rows, sku_header=sku_h, title_h=title_h
)
if pl_mx:
matrix_llm_rec["attempted"] = True
try:
from .llm_generate import generate_matrix_group_summaries_llm
llm_matrix_md = generate_matrix_group_summaries_llm(
pl_mx, keyword=kw
)
matrix_llm_rec["ok"] = True
matrix_llm_rec["chars"] = len(llm_matrix_md)
except Exception as e:
matrix_llm_rec["ok"] = False
matrix_llm_rec["error"] = str(e)
else:
matrix_llm_rec["skipped"] = "empty_matrix_payload"
# §5 正文已取消细类矩阵表,不再向报告嵌入矩阵要点 LLM 段落。
matrix_llm_rec["skipped"] = "matrix_table_removed_from_report"
elif skip_mx:
matrix_llm_rec["skipped"] = "MA_SKIP_LLM_MATRIX_GROUP_SUMMARIES"
elif not want_mx: