feat(brief): plain-language concentration keys and copy

- Add first_share/top_three_combined_share plus cr1/cr3 readers
- Brief/schema/footnotes and LLM prompts avoid dev jargon where possible
- Frontend JD analysis/dataset hints in business-friendly wording

Made-with: Cursor
This commit is contained in:
hub-gif 2026-04-14 17:08:55 +08:00
parent c3fdb2b970
commit 3d6193af06
8 changed files with 81 additions and 37 deletions

View File

@ -2169,7 +2169,7 @@ def build_competitor_markdown(
)
)
lines.append(
"*更细行数分布见结构化摘要 JSON 的 ``list_brand_mix_top``。*"
"*更细的品牌行数分布见本任务「结构化摘要」数据包。*"
)
elif list_export:
lines.append(
@ -2211,7 +2211,7 @@ def build_competitor_markdown(
)
)
lines.append(
"*更细店铺行数分布见结构化摘要 ``list_shop_mix_top``。*"
"*更细的店铺行数分布见本任务「结构化摘要」数据包。*"
)
else:
lines.append("*无店铺字段。*")
@ -2772,8 +2772,8 @@ def build_competitor_brief(
list_brand_block: dict[str, Any] | None
if len(brands_s) >= min_brand_rows:
list_brand_block = {
"cr1": cr1_list_brand,
"cr3": cr3_list_brand,
"first_share": cr1_list_brand,
"top_three_combined_share": cr3_list_brand,
"top_label": top_list_brand,
}
else:
@ -2800,15 +2800,15 @@ def build_competitor_brief(
"list_visibility_proxy": proxy,
"concentration": {
"shops_from_list": {
"cr1": cr1_shop,
"cr3": cr3_shop,
"first_share": cr1_shop,
"top_three_combined_share": cr3_shop,
"top_label": top_shop_s,
"top_share_pct": top_shop_share,
},
"list_brand_field": list_brand_block,
"detail_brand_among_merged": {
"cr1": cr1_deep,
"cr3": cr3_deep,
"first_share": cr1_deep,
"top_three_combined_share": cr3_deep,
"top_label": top_brand_deep,
"top_share_pct": top_brand_deep_share,
},
@ -2857,9 +2857,10 @@ def build_competitor_brief(
"consumer_feedback_by_matrix_group": feedback_by_group,
"comment_sentiment_lexicon": comment_sentiment_lexicon,
"notes": [
"与在线分析报告各章统计口径一致;关注词与场景以任务 report_config 为底,生成报告时还可经 keyword_suggest_llm 合并延伸(仍为子串命中统计,非深度主题模型)。",
"价格来自页面展示字段抽取含促销与规格差异price_promotion_signals 为标价/券后对齐与卖点话术的启发式摘录。",
"comment_sentiment_lexicon 为关键词粗判,非深度学习情感模型。",
"与在线分析报告各章统计口径一致;关注词与场景以任务中的分析规则为准(子串命中统计,非深度主题模型)。",
"价格来自页面展示字段抽取,含促销与规格差异;促销与标价对齐等为启发式摘录,仅供对照。",
"评价语气为关键词粗判,非深度学习情感模型。",
"「集中度」中:最大一家占比、前三名合计占比为小数(如 0.12 表示约 12%),对应列表或深入样本中的相关行。",
],
}
return _sanitize_json_numbers(out)

View File

@ -0,0 +1,24 @@
"""竞品简报「集中度」块:对外字段名面向非技术用户,并兼容旧键名。"""
from __future__ import annotations
from typing import Any
def concentration_first_share(block: dict[str, Any] | None) -> Any:
"""最大一家占全部相关行的比例01。新键 ``first_share``,旧键 ``cr1``。"""
if not block:
return None
v = block.get("first_share")
if v is not None:
return v
return block.get("cr1")
def concentration_top_three_share(block: dict[str, Any] | None) -> Any:
"""前三名合计占全部相关行的比例01。新键 ``top_three_combined_share``,旧键 ``cr3``。"""
if not block:
return None
v = block.get("top_three_combined_share")
if v is not None:
return v
return block.get("cr3")

View File

@ -7,6 +7,11 @@ import zipfile
from pathlib import Path
from typing import Any
from pipeline.brief_concentration import (
concentration_first_share,
concentration_top_three_share,
)
def _pct(x: Any) -> str:
if x is None:
@ -32,7 +37,7 @@ def markdown_summary_from_brief(brief: dict[str, Any]) -> str:
lines: list[str] = [
"# 竞品要点摘录(机器整理)",
"",
"> 与同批 **完整报告**、**结构化 JSON** 同源;规则汇总,定稿前请人工核对。",
"> 与同批 **完整报告**、**数据汇总**同源;规则汇总,定稿前请人工核对。",
"",
]
kw = brief.get("keyword") or ""
@ -65,33 +70,33 @@ def markdown_summary_from_brief(brief: dict[str, Any]) -> str:
if raw.get("result_count_consensus") is not None:
lines.extend(
[
"## 列表侧检索规模(接口申报",
"## 列表侧检索规模(平台展示",
"",
f"- **resultCount 共识值**{_num(raw.get('result_count_consensus'))}",
f"- **检索结果条数(多份响应取一致值)**{_num(raw.get('result_count_consensus'))}",
"",
]
)
conc = brief.get("concentration") or {}
shops = conc.get("shops_from_list") or {}
if shops.get("cr1") is not None or shops.get("top_label"):
if concentration_first_share(shops) is not None or shops.get("top_label"):
lines.extend(
[
"## 店铺集中度(列表)",
"",
f"- **第一大店铺份额**{_pct(shops.get('cr1'))}(第一店铺:{shops.get('top_label') or ''}",
f"- **前三店铺合计份额**{_pct(shops.get('cr3'))}",
f"- **第一大店铺份额**{_pct(concentration_first_share(shops))}(第一店铺:{shops.get('top_label') or ''}",
f"- **前三店铺合计份额**{_pct(concentration_top_three_share(shops))}",
"",
]
)
dbrand = conc.get("detail_brand_among_merged") or {}
if dbrand.get("cr1") is not None or dbrand.get("top_label"):
if concentration_first_share(dbrand) is not None or dbrand.get("top_label"):
lines.extend(
[
"## 品牌(深入样本)",
"",
f"- **第一大品牌份额(深入样本)**{_pct(dbrand.get('cr1'))}(头部:{dbrand.get('top_label') or ''}",
f"- **前三品牌合计份额**{_pct(dbrand.get('cr3'))}",
f"- **第一大品牌份额(深入样本)**{_pct(concentration_first_share(dbrand))}(头部:{dbrand.get('top_label') or ''}",
f"- **前三品牌合计份额**{_pct(concentration_top_three_share(dbrand))}",
"",
]
)
@ -103,7 +108,7 @@ def markdown_summary_from_brief(brief: dict[str, Any]) -> str:
[
"## 价格(展示价统计)",
"",
f"- **样本量 n**{_num(pst.get('n'))}**统计口径**{src}",
f"- **样本量(条)**{_num(pst.get('n'))}**统计口径**{src}",
f"- **区间**{_num(pst.get('min'))} {_num(pst.get('max'))}**中位数**{_num(pst.get('median'))}",
"",
]

View File

@ -49,7 +49,7 @@ REPORT_SYSTEM = """你是业务与产品读者顾问。输入 JSON 含 `keyword`
- **不要**使用## 一」「## 八」等会打乱宿主文档的顶级章节号;请使用 ``####`` 或必要时 ``###`` 作为本段内小节标题;
- **不要**输出完整报告目录或复述研究范围与方法长章
- **不要**撰写 Markdown 表格版竞品对比矩阵或罗列 SKU 明细**正文已含矩阵**此处只做分组级语义归纳
- **不要使用** CR1CR3 英文缩写集中度请用第一大品牌份额前三品牌合计份额
- **不要使用** CR1CR3 集中度缩写作主表述集中度请用第一大店铺/品牌份额前三家合计份额输入中的英文字段名勿照抄进正文请写成中文业务用语
**请输出**仅输出将置于 §8.5 下的正文不要自造### 8.5」标题行):
- **Markdown** **8001500 **
@ -265,7 +265,7 @@ STRATEGY_SYSTEM = """你是市场策略顾问,根据**结构化监测摘要**
**规则**
- 输入 JSON `rules_draft_markdown`规则引擎生成的底稿与同任务数据一致`structured_brief`摘要子集`strategy_decisions``business_notes`
- **不得编造**输入中不存在的销量占比价格数字若底稿与摘要中有数字须保持一致表述集中度时用第一大品牌份额等中文**不要用** CR1CR3 缩写
- **不得编造**输入中不存在的销量占比价格数字若底稿与摘要中有数字须保持一致表述集中度时用第一大份额前三家合计等中文**不要用** CR1CR3 缩写
- `structured_brief` `matrix_overview_for_llm` 或矩阵相关字段策略中应**呼应**细分类目分组与竞品矩阵结论不得无故删光矩阵相关建议
- 可调整段落衔接标题层级列表与表格呈现使更易读可补充建议待业务确认类表述但不虚构竞品名称或数据
- **仅输出** Markdown 正文不要 ``` 围栏包裹全文"""

View File

@ -9,6 +9,11 @@ from __future__ import annotations
import math
from typing import Any
from pipeline.brief_concentration import (
concentration_first_share,
concentration_top_three_share,
)
def _esc(s: Any) -> str:
t = "" if s is None else str(s).strip()
@ -154,8 +159,18 @@ def build_strategy_draft_markdown(
shops = conc.get("shops_from_list") or {}
dbrand = conc.get("detail_brand_among_merged") or {}
lines.extend(["## 三、竞争格局 → 策略含义", ""])
n_shop = _cr_narrative("列表侧店铺集中度", shops.get("cr1"), shops.get("cr3"), shops.get("top_label"))
n_brand = _cr_narrative("深入样本内品牌集中度", dbrand.get("cr1"), dbrand.get("cr3"), dbrand.get("top_label"))
n_shop = _cr_narrative(
"列表侧店铺集中度",
concentration_first_share(shops),
concentration_top_three_share(shops),
shops.get("top_label"),
)
n_brand = _cr_narrative(
"深入样本内品牌集中度",
concentration_first_share(dbrand),
concentration_top_three_share(dbrand),
dbrand.get("top_label"),
)
if n_shop:
lines.append(n_shop)
if n_brand:

View File

@ -26,15 +26,15 @@
},
"concentration": {
"shops_from_list": {
"cr1": 0.12,
"cr3": 0.35,
"first_share": 0.12,
"top_three_combined_share": 0.35,
"top_label": "某旗舰店",
"top_share_pct": "12.0%"
},
"list_brand_field": null,
"detail_brand_among_merged": {
"cr1": 0.2,
"cr3": 0.6,
"first_share": 0.2,
"top_three_combined_share": 0.6,
"top_label": "品牌A",
"top_share_pct": "20.0%"
}

View File

@ -208,9 +208,9 @@ watch(
<h2>分析报告查看</h2>
<p class="hint-top">
选择<strong>已成功</strong>的任务在线阅读报告或下载
流水线生成报告时会<strong>自动</strong>基于<strong>全部评价正文</strong>分块调用大模型扩展<strong>关注词</strong>并单次调用归纳<strong>使用场景</strong>在预设场景组之外追加 label+触发子串再写入统计图PNG二点五章与简报包 <code>report_assets</code>
<strong>一键下载简报包</strong>含报告稿统计图结构化 JSON要点摘录
需要改规则或重算请至
若开启大模型系统会在后台根据评价正文补充<strong>关注词</strong><strong>使用场景</strong>标签并生成报告中的统计图与报告插图章节对应
<strong>一键下载简报包</strong>内含报告正文插图文件夹机器整理的<strong>数据摘要</strong>以及便于扫读的<strong>要点摘录</strong>
需要改分析规则或重新出稿请至
<RouterLink to="/jd/analysis-build">报告生成</RouterLink>
</p>
@ -290,7 +290,7 @@ watch(
<section v-if="briefJson" class="ma-card preview-card">
<div class="preview-head">
<h2>结构化竞品摘要</h2>
<h2>竞品数据摘要机器整理</h2>
<div class="tabs">
<button type="button" class="ma-btn ma-btn-secondary brief-tool" @click="copyBriefJson">
{{ briefCopyOk ? '已复制' : '复制' }}
@ -298,7 +298,7 @@ watch(
<button type="button" class="ma-btn ma-btn-secondary brief-tool" @click="downloadBriefJson">下载文件</button>
</div>
</div>
<p class="hint-top brief-hint">与上方报告统计口径一致的数据汇总可复制或下载给其它工具使用</p>
<p class="hint-top brief-hint">与上方报告统计口径一致供复制或存档一般业务阅读以报告正文与要点摘录为主本块主要用于存档或交给同事继续分析</p>
<pre class="raw-md brief-json">{{ briefJson }}</pre>
</section>

View File

@ -73,9 +73,8 @@ watch(selectedId, () => {
<button type="button" class="ma-btn ma-btn-secondary btn-refresh" @click="load">刷新任务列表</button>
</div>
<p class="lead">
选择任务后浏览已入库的搜索商详评价与<strong>整合宽表</strong>合并表按列拆分入库
<code>keyword_pipeline_merged.csv</code> lean 列一致 Tab 导出当前表可导出 JSON / CSV /
Excel竞品报告请在报告查看阅读或报告生成重新生成
选择任务后可查看本批已入库的<strong>搜索列表</strong><strong>商品详情</strong><strong>评价</strong><strong>整合同步表</strong>一行对应一个商品在报告里用到的主要字段
各标签下可**导出**为表格或数据文件完整文字报告请在报告查看阅读或在报告生成中重新出稿
</p>
<p v-if="loadError" class="ma-err">{{ loadError }}</p>