mirror of
https://github.com/primedigitaltech/market-assistant.git
synced 2026-07-21 23:41:39 +08:00
feat(strategy): 表单可填本阶段策略目标类型 stage_goal_type
接入 StrategyDraftRequestSerializer、规则底稿表与 1.3 要点、strategy_decisions_substantive 与 STRATEGY_SYSTEM 决策边界;前端策略生成页增加输入框。 Made-with: Cursor
This commit is contained in:
parent
6c37a2def2
commit
6faef2180e
@ -36,6 +36,7 @@ os.environ.setdefault("DJANGO_SETTINGS_MODULE", "config.settings")
|
||||
def _strategy_decisions_empty() -> dict[str, Any]:
|
||||
return {
|
||||
"product_role": "",
|
||||
"stage_goal_type": "",
|
||||
"time_horizon": "",
|
||||
"success_criteria": "",
|
||||
"non_goals": "",
|
||||
|
||||
@ -15,6 +15,7 @@ from .llm_client import call_llm, estimate_chat_input_tokens, llm_context_window
|
||||
# 与策略生成表单 POST 字段一致:任一则视为业务已提供「实质决策」,否则由模型基于数据推断草案。
|
||||
_STRATEGY_DECISION_SUBSTANTIVE_KEYS: tuple[str, ...] = (
|
||||
"product_role",
|
||||
"stage_goal_type",
|
||||
"battlefield_one_line",
|
||||
"audience_segment",
|
||||
"time_horizon",
|
||||
@ -142,7 +143,7 @@ STRATEGY_SYSTEM = f"""你是市场策略顾问,根据**结构化监测摘要**
|
||||
- 仍须遵守全局禁止编造:数字、品牌、店铺、用户原话、活动规则仅可来自输入依据;无依据处用假设语气。
|
||||
|
||||
**决策边界(硬性)**:
|
||||
- **当 `strategy_decisions_substantive` 为 true 时**:业务已在 `strategy_decisions` 中填写的项(角色、时间、成功标准、战场一句话、定位勾选、竞争倾向、四柱、目标客群/对标/资源备注、**营销策略**与**总体策略**等)视为**已定决策**:成稿须**落实为具体执行句**,**不得**改写成相反结论或再要求用户「请选择」。
|
||||
- **当 `strategy_decisions_substantive` 为 true 时**:业务已在 `strategy_decisions` 中填写的项(角色、**本阶段策略目标类型**(`stage_goal_type`)、时间、成功标准、战场一句话、定位勾选、竞争倾向、四柱、目标客群/对标/资源备注、**营销策略**与**总体策略**等)视为**已定决策**:成稿须**落实为具体执行句**,**不得**改写成相反结论或再要求用户「请选择」。**若 `stage_goal_type` 非空**,「策略范围与前提」表中「本阶段策略目标类型」列须**直接采用该表述**(可略作语序润色),**不得**改判为另一类阶段目标。
|
||||
- **当 `strategy_decisions_substantive` 为 true** 而部分表单项仍为空或占位:结合监测摘要与节选**补全为可执行表述**,与数据方向一致。
|
||||
- **当 `strategy_decisions_substantive` 为 false 时**:适用上文「业务决策未填写时的成稿义务」,**禁止**以「请先填表」类表述搪塞全篇。
|
||||
- **成稿阶段避免**:反复「请业务决策」;不确定时在 §2.1 用「类目/细类」+「假设:」「待业务确认:」**写清**,**禁止**只写泛化一句。
|
||||
|
||||
@ -238,11 +238,13 @@ def build_strategy_draft_markdown(
|
||||
if not for_llm_input
|
||||
else "*(待确认)*"
|
||||
)
|
||||
_table_goal_type = (
|
||||
"*(成稿从规划文档「目标类型选项」择一或组合,并与下列成功标准一致)*"
|
||||
if not for_llm_input
|
||||
else "*(待确认)*"
|
||||
sgt = _esc(d.get("stage_goal_type") or "").strip()
|
||||
_goal_type_placeholder = (
|
||||
"待确认:可填如「A 心智占位 / B 转化爬坡 …」,见规划文档「目标类型选项」"
|
||||
if for_llm_input
|
||||
else "成稿从规划文档「目标类型选项」择一或组合,并与下列成功标准一致"
|
||||
)
|
||||
_table_goal_type = _scope_cell(sgt, _goal_type_placeholder)
|
||||
_summary_user_side = (
|
||||
"- **用户侧**:*(一两句结论即可:讨论焦点与负向主题;**按细类分句**归纳,**勿**混成「全站用户」一句;**勿**展开与报告重复的细类统计、词频。)*"
|
||||
if not for_llm_input
|
||||
@ -357,6 +359,11 @@ def build_strategy_draft_markdown(
|
||||
]
|
||||
),
|
||||
_goal_bullet("本品角色", str(d.get("product_role") or ""), "新品 / 追赶 / 防守 / 拓品类 …"),
|
||||
_goal_bullet(
|
||||
"本阶段策略目标类型",
|
||||
str(d.get("stage_goal_type") or ""),
|
||||
"见规划文档「目标类型选项」或自填(如心智占位、转化爬坡)",
|
||||
),
|
||||
_goal_bullet(
|
||||
"目标客群",
|
||||
str(d.get("audience_segment") or ""),
|
||||
|
||||
@ -410,6 +410,13 @@ class StrategyDraftRequestSerializer(serializers.Serializer):
|
||||
product_role = serializers.CharField(
|
||||
required=False, allow_blank=True, default="", max_length=500, trim_whitespace=False
|
||||
)
|
||||
stage_goal_type = serializers.CharField(
|
||||
required=False,
|
||||
allow_blank=True,
|
||||
default="",
|
||||
max_length=1000,
|
||||
trim_whitespace=False,
|
||||
)
|
||||
time_horizon = serializers.CharField(
|
||||
required=False, allow_blank=True, default="", max_length=200, trim_whitespace=False
|
||||
)
|
||||
|
||||
@ -20,6 +20,9 @@ class StrategyDraftTests(SimpleTestCase):
|
||||
self.assertTrue(
|
||||
strategy_decisions_substantive({"product_role": "新品"})
|
||||
)
|
||||
self.assertTrue(
|
||||
strategy_decisions_substantive({"stage_goal_type": "B 转化爬坡"})
|
||||
)
|
||||
|
||||
def test_for_llm_input_omits_dev_traces(self) -> None:
|
||||
brief = {
|
||||
@ -78,6 +81,24 @@ class StrategyDraftTests(SimpleTestCase):
|
||||
self.assertIn("## 七、品牌四线", md)
|
||||
self.assertIn("市场策略制定草稿", md)
|
||||
|
||||
def test_stage_goal_type_in_scope_table(self) -> None:
|
||||
brief = {
|
||||
"schema_version": 1,
|
||||
"keyword": "K",
|
||||
"batch_label": "b1",
|
||||
"scope": {"merged_sku_count": 2},
|
||||
}
|
||||
md = build_strategy_draft_markdown(
|
||||
job_id=1,
|
||||
keyword="K",
|
||||
brief=brief,
|
||||
strategy_decisions={"stage_goal_type": "A 心智占位"},
|
||||
generated_at_iso="2026-04-09T12:00:00",
|
||||
for_llm_input=True,
|
||||
)
|
||||
self.assertIn("| **本阶段策略目标类型** | A 心智占位 |", md)
|
||||
self.assertIn("**本阶段策略目标类型**:A 心智占位", md)
|
||||
|
||||
def test_strategy_decisions_merge(self) -> None:
|
||||
brief = {"schema_version": 1, "keyword": "K", "batch_label": "b"}
|
||||
decisions = {
|
||||
|
||||
@ -141,6 +141,7 @@ class JobStrategyDraftView(APIView):
|
||||
notes = (vd.get("business_notes") or "").strip()
|
||||
strategy_decisions = {
|
||||
"product_role": vd.get("product_role") or "",
|
||||
"stage_goal_type": vd.get("stage_goal_type") or "",
|
||||
"time_horizon": vd.get("time_horizon") or "",
|
||||
"success_criteria": vd.get("success_criteria") or "",
|
||||
"non_goals": vd.get("non_goals") or "",
|
||||
|
||||
@ -49,6 +49,7 @@ const briefMatrixErr = ref('')
|
||||
|
||||
const decisions = reactive({
|
||||
product_role: '',
|
||||
stage_goal_type: '',
|
||||
time_horizon: '',
|
||||
success_criteria: '',
|
||||
non_goals: '',
|
||||
@ -96,6 +97,7 @@ function buildPayload() {
|
||||
generator,
|
||||
business_notes: businessNotes.value,
|
||||
product_role: decisions.product_role,
|
||||
stage_goal_type: decisions.stage_goal_type,
|
||||
time_horizon: decisions.time_horizon,
|
||||
success_criteria: decisions.success_criteria,
|
||||
non_goals: decisions.non_goals,
|
||||
@ -315,7 +317,7 @@ watch(
|
||||
<fieldset class="fieldset">
|
||||
<legend>策略范围与前提</legend>
|
||||
<p class="fieldset-hint">
|
||||
界定本次策略的任务边界与阶段目标。监测词、批次由任务自动带出;<strong>主推类目</strong>以顶部「矩阵细类」为准。角色、战场、客群会进入策略稿开篇,并与下一栏「主要对标」衔接。
|
||||
界定本次策略的任务边界与阶段目标。监测词、批次由任务自动带出;<strong>主推类目</strong>以顶部「矩阵细类」为准。角色、<strong>本阶段策略目标类型</strong>、战场、客群会进入策略稿开篇;目标类型填好后大模型不得擅自改判。并与下一栏「主要对标」衔接。
|
||||
</p>
|
||||
<label class="fld">
|
||||
<span>本品角色(策略服务对象)</span>
|
||||
@ -325,6 +327,14 @@ watch(
|
||||
placeholder="如:追赶型 / 新品 / 防守 / 拓品类"
|
||||
/>
|
||||
</label>
|
||||
<label class="fld fld-block">
|
||||
<span>本阶段策略目标类型</span>
|
||||
<textarea
|
||||
v-model="decisions.stage_goal_type"
|
||||
rows="2"
|
||||
placeholder="由业务选定,如:A 心智占位 / B 转化与复购爬坡 / C 份额防守……详见仓库 docs/planning/策略生成-框架确定.md;填后大模型须落实,不得擅自改判"
|
||||
/>
|
||||
</label>
|
||||
<label class="fld fld-block">
|
||||
<span>一句话战场</span>
|
||||
<textarea
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user