fix(report): default LLM flags, embed §5 matrix charts, slim table

Merge missing keys from get_default_report_config into effective report_config so llm_* switches default on when absent. §5 embeds chart_matrix_prices_reviews per fine category (price + review-volume bars, Y=title) and replaces the wide matrix with a compact table. Matrix chart Y-labels allow longer titles (48 chars).

Made-with: Cursor
This commit is contained in:
hub-gif 2026-04-14 15:40:34 +08:00
parent 4bb94173a5
commit 9d1ff31b23
3 changed files with 53 additions and 16 deletions

View File

@ -1472,6 +1472,21 @@ def _competitor_matrix_md_line(
)
def _competitor_matrix_compact_md_line(
row: dict[str, str], *, sku_header: str, title_h: str
) -> str:
"""§5 简表:与矩阵条形图同源字段,便于对照;不含配料与长摘要。"""
sku = _md_cell(_cell(row, sku_header), 14)
title = _md_cell(_cell(row, title_h), 72)
brand = _md_cell(_cell(row, "detail_brand"), 16)
df = _cell(row, "detail_price_final").strip()
pj = _cell(row, "标价(jdPrice,jdPriceText,realPrice)").strip()
price_cell = _md_cell(df or pj or "", 12)
cc = _md_cell(_cell(row, "评价量(commentFuzzy)"), 12)
shop = _md_cell(_cell(row, "店铺名(shopName)", "detail_shop_name"), 20)
return f"| {sku} | {title} | {brand} | {shop} | {price_cell} | {cc} |"
def _strategy_hints(
*,
cr1: float | None,
@ -1555,6 +1570,12 @@ def _focus_keywords_group_bar_filename(group: str, index: int) -> str:
return f"chart_focus_keywords_bar__{slug}.png"
def _matrix_prices_reviews_chart_filename(group: str, index: int) -> str:
"""与 ``report_charts.generate_report_charts`` 中 ``chart_matrix_prices_reviews__*`` 一致。"""
slug = _scenario_group_asset_slug(group, index)
return f"chart_matrix_prices_reviews__{slug}.png"
def _lines_4_reading_brand(
*,
cr1: float | None,
@ -2104,18 +2125,14 @@ def build_competitor_markdown(
"分组**仅**使用合并表列 ``detail_category_path``(商详类目路径):**三级路径**取中间一段(如 … > **饼干** > 粗粮饼干),"
"**四级及以上**取倒数第二段(如 … > **面条** > 挂面)。**该列为空**或路径段均为内部编码、**无法解析出可读细类**的 SKU **不进入**本矩阵,亦**不参与**第八章按细类的评价统计。",
"",
"维度说明:**产品**(标题/规格)、**价格**(列表展示)、**渠道**(京东店铺)、**推广**(卖点/榜单文案)、"
"**类目路径**``detail_category_path``)、**配料表**(见下)、**声量**(评价量与摘要)。",
"",
"**配料表**:优先使用配料正文列(开启配料视觉解析时为识别出的文字);"
"仅有详情长图链接时列内会提示;若商详参数含「配料/配料表:」则摘录该段。"
"均为页面信息摘录,**以包装实物与法规标签为准**。",
"**读图方式**:每个细类下优先给出**并列横向条形图**(左:**展示价**(元);右:**评价量**(搜索列表侧文案,作**声量 proxy****非**平台销量/动销)),"
"纵轴为**产品标题**(与图文件 ``chart_matrix_prices_reviews__*.png`` 同源)。下方附**精简对照表**SKU、店铺、价与评价量配料、卖点与评价摘要等完整列见合并表 CSV。",
"",
]
)
matrix_header = [
"| SKU | 产品(标题) | 品牌 | 标价 | 详情价 | 渠道(店铺) | 推广(卖点) | 榜单/标签 | 类目路径(商详) | 配料表 | 评价量(搜索) | 消费者反馈摘要 |",
"| --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- |",
matrix_header_compact = [
"| SKU | 产品(标题) | 品牌 | 渠道(店铺) | 展示价(详情优先) | 评价量(搜索) |",
"| --- | --- | --- | --- | --- | --- |",
]
grouped_matrix = _merged_rows_grouped_for_matrix(merged_rows)
if not grouped_matrix:
@ -2127,14 +2144,30 @@ def build_competitor_markdown(
else:
lines.append("*无合并表 SKU。*")
lines.append("")
for gname, grows in grouped_matrix:
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 "")
mx_chart = _matrix_prices_reviews_chart_filename(gname, gi)
lines.extend(
_embed_chart(
run_dir,
mx_chart,
f"{_md_cell(gname, 20)}」· 展示价与评价量(搜索侧声量 proxy纵轴为产品标题与下图简表一致。",
)
)
if not (run_dir / "report_assets" / mx_chart).is_file():
lines.append(
f"*(尚未生成 ``report_assets/{mx_chart}``:请确认已执行报告出图流程,或重新生成报告。)*"
)
lines.append("")
lines.extend(matrix_header_compact)
grows_sorted = sorted(
grows,
key=lambda r: (_cell(r, title_h) or _cell(r, sku_header) or "").strip(),
)
for row in grows_sorted:
lines.append(
_competitor_matrix_md_line(
_competitor_matrix_compact_md_line(
row, sku_header=sku_header, title_h=title_h
)
)
@ -2145,9 +2178,9 @@ def build_competitor_markdown(
lines.extend(
[
"",
"#### 细类要点归纳(大模型,与上矩阵互补)",
"#### 细类要点归纳(大模型,与上文条形图及简表互补)",
"",
"> **说明**:与 §5 相同的细类划分下归纳卖点与配料共性;**具体 SKU、价格与表格列以正文为准**。",
"> **说明**:与 §5 相同的细类划分下归纳卖点与配料共性;**具体 SKU、价格与条形图以正文为准**。",
"",
_llm_mx,
"",

View File

@ -219,6 +219,10 @@ def write_competitor_analysis_for_run_dir(
eff_rc: dict[str, Any] = (
dict(report_config) if isinstance(report_config, dict) else {}
)
# 任务仅保存部分调参时,补齐默认项(含各 llm_* 开关);显式写入的键不被覆盖
for _k, _v in get_default_report_config().items():
if _k not in eff_rc:
eff_rc[_k] = _v
all_tx = _flat_comment_texts(comment_rows)
suggest_path = run_dir / "keyword_suggest_llm.json"
suggest_record: dict[str, Any] = {

View File

@ -426,7 +426,7 @@ def generate_report_charts(run_dir: Path, brief: dict[str, Any]) -> list[str]:
sku = str(s.get("sku_id") or "").strip()
# 与 §5 矩阵「产品」列一致:纵轴优先品名,无标题时再退化为 SKU
if title:
label = title if len(title) <= 30 else title[:28] + ""
label = title if len(title) <= 48 else title[:46] + ""
elif sku:
label = sku if len(sku) <= 22 else sku[:20] + ""
else: