siluzan-tso-cli 1.1.53-beta.2 → 1.1.53-beta.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -51,7 +51,7 @@ siluzan-tso init -d /path/to/skills # 写入自定义目录
51
51
  siluzan-tso init --force # 强制覆盖已存在文件
52
52
  ```
53
53
 
54
- > **注意**:当前为测试版(1.1.53-beta.2),供内部测试使用。正式发布后安装命令将改为 `npm install -g siluzan-tso-cli`。
54
+ > **注意**:当前为测试版(1.1.53-beta.3),供内部测试使用。正式发布后安装命令将改为 `npm install -g siluzan-tso-cli`。
55
55
 
56
56
  | 助手 | 建议 `--ai` |
57
57
  | ----------------------- | ------------------------------------ |
package/dist/index.js CHANGED
@@ -134464,20 +134464,36 @@ function countByCountry(rows) {
134464
134464
  }
134465
134465
 
134466
134466
  // src/commands/google-quarterly/plan-defaults.ts
134467
+ var PLAN_MONTH_SHARES = [0.3, 0.35, 0.35];
134468
+ function splitSuggested(spendLow) {
134469
+ const a = round22(spendLow * PLAN_MONTH_SHARES[0]);
134470
+ const b = round22(spendLow * PLAN_MONTH_SHARES[1]);
134471
+ const c = round22(spendLow - a - b);
134472
+ return [a, b, c];
134473
+ }
134467
134474
  function buildSpendPlanDefaults(currentSpend) {
134468
134475
  if (!(currentSpend > 0)) {
134469
134476
  return {
134470
134477
  currentSpend: 0,
134478
+ spendFloor: null,
134471
134479
  spendLow: null,
134472
134480
  spendHigh: null,
134473
- note: "\u672C\u671F\u65E0\u82B1\u8D39\uFF0C\u65E0\u6CD5\u7ED9\u9ED8\u8BA4\u9884\u7B97"
134481
+ monthShares: PLAN_MONTH_SHARES,
134482
+ monthSuggested: null,
134483
+ note: "\u672C\u671F\u65E0\u82B1\u8D39\uFF0C\u65E0\u6CD5\u7ED9\u9ED8\u8BA4\u9884\u7B97\uFF1B\u7981\u6B62\u624B\u7F16\u91D1\u989D"
134474
134484
  };
134475
134485
  }
134486
+ const floor = round22(currentSpend);
134487
+ const low = round22(currentSpend * 1.3);
134488
+ const high = round22(currentSpend * 1.5);
134476
134489
  return {
134477
- currentSpend: round22(currentSpend),
134478
- spendLow: round22(currentSpend * 1.3),
134479
- spendHigh: round22(currentSpend * 1.5),
134480
- note: "\u9ED8\u8BA4\u76EE\u6807\uFF1A\u82B1\u8D39\u8F83\u672C\u671F\u63D0\u5347 30%\uFF5E50%"
134490
+ currentSpend: floor,
134491
+ spendFloor: floor,
134492
+ spendLow: low,
134493
+ spendHigh: high,
134494
+ monthShares: PLAN_MONTH_SHARES,
134495
+ monthSuggested: splitSuggested(low),
134496
+ note: "\u4E09\u6863\u57FA\u4E8E\u672C\u671F\u82B1\u8D39\u80FD\u529B\uFF0C\u4E0D\u662F\u5BA2\u6237\u5DF2\u6279\u9884\u7B97\uFF1B\u5EFA\u8BAE\u6863\u6708\u5EA6\u9ED8\u8BA4 30%/35%/35%\uFF1BCPA / \u8BE2\u76D8\u4E0D\u5916\u63A8"
134481
134497
  };
134482
134498
  }
134483
134499
 
@@ -134490,14 +134506,19 @@ var continentMap = null;
134490
134506
  function continentsPath() {
134491
134507
  const dir = path27.dirname(fileURLToPath6(import.meta.url));
134492
134508
  const candidates = [
134509
+ // dist/index.js → dist/skill/references/...
134510
+ path27.join(dir, "skill", "references", "analytics", "geo-continents.json"),
134493
134511
  path27.join(dir, "skill", "siluzan-ads", "references", "analytics", "geo-continents.json"),
134512
+ // dist/index.js → tso-cli/assets/...
134513
+ path27.join(dir, "..", "assets", "siluzan-ads", "references", "analytics", "geo-continents.json"),
134514
+ // src/commands/google-quarterly/region.ts → tso-cli/assets/...
134494
134515
  path27.join(dir, "..", "..", "assets", "siluzan-ads", "references", "analytics", "geo-continents.json"),
134495
134516
  path27.join(dir, "..", "..", "..", "assets", "siluzan-ads", "references", "analytics", "geo-continents.json")
134496
134517
  ];
134497
134518
  for (const p of candidates) {
134498
134519
  if (fs22.existsSync(p)) return p;
134499
134520
  }
134500
- return candidates[1];
134521
+ return candidates[0];
134501
134522
  }
134502
134523
  function loadContinentMap() {
134503
134524
  if (continentMap) return continentMap;
@@ -134538,6 +134559,12 @@ function countByRegion(countryCounts, continents = loadContinentMap()) {
134538
134559
  }
134539
134560
 
134540
134561
  // src/commands/google-quarterly/special-ads.ts
134562
+ var EXPLORE_TYPE_META = [
134563
+ { id: "video", label: "\u89C6\u9891 / YouTube", role: "\u54C1\u724C\u58F0\u91CF", title: "\u89C6\u9891 / YouTube \u54C1\u724C\u58F0\u91CF", chip: "\u54C1\u724C\u58F0\u91CF" },
134564
+ { id: "pmax", label: "\u6548\u679C\u6700\u5927\u5316", role: "\u8F6C\u5316\u6548\u7387", title: "\u6548\u679C\u6700\u5927\u5316", chip: "\u8DE8\u6E20\u9053\u6548\u679C" },
134565
+ { id: "demand", label: "\u9700\u6C42\u5F00\u53D1 / \u53D1\u73B0", role: "\u5185\u5BB9\u79CD\u8349", title: "\u9700\u6C42\u5F00\u53D1 / \u53D1\u73B0\u5E7F\u544A", chip: "\u5185\u5BB9\u79CD\u8349" },
134566
+ { id: "display", label: "\u5C55\u793A", role: "\u518D\u8425\u9500", title: "\u5C55\u793A\u5E7F\u544A", chip: "\u8986\u76D6\xB7\u518D\u8425\u9500" }
134567
+ ];
134541
134568
  function hasTraffic(row) {
134542
134569
  return rowSpend(row) > 0 || num2(row.impressions) > 0 || num2(row.clicks) > 0;
134543
134570
  }
@@ -134588,6 +134615,27 @@ function fromCampaign(row) {
134588
134615
  conversions: num2(row.conversions)
134589
134616
  };
134590
134617
  }
134618
+ function sortBySpend(items3) {
134619
+ return [...items3].sort((a, b) => b.spend - a.spend);
134620
+ }
134621
+ function metricsFromRows(items3) {
134622
+ let spend = 0;
134623
+ let impressions = 0;
134624
+ let clicks = 0;
134625
+ let conversions = 0;
134626
+ for (const row of items3) {
134627
+ spend += row.spend;
134628
+ impressions += row.impressions;
134629
+ clicks += row.clicks;
134630
+ conversions += row.conversions;
134631
+ }
134632
+ return withRates({ spend, impressions, clicks, conversions });
134633
+ }
134634
+ function campaignsInBucket(campaigns, bucket) {
134635
+ return sortBySpend(
134636
+ campaigns.filter((row) => channelBucket(row.channelTypeV2) === bucket && hasTraffic(row)).map(fromCampaign)
134637
+ );
134638
+ }
134591
134639
  function pickYoutubeAdSubscriptions(actions) {
134592
134640
  const hits = actions.filter((row) => {
134593
134641
  const name2 = pickStr2(row, ["name", "Name", "conversionAction"]).toLowerCase();
@@ -134608,32 +134656,47 @@ function pickYoutubeAdSubscriptions(actions) {
134608
134656
  note: "\u53E3\u5F84=Google Ads \u8F6C\u5316\u52A8\u4F5C\uFF08YouTube \u8BA2\u9605\uFF09\uFF0C\u4E0D\u662F\u9891\u9053\u7C89\u6570"
134609
134657
  };
134610
134658
  }
134611
- function pickSpecialAds(videos, campaigns) {
134612
- const videoHits = videos.filter(hasTraffic);
134613
- if (videoHits.length > 0) {
134659
+ function videoRowsFromReport(videos) {
134660
+ return sortBySpend(videos.filter(hasTraffic).map(fromVideo));
134661
+ }
134662
+ function buildExploreAds(videos, campaigns, accountSpend) {
134663
+ const videoFromReport = videoRowsFromReport(videos);
134664
+ const videoItems = videoFromReport.length > 0 ? videoFromReport : campaignsInBucket(campaigns, "\u89C6\u9891");
134665
+ const byId = {
134666
+ video: videoItems,
134667
+ pmax: campaignsInBucket(campaigns, "\u6548\u679C\u6700\u5927\u5316"),
134668
+ demand: campaignsInBucket(campaigns, "\u53D1\u73B0"),
134669
+ display: campaignsInBucket(campaigns, "\u5C55\u793A")
134670
+ };
134671
+ const types = EXPLORE_TYPE_META.map((meta) => {
134672
+ const items3 = byId[meta.id];
134614
134673
  return {
134615
- source: "youtube",
134616
- label: "YouTube / \u89C6\u9891\u5E7F\u544A",
134617
- items: videoHits.map(fromVideo).sort((a, b) => b.spend - a.spend)
134674
+ ...meta,
134675
+ spend: items3.reduce((sum, row) => sum + row.spend, 0),
134676
+ metrics: metricsFromRows(items3),
134677
+ items: items3
134618
134678
  };
134679
+ });
134680
+ return {
134681
+ types,
134682
+ accountSpend,
134683
+ hasAny: types.some((t) => t.items.length > 0)
134684
+ };
134685
+ }
134686
+ function pickSpecialAds(videos, campaigns) {
134687
+ const videoHits = videoRowsFromReport(videos);
134688
+ if (videoHits.length > 0) {
134689
+ return { source: "youtube", label: "YouTube / \u89C6\u9891\u5E7F\u544A", items: videoHits };
134619
134690
  }
134620
- const pmax = campaigns.filter(
134621
- (row) => String(row.channelTypeV2 ?? "").toUpperCase() === "PERFORMANCE_MAX" && hasTraffic(row)
134622
- );
134691
+ const pmax = campaignsInBucket(campaigns, "\u6548\u679C\u6700\u5927\u5316");
134623
134692
  if (pmax.length > 0) {
134624
- return {
134625
- source: "pmax",
134626
- label: "\u6548\u679C\u6700\u5927\u5316",
134627
- items: pmax.map(fromCampaign).sort((a, b) => b.spend - a.spend)
134628
- };
134693
+ return { source: "pmax", label: "\u6548\u679C\u6700\u5927\u5316", items: pmax };
134629
134694
  }
134630
- const other = campaigns.filter((row) => !isSearchChannel(row.channelTypeV2) && hasTraffic(row));
134695
+ const other = sortBySpend(
134696
+ campaigns.filter((row) => !isSearchChannel(row.channelTypeV2) && hasTraffic(row)).map(fromCampaign)
134697
+ );
134631
134698
  if (other.length > 0) {
134632
- return {
134633
- source: "other",
134634
- label: "\u975E\u641C\u7D22\u5E7F\u544A",
134635
- items: other.map(fromCampaign).sort((a, b) => b.spend - a.spend)
134636
- };
134699
+ return { source: "other", label: "\u975E\u641C\u7D22\u5E7F\u544A", items: other };
134637
134700
  }
134638
134701
  return { source: "none", label: "", items: [] };
134639
134702
  }
@@ -134728,7 +134791,8 @@ function buildQuarterlyFacts(input) {
134728
134791
  });
134729
134792
  const videos = input.currentSnap.videos ?? [];
134730
134793
  const specialAds = pickSpecialAds(videos, input.currentSnap.campaigns);
134731
- const videoRows = specialAds.source === "youtube" ? specialAds.items : [];
134794
+ const exploreAds = buildExploreAds(videos, input.currentSnap.campaigns, currentMetrics.spend);
134795
+ const videoRows = videoRowsFromReport(videos);
134732
134796
  const videoImpr = videoRows.reduce((s, r) => s + r.impressions, 0);
134733
134797
  const videoSpend = videoRows.reduce((s, r) => s + r.spend, 0);
134734
134798
  const videoClicks = videoRows.reduce((s, r) => s + r.clicks, 0);
@@ -134809,6 +134873,7 @@ function buildQuarterlyFacts(input) {
134809
134873
  countries,
134810
134874
  usStates,
134811
134875
  specialAds,
134876
+ exploreAds,
134812
134877
  videosSummary: {
134813
134878
  impressions: videoImpr,
134814
134879
  spend: videoSpend,
@@ -134869,6 +134934,25 @@ function roundFacts(facts) {
134869
134934
  ...facts.videosSummary,
134870
134935
  spend: round22(facts.videosSummary.spend),
134871
134936
  averageCpv: facts.videosSummary.averageCpv == null ? null : round22(facts.videosSummary.averageCpv)
134937
+ },
134938
+ exploreAds: {
134939
+ ...facts.exploreAds,
134940
+ accountSpend: round22(facts.exploreAds.accountSpend),
134941
+ types: facts.exploreAds.types.map((t) => ({
134942
+ ...t,
134943
+ spend: round22(t.spend),
134944
+ metrics: {
134945
+ ...t.metrics,
134946
+ spend: round22(t.metrics.spend),
134947
+ cpc: round22(t.metrics.cpc),
134948
+ cpa: round22(t.metrics.cpa)
134949
+ },
134950
+ items: t.items.map((row) => ({
134951
+ ...row,
134952
+ spend: round22(row.spend),
134953
+ averageCpv: row.averageCpv == null ? row.averageCpv : round22(row.averageCpv)
134954
+ }))
134955
+ }))
134872
134956
  }
134873
134957
  };
134874
134958
  }
@@ -135054,6 +135138,7 @@ async function runGoogleQuarterlyCollect(opts) {
135054
135138
  ],
135055
135139
  inquiryNote: facts.meta.inquiryNote,
135056
135140
  specialAds: facts.specialAds.source,
135141
+ exploreAds: facts.exploreAds.types.filter((t) => t.items.length > 0).map((t) => t.id),
135057
135142
  planDefaults: facts.planDefaults
135058
135143
  }
135059
135144
  };
@@ -135066,6 +135151,7 @@ async function runGoogleQuarterlyCollect(opts) {
135066
135151
  accountId,
135067
135152
  current,
135068
135153
  specialAds: facts.specialAds.source,
135154
+ exploreAds: facts.exploreAds.types.filter((t) => t.items.length > 0).map((t) => t.id),
135069
135155
  inquirySource: facts.meta.inquirySource,
135070
135156
  usGeoSource: facts.meta.usGeoSource,
135071
135157
  drillCountry: facts.meta.drillCountry,
@@ -12,6 +12,7 @@
12
12
  | 命令参数 / 字段 | 路由表「必读文档」 |
13
13
  | 纪律与对用户怎么说 | `references/core/agent-conventions.md` |
14
14
  | 报告结构纲要 | `report-templates/*.md`(勿读 `*.html`) |
15
+ | P10 下期规划怎么写 | `report-templates/google-quarterly-plan.md` |
15
16
 
16
17
  ## 启动顺序
17
18
 
@@ -121,7 +121,7 @@ siluzan-tso -h
121
121
  | ------------------ | ------ | -------- |
122
122
  | Google 广告账户诊断 / 健康检查(**含账户 ID**) | **P1** | `references/analytics/account-analytics.md` + `report-templates/google-ads-diagnosis.md`(可选对照 `report-templates/google-account-diagnosis-report.md`) |
123
123
  | Google 账户周期 **数据汇总**(只要花费/转化/8 维/Excel;**无**规划/客户版/大族) | **P4** | `report-templates/google-period-report.md` + `references/analytics/account-analytics.md`;Excel 加 `google-period-report-excel.md` |
124
- | Google/谷歌 **月度报告** / **月总结与规划** / **季度报告** / **Q3 汇总** / **Q4 规划** / **季度汇报** / **投放季报** / 客户版 / 大族模板 / 下月规划(效率+区域询盘+国家明细) | **P10** | `report-templates/google-quarterly-report.md`(询盘载体须先转 JSON 再 `--inquiries`,禁止把 xlsx 丢给 collect;`collect`+`render`;花费默认 +30%~+50%;**不做**行业数据) |
124
+ | Google/谷歌 **月度报告** / **月总结与规划** / **季度报告** / **Q3 汇总** / **Q4 规划** / **季度汇报** / **投放季报** / 客户版 / 大族模板 / 下月规划(效率+区域询盘+国家明细) | **P10** | `report-templates/google-quarterly-report.md` + **`report-templates/google-quarterly-plan.md`**(**未带询盘表须先问并等待**,禁止不问就 collect;有表须先转 JSON 再 `--inquiries`,禁止把 xlsx 丢给 collect;`collect`+`render`;规划九章;花费三档基于本期、不是已批预算;**不做**行业数据) |
125
125
  | Meta/Facebook 周期或诊断 | **P4-FB** | `report-templates/meta-period-report.md` + `assets/meta-period-report-rules.md` + `references/analytics/facebook-analysis-guide.md` |
126
126
  | TikTok / Bing 周期报告 | **P4** | `references/analytics/account-analytics.md` + 对应 `report-templates/*-period-report.md` |
127
127
  | Yandex 账户分析 / 周期 / 月报 | **P4** | `report-templates/yandex-period-report.md` + `references/analytics/account-analytics.md`;Excel 加 `yandex-period-report-excel.md` |
@@ -1,5 +1,5 @@
1
1
  {
2
2
  "slug": "siluzan-tso",
3
- "version": "1.1.53-beta.2",
4
- "publishedAt": 1789552686722
3
+ "version": "1.1.53-beta.3",
4
+ "publishedAt": 1789698851730
5
5
  }
@@ -2,7 +2,7 @@
2
2
  "$schema": "https://json-schema.org/draft/2020-12/schema",
3
3
  "$id": "siluzan-tso/google-quarterly-report.schema.json",
4
4
  "title": "GoogleQuarterlyReport",
5
- "description": "Google 季度经营报告。Agent 只写 narrative;kpis/monthly/specialAds/planDefaults/usStates(消耗最高国家的一级行政区)由 collect 覆盖。",
5
+ "description": "Google 季度经营报告。Agent 只写 narrative;kpis/monthly/exploreAds/specialAds/planDefaults/usStates(消耗最高国家的一级行政区)由 collect 覆盖。",
6
6
  "type": "object",
7
7
  "required": ["narrative"],
8
8
  "properties": {
@@ -29,7 +29,7 @@
29
29
  "summary": { "$ref": "#/$defs/analysisBlock" }
30
30
  }
31
31
  },
32
- "plan": { "$ref": "#/$defs/analysisBlock" }
32
+ "plan": { "$ref": "#/$defs/planBlock" }
33
33
  }
34
34
  }
35
35
  },
@@ -41,6 +41,22 @@
41
41
  "goals": { "type": "array", "items": { "type": "string" } },
42
42
  "text": { "type": "string" }
43
43
  }
44
+ },
45
+ "planBlock": {
46
+ "type": "object",
47
+ "description": "下期规划九章。analysis 必填;其余八章写规划时都应有。规则见 report-templates/google-quarterly-plan.md。",
48
+ "properties": {
49
+ "analysis": { "type": "array", "minItems": 1, "items": { "type": "string" } },
50
+ "goals": { "$ref": "#/$defs/analysisBlock" },
51
+ "budget": { "$ref": "#/$defs/analysisBlock" },
52
+ "structure": { "$ref": "#/$defs/analysisBlock" },
53
+ "keywords": { "$ref": "#/$defs/analysisBlock" },
54
+ "audience": { "$ref": "#/$defs/analysisBlock" },
55
+ "creative": { "$ref": "#/$defs/analysisBlock" },
56
+ "roadmap": { "$ref": "#/$defs/analysisBlock" },
57
+ "risks": { "$ref": "#/$defs/analysisBlock" },
58
+ "text": { "type": "string" }
59
+ }
44
60
  }
45
61
  }
46
62
  }
@@ -108,6 +108,7 @@
108
108
  | ---------------------------------------------------------------- | ------------------------------ |
109
109
  | `google-period-report.md` | Google 账户周期/月度报告(P4) |
110
110
  | `google-quarterly-report.md` | Google 季度经营报告(P10) |
111
+ | `google-quarterly-plan.md` | P10 下期规划规则(九章) |
111
112
  | `google-period-report-excel.md` | P4 用户要 Excel |
112
113
  | `stats-daily-excel.md` | P4-DAILY 按日 Excel |
113
114
  | `meta-period-report.md` / `meta-period-report-excel.md` | Meta 周期(P4-FB) |
@@ -535,6 +535,7 @@ siluzan-tso yandex-analysis insights -a <porg-xxx> --json-out ./snap-yandex \
535
535
  | ------------------------------------ | --------------------------------------------------------------------------------------------------------------- |
536
536
  | `google-period-report.md` | Google 周期汇总报告章节 |
537
537
  | `google-quarterly-report.md` | Google 季度经营报告(`google-quarterly collect`+`render`,P10) |
538
+ | `google-quarterly-plan.md` | P10 下期规划规则(写 `narrative.plan` 前必读) |
538
539
  | `google-account-diagnosis-report.md` | Google 诊断报告纲要 |
539
540
  | `google-ads-diagnosis.md` | Google Ads 诊断报告完整骨架 |
540
541
  | `meta-period-report.md` | Meta(Facebook)周期报告 |
@@ -180,6 +180,8 @@
180
180
 
181
181
  涉及「投放数据 / 消耗 / 报告 / 周报 / 月报 / 优化建议」且用户未给明确起止日期时**必须反问**(示例:A) 最近完整自然周 B) 本月 1 号到昨天 C) 自定义 YYYY-MM-DD)。给出范围后,报告首行标注 `统计区间:YYYY-MM-DD ~ YYYY-MM-DD(货币:XXX)`。
182
182
 
183
+ **P10 询盘表**:季度/月度经营报告若用户**没附**询盘表,必须先问有没有、等回复;未回答前禁止 `google-quarterly collect` / render。用户说「没有」再继续。话术见 `report-templates/google-quarterly-report.md`「询盘入场」。
184
+
183
185
  **例外**(不反问):
184
186
 
185
187
  - `list-accounts` 列全部 / 数个数:一次 `list-accounts -m <媒体> --page-size 999 --json-out <dir>`,脚本读落盘 `total` / `items[]`;**禁止**默认 page-size 20 再翻页(详见 `references/accounts/accounts-list.md` § Agent 意图速查)。
@@ -323,7 +323,7 @@
323
323
  | P1 / P4 / P10 | 账户 ID | 「请提供 Google 广告账户 ID(mediaCustomerId)。」 |
324
324
  | P1 / P4 | 时间 | 按 `agent-conventions.md` §五 反问区间 |
325
325
  | P10 | 时间 | 季度话术可省略(上一完整季);月度报告 / 下月规划用上一完整月或用户指定月 |
326
- | P10 | 询盘附件 | 先转 `inquiries.json` 再 `--inquiries`;**禁止**把 xlsx/csv 原文件传给 collect |
326
+ | P10 | 询盘附件 | **未带表先问「有没有本期询盘表」并等待**;有则转 `inquiries.json` 再 `--inquiries`;用户说没有再 collect。**禁止**不问就出报告;**禁止**把 xlsx/csv 原文件传给 collect |
327
327
  | P6 | 非 OKKI 话术 | 若用户实际要普通周期报告 → 改 P4 并说明 |
328
328
  | P7 | 无询盘文件 | 贴字段清单,**禁止编造询盘**;若用户只要账户级零转化 → 改 **P3** |
329
329
  | P3(§零·D) | 时间区间 | 按 `agent-conventions.md` §五 反问;巡检默认近 7 天须写明区间 |
@@ -195,14 +195,14 @@
195
195
  ## P10 · Google 季度经营报告
196
196
 
197
197
  - **触发**(命中越多越稳):Google/谷歌广告 **月度报告**、**月总结与规划**、**季度报告**、**Q3 汇总**、**Q4 规划**、**季度汇报**、**投放季报**、客户版/对外、大族模板、下月规划;或要「效率 + 区域询盘 + 国家明细 + 规划」;或「结论→效率→结构→新探索→总结→Q4」。**不是** P1 诊断、**不是** P4「只要花费转化」的周期表。用户提「行业参考」仍走 P10,**禁止编造行业数**(本批无行业章)。
198
- - **必读**:`report-templates/google-quarterly-report.md`。
198
+ - **必读**:`report-templates/google-quarterly-report.md`;写 `narrative.plan` 前再 Read `report-templates/google-quarterly-plan.md`。
199
199
  - **默认产物**:**HTML**(`google-quarterly render`)。
200
200
  - **步骤**:
201
201
  1. 确认账户与区间:季度话术省略日期 → 上一完整自然季;**月度报告 / 下月规划** → 上一完整自然月(或用户指定月)作 `--start/--end`。
202
- 2. `list-accounts -m Google -k <mediaCustomerId> --json-out ./snap-p10`(取户名、币种)。
203
- 3. **询盘载体 JSON(CLI 不读 xlsx)**:用户给了 xlsx / csv / 截图 / 粘贴表 → Agent 抽行落盘 `./snap-p10/inquiries.json`(字段见 `google-quarterly-report.md`「询盘入场」)再 `--inquiries`;**禁止**把原始文件传给 collect。无载体可 `--inquiry-conversion <名>`;再没有则询盘位留空,**禁止编造**。
204
- 4. `google-quarterly collect -a <id> --start <S> --end <D> --json-out ./snap-p10 --account-name "<名>"`(可选 `--keyword-seeds`)。YouTube 广告层用 Ads 视频报表,不拉频道订阅总数 / 来源饼 / 视频类型。
202
+ 2. **询盘未入场则先问再停**(话术见 `google-quarterly-report.md`「询盘入场」)。没有表格且用户未说「没有」时,**禁止** collect / narrative / render。用户发来表 → 抽行落盘 `./snap-p10/inquiries.json` 再 `--inquiries`(**禁止**把 xlsx 原文件传给 collect);用户说没有 → 询盘位留空(可选 `--inquiry-conversion`,须用户点名),**禁止编造**。
203
+ 3. `list-accounts -m Google -k <mediaCustomerId> --json-out ./snap-p10`(取户名、币种)。可与等询盘回复并行,但 collect 必须等询盘问完。
204
+ 4. `google-quarterly collect -a <id> --start <S> --end <D> --json-out ./snap-p10 --account-name "<名>"`(可选 `--keyword-seeds`)。新探索四类并行(视频 / PMax / 发现 / 展示);视频用 Ads 视频报表,不拉频道订阅总数 / 来源饼 / 视频类型。
205
205
  5. 读 `google-quarterly-collect.json`,只写 `narrative` 到 `google-quarterly.json`(禁止改 kpis / monthly / planDefaults 数字)。
206
- 6. 下期规划默认花费 +30%~+50%(用 `planDefaults.spendLow`~`spendHigh`);CPA/询盘目标不自动外推。
206
+ 6. 下期规划按 `google-quarterly-plan.md` 写九章。预算默认三档(维持本期 / +30% / +50%)+ 建议档月度 30/35/35,标「基于历史数据」,不是已批预算;CPA/询盘不外推;禁止自动调价/暂停指令。
207
207
  7. `google-quarterly render --data ./snap-p10/google-quarterly.json --out ./snap-p10/google-quarterly-report.html`。
208
- - **产物**:HTML 路径;专项广告无量则模板自动隐藏该章。
208
+ - **产物**:HTML 路径;新探索(视频 / PMax / 发现 / 展示)全无量则模板自动隐藏该章。
@@ -22,7 +22,8 @@
22
22
  | 文件 | 适用场景 |
23
23
  | -------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------- |
24
24
  | `google-period-report.md` | **Google 周期分析报告(默认)**:终稿=HTML,四步流程 → `google-analysis render` 出 HTML |
25
- | `google-quarterly-report.md` / `.html` | **Google 季度经营报告(P10)**:`google-quarterly collect`+`render`;花费默认 +30%~+50%;无行业章 |
25
+ | `google-quarterly-report.md` / `.html` | **Google 季度经营报告(P10)**:`google-quarterly collect`+`render`;花费三档基于本期;无行业章 |
26
+ | `google-quarterly-plan.md` | **P10 下期规划规则**:九章 + Q01–Q30;写 `narrative.plan` 前必读 |
26
27
  | `google-period-report-excel.md` | **仅用户要 Excel 时**(P4 定制 Sheet):先 `*.outline.txt` 后脚本写 xlsx;账户须 `-k` 核验,无 CLI excel 子命令 |
27
28
  | `stats-daily-excel.md` | **P4-DAILY**:账户级按日 Excel(`stats --by-day`) |
28
29
  | `google-account-diagnosis-report.md` | Google 账户深度诊断(健康度/转化/结构等) |
@@ -0,0 +1,126 @@
1
+ # Google 季度投放规划(P10 · 下期规划)
2
+
3
+ > 写 `narrative.plan` 前必读。只规划下一季/下一月怎么投,**不**做实时诊断、日周调价、自动暂停/扩量、未确认的广告写入。
4
+ >
5
+ > 数据只消费 `google-quarterly-collect.json`(及用户明确给的业务约束)。**禁止**再算一遍账户诊断,**禁止**编造预算 / CPA / 询盘 / 市场数。
6
+ >
7
+ > 编号 Q01–Q30 是规划条,**不是** playbook P10。
8
+
9
+ ## 范围
10
+
11
+ | 做 | 不做 |
12
+ | -- | ---- |
13
+ | 季度目标、核心转化、业务目标 | 账户拒审 / 余额 / 预算熔断等实时问题 |
14
+ | 预算三档、月度拆分、市场分层与分配 | 每日调价、自动暂停、自动扩量规则 |
15
+ | Search / PMax 等类型与 Campaign 结构 | 自己拉 API、改 collect 数字 |
16
+ | 关键词方向、匹配、否词、PMax Search Themes | 未确认就 `ad campaign-create` / 改预算 |
17
+ | 素材主题、资产缺口、落地页与广告匹配 | 行业指数、竞品粉数等本批没有的数 |
18
+ | 受众信号、再营销、客户名单方向 | 把假设写成已确认事实 |
19
+ | 三月节奏、依赖、风险、待确认 | |
20
+
21
+ 上游只用 collect 里已有的:`kpis` / `monthly` / `countries` / `regions` / `exploreAds` / `videosSummary` / `keywordSeeds` / `planDefaults` / `narrative.sections` 结论。业务约束(已批预算、目标市场、销售承接、库存)**用户没给就标「待业务确认」**,不虚构。
22
+
23
+ ## 输出九章(`narrative.plan`)
24
+
25
+ 先结论,再表,最后路线图和风险。每条建议至少挂一项依据:目标 / 历史表现 / 业务战略 / 资源约束。缺数据的市场、类型、受众、词 → 写**验证计划**,禁止直接扩量。
26
+
27
+ | 序 | 键 | 章节 | 最低要写 |
28
+ | -- | -- | ---- | -------- |
29
+ | 1 | `analysis` | 季度投放结论 | 3~5 条:主攻方向、重点市场、核心广告类型、主要建设任务 |
30
+ | 2 | `goals` | 季度目标与 KPI | 1 个主目标 + 2~4 个辅助;业务 / 广告 / 执行三级;核心转化 |
31
+ | 3 | `budget` | 预算与市场规划 | 三档预算(或声明用 `planDefaults`)、月度拆分、市场分层与分配 |
32
+ | 4 | `structure` | 广告账户结构 | 类型组合、Campaign 拆分、P0/P1/P2 建设顺序 |
33
+ | 5 | `keywords` | 关键词与搜索主题 | Search 分组 + 匹配 + 否词方向;有 PMax 则 Search Themes |
34
+ | 6 | `audience` | 受众规划 | Audience Signal / 名单 / 再营销;没有的标「需创建或导入」 |
35
+ | 7 | `creative` | 素材与落地页 | 主题、资产缺口、测试假设(每轮 1~2 变量)、广告↔页面映射 |
36
+ | 8 | `roadmap` | 季度执行路线图 | 第 1 / 2 / 3 月:任务、交付物、完成条件、依赖 |
37
+ | 9 | `risks` | 风险与待确认 | 预算、数据、素材、页面、销售承接、合规;假设清单 |
38
+
39
+ 每条结论前加口径标签之一:`已确认` / `基于历史数据` / `建议验证` / `待业务确认`。
40
+
41
+ ```json
42
+ {
43
+ "plan": {
44
+ "analysis": ["已确认:…", "基于历史数据:…", "建议验证:…"],
45
+ "goals": { "analysis": ["主目标:有效询盘…", "广告目标:…", "执行目标:…"] },
46
+ "budget": { "analysis": ["建议档取 planDefaults.spendLow…", "核心市场优先…"] },
47
+ "structure": { "analysis": ["Search 守基本盘…", "P0:…"] },
48
+ "keywords": { "analysis": ["产品词精确/词组…", "否词方向:求职/教程/免费"] },
49
+ "audience": { "analysis": ["待业务确认:客户名单未导入"] },
50
+ "creative": { "analysis": ["主题:工业场景…", "缺口:PMax 视频"] },
51
+ "roadmap": { "analysis": ["第1月:目标与基础…", "第2月:核心建设…", "第3月:已验证方向扩"] },
52
+ "risks": { "analysis": ["销售回传缺失 → 主目标暂用表单并标注"] }
53
+ }
54
+ }
55
+ ```
56
+
57
+ `analysis` 必填(render 硬门禁)。其余八章写规划时**都要有**;某章确实无输入则写「待业务确认:缺××,本季不扩」。
58
+
59
+ ## 规划条(判断 → 动作)
60
+
61
+ ### 目标 · 转化
62
+
63
+ | 条 | 模块 | 规则 |
64
+ | -- | ---- | ---- |
65
+ | Q01 | 季度目标 | 1 主 + 2~4 辅。主目标优先有效线索 / 合格询盘 / 销售机会,**不**默认点击量 |
66
+ | Q02 | 转化选择 | 优先有效询盘、表单、电话、WhatsApp/邮件、销售确认;无质量数据则写「需补充销售回传」 |
67
+ | Q03 | 目标分层 | 业务=要什么客户;广告=广告带来什么;执行=本季建设什么 |
68
+
69
+ ### 预算 · 市场
70
+
71
+ | 条 | 模块 | 规则 |
72
+ | -- | ---- | ---- |
73
+ | Q04 | 预算三档 | **最低可执行** = 本期花费(`planDefaults.spendFloor`);**建议** = `spendLow`(+30%);**扩量** = `spendHigh`(+50%)。这是历史花费能力建议,**不是**客户已批预算。用户给了季度预算则用用户数,不用默认档。无本期花费则三档为空,禁止手编金额 |
74
+ | Q05 | 月度拆分 | 默认建议档按 **30% / 35% / 35%**(`planDefaults.monthSuggested`)。有旺季 / 展会 / 促销 / 销售周期则改拆分并写原因 |
75
+ | Q06 | 市场优先级 | 核心 / 成长 / 测试。看转化量、CPA、**转化质量**、客单价、业务战略,**不只按 CPA** |
76
+ | Q07 | 市场预算 | 核心保稳定获客;成长做增量;测试设独立上限 |
77
+ | Q08 | 新市场 | 先小预算验证;写清语言、落地页、销售承接、转化事件。禁止直接大规模扩量 |
78
+
79
+ CPA、询盘目标**不**从历史外推。要写数字须用户给,或标「待业务确认」。
80
+
81
+ ### 结构 · 词 · 受众
82
+
83
+ | 条 | 模块 | 规则 |
84
+ | -- | ---- | ---- |
85
+ | Q09 | 广告类型 | Search = 承接明确需求;PMax = 转化数据和素材较完整时扩覆盖;视频 / 发现 / 展示看 `exploreAds` 并写使用场景。无量类型写验证计划 |
86
+ | Q10 | Campaign | 按市场 / 业务线 / 产品 / 目标 / 类型拆;禁止无业务意义的过度拆分 |
87
+ | Q11 | 建设顺序 | P0 已有需求+转化+销售承接;测试排在稳定项之后 |
88
+ | Q12 | Search 词 | 品牌 / 产品 / 解决方案 / 行业 / 场景 / 高意图长尾。禁止只堆泛行业词 |
89
+ | Q13 | 匹配 | 高意图核心:精确或词组;广泛仅在转化追踪、否词、数据基础较全时用 |
90
+ | Q14 | 否词 | 无关行业、求职、教程、免费、二手、个人消费等方向;具体词须来自搜索词或用户确认 |
91
+ | Q15 | Search Themes | 从高价值词、搜索词、产品/方案、场景提炼分组;禁止复制大量重复词 |
92
+ | Q16 | PMax 信号 | 名单、再营销、GA4、网站访问、购买/询盘、自定义意图。没有则标需创建/导入 |
93
+ | Q17 | 客户名单 | 已成交 / 有效询盘 / 重点 / 流失;写来源、更新频率、合规前提 |
94
+ | Q18 | 再营销 | 按访问深度、关键页、表单未提交、产品页分层;禁止「全部访客」一锅 |
95
+
96
+ ### 素材 · 页面 · 销售 · 节奏
97
+
98
+ | 条 | 模块 | 规则 |
99
+ | -- | ---- | ---- |
100
+ | Q19 | 卖点 | 产品价值、痛点、场景、差异化、信任、交付。禁止只写「做更多素材」 |
101
+ | Q20 | 资产 | Search:标题/描述/附加信息;PMax:图/Logo/视频/短标题/长标题/描述。缺口列建设清单 |
102
+ | Q21 | 测试 | 每轮 1~2 个变量(卖点 / CTA / 场景 / 信任)。写假设和观察指标 |
103
+ | Q22 | 落地页 | 按市场/产品/意图规划;盯首屏价值、信任、案例、表单、CTA、移动端 |
104
+ | Q23 | 广告↔页 | 每个核心主题至少一个明确页面方向。禁止广告讲 A、页面展 B |
105
+ | Q24 | 转化路径 | CTA、表单字段、感谢页、电话/WhatsApp/邮件、CRM 回传、销售跟进 |
106
+ | Q25 | 销售承接 | 接收人、响应时效、分配、无效反馈、结果回传。无承接则降低扩量优先级 |
107
+ | Q26 | 三月节奏 | 见下表。月度报告把「三月」收成「本月 / 次月 / 再次月」 |
108
+ | Q27 | 依赖 | 每项关键规划标依赖、负责人或待确认 |
109
+ | Q28 | 风险 | 至少覆盖:预算不足、转化量不足、数据质量、落地页未完成、素材不足、销售承接、市场合规 |
110
+ | Q29 | 假设 | 假设不得写成事实;用四类口径标签 |
111
+ | Q30 | 文风 | 先结论 → 规划表 → 节奏 / 依赖 / 风险。不写实时诊断和自动化指令 |
112
+
113
+ ## 三月路线图
114
+
115
+ | 阶段 | 核心任务 | 交付 | 完成条件 |
116
+ | ---- | -------- | ---- | -------- |
117
+ | 第 1 月 · 基础与验证 | 目标、预算、市场优先级;补转化追踪、落地页、素材、受众 | 目标表、预算表、Campaign 结构、词/Themes、素材与页面清单 | 核心投放基础具备,关键依赖写清 |
118
+ | 第 2 月 · 重点建设 | 核心市场与核心类型;有限变量测试 | 核心 Campaign、素材测试批次、受众组合、页面优化版 | 核心方向验证完,形成可续投项目 |
119
+ | 第 3 月 · 扩展与沉淀 | 只围已验证方向扩市场/预算/词/素材/受众 | 扩展计划、下季建议、有效资产、未完成项 | 可扩展方向和下季待解决问题都明确 |
120
+
121
+ ## 硬约束
122
+
123
+ 1. 规划只读上游事实,不改 `kpis` / `monthly` / `planDefaults` / `exploreAds`。
124
+ 2. 默认结果是**建议方案**;用户确认前不进入搭建或写命令。
125
+ 3. 数值阈值可被用户预算/目标覆盖;没有输入就用 `planDefaults` 并标「基于历史数据」。
126
+ 4. 可执行性优先:少拆、少测,每项能落到预算 / 市场 / Campaign / 词 / 素材 / 页面 / 受众之一。
@@ -1,19 +1,28 @@
1
1
  # Google 季度经营报告
2
2
 
3
3
  > collect 只出事实;Agent 只写 `narrative`;`render` 注入 `google-quarterly-report.html`(大族版式 + ECharts)。
4
- > **禁止** Read HTML 模板。行业数据不做。专项广告无量则整章隐藏。
4
+ > **禁止** Read HTML 模板。行业数据不做。新探索(视频 / PMax / 发现 / 展示)全无量则整章隐藏。
5
+ >
6
+ > **下期规划**:写 `narrative.plan` 前必读 `report-templates/google-quarterly-plan.md`(九章 + Q01–Q30)。规划只消费 collect,不编造预算/CPA/询盘,不写自动调价/暂停。
5
7
  >
6
8
  > **触发(命中即 P10,勿改 P4/P9)**:月度报告、月总结与规划、季度报告、Q3 汇总、Q4 规划、季度汇报、投放季报、客户版、大族模板、下月规划;或「效率 + 区域询盘 + 国家明细 + 规划」。用户说月度则 `--start/--end` 用该月(默认上一完整自然月)。
7
9
 
8
10
  ## 询盘入场(硬规则)
9
11
 
10
- CLI **不读** xlsx / csv / 截图。流程固定三步:
12
+ CLI **不读** xlsx / csv / 截图。
13
+
14
+ **开场必问(未带表则停)**:本轮消息里**没有**询盘表(xlsx / csv / 截图 / 粘贴表 / 已落盘 `inquiries.json`)时,**先问再拉数**。问完必须等用户回复;在用户发来表格、或明确说「没有 / 跳过 / 先不出询盘」之前,**禁止** `google-quarterly collect`、写 `narrative`、`render`。已附表格或用户已回答过则不必再问。
15
+
16
+ 问法(可原样用):
11
17
 
12
- 1. 用户给询盘载体(xlsx / csv / 截图 / 粘贴表,格式不限)。
13
- 2. **Agent** 抽行、归一后落盘 `./snap-p10/inquiries.json`,再 `--inquiries` 传给 collect。
14
- 3. collect → 只写 `narrative` → `render` 出 HTML。
18
+ > 季度报告里的区域询盘、询盘成本和规划主目标会用到询盘明细。您这边有本期询盘表吗(Excel / CSV / 截图 / 粘贴表均可)?有的话请发过来;没有回复「没有」即可,我按广告数据继续出报告。
15
19
 
16
- **禁止**把原始 `.xlsx` / `.csv` 传给 `--inquiries`(会 `JSON.parse` 失败)。没有载体:可 `--inquiry-conversion <转化动作名>`;再没有则询盘位留空并脚注。**禁止编造询盘行。**
20
+ 收到表格后:
21
+
22
+ 1. **Agent** 抽行、归一后落盘 `./snap-p10/inquiries.json`,再 `--inquiries` 传给 collect。
23
+ 2. collect → 只写 `narrative` → `render` 出 HTML。
24
+
25
+ **禁止**把原始 `.xlsx` / `.csv` 传给 `--inquiries`(会 `JSON.parse` 失败)。用户说没有:可 `--inquiry-conversion <转化动作名>`(须用户点名);再没有则询盘位留空并脚注。**禁止编造询盘行。**
17
26
 
18
27
  落盘 JSON:根为数组或 `{ "items": [...] }`。每行至少有国家 +(月份或日期):
19
28
 
@@ -55,7 +64,7 @@ siluzan-tso google-quarterly render \
55
64
 
56
65
  省略 `--start/--end` 时 collect 用上一完整自然季(上海日历)。
57
66
 
58
- 可选:`--keyword-seeds "a,b"` 走 Keyword Planner(标注数据源,禁止写成行业指数)。**不做**频道订阅总数、订阅来源饼、视频类型(要 YouTube Analytics / 人工分类)。YouTube 广告能拿到的曝光 / TrueView / 完播 / CPV / 频道 ID /(若已关联)广告带来订阅,collect 会写入 `specialAds` + `videosSummary`,render 直接展示。
67
+ 可选:`--keyword-seeds "a,b"` 走 Keyword Planner(标注数据源,禁止写成行业指数)。**不做**频道订阅总数、订阅来源饼、视频类型(要 YouTube Analytics / 人工分类)。YouTube 广告能拿到的曝光 / TrueView / 完播 / CPV / 频道 ID /(若已关联)广告带来订阅,collect 写入 `exploreAds.types[video]` + `videosSummary`;PMax / 发现 / 展示并行写入 `exploreAds`。render 用 tab 切换,不编造 ABCD / 素材组 / 受众切分。
59
68
 
60
69
  Agent JSON Schema:`assets/google-quarterly-report.schema.json`。
61
70
 
@@ -64,7 +73,7 @@ Agent JSON Schema:`assets/google-quarterly-report.schema.json`。
64
73
  | 能力 | 官方 | Sammamish | CLI |
65
74
  | ---- | ---- | --------- | --- |
66
75
  | 消耗最高国一级行政区 | `geographic_view` + `geo_target_state` / `province` / `region` | `GET …/reports/geo/subdivisions`(可带 `countryCriterionId`) | collect 自动选本期花费最高国家;未发布或该国无分段则国家级回退 |
67
- | YouTube 广告层 | `video` 资源:title / channel_id / TrueView / 四分位完播 / CPV | 已有 `GetVideoReport` | 专项章 KPI + 视频表;无量则隐藏整章 |
76
+ | YouTube 广告层 | `video` 资源:title / channel_id / TrueView / 四分位完播 / CPV | 已有 `GetVideoReport` | 新探索「视频」tab;无四类试验线则隐藏整章 |
68
77
  | 广告带来订阅 | Ads 转化动作名含 subscribe/订阅 | 已有 `conversion-actions` | 有则展示;**不是**频道粉数 |
69
78
 
70
79
  ## 事实字段(collect,禁止改)
@@ -75,13 +84,15 @@ Agent JSON Schema:`assets/google-quarterly-report.schema.json`。
75
84
  | `inquiries` | Agent 转好的 JSON(`--inquiries`)优先;否则 `--inquiry-conversion`;再否则空,页脚注明口径 |
76
85
  | `regions` / `countries` | 询盘按国家归并(印度/印尼单独成桶)+ 花费来自 geographic |
77
86
  | `usStates` / `meta.drillCountry` | 下钻对象=本期花费最高国家;Sammamish `GET …/reports/geo/subdivisions`(`geo_target_state` / `province` / `region`);无分段则回退该国国家行,**不写死美国** |
78
- | `specialAds` / `videosSummary` | YouTube 视频名 + Ads 视频指标 → PMax 系列名 其他非搜索 `none`(HTML 隐藏整章)。不做订阅总数 / 来源饼 / 视频类型 |
79
- | `planDefaults` | 花费 ×1.3~×1.5;**不**自动给 CPA/询盘目标 |
87
+ | `exploreAds` / `videosSummary` | 四类并行:视频(Ads 视频报表,无则 VIDEO 系列)/ PMax / 发现 / 展示。`specialAds` 仍保留兼容(只取一类)。不做订阅总数 / 来源饼 / 视频类型。四类全无量则 HTML 隐藏整章 |
88
+ | `planDefaults` | 三档:`spendFloor`=本期、`spendLow`=+30%、`spendHigh`=+50%;建议档月度 `monthSuggested` 默认 30/35/35。**不是**已批预算。**不**自动给 CPA/询盘目标 |
80
89
 
81
90
  频道订阅总数、订阅来源、视频类型本报告不拉。
82
91
 
83
92
  ## Agent 只写 `narrative`
84
93
 
94
+ 写 `plan` 前 Read `google-quarterly-plan.md`。`plan.analysis` 必填(3~5 条结论);`goals` / `budget` / `structure` / `keywords` / `audience` / `creative` / `roadmap` / `risks` 写规划时都要有。每条带口径标签。
95
+
85
96
  ```json
86
97
  {
87
98
  "narrative": {
@@ -92,9 +103,19 @@ Agent JSON Schema:`assets/google-quarterly-report.schema.json`。
92
103
  "region": { "analysis": ["…"] },
93
104
  "summary": { "analysis": ["…"] }
94
105
  },
95
- "plan": { "analysis": ["下期花费目标取 collect.planDefaults.spendLow~spendHigh", "动作…"] }
106
+ "plan": {
107
+ "analysis": ["已确认:…", "基于历史数据:建议档取 planDefaults.spendLow"],
108
+ "goals": { "analysis": ["主目标:有效询盘…"] },
109
+ "budget": { "analysis": ["核心市场优先;测试设上限"] },
110
+ "structure": { "analysis": ["Search 守基本盘;P0:…"] },
111
+ "keywords": { "analysis": ["产品词精确/词组;否词:求职/教程"] },
112
+ "audience": { "analysis": ["待业务确认:客户名单未导入"] },
113
+ "creative": { "analysis": ["主题:工业场景;缺口:PMax 视频"] },
114
+ "roadmap": { "analysis": ["第1月基础验证;第2月核心建设;第3月已验证方向扩"] },
115
+ "risks": { "analysis": ["销售回传缺失"] }
116
+ }
96
117
  }
97
118
  }
98
119
  ```
99
120
 
100
- 缺 `executiveSummary` / `sections.*.analysis` / `plan` render 失败。
121
+ 缺 `executiveSummary` / `sections.*.analysis` / `plan.analysis`(或 `plan.goals` / `plan.text`)时 render 失败。九章缺章不拒渲染,但规划不合格。
@@ -22,7 +22,8 @@
22
22
  | 文件 | 适用场景 |
23
23
  | -------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------- |
24
24
  | `google-period-report.md` | **Google 周期分析报告(默认)**:终稿=HTML,四步流程 → `google-analysis render` 出 HTML |
25
- | `google-quarterly-report.md` / `.html` | **Google 季度经营报告(P10)**:`google-quarterly collect`+`render`;花费默认 +30%~+50%;无行业章 |
25
+ | `google-quarterly-report.md` / `.html` | **Google 季度经营报告(P10)**:`google-quarterly collect`+`render`;花费三档基于本期;无行业章 |
26
+ | `google-quarterly-plan.md` | **P10 下期规划规则**:九章 + Q01–Q30;写 `narrative.plan` 前必读 |
26
27
  | `google-period-report-excel.md` | **仅用户要 Excel 时**(P4 定制 Sheet):先 `*.outline.txt` 后脚本写 xlsx;账户须 `-k` 核验,无 CLI excel 子命令 |
27
28
  | `stats-daily-excel.md` | **P4-DAILY**:账户级按日 Excel(`stats --by-day`) |
28
29
  | `google-account-diagnosis-report.md` | Google 账户深度诊断(健康度/转化/结构等) |