diff --git a/backend/pipeline/llm/generate_strategy.py b/backend/pipeline/llm/generate_strategy.py index 16df065..a751715 100644 --- a/backend/pipeline/llm/generate_strategy.py +++ b/backend/pipeline/llm/generate_strategy.py @@ -12,17 +12,24 @@ from ..reporting.strategy_draft import ( ) from .llm_client import call_llm, estimate_chat_input_tokens, llm_context_window_size -STRATEGY_SYSTEM = """你是市场策略顾问,根据**结构化监测摘要**与业务侧填写的**决策字段**,把「规则底稿」润色为可读的策略 Markdown。 +STRATEGY_SYSTEM = """你是市场策略顾问,根据**结构化监测摘要**与业务侧填写的**决策字段**,把「规则底稿」写成**短、可执行**的策略 Markdown 成稿。 -**规则**: -- 输入 JSON 含 `rules_draft_markdown`(规则引擎生成的底稿,与同任务数据一致)、`structured_brief`(摘要子集)、`strategy_decisions`、`business_notes` 等;可选 `report_strategy_excerpt`(与同任务宿主报告**第九章**「策略与机会」正文同源,来自已落盘产物)。 -- **与报告第九章对齐(硬性,当 `report_strategy_excerpt` 非空时)**:润色后的全文在**战略方向与主要判断**上须与该节选**一致**,不得写出与之**明显矛盾**的结论(如定价档位、差异化主线、主要风险判断);若 `business_notes` 或 `strategy_decisions` 与节选冲突,须在正文适当位置简短说明「与报告第九章策略归纳不一致之处见业务备注」类表述,且**不得**把节选与 `structured_brief` 中均未出现的数字当作既定事实。 -- **当 `report_strategy_excerpt` 为空或仅为占位说明**:表示报告侧未生成或未重跑第九章大模型正文,仅依据 `rules_draft_markdown` 与 `structured_brief` 润色,**不得编造**与可能存在的报告第九章结论。 -- **不得编造**输入中不存在的销量、占比、价格数字;若底稿与摘要中有数字,须保持一致;表述集中度时用「第一大……份额」「前三家合计」等中文,**不要用** CR1、CR3 等缩写; -- 若 `structured_brief` 含 `matrix_overview_for_llm` 或矩阵相关字段,策略中应**呼应**细分类目分组与竞品矩阵结论,不得无故删光矩阵相关建议; -- 可调整段落衔接、标题层级、列表与表格呈现,使更易读;可补充「建议」「待业务确认」类表述,但不虚构竞品名称或数据; -- **仅输出** Markdown 正文(不要 ``` 围栏包裹全文); -- **输出完整性**:须收束各小节与全文,勿在句子或列表中途戛然而止。""" +**输入**:`rules_draft_markdown`(规则骨架,与同任务数据一致)、`structured_brief`(摘要子集)、`strategy_decisions`、`business_notes`;可选 `report_strategy_excerpt`(与同任务宿主报告**第九章**「策略与机会」正文同源)。 + +**决策边界(硬性)**: +- **业务已在 `strategy_decisions` 中填写的项**(角色、时间、成功标准、战场一句话、定位勾选、竞争倾向、四柱、目标客群/对标/资源备注等)视为**已定决策**:成稿须**落实为具体执行句**,**不得**改写成相反结论或再要求用户「请选择」。 +- **表单中为空或占位(如 *待填*、*骨架占位*)的项**:由你结合 `structured_brief`、`report_strategy_excerpt`(若非空)与数据摘录**补全为可执行表述**;补全须与数据方向一致,**不得**编造输入中不存在的销量、GMV、未给出的价格或占比。 +- **成稿阶段禁止**:再写一轮「请业务决策」「待确认后再定」「假设:待验证」等**二次决策话术**;不确定性用一句带过即可(如「需下周用原评论抽样核对」),勿重复堆砌。 + +**与报告第九章对齐(当 `report_strategy_excerpt` 非空时)**:战略方向与主要判断须与该节选**一致**,不得明显矛盾;若 `business_notes` 或表单与节选冲突,正文中简短点明「与报告第九章归纳差异见业务备注」,且**不得**把节选与 `structured_brief` 均未出现的数字当作事实。 + +**当 `report_strategy_excerpt` 为空**:仅依据底稿与摘要润色,**不得编造**报告第九章结论。 + +**数据**:不得编造销量、占比、价格数字;底稿与摘要中的数字须保持一致;集中度用「第一大……份额」「前三家合计」等中文,**不要用** CR1、CR3。 + +**矩阵**:若 `structured_brief` 含矩阵相关字段,须**呼应**细分类目与竞品矩阵结论,不得无故删光。 + +**输出**:仅 Markdown 正文(不要 ``` 围栏);须收束各小节与全文,勿中途截断。""" STRATEGY_USER_PREFIX = """请基于以下 JSON 输出最终策略稿(Markdown)。\n\n""" @@ -93,8 +100,8 @@ def generate_strategy_draft_markdown_llm( } if report_uses_chapter8_text_mining_probe(report_config): payload["structured_brief_omission_note"] = ( - "已启用第八章文本挖掘:本 JSON 中的 structured_brief 已省略关注词/场景子串计数字段," - "避免与当前报告正文口径冲突;请依规则底稿第五节说明与报告 §8 自拟假设,勿逐条编造子串命中列表。" + "已启用第八章文本挖掘:structured_brief 已省略关注词/场景子串计数字段,避免与报告正文口径冲突。" + "请依报告 §8 与摘要其他字段,将底稿第五节写成具体执行要点(需求焦点、场景、传播),勿逐条编造子串命中列表。" ) raw = json.dumps(payload, ensure_ascii=False) if len(raw) > 500_000: diff --git a/backend/pipeline/reporting/strategy_draft.py b/backend/pipeline/reporting/strategy_draft.py index 5c1d3c2..110342f 100644 --- a/backend/pipeline/reporting/strategy_draft.py +++ b/backend/pipeline/reporting/strategy_draft.py @@ -1,8 +1,8 @@ """ -市场策略 Markdown 草稿:侧重**策略制定框架**(目标、战场、定位、支柱、行动), -基于同任务结构化摘要与可选业务备注规则生成;附录为关键数据速览。 +市场策略 Markdown 草稿:**规则骨架**(占位 + 少量数据摘录),供业务与大模型成稿对齐。 -后续可接 LLM 润色;当前无模型调用,便于验收与追溯。 +- 决策在「策略生成」表单完成;未填项由大模型结合摘要与报告节选补全。 +- 骨架刻意短、可执行;避免与成稿重复的「假设 / 待验证」套话。 """ from __future__ import annotations @@ -121,7 +121,7 @@ def build_strategy_draft_markdown( strategy_decisions: dict[str, Any] | None = None, report_config: dict[str, Any] | None = None, ) -> str: - """生成可下载的 Markdown:策略框架为主,附录为数据速览。""" + """生成可下载的 Markdown:策略骨架为主,附录为数据速览。""" use_ch8_probe = report_uses_chapter8_text_mining_probe(report_config) d = strategy_decisions or {} pos = _esc(d.get("positioning_choice") or "").strip() @@ -129,7 +129,8 @@ def build_strategy_draft_markdown( lines: list[str] = [ f"# 市场策略制定草稿 · 「{kw}」", "", - "> 本稿用于**辅助制定市场策略**;由规则根据本批次结构化摘要与业务备注生成,**非大模型自由发挥**,定稿前请业务修订。", + "> **骨架说明**:本页为**规则骨架**(占位与少量摘录)。勾选大模型生成时,在骨架与数据上写**短、可执行**成稿。" + "**决策在策略生成表单完成**;未填项由模型结合本任务摘要与报告节选补全,**成稿不再写「请再选 / 请决策」式套话**。", "", ] if generated_at_iso: @@ -140,7 +141,7 @@ def build_strategy_draft_markdown( [ "---", "", - "## 一、战略背景与目标(请业务补全)", + "## 一、目标与边界", "", _goal_bullet("本品角色", str(d.get("product_role") or ""), "新品 / 追赶 / 防守 / 拓品类 …"), _goal_bullet("时间范围", str(d.get("time_horizon") or ""), "如:本季度 / 未来 12 周"), @@ -149,7 +150,22 @@ def build_strategy_draft_markdown( str(d.get("success_criteria") or ""), "如:搜索位次、转化率、声量、复购 …", ), - _goal_bullet("非目标(明确不做什么)", str(d.get("non_goals") or ""), "可选"), + _goal_bullet("非目标", str(d.get("non_goals") or ""), "明确不做什么(可选)"), + _goal_bullet( + "目标客群", + str(d.get("audience_segment") or ""), + "一句话:为谁、什么场景(可选)", + ), + _goal_bullet( + "主要对标", + str(d.get("competitor_reference") or ""), + "品牌或价位带参照(可选)", + ), + _goal_bullet( + "资源与预算备注", + str(d.get("resource_notes") or ""), + "人力、投放、产能等量级(可选)", + ), "", ] ) @@ -159,7 +175,7 @@ def build_strategy_draft_markdown( comm_n = scope.get("comment_flat_rows") lines.extend( [ - "## 二、战场界定(监测语境)", + "## 二、战场与样本", "", f"- **监测关键词 / 货架语境**:{kw}", f"- **批次**:{_esc(brief.get('batch_label')) or '—'}", @@ -167,22 +183,21 @@ def build_strategy_draft_markdown( ) if merged_n is not None or comm_n is not None: lines.append( - f"- **深入样本规模**:深入 SKU ≈ {_num(merged_n)};评价扁平条数 ≈ {_num(comm_n)}。" - "*策略含义:样本越大,以下「假设」越需抽样复核原评论。*" + f"- **深入样本**:深入 SKU ≈ {_num(merged_n)};评价扁平条数 ≈ {_num(comm_n)}。" ) bf = _esc(d.get("battlefield_one_line") or "").strip() if bf: lines.append(f"- **一句话战场**:{bf}") else: lines.append( - "- **一句话战场**:*(请用业务语言写:我们在哪个需求场景、与谁抢同一批用户?)*" + "- **一句话战场**:*(在哪个需求场景、与谁抢同一批用户?)*" ) lines.append("") 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), @@ -200,23 +215,20 @@ def build_strategy_draft_markdown( if n_brand: lines.append(n_brand) if not n_shop and not n_brand: - lines.append("*本摘要未含集中度指标,请结合本批次竞争结构数据补全后再写判断。*") + lines.append("*本摘要未含集中度指标,请结合本批次竞争结构数据补全。*") lines.extend( [ "", - "**可下判断的提问(自测)**", - "", - "- 若头部已占稳心智,本品是**侧翼**还是**正面替代**?", - "- 店铺/品牌分散时,是否适合用**细分场景**或**内容教育**切入?", + "- **自测**:头部已占稳心智时,侧翼还是正面替代?分散时是否用细分场景或内容教育切入?", "", ] ) stance = _esc(d.get("competitive_stance") or "").strip() stance_line = { - "flank": "- **本品倾向**:倾向**侧翼切入**,避免与头部正面硬碰。", - "head_on": "- **本品倾向**:倾向**正面替代**,对标头部主战场。", - "both": "- **本品倾向**:计划**分层推进**(部分场景侧翼、部分场景正面)。", - "undecided": "- **本品倾向**:**尚未拍板**,需在会议中对齐后再定主战场叙事。", + "flank": "- **本品倾向**:侧翼切入,避免与头部正面硬碰。", + "head_on": "- **本品倾向**:正面替代,对标头部主战场。", + "both": "- **本品倾向**:分层推进(部分场景侧翼、部分场景正面)。", + "undecided": "- **本品倾向**:*(表单未选;成稿时据数据写清倾向)*", }.get(stance) if stance_line: lines.append(stance_line) @@ -224,16 +236,15 @@ def build_strategy_draft_markdown( mix = brief.get("category_mix_top") or [] if mix: - lines.append("### 类目结构提示(Top)") + lines.append("### 类目结构(Top)") lines.append("") - lines.append("*以下仅作「货架长什么样」的速记。*") for item in mix[:6]: if isinstance(item, dict): lines.append(f"- {_esc(item.get('label'))}:{_num(item.get('count'))}") 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( @@ -241,46 +252,46 @@ def build_strategy_draft_markdown( f"- **价格来源**:{src},有效价样本 n = {_num(pst.get('n'))}。", f"- **展示价区间**:{_num(pst.get('min'))} ~ {_num(pst.get('max'))};**中位数** {_num(pst.get('median'))}。", "", - "**定位选项(请勾一条或改写,并写明理由)**", + "**主定位(与表单一致)**", "", - f"- {_pos_mark(pos, 'top')} **贴顶**:对标中高位或头部价位带,强调品质/成分/背书。", + f"- {_pos_mark(pos, 'top')} **贴顶**:中高位或头部价位带,强调品质/成分/背书。", f"- {_pos_mark(pos, 'mid')} **卡腰**:围绕中位数一带,强调性价比与场景匹配。", - f"- {_pos_mark(pos, 'entry')} **下探**:贴近区间下限,强调入门与拉新(注意毛利与品牌调性)。", - f"- {_pos_mark(pos, 'different')} **另起带**:刻意避开主价格带,用规格/组合/服务差异化。", + f"- {_pos_mark(pos, 'entry')} **下探**:贴近区间下限,强调入门与拉新(注意毛利与调性)。", + f"- {_pos_mark(pos, 'different')} **另起带**:避开主价格带,用规格/组合/服务差异化。", "", ] ) else: - lines.append("*摘要中无价带统计,请结合本批次价格相关数据补全后再填上表。*") + lines.append("*摘要中无价带统计,请结合本批次价格数据补全。*") lines.append("") lines.extend( [ - "**定位选项(请勾一条或改写,并写明理由)**", + "**主定位(与表单一致)**", "", - f"- {_pos_mark(pos, 'top')} **贴顶**:对标中高位或头部价位带,强调品质/成分/背书。", + f"- {_pos_mark(pos, 'top')} **贴顶**:中高位或头部价位带,强调品质/成分/背书。", f"- {_pos_mark(pos, 'mid')} **卡腰**:围绕中位数一带,强调性价比与场景匹配。", - f"- {_pos_mark(pos, 'entry')} **下探**:贴近区间下限,强调入门与拉新(注意毛利与品牌调性)。", - f"- {_pos_mark(pos, 'different')} **另起带**:刻意避开主价格带,用规格/组合/服务差异化。", + f"- {_pos_mark(pos, 'entry')} **下探**:贴近区间下限,强调入门与拉新(注意毛利与调性)。", + f"- {_pos_mark(pos, 'different')} **另起带**:避开主价格带,用规格/组合/服务差异化。", "", ] ) ckw = brief.get("comment_focus_keywords") or [] usc = brief.get("usage_scenarios") or [] - lines.extend(["## 五、用户需求与场景 — 可写成策略的假设", ""]) + lines.extend(["## 五、用户与评论侧", ""]) if use_ch8_probe: lines.extend( [ - "*当前任务报告以**第八章评论侧文本挖掘**为主线(替代原关注词/场景条形图为主呈现)时,**请勿**再逐条照搬「子串命中次数」式假设列表——结构化摘要里仍可能含 `comment_focus_keywords` / `usage_scenarios` 等字段,**与当前报告正文展示口径可能不一致**。*", + "*当前任务报告以**第八章评论侧文本挖掘**为主呈现时,此处**不**逐条罗列子串命中次数(与报告 §8 口径一致)。*", "", - "*请结合报告中 **§8** 评论与情感归纳、文本挖掘小节,以及评价原文抽样,自拟若干条「待验证假设」(需可复核、可证伪)。*", + "- **需求焦点**:*(骨架占位;成稿写 1~2 句可执行结论)*", + "- **场景侧重**:*(骨架占位)*", + "- **传播切入点**:*(骨架占位)*", "", ] ) else: - lines.append( - "*下列由关注词/场景**计数**转化而来,是「待验证假设」而非结论;请结合评价原文抽样修订。*" - ) + lines.append("*下列为关注词/场景**统计摘录**(口径同本批次摘要);成稿时写成具体动作,勿重复统计句。*") lines.append("") if ckw: for item in ckw[:8]: @@ -288,8 +299,7 @@ def build_strategy_draft_markdown( w = _esc(item.get("word")) c = _num(item.get("count")) lines.append( - f"- **假设**:用户决策中「{w}」被频繁提及(约 {c} 次统计命中)—— " - f"*可追问:本品故事是否正面回应?传播关键词是否覆盖?*" + f"- 「{w}」:子串统计命中约 **{c}** 次(口径同报告关注词)。" ) if usc: for item in usc[:6]: @@ -298,19 +308,18 @@ def build_strategy_draft_markdown( cn = _num(item.get("count")) sh = _pct(item.get("share_of_text_units")) lines.append( - f"- **场景命题**:「{sc}」在预设场景中约 {cn} 条、约占 {sh} 文本单元—— " - f"*可追问:主图/详情/客服话术是否对齐该场景?*" + f"- 场景「{sc}」:约 **{cn}** 条,约占 **{sh}** 文本单元(预设场景分组)。" ) if not ckw and not usc: - lines.append("*摘要中无关注词/场景组结果,请补全评论侧分析后再写本节。*") + lines.append("*摘要中无关注词/场景组,请结合评论侧分析补全本节。*") lines.append("") hints = brief.get("strategy_hints") or [] lines.extend( [ - "## 六、机会方向与策略支柱(草案)", + "## 六、机会与策略支柱", "", - "### 规则引擎提示(来自摘要 `strategy_hints`)", + "### 摘要提示(`strategy_hints`)", "", ] ) @@ -318,7 +327,7 @@ def build_strategy_draft_markdown( for h in hints: lines.append(f"- {_esc(h)}") else: - lines.append("*(当前无自动线索,请结合本批次结论手写 3~5 条机会)*") + lines.append("*(当前无自动线索)*") pp = str(d.get("pillar_product") or "") pr = str(d.get("pillar_price") or "") pch = str(d.get("pillar_channel") or "") @@ -326,10 +335,10 @@ def build_strategy_draft_markdown( lines.extend( [ "", - "### 策略支柱 — 请业务逐项填空", + "### 策略支柱(表单已填优先)", "", - "| 支柱 | 本品打算怎么做 | 与头部差异 | 证据 / 出处 |", - "|------|----------------|------------|-------------|", + "| 支柱 | 本品动作 | 与头部差异 | 证据 / 出处 |", + "|------|----------|------------|-------------|", f"| 产品 | {_pillar_cell(pp)} | *待填* | *§* |", f"| 价格 | {_pillar_cell(pr)} | *待填* | *§* |", f"| 渠道/触点 | {_pillar_cell(pch)} | *待填* | *§* |", @@ -342,17 +351,17 @@ def build_strategy_draft_markdown( rp = bool(d.get("ack_risk_price")) rc = bool(d.get("ack_risk_concentration")) rk_kw = ( - "评论侧量化/关键词归纳是否**以偏概全**?(需原评论抽样)" + "评论侧归纳是否以偏概全?(需原评论抽样)" if use_ch8_probe - else "关注词/场景是否**以偏概全**?(需原评论抽样)" + else "关注词/场景统计是否以偏概全?(需原评论抽样)" ) lines.extend( [ - "## 七、风险与待证伪", + "## 七、风险核对项", "", _risk_line(rk, rk_kw), _risk_line(rp, "价格带是否含大促/异常挂价?(需核对清洗规则)"), - _risk_line(rc, "列表集中度与深入样本品牌是否**矛盾**?(需解释渠道差异)"), + _risk_line(rc, "列表集中度与深入样本品牌是否不一致?(需解释渠道差异)"), "", ] ) @@ -360,21 +369,19 @@ def build_strategy_draft_markdown( notes = _esc(business_notes) lines.extend( [ - "## 八、业务约束与内部判断", + "## 八、业务约束与备注", "", - (notes if notes else "*(未填写。建议补充:渠道红线、价位策略、竞品对标名单、预算量级等。)*"), + (notes if notes else "*(未填写业务备注。)*"), "", ] ) lines.extend( [ - "## 九、建议下一步(策略向)", + "## 九、下一步(可执行)", "", - "- [ ] 开会对齐:**§一** 目标与 **§八** 约束,确认 1~2 条主策略命题。", - "- [ ] 为 **§六** 策略支柱表格每一行各找 **1 条数据证据**(注明出处)。", - "- [ ] 产出 **12 周节奏表**(里程碑 + 负责人),与本品排期挂钩。", - "- [ ] 定义 **3 个可观测指标**(周或双周复盘)。", + "- [ ] 对齐 **§一** 目标与 **§八** 约束,锁定 1~2 条主命题。", + "- [ ] 为 **§六** 支柱各补 **1 条数据证据** + **12 周内可交付动作**(负责人 + 时间)。", "", "---", "", diff --git a/backend/pipeline/serializers.py b/backend/pipeline/serializers.py index b165830..84edcc0 100644 --- a/backend/pipeline/serializers.py +++ b/backend/pipeline/serializers.py @@ -438,6 +438,15 @@ class StrategyDraftRequestSerializer(serializers.Serializer): pillar_comm = serializers.CharField( required=False, allow_blank=True, default="", max_length=800, trim_whitespace=False ) + audience_segment = serializers.CharField( + required=False, allow_blank=True, default="", max_length=500, trim_whitespace=False + ) + competitor_reference = serializers.CharField( + required=False, allow_blank=True, default="", max_length=800, trim_whitespace=False + ) + resource_notes = serializers.CharField( + required=False, allow_blank=True, default="", max_length=1000, 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) diff --git a/backend/pipeline/tests/test_strategy_draft.py b/backend/pipeline/tests/test_strategy_draft.py index 59a1c89..e755d3a 100644 --- a/backend/pipeline/tests/test_strategy_draft.py +++ b/backend/pipeline/tests/test_strategy_draft.py @@ -26,7 +26,7 @@ class StrategyDraftTests(SimpleTestCase): self.assertIn("任务 ID**:99", md) self.assertIn("假设A", md) self.assertIn("重点:华东", md) - self.assertIn("战略背景与目标", md) + self.assertIn("目标与边界", md) self.assertIn("市场策略制定草稿", md) def test_strategy_decisions_merge(self) -> None: @@ -52,9 +52,9 @@ class StrategyDraftTests(SimpleTestCase): self.assertIn("- [ ] **贴顶**", md) self.assertIn("侧翼切入", md) self.assertIn("| 产品 | 做低糖配方 |", md) - self.assertIn("- [x] 关注词/场景是否**以偏概全**", md) + self.assertIn("- [x] 关注词/场景统计是否以偏概全", md) self.assertIn("- [ ] 价格带是否含大促", md) - self.assertIn("- [x] 列表集中度与深入样本品牌是否**矛盾**", md) + self.assertIn("- [x] 列表集中度与深入样本品牌是否不一致", md) def test_chapter8_probe_omits_focus_scenario_count_bullets(self) -> None: brief = { @@ -76,8 +76,8 @@ class StrategyDraftTests(SimpleTestCase): report_config={"chapter8_text_mining_probe": True}, ) self.assertIn("文本挖掘", md) - self.assertNotIn("假设**:用户决策中「口感」", md) - self.assertNotIn("场景命题**:「控糖", md) + self.assertNotIn("子串统计命中约 **501**", md) + self.assertNotIn("场景「控糖", md) def test_legacy_report_shows_focus_scenario_bullets(self) -> None: brief = { @@ -98,5 +98,5 @@ class StrategyDraftTests(SimpleTestCase): brief=brief, report_config={"chapter8_text_mining_probe": False}, ) - self.assertIn("假设**:用户决策中「口感」", md) - self.assertIn("场景命题**:「控糖/血糖相关」", md) + self.assertIn("「口感」:子串统计命中约 **501** 次", md) + self.assertIn("场景「控糖/血糖相关」:约 **305** 条", md) diff --git a/backend/pipeline/views/job_report_views.py b/backend/pipeline/views/job_report_views.py index f927135..3d62839 100644 --- a/backend/pipeline/views/job_report_views.py +++ b/backend/pipeline/views/job_report_views.py @@ -133,6 +133,9 @@ class JobStrategyDraftView(APIView): "pillar_price": vd.get("pillar_price") or "", "pillar_channel": vd.get("pillar_channel") or "", "pillar_comm": vd.get("pillar_comm") or "", + "audience_segment": vd.get("audience_segment") or "", + "competitor_reference": vd.get("competitor_reference") or "", + "resource_notes": vd.get("resource_notes") 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")), diff --git a/docs/openapi/pipeline-jobs.openapi.yaml b/docs/openapi/pipeline-jobs.openapi.yaml index 185a03a..9988727 100644 --- a/docs/openapi/pipeline-jobs.openapi.yaml +++ b/docs/openapi/pipeline-jobs.openapi.yaml @@ -549,6 +549,18 @@ components: pillar_comm: type: string maxLength: 800 + audience_segment: + type: string + maxLength: 500 + description: 目标客群一句话(可选) + competitor_reference: + type: string + maxLength: 800 + description: 主要对标品牌或价位带(可选) + resource_notes: + type: string + maxLength: 1000 + description: 资源与预算量级备注(可选) ack_risk_keywords: type: boolean default: false diff --git a/frontend/src/views/jd/JdStrategyBuildView.vue b/frontend/src/views/jd/JdStrategyBuildView.vue index ab05a62..8ab82cc 100644 --- a/frontend/src/views/jd/JdStrategyBuildView.vue +++ b/frontend/src/views/jd/JdStrategyBuildView.vue @@ -48,6 +48,9 @@ const decisions = reactive({ pillar_price: '', pillar_channel: '', pillar_comm: '', + audience_segment: '', + competitor_reference: '', + resource_notes: '', ack_risk_keywords: false, ack_risk_price: false, ack_risk_concentration: false, @@ -89,6 +92,9 @@ function buildPayload() { pillar_price: decisions.pillar_price, pillar_channel: decisions.pillar_channel, pillar_comm: decisions.pillar_comm, + audience_segment: decisions.audience_segment, + competitor_reference: decisions.competitor_reference, + resource_notes: decisions.resource_notes, ack_risk_keywords: decisions.ack_risk_keywords, ack_risk_price: decisions.ack_risk_price, ack_risk_concentration: decisions.ack_risk_concentration, @@ -176,7 +182,7 @@ watch(

选择已成功任务,在下方填空与勾选。默认使用大模型在规则底稿与同任务数据摘要基础上成稿(需服务端已配置网关)。若需更快、不调用智能服务,可勾选「本次仅生成规则稿」。策略配置与「分析报告生成」页的报告配置相互独立。策略稿与宿主报告第九章的归纳默认对齐。提交后跳转到 策略稿预览 - 。未填项在文稿中仍保留占位提示。 + 。决策在本页完成:已填项会写入底稿并由大模型落实为执行句;未填项由大模型结合数据补全。成稿不再重复「请再选」式表述。

@@ -226,6 +232,30 @@ watch( 非目标