mirror of
https://github.com/primedigitaltech/market-assistant.git
synced 2026-07-21 23:41:39 +08:00
feat(pipeline): 策略表单增加营销与总体策略并顺延规则稿章节
- API:marketing_strategy、general_strategy;规则骨架新增「二、营销策略与总体方向」,原 §2–§9 顺延为 §3–§10。 - LLM 提示与探针说明用语同步;OpenAPI、单测与策略生成页字段集「五」已对齐。 Made-with: Cursor
This commit is contained in:
parent
16d64565a3
commit
f09a46a79e
@ -17,7 +17,7 @@ STRATEGY_SYSTEM = """你是市场策略顾问,根据**结构化监测摘要**
|
||||
**输入**:`rules_draft_markdown`(规则骨架,与同任务数据一致)、`structured_brief`(摘要子集)、`strategy_decisions`、`business_notes`;可选 `report_strategy_excerpt`(与同任务宿主报告**第九章**「策略与机会」正文同源)。
|
||||
|
||||
**决策边界(硬性)**:
|
||||
- **业务已在 `strategy_decisions` 中填写的项**(角色、时间、成功标准、战场一句话、定位勾选、竞争倾向、四柱、目标客群/对标/资源备注等)视为**已定决策**:成稿须**落实为具体执行句**,**不得**改写成相反结论或再要求用户「请选择」。
|
||||
- **业务已在 `strategy_decisions` 中填写的项**(角色、时间、成功标准、战场一句话、定位勾选、竞争倾向、四柱、目标客群/对标/资源备注、**营销策略**与**总体策略**等)视为**已定决策**:成稿须**落实为具体执行句**,**不得**改写成相反结论或再要求用户「请选择」。
|
||||
- **表单中为空或占位(如 *待填*、*骨架占位*)的项**:由你结合 `structured_brief`、`report_strategy_excerpt`(若非空)与数据摘录**补全为可执行表述**;补全须与数据方向一致,**不得**编造输入中不存在的销量、GMV、未给出的价格或占比。
|
||||
- **成稿阶段禁止**:再写一轮「请业务决策」「待确认后再定」「假设:待验证」等**二次决策话术**;不确定性用一句带过即可(如「需下周用原评论抽样核对」),勿重复堆砌。
|
||||
|
||||
@ -101,7 +101,7 @@ def generate_strategy_draft_markdown_llm(
|
||||
if report_uses_chapter8_text_mining_probe(report_config):
|
||||
payload["structured_brief_omission_note"] = (
|
||||
"已启用第八章文本挖掘:structured_brief 已省略关注词/场景子串计数字段,避免与报告正文口径冲突。"
|
||||
"请依报告 §8 与摘要其他字段,将底稿第五节写成具体执行要点(需求焦点、场景、传播),勿逐条编造子串命中列表。"
|
||||
"请依报告 §8 与摘要其他字段,将底稿「用户与评论侧」一节写成具体执行要点(需求焦点、场景、传播),勿逐条编造子串命中列表。"
|
||||
)
|
||||
raw = json.dumps(payload, ensure_ascii=False)
|
||||
if len(raw) > 500_000:
|
||||
|
||||
@ -102,7 +102,7 @@ def report_uses_chapter8_text_mining_probe(report_config: dict[str, Any] | None)
|
||||
"""
|
||||
与任务 ``report_config`` 中 ``chapter8_text_mining_probe`` 一致;未显式设置时默认 ``True``
|
||||
(与 ``jd.runner.get_default_report_config`` 一致)。
|
||||
为 ``True`` 时,策略稿第五节不再逐条列举关注词/场景子串命中,以免与当前报告正文口径冲突。
|
||||
为 ``True`` 时,策略稿「用户与评论侧」一节不再逐条列举关注词/场景子串命中,以免与当前报告正文口径冲突。
|
||||
"""
|
||||
if not isinstance(report_config, dict):
|
||||
return True
|
||||
@ -170,12 +170,30 @@ def build_strategy_draft_markdown(
|
||||
]
|
||||
)
|
||||
|
||||
lines.extend(
|
||||
[
|
||||
"## 二、营销策略与总体方向",
|
||||
"",
|
||||
_goal_bullet(
|
||||
"营销策略",
|
||||
str(d.get("marketing_strategy") or ""),
|
||||
"传播、活动、投放、内容主线等(可选)",
|
||||
),
|
||||
_goal_bullet(
|
||||
"总体策略",
|
||||
str(d.get("general_strategy") or ""),
|
||||
"增长/品类/经营层面的总原则,可与 4P 支柱呼应(可选)",
|
||||
),
|
||||
"",
|
||||
]
|
||||
)
|
||||
|
||||
scope = brief.get("scope") or {}
|
||||
merged_n = scope.get("merged_sku_count")
|
||||
comm_n = scope.get("comment_flat_rows")
|
||||
lines.extend(
|
||||
[
|
||||
"## 二、战场与样本",
|
||||
"## 三、战场与样本",
|
||||
"",
|
||||
f"- **监测关键词 / 货架语境**:{kw}",
|
||||
f"- **批次**:{_esc(brief.get('batch_label')) or '—'}",
|
||||
@ -197,7 +215,7 @@ def build_strategy_draft_markdown(
|
||||
conc = brief.get("concentration") or {}
|
||||
shops = conc.get("shops_from_list") or {}
|
||||
dbrand = conc.get("detail_brand_among_merged") or {}
|
||||
lines.extend(["## 三、竞争结构(摘录)", ""])
|
||||
lines.extend(["## 四、竞争结构(摘录)", ""])
|
||||
n_shop = _cr_narrative(
|
||||
"列表侧店铺集中度",
|
||||
concentration_first_share(shops),
|
||||
@ -244,7 +262,7 @@ def build_strategy_draft_markdown(
|
||||
lines.append("")
|
||||
|
||||
pst = brief.get("price_stats") or {}
|
||||
lines.extend(["## 四、价格带与定位(业务已选)", ""])
|
||||
lines.extend(["## 五、价格带与定位(业务已选)", ""])
|
||||
if pst.get("n"):
|
||||
src = _esc(brief.get("price_stats_source")) or "—"
|
||||
lines.extend(
|
||||
@ -278,7 +296,7 @@ def build_strategy_draft_markdown(
|
||||
|
||||
ckw = brief.get("comment_focus_keywords") or []
|
||||
usc = brief.get("usage_scenarios") or []
|
||||
lines.extend(["## 五、用户与评论侧", ""])
|
||||
lines.extend(["## 六、用户与评论侧", ""])
|
||||
if use_ch8_probe:
|
||||
lines.extend(
|
||||
[
|
||||
@ -317,7 +335,7 @@ def build_strategy_draft_markdown(
|
||||
hints = brief.get("strategy_hints") or []
|
||||
lines.extend(
|
||||
[
|
||||
"## 六、机会与策略支柱",
|
||||
"## 七、机会与策略支柱",
|
||||
"",
|
||||
"### 摘要提示(`strategy_hints`)",
|
||||
"",
|
||||
@ -357,7 +375,7 @@ def build_strategy_draft_markdown(
|
||||
)
|
||||
lines.extend(
|
||||
[
|
||||
"## 七、风险核对项",
|
||||
"## 八、风险核对项",
|
||||
"",
|
||||
_risk_line(rk, rk_kw),
|
||||
_risk_line(rp, "价格带是否含大促/异常挂价?(需核对清洗规则)"),
|
||||
@ -369,7 +387,7 @@ def build_strategy_draft_markdown(
|
||||
notes = _esc(business_notes)
|
||||
lines.extend(
|
||||
[
|
||||
"## 八、业务约束与备注",
|
||||
"## 九、业务约束与备注",
|
||||
"",
|
||||
(notes if notes else "*(未填写业务备注。)*"),
|
||||
"",
|
||||
@ -378,10 +396,10 @@ def build_strategy_draft_markdown(
|
||||
|
||||
lines.extend(
|
||||
[
|
||||
"## 九、下一步(可执行)",
|
||||
"## 十、下一步(可执行)",
|
||||
"",
|
||||
"- [ ] 对齐 **§一** 目标与 **§八** 约束,锁定 1~2 条主命题。",
|
||||
"- [ ] 为 **§六** 支柱各补 **1 条数据证据** + **12 周内可交付动作**(负责人 + 时间)。",
|
||||
"- [ ] 对齐 **§一** 目标与 **§九** 约束,锁定 1~2 条主命题。",
|
||||
"- [ ] 为 **§七** 支柱各补 **1 条数据证据** + **12 周内可交付动作**(负责人 + 时间)。",
|
||||
"",
|
||||
"---",
|
||||
"",
|
||||
|
||||
@ -447,6 +447,12 @@ class StrategyDraftRequestSerializer(serializers.Serializer):
|
||||
resource_notes = serializers.CharField(
|
||||
required=False, allow_blank=True, default="", max_length=1000, trim_whitespace=False
|
||||
)
|
||||
marketing_strategy = serializers.CharField(
|
||||
required=False, allow_blank=True, default="", max_length=2000, trim_whitespace=False
|
||||
)
|
||||
general_strategy = serializers.CharField(
|
||||
required=False, allow_blank=True, default="", max_length=2000, trim_whitespace=False
|
||||
)
|
||||
ack_risk_keywords = serializers.BooleanField(required=False, default=False)
|
||||
ack_risk_price = serializers.BooleanField(required=False, default=False)
|
||||
ack_risk_concentration = serializers.BooleanField(required=False, default=False)
|
||||
|
||||
@ -27,6 +27,7 @@ class StrategyDraftTests(SimpleTestCase):
|
||||
self.assertIn("假设A", md)
|
||||
self.assertIn("重点:华东", md)
|
||||
self.assertIn("目标与边界", md)
|
||||
self.assertIn("营销策略与总体方向", md)
|
||||
self.assertIn("市场策略制定草稿", md)
|
||||
|
||||
def test_strategy_decisions_merge(self) -> None:
|
||||
@ -35,6 +36,8 @@ class StrategyDraftTests(SimpleTestCase):
|
||||
"product_role": "追赶型",
|
||||
"positioning_choice": "mid",
|
||||
"competitive_stance": "flank",
|
||||
"marketing_strategy": "内容种草+搜索承接",
|
||||
"general_strategy": "先腰后顶",
|
||||
"pillar_product": "做低糖配方",
|
||||
"ack_risk_keywords": True,
|
||||
"ack_risk_price": False,
|
||||
@ -48,6 +51,8 @@ class StrategyDraftTests(SimpleTestCase):
|
||||
report_config={"chapter8_text_mining_probe": False},
|
||||
)
|
||||
self.assertIn("**本品角色**:追赶型", md)
|
||||
self.assertIn("**营销策略**:内容种草+搜索承接", md)
|
||||
self.assertIn("**总体策略**:先腰后顶", md)
|
||||
self.assertIn("- [x] **卡腰**", md)
|
||||
self.assertIn("- [ ] **贴顶**", md)
|
||||
self.assertIn("侧翼切入", md)
|
||||
|
||||
@ -136,6 +136,8 @@ class JobStrategyDraftView(APIView):
|
||||
"audience_segment": vd.get("audience_segment") or "",
|
||||
"competitor_reference": vd.get("competitor_reference") or "",
|
||||
"resource_notes": vd.get("resource_notes") or "",
|
||||
"marketing_strategy": vd.get("marketing_strategy") or "",
|
||||
"general_strategy": vd.get("general_strategy") or "",
|
||||
"ack_risk_keywords": bool(vd.get("ack_risk_keywords")),
|
||||
"ack_risk_price": bool(vd.get("ack_risk_price")),
|
||||
"ack_risk_concentration": bool(vd.get("ack_risk_concentration")),
|
||||
|
||||
@ -561,6 +561,14 @@ components:
|
||||
type: string
|
||||
maxLength: 1000
|
||||
description: 资源与预算量级备注(可选)
|
||||
marketing_strategy:
|
||||
type: string
|
||||
maxLength: 2000
|
||||
description: 营销策略(传播、活动、投放、内容等,可选)
|
||||
general_strategy:
|
||||
type: string
|
||||
maxLength: 2000
|
||||
description: 总体策略(增长/品类/经营总原则,可选)
|
||||
ack_risk_keywords:
|
||||
type: boolean
|
||||
default: false
|
||||
|
||||
@ -51,6 +51,8 @@ const decisions = reactive({
|
||||
audience_segment: '',
|
||||
competitor_reference: '',
|
||||
resource_notes: '',
|
||||
marketing_strategy: '',
|
||||
general_strategy: '',
|
||||
ack_risk_keywords: false,
|
||||
ack_risk_price: false,
|
||||
ack_risk_concentration: false,
|
||||
@ -95,6 +97,8 @@ function buildPayload() {
|
||||
audience_segment: decisions.audience_segment,
|
||||
competitor_reference: decisions.competitor_reference,
|
||||
resource_notes: decisions.resource_notes,
|
||||
marketing_strategy: decisions.marketing_strategy,
|
||||
general_strategy: decisions.general_strategy,
|
||||
ack_risk_keywords: decisions.ack_risk_keywords,
|
||||
ack_risk_price: decisions.ack_risk_price,
|
||||
ack_risk_concentration: decisions.ack_risk_concentration,
|
||||
@ -294,6 +298,26 @@ watch(
|
||||
</label>
|
||||
</fieldset>
|
||||
|
||||
<fieldset class="fieldset">
|
||||
<legend>五、营销策略与总体策略</legend>
|
||||
<label class="fld fld-block">
|
||||
<span>营销策略</span>
|
||||
<textarea
|
||||
v-model="decisions.marketing_strategy"
|
||||
rows="3"
|
||||
placeholder="传播、活动、投放、内容主线等(可选)"
|
||||
/>
|
||||
</label>
|
||||
<label class="fld fld-block">
|
||||
<span>总体策略</span>
|
||||
<textarea
|
||||
v-model="decisions.general_strategy"
|
||||
rows="3"
|
||||
placeholder="增长 / 品类 / 经营总原则,可与下方 4P 支柱呼应(可选)"
|
||||
/>
|
||||
</label>
|
||||
</fieldset>
|
||||
|
||||
<fieldset class="fieldset">
|
||||
<legend>六、策略支柱 — 本品打算怎么做(可先填一列)</legend>
|
||||
<label class="fld fld-block">
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user