siluzan-tso-cli 1.1.20-beta.2 → 1.1.20-beta.21

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.
Files changed (89) hide show
  1. package/README.md +1 -1
  2. package/dist/index.js +3777 -1190
  3. package/dist/skill/SKILL.md +144 -125
  4. package/dist/skill/_meta.json +2 -2
  5. package/dist/skill/assets/campaign-create-template.json +146 -93
  6. package/dist/skill/assets/campaign-create-template.md +238 -0
  7. package/dist/skill/references/account-analytics.md +39 -32
  8. package/dist/skill/references/accounts.md +13 -11
  9. package/dist/skill/references/currency.md +72 -0
  10. package/dist/skill/references/geo-continents.json +1 -9
  11. package/dist/skill/references/google-ads-campaign-plan.md +89 -0
  12. package/dist/skill/references/google-ads-rules/google-ads-campaign-optimization.md +1 -15
  13. package/dist/skill/references/google-ads-rules/google-ads-compliance.md +7 -8
  14. package/dist/skill/references/google-ads-rules/google-ads-keyword-strategy.md +2 -3
  15. package/dist/skill/references/google-ads-rules/google-ads-keyword-taxonomy.md +86 -0
  16. package/dist/skill/references/google-ads-rules/google-ads-launch-plan-template.md +59 -129
  17. package/dist/skill/references/google-ads.md +256 -144
  18. package/dist/skill/references/google-analysis-batch.md +18 -18
  19. package/dist/skill/references/hosted-automation-optimize-weak-downbid.md +1 -1
  20. package/dist/skill/references/hosted-automation-self-control.md +2 -2
  21. package/dist/skill/references/keyword-planner-workflows.md +132 -28
  22. package/dist/skill/references/rag.md +104 -0
  23. package/dist/skill/references/reporting.md +1 -1
  24. package/dist/skill/references/tips.md +12 -6
  25. package/dist/skill/references/workflows.md +24 -172
  26. package/dist/skill/report-templates/README.md +8 -8
  27. package/dist/skill/report-templates/REPORT-WORKFLOW.md +12 -12
  28. package/dist/skill/report-templates/google-inquiry-analysis.md +185 -127
  29. package/dist/skill/report-templates/okki-weekly-google-client.md +66 -66
  30. package/dist/skill/scripts/install.ps1 +8 -6
  31. package/dist/skill/scripts/install.sh +7 -3
  32. package/eval/cases/accounts-entityid-vs-mediaccustomerid.scenario.json +14 -2
  33. package/eval/cases/accounts-mcc-bind-inquiry.scenario.json +3 -1
  34. package/eval/cases/accounts-single-balance-not-bulk.scenario.json +14 -3
  35. package/eval/cases/budget-display-not-raw-micros.scenario.json +10 -3
  36. package/eval/cases/clue-meta-leads-json.scenario.json +14 -2
  37. package/eval/cases/clue-tiktok-leads-json.scenario.json +11 -2
  38. package/eval/cases/destructive-account-delink-needs-confirm.scenario.json +9 -3
  39. package/eval/cases/destructive-forewarning-delete-needs-confirm.scenario.json +9 -3
  40. package/eval/cases/destructive-invoice-apply-needs-confirm.scenario.json +9 -3
  41. package/eval/cases/finance-invoice-info-list.scenario.json +11 -3
  42. package/eval/cases/forewarning-list-google.scenario.json +14 -3
  43. package/eval/cases/google-ads-no-structural-without-confirm.scenario.json +6 -2
  44. package/eval/cases/google-analysis-keywords-route.scenario.json +14 -2
  45. package/eval/cases/human-p1-multiturn.scenario.json +5 -1
  46. package/eval/cases/meta-single-balance-not-bulk.scenario.json +17 -3
  47. package/eval/cases/open-account-bing-noninteractive.scenario.json +4 -1
  48. package/eval/cases/open-account-google-noninteractive.scenario.json +3 -1
  49. package/eval/cases/open-account-tiktok-license-file.scenario.json +3 -1
  50. package/eval/cases/optimize-list-by-account.scenario.json +11 -3
  51. package/eval/cases/p1-single-account-profile.scenario.json +11 -1
  52. package/eval/cases/p2-balance-scan-bulk.scenario.json +9 -2
  53. package/eval/cases/p3-accounts-digest.scenario.json +5 -1
  54. package/eval/cases/p4-period-report-window.scenario.json +8 -1
  55. package/eval/cases/rag-before-keyword-expand.scenario.json +24 -0
  56. package/eval/cases/rag-list-then-query.scenario.json +23 -0
  57. package/eval/cases/report-list-google.scenario.json +11 -2
  58. package/eval/cases/report-push-list-google.scenario.json +11 -2
  59. package/eval/cases/reporting-vs-account-analytics-routing.scenario.json +4 -1
  60. package/eval/cases/setup-login-or-env.scenario.json +3 -1
  61. package/eval/cases/setup-siluzan-data-permission-env.scenario.json +3 -1
  62. package/eval/cases/skill-optimize-vs-google-ads-distinction.scenario.json +4 -1
  63. package/eval/cases/tiktok-bc-bind-inquiry.scenario.json +6 -2
  64. package/eval/cases/time-range-user-delegates-default.scenario.json +8 -1
  65. package/eval/cases/tips-json-filtering.scenario.json +3 -1
  66. package/eval/cases/tips-large-json-pagination.scenario.json +3 -1
  67. package/eval/cases/uj-ad-campaign-validate-before-create-stub.scenario.json +20 -0
  68. package/eval/cases/uj-ad-keywords-camping-tent-outdoor-plan.scenario.json +1 -1
  69. package/eval/cases/uj-ad-outdoor-campgear-search-plan.scenario.json +5 -3
  70. package/eval/cases/uj-analytics-30d-pdf-campaign-device-geo.scenario.json +18 -6
  71. package/eval/cases/uj-analytics-compare-google-tiktok-last-month-roi.scenario.json +8 -1
  72. package/eval/cases/uj-analytics-google-weekly-trends-campaigns-keywords.scenario.json +11 -2
  73. package/eval/cases/uj-analytics-report-push-weekly-email.scenario.json +3 -1
  74. package/eval/cases/uj-finance-invoice-records-this-month.scenario.json +11 -2
  75. package/eval/cases/uj-life-newbie-siluzan-google-end-to-end.scenario.json +4 -1
  76. package/eval/cases/uj-ops-google-accounts-list-normal.scenario.json +14 -2
  77. package/eval/cases/uj-ops-google-yesterday-spend-conversions.scenario.json +14 -2
  78. package/eval/cases/uj-ops-open-google-b2c-usd-shenzhen.scenario.json +4 -1
  79. package/eval/cases/uj-ops-pause-worst-adgroup-confirm.scenario.json +6 -2
  80. package/eval/cases/uj-ops-tiktok-leads-last-week.scenario.json +17 -3
  81. package/eval/cases/uj-patrol-cpc-spike-adgroups-over-15.scenario.json +9 -2
  82. package/eval/cases/uj-patrol-forewarning-create-daily-cap-3000.scenario.json +3 -1
  83. package/eval/cases/uj-patrol-forewarning-trigger-records.scenario.json +17 -3
  84. package/eval/cases/uj-patrol-google-balances-low.scenario.json +11 -2
  85. package/eval/cases/uj-roi-optimize-records-then-execute-cautiously.scenario.json +14 -3
  86. package/eval/cases/uj-roi-search-terms-add-negative-keywords.scenario.json +14 -2
  87. package/eval/stub-fixtures/rag-list.json +19 -0
  88. package/eval/stub-fixtures/rag-query.json +20 -0
  89. package/package.json +1 -1
@@ -1,99 +1,152 @@
1
1
  {
2
- "account": "6326027735",
3
- "customerName": "账户显示名称(来自 list-accounts --json 的 mediaAccountName)",
4
- "name": "搜索-品牌词-2026",
5
- "budget": 100,
6
- "bidding": "TARGET_SPEND",
7
- "bidCeiling": 1.5,
8
- "locationIds": ["2840", "2826", "2036"],
9
- "languageIds": ["1000", "1017"],
10
- "startDate": "2026-04-01",
11
- "endDate": "2027-04-01",
2
+ "_meta": {
3
+ "schema": "campaign-create/v2",
4
+ "doc": "campaign-create-template.md",
5
+ "note": "以 _ 开头的键仅作说明,CLI 提交前会剥离;字段名一律 PascalCase,直接对齐后端 CampaignCreationRecord + Campaign 契约",
6
+ "agentPitfalls": [
7
+ "campaign-validate 不支持 --json-out, --json ",
8
+ "RSA 须同时写 DestinationUrl 与 Finalurl(后端必填 Finalurl;仅有 DestinationUrl 时 validate 可能仍通过)",
9
+ "SITELINK 须写 AssetFieldType: SITELINK(与 typeV2 一致)",
10
+ "提交前:ad campaign-validate → 用户确认 → ad campaign-create(勿对 validate 加 --json-out)"
11
+ ]
12
+ },
13
+
14
+ "account": "REPLACE_mediaCustomerId",
15
+ "customerName": "REPLACE_mediaAccountName_from_list_accounts",
16
+ "name": "Search_Brand_US_2026",
12
17
  "url": "https://www.example.com",
13
- "status": "Enabled",
14
- "adgroupName": "核心词_品牌词",
15
- "maxCpc": 1.5,
16
- "matchType": "BROAD",
17
- "keywords": ["brand keyword 1", "brand keyword 2", "brand keyword 3"],
18
- "headlines": [
19
- "Brand Name: Quality Products",
20
- "Trusted For Over 20 Years",
21
- "Global Reach, Local Impact",
22
- "Contact Us Today",
23
- "Request A Free Quote Now"
24
- ],
25
- "descriptions": [
26
- "Top-quality products with certified standards. Contact us today!",
27
- "Trusted by global partners. Request a free quote now!"
28
- ],
29
- "finalUrl": "https://www.example.com/products",
30
- "path1": "products",
31
- "path2": "2026",
18
+ "locations": ["United States"],
32
19
  "productWords": ["brand", "product"],
33
- "negativeKeywords": ["free", "cheap", "wikipedia", "pdf", "ebay", "amazon"],
34
- "extensions": [
35
- {
36
- "level": "Campaign",
37
- "typeV2": "CALL",
38
- "AssetFieldType": "CALL",
39
- "Properties": { "ContryCode": "CN", "PhoneNumber": "+86 400-XXX-XXXX" }
40
- },
41
- {
42
- "level": "Campaign",
43
- "typeV2": "SITELINK",
44
- "AssetFieldType": "SITELINK",
45
- "properties": {
46
- "DestinationUrl": "https://www.example.com/about",
47
- "Text": "About Us",
48
- "Line2": "Learn about our story",
49
- "Line3": "Explore our mission"
20
+ "googleDataRecordId": null,
21
+ "draft": false,
22
+
23
+ "campaign": {
24
+ "_comment_budget": "金额字段(Budget / TargetSpend_BidCeilingAmount / TargetCpa_BidingAmount / 各 AdGroup.MaxCPCAmount)以主币种「元」填写,CLI 提交前自动 ×100 转「分」",
25
+ "Name": "Search_Brand_US_2026",
26
+ "StatusV2": "Enabled",
27
+ "ChannelTypeV2": "SEARCH",
28
+ "BiddingStrategyTypeV2": "TARGET_SPEND",
29
+ "Budget": 100,
30
+ "BudgetShared": false,
31
+ "BudgetId": 0,
32
+ "BudgetBudgetDeliveryMethodV2": "STANDARD",
33
+ "TargetSpend_BidCeilingAmount": 1.5,
34
+ "ManualCpc_EnhancedCpcEnabled": false,
35
+ "TargetGoogleSearch": true,
36
+ "TargetSearchNetwork": false,
37
+ "TargetContentNetwork": false,
38
+ "TargetPartnerSearchNetwork": false,
39
+ "PositiveGeoTargetType": 0,
40
+ "NegativeGeoTargetType": 0,
41
+ "DSADomainName": "",
42
+ "DSALanguageCode": "en",
43
+ "StartTime": "2026-04-01",
44
+ "EndTime": "2037-12-30",
45
+ "targetedLocations": [{ "id": "2840", "bidModifier": 0, "bidModifierSpecified": false }],
46
+ "excludedLocations": [],
47
+ "excludedIpAddresses": [],
48
+ "targetedLanguages": [{ "id": 1000 }],
49
+ "targetedPlatforms": [
50
+ { "id": 30001, "bidModifier": 0 },
51
+ { "id": 30002, "bidModifier": 0 },
52
+ { "id": 30000, "bidModifier": 0 }
53
+ ],
54
+
55
+ "adSchedules": [
56
+ { "DayOfWeekV2": 2, "startHour": 0, "StartMinuteV2": 2, "endHour": 24, "EndMinuteV2": 2 },
57
+ { "DayOfWeekV2": 3, "startHour": 0, "StartMinuteV2": 2, "endHour": 24, "EndMinuteV2": 2 },
58
+ { "DayOfWeekV2": 4, "startHour": 0, "StartMinuteV2": 2, "endHour": 24, "EndMinuteV2": 2 },
59
+ { "DayOfWeekV2": 5, "startHour": 0, "StartMinuteV2": 2, "endHour": 24, "EndMinuteV2": 2 },
60
+ { "DayOfWeekV2": 6, "startHour": 0, "StartMinuteV2": 2, "endHour": 24, "EndMinuteV2": 2 },
61
+ { "DayOfWeekV2": 7, "startHour": 0, "StartMinuteV2": 2, "endHour": 24, "EndMinuteV2": 2 },
62
+ { "DayOfWeekV2": 8, "startHour": 0, "StartMinuteV2": 2, "endHour": 24, "EndMinuteV2": 2 }
63
+ ],
64
+
65
+ "NegativeKeywordsForBatchJob": [
66
+ { "KeywordText": ["free", "cheap", "download"], "MatchTypeV2": "BROAD", "FinalURL": "" }
67
+ ],
68
+
69
+ "ExtensionsForBatchJob": [
70
+ {
71
+ "level": "Campaign",
72
+ "typeV2": "CALL",
73
+ "AssetFieldType": "CALL",
74
+ "Properties": { "ContryCode": "US", "PhoneNumber": "+1 800-555-0100" }
75
+ },
76
+ {
77
+ "typeV2": "SITELINK",
78
+ "Level": "Campaign",
79
+ "Properties": {
80
+ "Text": "About Us",
81
+ "Line2": "xxx",
82
+ "Line3": "xxxx",
83
+ "DestinationUrl": "https://www.example.com/about"
84
+ }
50
85
  }
51
- },
52
- {
53
- "level": "Campaign",
54
- "typeV2": "STRUCTURED_SNIPPET",
55
- "AssetFieldType": "STRUCTURED_SNIPPET",
56
- "StructuredSnippetHeaderValue": {
57
- "key": "Services",
58
- "value": ["Quality Certified", "Global Shipping", "24/7 Support"]
86
+ ],
87
+
88
+ "AdGroupsForBatchJob": [
89
+ {
90
+ "Name": "Core_Brand_Keywords",
91
+ "StatusV2": "Enabled",
92
+ "TypeV2": "SEARCH_STANDARD",
93
+ "RotationModeV2": "Unspecified",
94
+ "MaxCPCAmount": 1.5,
95
+
96
+ "KeywordsForBatchJob": [
97
+ {
98
+ "KeywordText": [
99
+ "brand name product",
100
+ "buy brand product online",
101
+ "brand official store"
102
+ ],
103
+ "MatchTypeV2": "BROAD",
104
+ "FinalURL": "https://www.example.com/products"
105
+ },
106
+ {
107
+ "KeywordText": ["\"brand name product\""],
108
+ "MatchTypeV2": "PHRASE",
109
+ "FinalURL": "https://www.example.com/products"
110
+ },
111
+ {
112
+ "KeywordText": ["[brand name product]"],
113
+ "MatchTypeV2": "EXACT",
114
+ "FinalURL": "https://www.example.com/products"
115
+ }
116
+ ],
117
+
118
+ "AdsForBatchJob": [
119
+ {
120
+ "_comment_finalurl": "Finalurl 与 DestinationUrl 填相同 URL;勿只写 DestinationUrl(BatchJob 会报 Finalurl 缺失)",
121
+ "TypeV2": "RESPONSIVE_SEARCH_AD",
122
+ "DestinationUrl": "https://www.example.com/products",
123
+ "Finalurl": "https://www.example.com/products",
124
+ "AdTitle": null,
125
+ "Path1": "products",
126
+ "Path2": "brand",
127
+ "headlinePart1": "Brand Name Official Store",
128
+ "headlinePart2": "Quality Products You Trust",
129
+ "headlinePart3": "Shop Direct From Brand",
130
+ "AddtionalHeadlines": [
131
+ "Fast Shipping Available",
132
+ "Certified Product Quality",
133
+ "Explore The Full Catalog",
134
+ "Trusted By Global Buyers",
135
+ "Request A Quote Today",
136
+ "Professional Grade Products",
137
+ "Browse Best Sellers Now",
138
+ "Reliable Supply Partner",
139
+ "Contact Us For Details"
140
+ ],
141
+ "adDescription": "Official products with certified quality. Contact us for pricing.",
142
+ "adDescription2": "Trusted by partners worldwide. Request a quote or browse the catalog.",
143
+ "AddtionalAdDescriptions": [
144
+ "Fast response from our sales team. See specs and certifications on our site.",
145
+ "Shop the full product line with reliable shipping and support options."
146
+ ]
147
+ }
148
+ ]
59
149
  }
60
- }
61
- ],
62
- "extraAdGroups": [
63
- {
64
- "MaxCPCAmount": 150,
65
- "Name": "通用词_行业词",
66
- "StatusV2": "Enabled",
67
- "TypeV2": "SEARCH_STANDARD",
68
- "RotationModeV2": "Unspecified",
69
- "KeywordsForBatchJob": [
70
- {
71
- "KeywordText": ["industry keyword 1", "industry keyword 2"],
72
- "MatchTypeV2": "BROAD",
73
- "Finalurl": "https://www.example.com"
74
- }
75
- ],
76
- "AdsForBatchJob": [
77
- {
78
- "AdTitle": null,
79
- "DestinationUrl": "https://www.example.com",
80
- "Finalurl": "https://www.example.com",
81
- "Path1": "",
82
- "Path2": "",
83
- "TypeV2": "RESPONSIVE_SEARCH_AD",
84
- "headlinePart1": "Industry Leading Products",
85
- "headlinePart2": "Certified Quality Standards",
86
- "headlinePart3": "Get A Free Quote Today",
87
- "AddtionalHeadlines": [
88
- "Trusted By Global Partners",
89
- "20+ Years Of Experience",
90
- "Contact Us Now"
91
- ],
92
- "adDescription": "Top-quality industry products with global shipping. Contact us today!",
93
- "adDescription2": "Certified standards and reliable delivery. Request a quote now!"
94
- }
95
- ]
96
- }
97
- ],
98
- "draft": false
150
+ ]
151
+ }
99
152
  }
@@ -0,0 +1,238 @@
1
+ # `ad campaign-create` JSON 配置说明
2
+
3
+ `siluzan-tso ad campaign-create` **仅**接受 `--config-file` 指向的 JSON 文件
4
+
5
+ **JSON 字段名保持 PascalCase**,与后端 `Campaign` / `CampaignCreationRecord` 契约一致;`ad campaign-validate` 阶段**不**改词面与结构。
6
+
7
+ **`ad campaign-create` 提交前**,CLI 在 JSON 原文之外额外处理(不影响 validate 读到的「元」口径):
8
+
9
+ 1. 剥除以 `_` 开头的注解键(如 `_meta`、`_comment_budget`、`_comment_finalurl`);
10
+ 2. 外层 body:`account` → 数字 `customerId`;补全 `KeywordRecommendationsV2`(按广告组名,`Value` 可为 `[]`);`googleDataRecordId` 缺省为 `""`(与 Web 智投一致);
11
+ 3. `campaign` 金额字段「元」→「分」(×100);
12
+ 4. `ExtensionsForBatchJob` 中 SITELINK 的 `Properties` 规范化(见下文「SITELINK」)。
13
+
14
+ JSON 模板:同目录 [`campaign-create-template.json`](campaign-create-template.json)。
15
+
16
+ ---
17
+
18
+ ## Agent 常见坑(实战)
19
+
20
+ > 摘自真实创建流水:多次 `--json-out` 误用、validate 通过但 create 因 `Finalurl` 失败。提交前对照本表,避免重复踩坑。
21
+
22
+ ### CLI 参数(勿混用)
23
+
24
+ | 命令 | 支持 `--json-out`? | 推荐用法 |
25
+ | ---- | :-----------------: | -------- |
26
+ | `ad campaign-validate` | **否** | `siluzan-tso ad campaign-validate --config-file ./campaign.json` 或加 `--json`;规范化输出用 `--write-normalized ./campaign.normalized.json` |
27
+ | `ad campaign-create` | **是**(较新版本) | 落盘任务响应:`--json-out ./snap-campaign`;与 `--json` 互斥。若报 `unknown option --json-out`,升级 `siluzan-tso` 或暂用 `--json` |
28
+ | `ad batch get` / `ad batch diff` | **是** | 轮询与 diff 结果落盘,见 `references/tips.md` |
29
+
30
+ **禁止**:对 `campaign-validate` 加 `--json-out`(会报参数不支持,浪费多轮重试)。
31
+
32
+ ### RSA 落地页:`Finalurl` 必填
33
+
34
+ 后端 BatchJob **要求**每条 `AdsForBatchJob[]` 带 **`Finalurl`**(小写 url)。仅写 `DestinationUrl` 时:
35
+
36
+ - `ad campaign-validate` **可能仍显示通过**(校验用 `Finalurl ?? DestinationUrl` 只检查 URL 格式,不强制字段存在);
37
+ - `ad campaign-create` / BatchJob **会失败**(报 `Finalurl` 缺失类错误)。
38
+
39
+ **正确写法**(两字段同值):
40
+
41
+ ```json
42
+ "DestinationUrl": "https://www.example.com/products",
43
+ "Finalurl": "https://www.example.com/products",
44
+ "AdTitle": null
45
+ ```
46
+
47
+ 生成 JSON 后可用脚本批量补全(勿手改几十条):
48
+
49
+ ```python
50
+ # 与实战 fix_campaign_v2.py 同逻辑:缺 Finalurl 则从 DestinationUrl 复制
51
+ for ag in camp.get("AdGroupsForBatchJob", []):
52
+ for ad in ag.get("AdsForBatchJob", []):
53
+ if "Finalurl" not in ad:
54
+ ad["Finalurl"] = ad.get("DestinationUrl", "")
55
+ if "AdTitle" not in ad:
56
+ ad["AdTitle"] = None
57
+ ```
58
+
59
+ ### SITELINK 附加信息
60
+
61
+ 每条 `typeV2: "SITELINK"` 须同时有 **`AssetFieldType": "SITELINK"`**(模板已示例)。缺省时部分账户 BatchJob 会失败。
62
+
63
+ ### 推荐命令顺序(单系列)
64
+
65
+ ```bash
66
+ siluzan-tso ad campaign-validate --config-file ./campaign.json
67
+ # 用户确认方案后:
68
+ siluzan-tso ad campaign-create --config-file ./campaign.json --commit '<campaign create description>'
69
+ siluzan-tso ad batch get --id <taskId> --config-file ./campaign.json --json-out ./snap-campaign
70
+ siluzan-tso ad batch diff --batch-id <taskId> --config-file ./campaign.json --json-out ./snap-campaign
71
+ ```
72
+
73
+ `Creating` 时每 5–10s 轮询 `batch get`,**勿**重复 `campaign-create`。
74
+
75
+ ---
76
+
77
+ ## Agent 编排
78
+
79
+ 流程、双轨入口、`campaign-validate` 门禁与分层规则阅读:**`references/google-ads-campaign-plan.md`**(本文件仅字段契约,不重复流水线)。
80
+
81
+ 一个 JSON 对应一个广告系列;多系列使用多个 JSON 文件(可选 `campaign-manifest.json`,见 campaign-plan)。
82
+
83
+ **提交前自检(代码改 JSON,不手填 Markdown):**
84
+
85
+ 1. 每条 RSA:`Finalurl` 与 `DestinationUrl` 同值;可选 `AdTitle: null`;
86
+ 2. 每条 SITELINK:`AssetFieldType` = `SITELINK`;
87
+ 3. `ad campaign-validate` 通过(**不加** `--json-out`);
88
+ 4. 用户确认后再 `campaign-create`。
89
+
90
+ ---
91
+
92
+ ## 外层字段(CampaignCreationRecord)
93
+
94
+ | 字段 | 类型 | 必填 | 说明 |
95
+ | ---------------------- | ------------------- | :--: | ------------------------------------------------------------------------------------------------------ |
96
+ | `account` | string | ✅ | 媒体账户 ID;提交时转为数字 `customerId`(勿依赖引号字符串) |
97
+ | `customerName` | string | ✅ | 须与 `list-accounts` 的 `mediaAccountName` **完全一致**,否则 `customer name error` |
98
+ | `name` | string | | 智投 `campaignName`;缺省取 `campaign.Name`;账户内不得与已有在投/暂停系列重名,否则 BatchJob 系列创建失败 |
99
+ | `url` | string | | 智投展示用 URL;后端只读,用于回显 |
100
+ | `locations` | string[] | | 展示用地区名(后端只读,可空数组) |
101
+ | `productWords` | string[] | | 智投/推荐用产品核心词 |
102
+ | `googleDataRecordId` | string \| null | | 智投记录 ID;省略时提交 `""`(与 Web 智投一致) |
103
+ | `draft` | boolean | | `false`(默认)立即发布到 Google;`true` 仅保存草稿,需后续 `ad batch publish` |
104
+ | `campaign` | object | ✅ | 内层 Campaign 对象,见下表 |
105
+
106
+ > 提交时 CLI 另附 `KeywordRecommendationsV2`:`[{ Key: <广告组 Name>, Value: [] }, …]`,与 Web `/advertising/AICreation` 结构一致;JSON 文件内无需手写。
107
+
108
+ ---
109
+
110
+ ## 内层字段(`campaign` 对象)
111
+
112
+ > **金额单位**:JSON 中以「元」填写,CLI 提交前 ×100 转为「分」(与后端 `Budget`、`MaxCPCAmount` 等字段一致)。
113
+ > 涉及字段:`Budget`、`TargetSpend_BidCeilingAmount`、`TargetCpa_BidingAmount`、`TargetCpaAmount`、`MaxCPCAmount`、`MaxCpmAmount`、`MaxCPVAmount`、`MaxCPC`。
114
+
115
+ ### 基础
116
+
117
+ | 字段 | 类型 | 必填 | 说明 |
118
+ | ------------------------------- | --------------------- | :--: | --------------------------------------------------------------------- |
119
+ | `Name` | string | ✅ | 广告系列名;须与外层 `name` 一致;账户内唯一(在投/暂停不可重名) |
120
+ | `StatusV2` | "Enabled" \| "Paused" | | 默认 `Enabled` |
121
+ | `ChannelTypeV2` | string | | 搜索系列填 `SEARCH` |
122
+
123
+ ### 预算与出价
124
+
125
+ | 字段 | 类型 | 必填条件 | 说明 |
126
+ | ------------------------------------- | ----------------------------------------------------- | ------------------------------ | ------------------------------------------ |
127
+ | `Budget` | number | ✅ > 0 | 日预算(元) |
128
+ | `BudgetShared` | boolean | | 共享预算时为 true |
129
+ | `BudgetId` | number / string | | 共享预算 id |
130
+ | `BudgetBudgetDeliveryMethodV2` | "STANDARD" \| "ACCELERATED" \| "UNSPECIFIED" \| "UNKNOWN" | | 默认 STANDARD |
131
+ | `BiddingStrategyTypeV2` | "TARGET_SPEND" \| "MANUAL_CPC" \| "TARGET_CPA" \| "TARGET_ROAS" \| "MAXIMIZE_CONVERSIONS" \| "MAXIMIZE_CONVERSION_VALUE" | ✅ | 出价策略 |
132
+ | `TargetSpend_BidCeilingAmount` | number | TARGET_SPEND 时 ✅ | 每次点击费用上限(元) |
133
+ | `TargetCpa_BidingAmount` | number | TARGET_CPA 时 ✅ | 目标 CPA(元) |
134
+ | `TargetRoas` | number | TARGET_ROAS 时 ✅ | 目标 ROAS(如 2.5 表示 250%) |
135
+ | `ManualCpc_EnhancedCpcEnabled` | boolean | | 是否增强 CPC |
136
+
137
+ ### 网络(后端硬约束)
138
+
139
+ | 字段 | 类型 | 说明 |
140
+ | ------------------------------- | ------- | --------------------------------------------------------------------------------------------- |
141
+ | `TargetGoogleSearch` | boolean | 默认 true;当 `TargetSearchNetwork=true` 时**必须** true |
142
+ | `TargetSearchNetwork` | boolean | Google 搜索网络合作伙伴;产品默认建议 false |
143
+ | `TargetContentNetwork` | boolean | 展示网络;搜索专属方案建议 false |
144
+ | `TargetPartnerSearchNetwork` | boolean | **必须 false**(后端拒绝:cannot set TargetPartnerSearchNetwork) |
145
+
146
+ ### 地理 / 语言 / 平台
147
+
148
+ | 字段 | 类型 | 必填 | 说明 |
149
+ | --------------------- | --------------------------------------------- | :--: | ------------------------------------------------------------------- |
150
+ | `targetedLocations` | `{ id: string, bidModifier?: number }[]` | ✅ | 至少 1 个;先 `siluzan-tso ad geo search -a <acct> -q <地区名>` |
151
+ | `excludedLocations` | 同上 | | 排除地区 |
152
+ | `targetedLanguages` | `{ id: number }[]` | ✅ | 英语 1000,中文 1017 |
153
+ | `targetedPlatforms` | `{ id: number, bidModifier?: number }[]` | | 30001 桌面 / 30002 平板 / 30000 移动 |
154
+ | `excludedIpAddresses` | string[] | | 排除 IP |
155
+ | `PositiveGeoTargetType` | number | | |
156
+ | `NegativeGeoTargetType` | number | | |
157
+
158
+ ### 时间与 DSA
159
+
160
+ | 字段 | 类型 | 说明 |
161
+ | ----------------- | ------------------- | --------------------------------------------------------------- |
162
+ | `StartTime` | YYYY-MM-DD | 开始日期 |
163
+ | `EndTime` | YYYY-MM-DD | 结束日期(必须晚于 StartTime) |
164
+ | `adSchedules` | object[] | 投放时段,缺省可填全周全天(见模板) |
165
+ | `DSADomainName` | string | 动态搜索广告域名 |
166
+ | `DSALanguageCode` | string | 默认 `en` |
167
+
168
+ ### 子结构
169
+
170
+ | 字段 | 类型 | 说明 |
171
+ | ------------------------------- | -------- | ----------------------------------------------------------------------------- |
172
+ | `AdGroupsForBatchJob` | object[] | **至少 1 组**;见下 |
173
+ | `NegativeKeywordsForBatchJob` | object[] | 系列级否词;元素:`{ KeywordText: string[], MatchTypeV2: "BROAD", FinalURL: "" }` |
174
+ | `ExtensionsForBatchJob` | object[] | 附加信息;`Properties` 须 **string→string**(勿用数组值)。SITELINK 见下表 |
175
+
176
+ #### SITELINK(`ExtensionsForBatchJob[i]`,`typeV2` / `AssetFieldType` = `SITELINK`)
177
+
178
+ | 字段 | 类型 | 说明 |
179
+ | ---- | ---- | ---- |
180
+ | `typeV2` | string | 必填 `SITELINK` |
181
+ | `AssetFieldType` | string | **必填** `SITELINK`(与 `typeV2` 一致;缺省会导致部分账户 BatchJob 失败) |
182
+ | `level` / `Level` | string | 系列级填 `Campaign` |
183
+ | `Properties` | object | 键值均为字符串;见下表 |
184
+ | `Properties.Text` | string | 链接文字(必填)。可写 `LinkText`,提交前会映射为 `Text` |
185
+ | `Properties.Line2` | string | 描述行 1,**≤ 25 字符**。可写 `Description1`,提交前映射为 `Line2` |
186
+ | `Properties.Line3` | string | 描述行 2,**≤ 25 字符**;**不可省略或空字符串**(Google V20 不允许 null,空时 CLI 用 `Line2` 回填)。可写 `Description2` |
187
+ | `Properties.DestinationUrl` | string | 落地页 URL(必填)。**勿**写 `FinalUrls` 数组——会导致 TSO 无法反序列化整包 body(`campaign creation record is null`) |
188
+
189
+ ---
190
+
191
+ ## 广告组(`AdGroupsForBatchJob[i]`)
192
+
193
+ | 字段 | 类型 | 必填 | 说明 |
194
+ | --------------------- | --------------------- | :--: | ------------------------------------------------------------------------------- |
195
+ | `Name` | string | ✅ | 组名;用于提交体 `KeywordRecommendationsV2[].Key` |
196
+ | `StatusV2` | "Enabled" \| "Paused" | | 默认 Enabled |
197
+ | `TypeV2` | string | | 搜索系列填 `SEARCH_STANDARD` |
198
+ | `RotationModeV2` | string | | 一般 `Unspecified` |
199
+ | `MaxCPCAmount` | number | ✅(MANUAL_CPC) | 元;MANUAL_CPC 出价策略必须 > 0 |
200
+ | `KeywordsForBatchJob` | object[] | | 见下 |
201
+ | `AdsForBatchJob` | object[] | | 见下 |
202
+
203
+ ### 关键词块(`KeywordsForBatchJob[j]`)
204
+
205
+ 每个块描述一组**同匹配类型**的关键词:
206
+
207
+ | 字段 | 类型 | 说明 |
208
+ | ------------- | --------------------------------- | --------------------------------------------------------------------- |
209
+ | `KeywordText` | string[] | 关键词词面数组;PHRASE 应写 `"keyword"`,EXACT 应写 `[keyword]` |
210
+ | `MatchTypeV2` | "BROAD" \| "PHRASE" \| "EXACT" | 与词面格式对应 |
211
+ | `FinalURL` | string | 关键词级落地页 |
212
+
213
+ ### 创意块(`AdsForBatchJob[j]`,RSA)
214
+
215
+ | 字段 | 类型 | 必填 | 说明 |
216
+ | ----------------------------- | -------- | :--: | --------------------------------------------- |
217
+ | `TypeV2` | string | | RSA 填 `RESPONSIVE_SEARCH_AD` |
218
+ | `DestinationUrl` | string | ✅ | 展示/编辑用落地页 URL |
219
+ | `Finalurl` | string | ✅ | **后端 BatchJob 必填**;与 `DestinationUrl` 填**相同** URL。勿只写前者——validate 可能仍通过,create 会失败 |
220
+ | `AdTitle` | null \| string | | 可选;无标题时写 `null`(与 Web 智投一致) |
221
+ | `Path1` / `Path2` | string | ✅ | 显示路径(**必填**,缺/null 会导致后端 BatchJob `ArgumentNullException`);**≤ 15 字符**(CJK 按 2 计);小写 a-z、数字、连字符 |
222
+ | `headlinePart1/2/3` | string | ✅ | 前 3 条标题;**每条 ≤ 30 字符**(CJK 按 2 计) |
223
+ | `AddtionalHeadlines` | string[] | | 第 4–15 条标题(合计 ≤ 15) |
224
+ | `adDescription` / `adDescription2` | string | ✅ | 前 2 条描述;**每条 ≤ 90 字符** |
225
+ | `AddtionalAdDescriptions` | string[] | | 第 3–4 条描述(合计 ≤ 4) |
226
+
227
+ ---
228
+
229
+ ## 校验规则(CLI 镜像后端)
230
+
231
+ | 来源 | 规则 |
232
+ | ------------------------------------------------------------------------------------------ | --------------------------------------------------------------------------------------------------- |
233
+ | `CampaignCommandController.CreateCampaignAsync` | `customerName` 非空 / `campaign` 非空 |
234
+ | `CampaignCommandController` 行 94–106 | `campaign.TargetPartnerSearchNetwork` 必须 false;`!TargetGoogleSearch && TargetSearchNetwork` 拒绝 |
235
+ | Google Ads BatchJob | RSA `Path1`/`Path2` 必填(缺/null → 后端 ArgumentNullException);字段数与字符上限;关键词词面非空;SITELINK `Line2`/`Line3` ≤25 字且不可 null |
236
+ | CLI 实务 | `Budget > 0`、地理/语言至少 1 项、日期格式与先后、出价策略与配套字段 |
237
+
238
+ `ad campaign-validate` 通过不保证 BatchJob 成功(例如仅写 `DestinationUrl` 未写 `Finalurl` 时 validate 仍可能 ✅)。异步结果用 `ad batch get` 轮询;`HasFailed` / 部分失败时用 `ad batch diff` 对照 JSON 补缺,系列级失败时改 JSON 重提,勿在半成品上反复整包创建。写操作须 `--commit`,见 `references/google-ads.md` § ad campaign-create。
@@ -76,14 +76,21 @@
76
76
 
77
77
  ### 金额单位
78
78
 
79
- - **永远使用 `*Display` 字段**(如 `maxCPCAmountDisplay`),不得将 `maxCPCAmount` 等 ×100 分单位直接当金额展示。**例外**:`siluzan-tso ad campaigns` 列表的 **`--json`/`--json-out` 中 `budget` 已由 CLI 换为「元」**,可直接作用户可见日预算,勿再 ÷100。
80
- - **`google-analysis` 落盘 `campaigns-<id>.json`(`CampaignSectionData`)特例**:响应里常见只有 **`budgetAmount`(无 `budgetDisplay`)**。该字段与网关 **`Budget` 同为「分」整数**(主币种 ×100),换算为元须 **÷100**。**禁止**误用 Google Ads API 的**微元**去 **÷1_000_000**,否则日预算会约偏小 **一万倍**(例如 `15000` 应为 ¥150.00,误除微元会得到 ¥0.015)。同文件 **`spend`、`averageCpc`、`costPerConversion` 等已是「元」小数**,与 `budgetAmount` 单位不同,写 Excel/报告脚本时须分支处理。对应 `*.outline.txt` 内写有**同等硬提示**(`campaigns` 维度)。
81
- - 金额保留 2 位小数,带货币代码(如 `¥50.00 CNY`),`currencyCode` 从响应读取。跨币种账户分表。
82
- - CNY使用 ¥50,USD使用 $50 不要混用!!!
79
+ CLI 出口的所有 JSON / 表格金额已统一为**元**,关键字段:
80
+
81
+ | 命令 / Section | 元字段 |
82
+ | --------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------- |
83
+ | `ad campaigns --json` | `budget`(元,与 `--budget` 写参同口径) |
84
+ | `ad groups --json` | `maxCPCAmountYuan`、`targetCpaAmountYuan` |
85
+ | `google-analysis campaigns` 落盘 `campaigns-*.json` | `budgetAmountYuan`、`campaignTargetCpaYuan`、`maximizeConversionsTargetCpaYuan`;同行 `spend` / `averageCpc` / `costPerConversion` 也是元 |
86
+ | `keyword suggest --json` | `averageCpc`、`lowTopOfPageBid`、`highTopOfPageBid`;`bidAmountCurrency`(有 `-a` 为账户币;无 `-a` 为 USD,可选 `averageCpcCNY` 参考) |
87
+ | `balance` 等账户余额接口 | `remainingAccountBudget`(元) |
88
+
89
+ 旧字段 `budgetAmount`(分)、`maxCPCAmountDisplay`、`*Micros`(微元)**已不再落盘**,下游脚本无需做单位换算。金额保留 2 位小数,带货币代码(如 `¥50.00 CNY`、`$50.00 USD`),`currencyCode` 从响应读取,跨币种账户分表;细则见 `references/currency.md`。
83
90
 
84
91
  ### 预算建议
85
92
 
86
- 基于当前实际预算(如 `ad campaigns --json` 的 **`budget`(元)** 或报表中的 `*Display`/分析字段)、历史日均消耗、用户给的预算上限给出建议。数据不足以判断时,在报告里写明「建议区间需用户确认」而非直接给高风险数字。
93
+ 基于当前实际预算(如 `ad campaigns --json` 的 `budget`(元)或 `google-analysis campaigns-*.json` 的 `budgetAmountYuan`(元))、历史日均消耗、用户给的预算上限给出建议。数据不足以判断时,在报告里写明「建议区间需用户确认」而非直接给高风险数字。
87
94
 
88
95
  ---
89
96
 
@@ -108,38 +115,38 @@ siluzan-tso google-analysis -a <id> --exclude materials,gold-account --json-out
108
115
 
109
116
  ### 选项
110
117
 
111
- | 选项 | 说明 |
112
- | ------------------------ | ------------------------------------------------------------------------------------ |
113
- | `-a, --account <id>` | Google `mediaCustomerId`(必填) |
114
- | `--json-out <dir>` | 必填;每维一个 `<section>-<accountId>.json` + `manifest-<accountId>.json` |
115
- | `--sections <list>` | 仅执行指定维度(逗号分隔);省略=全部 23 个 |
116
- | `--exclude <list>` | 排除指定维度;与 `--sections` 可叠加 |
117
- | `--start` / `--end` | 统计区间(YYYY-MM-DD);省略=近 7 天截至昨天;`final-urls`/`campaign-types` 自动忽略 |
118
- | `--concurrency <n>` | 并发数,默认 5,上限 16 |
119
- | `--limit <n>` | 透传给 `keywords`/`search-terms`(默认 **200**,`orderByCost=true`) |
120
- | `--level <lvl>` | 透传给 `extensions`(Account/Campaign/Ad Group) |
121
- | `--audience-type <type>` | 透传给 `audience`(SystemDefined/UserDefined) |
122
- | `--no-order-by-cost` | 透传给 `keywords`/`search-terms` |
123
- | `--cost-greater <n>` | 仅 **`campaign-geo`**:网关 `costGreater`(整数,单位以后端为准) |
124
- | `--click-greater <n>` | 仅 **`campaign-geo`**:网关 `clickGreater` |
125
- | `--conversions-greater <n>` | 仅 **`campaign-geo`**:网关 `conversionsGreater` |
126
- | `--verbose` | 打印详细错误 |
118
+ | 选项 | 说明 |
119
+ | --------------------------- | ------------------------------------------------------------------------------------ |
120
+ | `-a, --account <id>` | Google `mediaCustomerId`(必填) |
121
+ | `--json-out <dir>` | 必填;每维一个 `<section>-<accountId>.json` + `manifest-<accountId>.json` |
122
+ | `--sections <list>` | 仅执行指定维度(逗号分隔);省略=全部 23 个 |
123
+ | `--exclude <list>` | 排除指定维度;与 `--sections` 可叠加 |
124
+ | `--start` / `--end` | 统计区间(YYYY-MM-DD);省略=近 7 天截至昨天;`final-urls`/`campaign-types` 自动忽略 |
125
+ | `--concurrency <n>` | 并发数,默认 5,上限 16 |
126
+ | `--limit <n>` | 透传给 `keywords`/`search-terms`(默认 **200**,`orderByCost=true`) |
127
+ | `--level <lvl>` | 透传给 `extensions`(Account/Campaign/Ad Group) |
128
+ | `--audience-type <type>` | 透传给 `audience`(SystemDefined/UserDefined) |
129
+ | `--no-order-by-cost` | 透传给 `keywords`/`search-terms` |
130
+ | `--cost-greater <n>` | 仅 **`campaign-geo`**:网关 `costGreater`(整数,单位以后端为准) |
131
+ | `--click-greater <n>` | 仅 **`campaign-geo`**:网关 `clickGreater` |
132
+ | `--conversions-greater <n>` | 仅 **`campaign-geo`**:网关 `conversionsGreater` |
133
+ | `--verbose` | 打印详细错误 |
127
134
 
128
135
  ### 维度列表(23 个)
129
136
 
130
137
  | 维度 | 说明 |
131
138
  | -------------------- | --------------------------------------------------------------------------------------------------- |
132
139
  | `overview` | 总览(实时,可查当天;当天高消耗账号排行首选) |
133
- | `keywords` | 关键词;默认 `limit=200`、`orderByCost=true`;可用 `--limit`、`--no-order-by-cost` 覆盖 |
134
- | `search-terms` | 搜索词;默认 `limit=200`、`orderByCost=true`;可用 `--limit`、`--no-order-by-cost` 覆盖 |
140
+ | `keywords` | 关键词;默认 `limit=200`、`orderByCost=true`;可用 `--limit`、`--no-order-by-cost` 覆盖 |
141
+ | `search-terms` | 搜索词;默认 `limit=200`、`orderByCost=true`;可用 `--limit`、`--no-order-by-cost` 覆盖 |
135
142
  | `campaigns` | 广告系列 |
136
143
  | `campaign-hour` | 系列按小时(根为 JSON 数组) |
137
144
  | `ads` | 广告;与 `ad list` 同源 |
138
145
  | `extensions` | 附加信息;可选 `--level` |
139
146
  | `devices` | 设备分布(账户级 `DeviceSectionData`) |
140
- | `geographic` | 地域分布(账户级 `GeographicSectionData`,网关侧常按国家聚合) |
147
+ | `geographic` | 地域分布(账户级 `GeographicSectionData`,网关侧常按国家聚合) |
141
148
  | `campaign-geo` | 广告系列维度地理 (可选 `--cost-greater` / `--click-greater` / `--conversions-greater` 与网关一致) |
142
- | `campaign-device` | 广告系列维度设备 (行可含系列/组) |
149
+ | `campaign-device` | 广告系列维度设备 (行可含系列/组) |
143
150
  | `audience` | 受众;可选 `--audience-type` |
144
151
  | `asset-images` | 图片素材 |
145
152
  | `videos` | 视频 |
@@ -191,19 +198,19 @@ siluzan-tso google-analysis -a <id> --exclude materials,gold-account --json-out
191
198
 
192
199
  CLI 在落盘前为以下维度自动补「中文译名字段」(**原英文字段保留**,便于排错;字典未命中时该字段缺省):
193
200
 
194
- | 维度 | 源字段(英文) | 新增字段(中文) | 字典源 |
195
- | ------------- | ------------------ | ---------------------- | --------------------------------------------------------------- |
196
- | `keywords` | `keywordMatchType` | `keywordMatchTypeZh` | `match-type-en2zh.json`(覆盖 `BROAD/PHRASE/EXACT` 大小写写法) |
197
- | `search-terms`| `matchType` | `matchTypeZh` | `match-type-en2zh.json` |
198
- | `campaign-geo`| `countryOrRegion` | `countryOrRegionZh` | `geo-en2zh.json`(覆盖 105 个国家/地区) |
201
+ | 维度 | 源字段(英文) | 新增字段(中文) | 字典源 |
202
+ | -------------- | ------------------ | -------------------- | --------------------------------------------------------------- |
203
+ | `keywords` | `keywordMatchType` | `keywordMatchTypeZh` | `match-type-en2zh.json`(覆盖 `BROAD/PHRASE/EXACT` 大小写写法) |
204
+ | `search-terms` | `matchType` | `matchTypeZh` | `match-type-en2zh.json` |
205
+ | `campaign-geo` | `countryOrRegion` | `countryOrRegionZh` | `geo-en2zh.json`(覆盖 105 个国家/地区) |
199
206
 
200
207
  写 Excel / 报表脚本可直接读 `keywordMatchTypeZh` / `matchTypeZh` / `countryOrRegionZh` 输出中文,**不要**自己维护字典或在线翻译。判断覆盖率用 `if (row.xxxZh)` 即可。
201
208
 
202
209
  ### CampaignSectionData 关键字段
203
210
 
204
- `campaigns[]` 每行额外包含:`conversionsValue`、`conversionsValuePerCost`(`spend ≤ 0` 时为 0)、`campaignTargetCpaMicros`、`maximizeConversionsTargetCpaMicros`、`manualCpcEnhancedCpcEnabled`、`percentCpcEnhancedCpcEnabled`。micros 字段须按 `currencyCode` 换算,不要直接当元展示。
211
+ `campaigns[]` 每行额外包含:`conversionsValue`、`conversionsValuePerCost`(`spend ≤ 0` 时为 0)、`campaignTargetCpaYuan`、`maximizeConversionsTargetCpaYuan`、`manualCpcEnhancedCpcEnabled`、`percentCpcEnhancedCpcEnabled`。所有金额字段(`*Yuan` 后缀)已统一为元,可直接展示,无需换算。
205
212
 
206
- **`budgetAmount`(再强调)**:**分**(÷100 元),**禁止 ÷1e6**;与 `tso-cli/src/commands/ad/_shared.ts` 中系列预算读写「分」口径一致。Python/Excel 脚本示例:`日预算元 = (row.get("budgetAmount") or 0) / 100`。
213
+ 日预算字段为 `budgetAmountYuan`(元),脚本可直接 `row.get("budgetAmountYuan")`;网关原始 `budgetAmount`(分)已不再落盘。
207
214
 
208
215
  ### campaign-hour 字段
209
216