siluzan-tso-cli 1.1.28-beta.1 → 1.1.28-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 +1 -1
- package/dist/index.js +158 -74
- package/dist/skill/SKILL.md +5 -3
- package/dist/skill/_meta.json +2 -2
- package/dist/skill/assets/meta-period-report-rules.md +169 -0
- package/dist/skill/assets/meta-period-report.schema.json +34 -5
- package/dist/skill/references/README.md +2 -0
- package/dist/skill/references/accounts/accounts.md +40 -11
- package/dist/skill/references/analytics/account-analytics.md +8 -7
- package/dist/skill/references/analytics/facebook-analysis-guide.md +49 -15
- package/dist/skill/references/analytics/website-diagnosis-guide.md +1 -1
- package/dist/skill/references/core/agent-conventions.md +1 -1
- package/dist/skill/references/core/deliverable-preflight.md +3 -2
- package/dist/skill/references/core/playbooks.md +15 -9
- package/dist/skill/references/core/tips.md +4 -2
- package/dist/skill/references/core/workflows.md +25 -8
- package/dist/skill/report-templates/README.md +2 -2
- package/dist/skill/report-templates/REPORT-WORKFLOW.md +15 -5
- package/dist/skill/report-templates/meta-period-report-excel.md +27 -9
- package/dist/skill/report-templates/meta-period-report.html +101 -1
- package/dist/skill/report-templates/meta-period-report.md +72 -27
- package/dist/skill/scripts/install.ps1 +1 -1
- package/dist/skill/scripts/install.sh +1 -1
- package/eval/cases/uj-ops-google-accounts-list-normal.scenario.json +4 -1
- package/package.json +1 -1
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.28-beta.
|
|
54
|
+
> **注意**:当前为测试版(1.1.28-beta.3),供内部测试使用。正式发布后安装命令将改为 `npm install -g siluzan-tso-cli`。
|
|
55
55
|
|
|
56
56
|
| 助手 | 建议 `--ai` |
|
|
57
57
|
| ----------------------- | ------------------------------------ |
|
package/dist/index.js
CHANGED
|
@@ -102729,7 +102729,7 @@ init_dist();
|
|
|
102729
102729
|
import * as fs18 from "fs";
|
|
102730
102730
|
import * as path26 from "path";
|
|
102731
102731
|
import { fileURLToPath as fileURLToPath6 } from "url";
|
|
102732
|
-
import { Command } from "commander";
|
|
102732
|
+
import { Command as Command2 } from "commander";
|
|
102733
102733
|
|
|
102734
102734
|
// src/commands/login/urls.ts
|
|
102735
102735
|
init_defaults();
|
|
@@ -106072,6 +106072,55 @@ async function writeReportAnalysisSnapshot(params) {
|
|
|
106072
106072
|
|
|
106073
106073
|
// src/commands/report/main.ts
|
|
106074
106074
|
init_version();
|
|
106075
|
+
|
|
106076
|
+
// src/commands/facebook-analysis/fetch.ts
|
|
106077
|
+
init_auth();
|
|
106078
|
+
function normalizeFacebookAccountId(raw) {
|
|
106079
|
+
const t = raw.trim();
|
|
106080
|
+
if (!t) {
|
|
106081
|
+
console.error("\n\u274C --account \u4E0D\u80FD\u4E3A\u7A7A\u3002\n");
|
|
106082
|
+
process.exit(1);
|
|
106083
|
+
}
|
|
106084
|
+
const digits = t.startsWith("act_") ? t.slice(4) : t;
|
|
106085
|
+
if (!/^\d+$/.test(digits)) {
|
|
106086
|
+
console.error(
|
|
106087
|
+
"\n\u274C --account \u987B\u4E3A Facebook \u5E7F\u544A\u8D26\u6237 ID\uFF08\u6570\u5B57 mediaCustomerId\uFF0C\u6216 act_<\u6570\u5B57>\uFF0C\u4E0E list-accounts -m MetaAd \u4E00\u81F4\uFF09\u3002\n"
|
|
106088
|
+
);
|
|
106089
|
+
process.exit(1);
|
|
106090
|
+
}
|
|
106091
|
+
return { apiId: `act_${digits}`, manifestId: digits };
|
|
106092
|
+
}
|
|
106093
|
+
function resolveFacebookDateRange(start, end) {
|
|
106094
|
+
if (start && end) return { startDate: start, endDate: end };
|
|
106095
|
+
if (!start && !end) {
|
|
106096
|
+
const endD = /* @__PURE__ */ new Date();
|
|
106097
|
+
endD.setDate(endD.getDate() - 1);
|
|
106098
|
+
const startD = new Date(endD);
|
|
106099
|
+
startD.setDate(startD.getDate() - 6);
|
|
106100
|
+
const fmt2 = (d) => d.toISOString().slice(0, 10);
|
|
106101
|
+
return { startDate: fmt2(startD), endDate: fmt2(endD) };
|
|
106102
|
+
}
|
|
106103
|
+
console.error("\n\u274C --start \u4E0E --end \u987B\u540C\u65F6\u4F20\u5165\uFF0C\u6216\u540C\u65F6\u7701\u7565\u4EE5\u4F7F\u7528\u9ED8\u8BA4\u8FD1 7 \u5929\uFF08\u622A\u81F3\u6628\u5929\uFF09\u3002\n");
|
|
106104
|
+
process.exit(1);
|
|
106105
|
+
}
|
|
106106
|
+
function facebookReportingUrl(config, apiId, segment, query) {
|
|
106107
|
+
const q = query ? query.startsWith("?") ? query : `?${query}` : "";
|
|
106108
|
+
return `${config.apiBaseUrl}/reporting/media-account/FacebookAds/${encodeURIComponent(apiId)}/${segment}${q}`;
|
|
106109
|
+
}
|
|
106110
|
+
async function fetchFacebookSectionPayload(def, opts, config, apiId) {
|
|
106111
|
+
const { startDate, endDate } = resolveFacebookDateRange(opts.start, opts.end);
|
|
106112
|
+
const params = new URLSearchParams({ startDate, endDate });
|
|
106113
|
+
if (def.countryLimitOption && opts.limit !== void 0 && Number.isFinite(opts.limit)) {
|
|
106114
|
+
params.set("limit", String(Math.max(1, Math.floor(opts.limit))));
|
|
106115
|
+
}
|
|
106116
|
+
const url = facebookReportingUrl(config, apiId, def.segment, params.toString());
|
|
106117
|
+
return apiFetch2(url, config, {}, opts.verbose ?? false);
|
|
106118
|
+
}
|
|
106119
|
+
function endpointHintForFacebookSection(def, apiId) {
|
|
106120
|
+
return `GET \u2026/FacebookAds/${apiId}/${def.segment}`;
|
|
106121
|
+
}
|
|
106122
|
+
|
|
106123
|
+
// src/commands/report/main.ts
|
|
106075
106124
|
async function runReportList(opts) {
|
|
106076
106125
|
if (!VALID_MEDIA_TYPES5.includes(opts.media)) {
|
|
106077
106126
|
console.error(`
|
|
@@ -106173,30 +106222,61 @@ function fmtNum2(n, digits = 2) {
|
|
|
106173
106222
|
parts[0] = parts[0].replace(/\B(?=(\d{3})+(?!\d))/g, ",");
|
|
106174
106223
|
return parts.join(".");
|
|
106175
106224
|
}
|
|
106225
|
+
function metaOverviewManifestId(raw) {
|
|
106226
|
+
const t = raw.trim();
|
|
106227
|
+
if (t.startsWith("act_")) return t.slice(4);
|
|
106228
|
+
return t;
|
|
106229
|
+
}
|
|
106230
|
+
function buildMetaOverviewUrl(config, accountId, mediaAccountType, startDate, endDate) {
|
|
106231
|
+
const { apiId, manifestId } = normalizeFacebookAccountId(accountId);
|
|
106232
|
+
const params = new URLSearchParams({ startDate, endDate }).toString();
|
|
106233
|
+
if (mediaAccountType === "FacebookAds") {
|
|
106234
|
+
return facebookReportingUrl(config, apiId, "OverviewSectionData", params);
|
|
106235
|
+
}
|
|
106236
|
+
return `${config.apiBaseUrl}/reporting/media-account/MetaAd/${manifestId}/OverviewSectionData?${params}`;
|
|
106237
|
+
}
|
|
106176
106238
|
async function runReportMetaOverview(opts) {
|
|
106177
|
-
const
|
|
106178
|
-
|
|
106179
|
-
|
|
106239
|
+
const rawAccount = opts.account.trim();
|
|
106240
|
+
let manifestId;
|
|
106241
|
+
try {
|
|
106242
|
+
manifestId = normalizeFacebookAccountId(rawAccount).manifestId;
|
|
106243
|
+
} catch {
|
|
106244
|
+
console.error(
|
|
106245
|
+
"\n\u274C --account \u987B\u4E3A\u6570\u5B57 mediaCustomerId \u6216 act_<\u6570\u5B57>\uFF08\u4E0E list-accounts -m MetaAd \u4E00\u81F4\uFF09\u3002\n"
|
|
106246
|
+
);
|
|
106180
106247
|
process.exit(1);
|
|
106181
106248
|
}
|
|
106182
106249
|
const { startDate, endDate } = resolveMetaOverviewDateRange(opts.startDate, opts.endDate);
|
|
106183
106250
|
const config = loadConfig(opts.token);
|
|
106184
|
-
const
|
|
106185
|
-
const
|
|
106251
|
+
const accountDetail = await lookupMetaAccountForOverview(config, manifestId, opts.verbose);
|
|
106252
|
+
const mediaAccountType = accountDetail?.mediaAccountType ?? "FacebookAds";
|
|
106253
|
+
const url = buildMetaOverviewUrl(config, rawAccount, mediaAccountType, startDate, endDate);
|
|
106186
106254
|
let data;
|
|
106187
106255
|
try {
|
|
106188
106256
|
data = await apiFetch2(url, config, {}, opts.verbose);
|
|
106189
106257
|
} catch (err) {
|
|
106190
|
-
|
|
106191
|
-
|
|
106258
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
106259
|
+
if (msg.includes("403")) {
|
|
106260
|
+
console.error(
|
|
106261
|
+
`
|
|
106262
|
+
\u274C \u8BF7\u6C42\u5931\u8D25\uFF1AHTTP 403
|
|
106263
|
+
\u8D26\u6237 ${manifestId} \u7684 mediaAccountType=${mediaAccountType ?? "\u672A\u77E5"}\u3002
|
|
106264
|
+
Facebook \u6388\u6743\u8D26\u6237\uFF08FacebookAds\uFF09\u987B\u8D70 FacebookAds/act_ \u8DEF\u5F84\uFF1B\u4E1D\u8DEF\u8D5E Meta \u5F00\u6237\u8D70 MetaAd \u8DEF\u5F84\u3002
|
|
106265
|
+
\u5468\u671F/\u591A\u7EF4\u5EA6\u62A5\u544A\u8BF7\u6539\u7528\uFF1Asiluzan-tso facebook-analysis -a ${manifestId} --sections overview --json-out <dir>
|
|
106266
|
+
`
|
|
106267
|
+
);
|
|
106268
|
+
} else {
|
|
106269
|
+
console.error(`
|
|
106270
|
+
\u274C \u8BF7\u6C42\u5931\u8D25\uFF1A${msg}
|
|
106192
106271
|
`);
|
|
106272
|
+
}
|
|
106193
106273
|
process.exit(1);
|
|
106194
106274
|
}
|
|
106195
106275
|
if (opts.jsonOut) {
|
|
106196
106276
|
const summary = await writeReportAnalysisSnapshot({
|
|
106197
106277
|
snapshotDir: opts.jsonOut,
|
|
106198
106278
|
section: "meta-overview",
|
|
106199
|
-
accountId:
|
|
106279
|
+
accountId: manifestId,
|
|
106200
106280
|
dateRange: { start: startDate, end: endDate },
|
|
106201
106281
|
payload: data,
|
|
106202
106282
|
cliVersion: getCurrentVersion2()
|
|
@@ -106211,7 +106291,7 @@ async function runReportMetaOverview(opts) {
|
|
|
106211
106291
|
const optRaw = row.optimizationScore;
|
|
106212
106292
|
const optPct = typeof optRaw === "number" && optRaw <= 1 && optRaw >= 0 ? (optRaw * 100).toFixed(0) : typeof optRaw === "number" ? String(optRaw) : "\u2014";
|
|
106213
106293
|
console.log(`
|
|
106214
|
-
\u2705 Meta \u8D26\u6237\u603B\u89C8 ${
|
|
106294
|
+
\u2705 Meta \u8D26\u6237\u603B\u89C8 ${manifestId} \u533A\u95F4 ${startDate} ~ ${endDate}
|
|
106215
106295
|
`);
|
|
106216
106296
|
console.log(
|
|
106217
106297
|
` \u8D27\u5E01 ${currency} \u4F18\u5316\u5F97\u5206 ${optPct}${typeof optRaw === "number" && optRaw <= 1 ? "%\uFF080\u20131 \u5DF2\u6362\u7B97\uFF09" : ""} \u603B\u6D88\u8017 ${fmtNum2(typeof row.totalCost === "number" ? row.totalCost : cur.spend)} \u4F59\u989D ${fmtNum2(typeof row.remainingAccountBudget === "number" ? row.remainingAccountBudget : void 0)} \u6D3B\u8DC3\u5929\u6570 ${row.activeDays ?? "\u2014"} \u65E5\u5747\u6D88\u8017 ${fmtNum2(typeof row.averageDailyCost === "number" ? row.averageDailyCost : void 0)}
|
|
@@ -106227,6 +106307,42 @@ async function runReportMetaOverview(opts) {
|
|
|
106227
106307
|
function toSlashDate(date) {
|
|
106228
106308
|
return date.replace(/-/g, "/");
|
|
106229
106309
|
}
|
|
106310
|
+
async function lookupMetaAccountForOverview(config, manifestId, verbose = false) {
|
|
106311
|
+
for (const idCandidate of [manifestId, `act_${manifestId}`]) {
|
|
106312
|
+
const params = new URLSearchParams({
|
|
106313
|
+
MediaTypes: "MetaAd|FacebookAds",
|
|
106314
|
+
advStatus: "",
|
|
106315
|
+
mediaAccountState: "Approved,Linked",
|
|
106316
|
+
isForce: "false",
|
|
106317
|
+
pageNum: "1",
|
|
106318
|
+
pageSize: "20",
|
|
106319
|
+
mediaCustomerIds: idCandidate
|
|
106320
|
+
});
|
|
106321
|
+
try {
|
|
106322
|
+
const data = await apiFetch2(
|
|
106323
|
+
`${config.apiBaseUrl}/query/media-account/SearchMediaAcountByCriteria?${params}`,
|
|
106324
|
+
config,
|
|
106325
|
+
{},
|
|
106326
|
+
verbose
|
|
106327
|
+
);
|
|
106328
|
+
for (const item of data?.mas ?? []) {
|
|
106329
|
+
const ma = item.ma ?? {};
|
|
106330
|
+
const cidDigits = metaOverviewManifestId(String(ma.mediaCustomerId ?? ""));
|
|
106331
|
+
if (cidDigits === manifestId) {
|
|
106332
|
+
return {
|
|
106333
|
+
entityId: String(ma.entityId ?? ""),
|
|
106334
|
+
mediaCustomerId: String(ma.mediaCustomerId ?? ""),
|
|
106335
|
+
mediaCustomerName: String(ma.mediaCustomerName ?? ""),
|
|
106336
|
+
mediaAccountType: String(ma.mediaAccountType ?? ""),
|
|
106337
|
+
mediaAccountGroupId: String(ma.mediaAccountGroupId ?? "")
|
|
106338
|
+
};
|
|
106339
|
+
}
|
|
106340
|
+
}
|
|
106341
|
+
} catch {
|
|
106342
|
+
}
|
|
106343
|
+
}
|
|
106344
|
+
return void 0;
|
|
106345
|
+
}
|
|
106230
106346
|
async function lookupAccountsByCustomerIds(apiBaseUrl, config, mediaType, customerIds, verbose = false) {
|
|
106231
106347
|
const params = new URLSearchParams({
|
|
106232
106348
|
MediaType: mediaType,
|
|
@@ -106239,14 +106355,17 @@ async function lookupAccountsByCustomerIds(apiBaseUrl, config, mediaType, custom
|
|
|
106239
106355
|
{},
|
|
106240
106356
|
verbose
|
|
106241
106357
|
);
|
|
106242
|
-
const idSet = new Set(
|
|
106358
|
+
const idSet = new Set(
|
|
106359
|
+
customerIds.map((id) => metaOverviewManifestId(id.trim())).filter(Boolean)
|
|
106360
|
+
);
|
|
106243
106361
|
const result = /* @__PURE__ */ new Map();
|
|
106244
106362
|
const items = Array.isArray(data) ? data : [];
|
|
106245
106363
|
for (const item of items) {
|
|
106246
106364
|
const ma = item.ma ?? {};
|
|
106247
106365
|
const cid = String(ma.mediaCustomerId ?? "");
|
|
106248
|
-
|
|
106249
|
-
|
|
106366
|
+
const cidDigits = metaOverviewManifestId(cid);
|
|
106367
|
+
if (idSet.has(cidDigits)) {
|
|
106368
|
+
result.set(cidDigits, {
|
|
106250
106369
|
entityId: String(ma.entityId ?? ""),
|
|
106251
106370
|
mediaCustomerId: cid,
|
|
106252
106371
|
mediaCustomerName: String(ma.mediaCustomerName ?? ""),
|
|
@@ -107480,7 +107599,9 @@ function register13(program2) {
|
|
|
107480
107599
|
verbose: opts.verbose
|
|
107481
107600
|
});
|
|
107482
107601
|
});
|
|
107483
|
-
reportCmd.command("meta-overview").description(
|
|
107602
|
+
reportCmd.command("meta-overview").description(
|
|
107603
|
+
"Meta \u8D26\u6237\u5206\u6790\u603B\u89C8\uFF08OverviewSectionData\uFF1BFacebookAds \u6388\u6743\u6237\u8D70 FacebookAds/act_\uFF0CMetaAd \u5F00\u6237\u8D70 MetaAd\uFF09"
|
|
107604
|
+
).requiredOption("-a, --account <id>", "Meta \u5E7F\u544A\u8D26\u6237 mediaCustomerId\uFF08\u6570\u5B57\u6216 act_<\u6570\u5B57>\uFF09").option("--start <date>", "\u5F00\u59CB\u65E5\u671F YYYY-MM-DD\uFF08\u4E0E --end \u540C\u4F20\u6216\u540C\u7701\u7565\uFF09").option("--end <date>", "\u7ED3\u675F\u65E5\u671F YYYY-MM-DD").option("--token <token>", "Auth Token").option(
|
|
107484
107605
|
"--json-out <path>",
|
|
107485
107606
|
"\u843D\u76D8\u76EE\u5F55\u5E76\u66F4\u65B0 report-manifest[-<accountId>].json\uFF1B\u6587\u4EF6\u540D\u4E3A `<section>[-<accountId>].json`\uFF1Bstdout \u4E00\u884C\u6458\u8981 JSON\uFF0C\u542B outlineFile\uFF08TS \u5F0F\u7C7B\u578B\u5728 `*.outline.txt`\uFF09"
|
|
107486
107607
|
).option("--verbose", "\u8BE6\u7EC6\u9519\u8BEF\u4FE1\u606F", false).action(async (opts) => {
|
|
@@ -121435,6 +121556,8 @@ function register25(program2) {
|
|
|
121435
121556
|
console.log(" CLI \u62C9\u6570\uFF1A");
|
|
121436
121557
|
console.log(" siluzan-tso website-diagnosis collect --url <url> --json-out ./snap-web");
|
|
121437
121558
|
console.log(" siluzan-tso website-diagnosis performance --url <url> --json-out ./snap-web");
|
|
121559
|
+
console.log(" \u9ED8\u8BA4\u4EA4\u4ED8 HTML\uFF1A");
|
|
121560
|
+
console.log(" siluzan-tso website-diagnosis render --data ./diagnosis.json --collect ./snap-web/<collect>.json");
|
|
121438
121561
|
console.log(" Skill\uFF1Areferences/analytics/website-diagnosis-guide.md\n");
|
|
121439
121562
|
console.log(" \u3010\u8D26\u6237\u5217\u8868 ARIT \u5206\u3011");
|
|
121440
121563
|
console.log(" list-accounts \u4F1A\u8865\u5145 ma.diagnoseReports \u4E2D reportSource=ARIT \u7684\u5F97\u5206\uFF1B");
|
|
@@ -121633,7 +121756,7 @@ async function runWebsiteDiagnosisCollect(opts) {
|
|
|
121633
121756
|
htmlPreview,
|
|
121634
121757
|
...htmlFull ? { htmlContent: htmlFull } : {},
|
|
121635
121758
|
...htmlError ? { htmlError } : {},
|
|
121636
|
-
agentHint: "1) \u6309 website-diagnosis-rules.md \u751F\u6210\u8BCA\u65AD JSON\uFF1B2) siluzan-tso website-diagnosis render --data <diagnosis.json> --collect <\u672C\u6587\u4EF6> \
|
|
121759
|
+
agentHint: "1) \u6309 website-diagnosis-rules.md \u751F\u6210\u8BCA\u65AD JSON\uFF1B2) siluzan-tso website-diagnosis render --data <diagnosis.json> --collect <\u672C\u6587\u4EF6> \u9ED8\u8BA4\u4EA7\u51FA HTML \u7EC8\u7A3F website-diagnosis-report.html\uFF08\u7981\u6B62\u4EC5 Markdown/JSON \u4EA4\u4ED8\uFF09\u3002"
|
|
121637
121760
|
};
|
|
121638
121761
|
if (await emitCliJsonOrSnapshot(
|
|
121639
121762
|
{ jsonOut: opts.jsonOut },
|
|
@@ -121756,7 +121879,7 @@ async function runWebsiteDiagnosisRender(opts) {
|
|
|
121756
121879
|
// src/commands/website-diagnosis/register.ts
|
|
121757
121880
|
function registerWebsiteDiagnosisCommands(program2) {
|
|
121758
121881
|
const root = program2.command("website-diagnosis").description(
|
|
121759
|
-
"\u7F51\u7AD9\u8BCA\u65AD\uFF1ALighthouse \u6027\u80FD\u3001HTML \u91C7\u96C6\
|
|
121882
|
+
"\u7F51\u7AD9\u8BCA\u65AD\uFF1ALighthouse \u6027\u80FD\u3001HTML \u91C7\u96C6\u3001ARIT \u5386\u53F2\u5F97\u5206\uFF1B\u9ED8\u8BA4\u4EA4\u4ED8 HTML \u62A5\u544A\uFF08render \u5B50\u547D\u4EE4\uFF09"
|
|
121760
121883
|
);
|
|
121761
121884
|
root.command("performance").description("\u62C9\u53D6 Lighthouse \u6027\u80FD\u6570\u636E\uFF08GET WebsiteDiagnosisReports/performance\uFF09").requiredOption("--url <url>", "\u7F51\u7AD9 URL\uFF08\u53EF\u7701\u7565 https://\uFF0C\u81EA\u52A8\u8865\u5168\uFF09").option("--token <token>", "JWT\uFF08\u9ED8\u8BA4\u8BFB config / \u73AF\u5883\u53D8\u91CF\uFF09").option("--verbose", "\u6253\u5370\u8BF7\u6C42\u8BE6\u60C5").option("--json-out <dir>", "\u843D\u76D8 cli-manifest + JSON").action(async (opts) => {
|
|
121762
121885
|
await runWebsiteDiagnosisPerformance({
|
|
@@ -121789,7 +121912,7 @@ function registerWebsiteDiagnosisCommands(program2) {
|
|
|
121789
121912
|
}
|
|
121790
121913
|
);
|
|
121791
121914
|
root.command("render").description(
|
|
121792
|
-
"\u6839\u636E\u8BCA\u65AD JSON \u751F\u6210\
|
|
121915
|
+
"\u6839\u636E\u8BCA\u65AD JSON \u751F\u6210 HTML \u7EC8\u7A3F\uFF08\u9ED8\u8BA4\u4EA4\u4ED8\u683C\u5F0F\uFF0Cwebsite-diagnosis-report.html\uFF09"
|
|
121793
121916
|
).requiredOption("--data <file>", "\u8BCA\u65AD\u7ED3\u679C JSON\uFF08getWebsiteDiagnosisData \u540C\u7ED3\u6784\uFF09").option("--collect <file>", "\u53EF\u9009\uFF1Acollect \u843D\u76D8 JSON\uFF0C\u7528\u4E8E\u5408\u5E76 lighthouse").option("--out <file>", "\u8F93\u51FA HTML \u8DEF\u5F84\uFF08\u9ED8\u8BA4\u540C --data \u76EE\u5F55\uFF09").action(async (opts) => {
|
|
121794
121917
|
await runWebsiteDiagnosisRender({
|
|
121795
121918
|
dataFile: opts.data,
|
|
@@ -122429,52 +122552,8 @@ var FACEBOOK_SECTION_ALIASES = {
|
|
|
122429
122552
|
videos: "material"
|
|
122430
122553
|
};
|
|
122431
122554
|
|
|
122432
|
-
// src/commands/facebook-analysis/
|
|
122433
|
-
|
|
122434
|
-
function normalizeFacebookAccountId(raw) {
|
|
122435
|
-
const t = raw.trim();
|
|
122436
|
-
if (!t) {
|
|
122437
|
-
console.error("\n\u274C --account \u4E0D\u80FD\u4E3A\u7A7A\u3002\n");
|
|
122438
|
-
process.exit(1);
|
|
122439
|
-
}
|
|
122440
|
-
const digits = t.startsWith("act_") ? t.slice(4) : t;
|
|
122441
|
-
if (!/^\d+$/.test(digits)) {
|
|
122442
|
-
console.error(
|
|
122443
|
-
"\n\u274C --account \u987B\u4E3A Facebook \u5E7F\u544A\u8D26\u6237 ID\uFF08\u6570\u5B57 mediaCustomerId\uFF0C\u6216 act_<\u6570\u5B57>\uFF0C\u4E0E list-accounts -m MetaAd \u4E00\u81F4\uFF09\u3002\n"
|
|
122444
|
-
);
|
|
122445
|
-
process.exit(1);
|
|
122446
|
-
}
|
|
122447
|
-
return { apiId: `act_${digits}`, manifestId: digits };
|
|
122448
|
-
}
|
|
122449
|
-
function resolveFacebookDateRange(start, end) {
|
|
122450
|
-
if (start && end) return { startDate: start, endDate: end };
|
|
122451
|
-
if (!start && !end) {
|
|
122452
|
-
const endD = /* @__PURE__ */ new Date();
|
|
122453
|
-
endD.setDate(endD.getDate() - 1);
|
|
122454
|
-
const startD = new Date(endD);
|
|
122455
|
-
startD.setDate(startD.getDate() - 6);
|
|
122456
|
-
const fmt2 = (d) => d.toISOString().slice(0, 10);
|
|
122457
|
-
return { startDate: fmt2(startD), endDate: fmt2(endD) };
|
|
122458
|
-
}
|
|
122459
|
-
console.error("\n\u274C --start \u4E0E --end \u987B\u540C\u65F6\u4F20\u5165\uFF0C\u6216\u540C\u65F6\u7701\u7565\u4EE5\u4F7F\u7528\u9ED8\u8BA4\u8FD1 7 \u5929\uFF08\u622A\u81F3\u6628\u5929\uFF09\u3002\n");
|
|
122460
|
-
process.exit(1);
|
|
122461
|
-
}
|
|
122462
|
-
function facebookReportingUrl(config, apiId, segment, query) {
|
|
122463
|
-
const q = query ? query.startsWith("?") ? query : `?${query}` : "";
|
|
122464
|
-
return `${config.apiBaseUrl}/reporting/media-account/FacebookAds/${encodeURIComponent(apiId)}/${segment}${q}`;
|
|
122465
|
-
}
|
|
122466
|
-
async function fetchFacebookSectionPayload(def, opts, config, apiId) {
|
|
122467
|
-
const { startDate, endDate } = resolveFacebookDateRange(opts.start, opts.end);
|
|
122468
|
-
const params = new URLSearchParams({ startDate, endDate });
|
|
122469
|
-
if (def.countryLimitOption && opts.limit !== void 0 && Number.isFinite(opts.limit)) {
|
|
122470
|
-
params.set("limit", String(Math.max(1, Math.floor(opts.limit))));
|
|
122471
|
-
}
|
|
122472
|
-
const url = facebookReportingUrl(config, apiId, def.segment, params.toString());
|
|
122473
|
-
return apiFetch2(url, config, {}, opts.verbose ?? false);
|
|
122474
|
-
}
|
|
122475
|
-
function endpointHintForFacebookSection(def, apiId) {
|
|
122476
|
-
return `GET \u2026/FacebookAds/${apiId}/${def.segment}`;
|
|
122477
|
-
}
|
|
122555
|
+
// src/commands/facebook-analysis/register-cli.ts
|
|
122556
|
+
import { Command } from "commander";
|
|
122478
122557
|
|
|
122479
122558
|
// src/commands/facebook-analysis/render-report.ts
|
|
122480
122559
|
import fs17 from "fs";
|
|
@@ -122746,20 +122825,19 @@ async function runFacebookAnalysisRender(opts) {
|
|
|
122746
122825
|
await fsPromises2.writeFile(outPath, injectReportData2(html, data), "utf8");
|
|
122747
122826
|
const runtimeSrc = metaPeriodReportRuntimePath();
|
|
122748
122827
|
const runtimeOut = path24.join(outDir, TEMPLATE_BASENAMES2.runtime);
|
|
122828
|
+
let runtimeCopied = false;
|
|
122749
122829
|
try {
|
|
122750
122830
|
await fsPromises2.copyFile(runtimeSrc, runtimeOut);
|
|
122831
|
+
runtimeCopied = true;
|
|
122751
122832
|
} catch {
|
|
122752
|
-
console.error(`
|
|
122753
|
-
\u274C \u672A\u627E\u5230\u62A5\u544A\u8FD0\u884C\u65F6\uFF1A${runtimeSrc}
|
|
122754
|
-
\u8BF7\u5148\u6267\u884C npm run build
|
|
122755
|
-
`);
|
|
122756
|
-
process.exit(1);
|
|
122757
122833
|
}
|
|
122758
122834
|
console.log(`
|
|
122759
122835
|
\u2705 Meta/Facebook \u5468\u671F\u5206\u6790 HTML \u62A5\u544A\u5DF2\u751F\u6210\uFF1A${outPath}
|
|
122760
122836
|
`);
|
|
122761
|
-
|
|
122837
|
+
if (runtimeCopied) {
|
|
122838
|
+
console.log(` \u8FD0\u884C\u65F6\u811A\u672C\uFF1A${runtimeOut}
|
|
122762
122839
|
`);
|
|
122840
|
+
}
|
|
122763
122841
|
console.log("\u5728\u6D4F\u89C8\u5668\u4E2D\u6253\u5F00 HTML \u6587\u4EF6\u5373\u53EF\u67E5\u770B\u56FE\u8868\u4E0E\u5B8C\u6574\u7AE0\u8282\uFF08\u9700\u8054\u7F51\u52A0\u8F7D Chart.js CDN\uFF09\u3002\n");
|
|
122764
122842
|
}
|
|
122765
122843
|
|
|
@@ -122887,10 +122965,9 @@ async function runAllFacebookSections(opts) {
|
|
|
122887
122965
|
}
|
|
122888
122966
|
|
|
122889
122967
|
// src/commands/facebook-analysis/register-cli.ts
|
|
122890
|
-
function
|
|
122891
|
-
|
|
122892
|
-
|
|
122893
|
-
"Facebook Ads \u8D26\u6237\u5206\u6790\u6279\u91CF\u62C9\u53D6\uFF08TSO reporting/media-account/FacebookAds/\u2026\uFF0C7 \u4E2A Section\uFF09"
|
|
122968
|
+
function registerBatchCommand(sectionHelp) {
|
|
122969
|
+
return new Command("run").description(
|
|
122970
|
+
"\u6279\u91CF\u62C9\u53D6 TSO reporting/media-account/FacebookAds Section \u6570\u636E\uFF08\u7701\u7565\u5B50\u547D\u4EE4\u540D\u65F6\u9ED8\u8BA4\u6267\u884C\u672C\u547D\u4EE4\uFF09"
|
|
122894
122971
|
).requiredOption(
|
|
122895
122972
|
"-a, --account <id>",
|
|
122896
122973
|
"Facebook \u5E7F\u544A\u8D26\u6237 mediaCustomerId\uFF08\u6570\u5B57\u6216 act_<\u6570\u5B57>\uFF09"
|
|
@@ -122910,6 +122987,13 @@ function registerFacebookAnalysisCommands(program2) {
|
|
|
122910
122987
|
).option("--concurrency <n>", "\u5E76\u53D1\u6570\uFF0C\u9ED8\u8BA4 5\uFF0C\u4E0A\u9650 16", (v) => parseInt(v, 10)).option("--verbose", "\u8BE6\u7EC6\u9519\u8BEF\u4FE1\u606F", false).action(async (opts) => {
|
|
122911
122988
|
await runAllFacebookSections(opts);
|
|
122912
122989
|
});
|
|
122990
|
+
}
|
|
122991
|
+
function registerFacebookAnalysisCommands(program2) {
|
|
122992
|
+
const sectionHelp = FACEBOOK_SECTION_NAMES.join(", ");
|
|
122993
|
+
const root = program2.command("facebook-analysis").description(
|
|
122994
|
+
"Facebook Ads \u8D26\u6237\u5206\u6790\uFF1A\u6279\u91CF\u62C9\u53D6 Section \u6570\u636E\uFF08\u9ED8\u8BA4\uFF09\uFF0C\u6216\u7531 render \u751F\u6210 HTML \u5468\u671F\u62A5\u544A"
|
|
122995
|
+
);
|
|
122996
|
+
root.addCommand(registerBatchCommand(sectionHelp), { isDefault: true });
|
|
122913
122997
|
root.command("render").description(
|
|
122914
122998
|
"\u6839\u636E Agent \u64B0\u5199\u7684\u62A5\u544A JSON \u751F\u6210 Meta/Facebook \u5468\u671F\u5206\u6790 HTML \u7EC8\u7A3F\uFF08meta-period-report.html\uFF09"
|
|
122915
122999
|
).requiredOption("--data <file>", "Agent \u4EA7\u51FA\u7684 meta-period-report.json").option(
|
|
@@ -122938,7 +123022,7 @@ function getVersion() {
|
|
|
122938
123022
|
return "0.0.0";
|
|
122939
123023
|
}
|
|
122940
123024
|
}
|
|
122941
|
-
var program = new
|
|
123025
|
+
var program = new Command2();
|
|
122942
123026
|
program.name("siluzan-tso").description(
|
|
122943
123027
|
"Siluzan \u5E7F\u544A\u8D26\u6237\u7BA1\u7406\uFF1A\u8D26\u6237\u67E5\u8BE2\u3001\u4F59\u989D\u3001\u6295\u653E\u6570\u636E\u3001\u5F00\u6237\u7533\u8BF7\uFF08Google/TikTok/Yandex/Bing/Kwai\uFF09\u3001\n\u8D26\u53F7\u5206\u4EAB/\u89E3\u7ED1\u3001\u4F18\u5316\u62A5\u544A\u3001Google \u8D26\u6237\u5206\u6790\u7F51\u5173\uFF08google-analysis\uFF09\u3001\u7F51\u7AD9\u8BCA\u65AD\uFF08website-diagnosis\uFF09\u3001\u6218\u7565\u5E02\u573A\u5206\u6790\uFF08market-analysis\uFF0CAgent \u751F\u6210\u62A5\u544A\uFF09\u3001\u5145\u503C\u8F6C\u8D26\u3001\u5F00\u7968\u3001\u667A\u80FD\u9884\u8B66\u3001Google \u5E7F\u544A\u7BA1\u7406\uFF08\u542B\u5F02\u6B65\u6279\u91CF\uFF09\u3002"
|
|
122944
123028
|
).version(getVersion());
|
package/dist/skill/SKILL.md
CHANGED
|
@@ -19,6 +19,8 @@ allowed-tools: Bash(siluzan-tso:*) Read Write
|
|
|
19
19
|
>
|
|
20
20
|
> **报告/Excel 交付前**:Read `references/core/deliverable-preflight.md`,**Read 最终产物**并按自检表确认币种与章节完整;币种只认当次 `list-accounts` 的 `currencyCode`。
|
|
21
21
|
>
|
|
22
|
+
> **HTML 终稿类报告(用户未指定格式时默认 HTML)**:网站诊断 P8(`website-diagnosis render`)、Meta 周期 P4-FB(`facebook-analysis render`)、战略市场 P9(`market-analysis render`)。Agent 只写 JSON,**禁止**仅交付 Markdown 摘要或纯 JSON 充当终稿。
|
|
23
|
+
>
|
|
22
24
|
> **开户**:首次进入开户话题须先向用户罗列该媒体(或未指明媒体时六平台)**全部必填项**,见 `references/accounts/open-account-by-media.md` §「首次响应硬规范」。
|
|
23
25
|
>
|
|
24
26
|
> **Subagent(可选)**:若宿主支持 Task / 子会话,复杂报告(P5/P6/P7)或长 CLI 输出前 Read `references/core/subagent-orchestration.md`,**自行决定**是否委派;写操作确认与对用户的最终交付留在主 Agent。
|
|
@@ -84,10 +86,10 @@ Windows:部分 Agent 通过 PowerShell 代执行时可能失败,改在 [Git
|
|
|
84
86
|
| 用户意图(关键词) | 必读(Read 后再 `-h` / 执行) |
|
|
85
87
|
| ------------------ | ----------------------------- |
|
|
86
88
|
| 任意任务(首次) | `core/agent-conventions.md` |
|
|
87
|
-
| 账户列表 / 余额 / 消耗 / 分享 / MCC
|
|
89
|
+
| 账户列表 / 有多少 / 列出全部 / 余额 / 消耗 / 分享 / MCC | `accounts/accounts.md`(§ list-accounts **Agent 意图速查**:`-m <媒体> --page-size 999 --json-out` 一步拉全);金额加 `accounts/currency.md` |
|
|
88
90
|
| google ads 拉数 / 报告 / 周报 / `google-analysis` | `analytics/account-analytics.md` + `core/tips.md` + **`core/deliverable-preflight.md`**;多账户加 `analytics/google-analysis-batch.md` |
|
|
89
|
-
| Meta/Facebook 周期或诊断报告 / `facebook-analysis` |
|
|
90
|
-
| 网站诊断 / 落地页评分 / `website-diagnosis` | `analytics/website-diagnosis-guide.md` + `assets/website-diagnosis-rules.md` + `core/playbooks.md` P8
|
|
91
|
+
| Meta/Facebook 周期或诊断报告 / `facebook-analysis` | **默认 HTML**:`meta-period-report.md` + **`assets/meta-period-report-rules.md`**(内容丰富度必读)+ `facebook-analysis-guide.md` + `deliverable-preflight.md` → 拉数 → 分析 → JSON(4 条建议≥150字+7维补充+HTML扩展)→ `render`;**仅用户要 Excel** 时加读 `meta-period-report-excel.md` |
|
|
92
|
+
| 网站诊断 / 落地页评分 / `website-diagnosis` | `analytics/website-diagnosis-guide.md` + `assets/website-diagnosis-rules.md` + `core/playbooks.md` P8 + `core/deliverable-preflight.md`;Agent 只写 JSON,**默认交付** `website-diagnosis render` 产出的 **HTML**(禁止仅 Markdown/JSON) |
|
|
91
93
|
| 市场分析 / 行业分析 / 战略市场报告 / `market-analysis` | `analytics/market-analysis-guide.md` + `assets/market-analysis-rules.md`(原始业务维度清单)+ `report-templates/market-analysis-report.md` + `core/playbooks.md` P9;`collect` → Agent 按维度调研写 `market-report.json` → `render`(缺项校验) |
|
|
92
94
|
| Google 新建搜索系列 | `google-ads/google-ads-campaign-plan.md` |
|
|
93
95
|
| Google 广告 CRUD / 拒审 | `google-ads/google-ads.md` |
|
package/dist/skill/_meta.json
CHANGED
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
# Meta 周期报告撰写规则(Agent 用)
|
|
2
|
+
|
|
3
|
+
> 对齐 **P8 网站诊断** 的「结构化 + 全量覆盖 + 每条必有建议」思路。
|
|
4
|
+
> 源:业务 Excel 模板(`无锡顺晟Facebook4月报告.xlsx`)+ `meta-period-report.html` 深度分析章节。
|
|
5
|
+
> **禁止**只写 KPI 表、4 条一句话建议或空白占位后交付。
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## 与网站诊断的对照(为何 FB 报告会显得「建议太少」)
|
|
10
|
+
|
|
11
|
+
| 网站诊断(P8) | Meta 周期报告(P4-FB) |
|
|
12
|
+
| -------------- | ---------------------- |
|
|
13
|
+
| `website-diagnosis-rules.md` 规定 29 子项全覆盖 | 本文件规定 **5 维数据 + 叙事 + 建议** 全覆盖 |
|
|
14
|
+
| 每项 `issue` + `suggestion`(较差项以「强烈建议:」开头) | 每条建议 `content` 须 **引用当次数字** + **可执行动作** |
|
|
15
|
+
| `coreIssuesIds` + `PriorityPlan` 高/中/低 | `priorityPlan` + `narrative.recommendations` 4 条 + `supplementaryRecommendations` |
|
|
16
|
+
| HTML 由 `render` 渲染全部章节 | HTML **必须**填 `executiveSummary`、`healthDiagnosis`、`sections.*.insight` 等(见下) |
|
|
17
|
+
| 交付前 Read HTML 对照章节清单 | 交付前 Read HTML/xlsx 对照 `deliverable-preflight.md` § P4-FB |
|
|
18
|
+
|
|
19
|
+
**常见失误(导致建议偏少)**:
|
|
20
|
+
|
|
21
|
+
1. 只写 Excel 叙事 4 条标题、每条 1~2 句泛化话术,未引用 CPL/花费/国家名。
|
|
22
|
+
2. HTML 默认路径只填 `narrative`,**未填** `executiveSummary` / `healthDiagnosis` / `sections.insight`(模板原先不展示 `narrative.recommendations`,现已修复)。
|
|
23
|
+
3. 未按 **7 维数据驱动清单** 逐条给建议,只写「优化素材」「调整预算」等空话。
|
|
24
|
+
|
|
25
|
+
---
|
|
26
|
+
|
|
27
|
+
## JSON 结构(摘要)
|
|
28
|
+
|
|
29
|
+
完整 Schema:`assets/meta-period-report.schema.json`。
|
|
30
|
+
**Excel 与 HTML 共用** `narrative`;**HTML 默认交付**还须填「深度分析扩展」字段。
|
|
31
|
+
|
|
32
|
+
---
|
|
33
|
+
|
|
34
|
+
## 一、Excel / 总数据叙事(`narrative` · 必填)
|
|
35
|
+
|
|
36
|
+
### 1. `narrative.overall`(整体表现)
|
|
37
|
+
|
|
38
|
+
| 要求 | 细则 |
|
|
39
|
+
| ---- | ---- |
|
|
40
|
+
| 字数 | **≥ 120 字**(中文) |
|
|
41
|
+
| 必含数字 | 花费、`results`、CPL(`costPerResult`)、`reach`、`impressions`、`frequency` **至少 5 项** |
|
|
42
|
+
| 必含口径 | `resultType`、`attributionSetting`(写入 `meta` 并在段中提及) |
|
|
43
|
+
| 可选 | 若有 `previousPeriod`,写 1~2 句环比(花费 ±%、CPL ±%) |
|
|
44
|
+
|
|
45
|
+
### 2. `narrative.regional[]`(区域 / 广告组)
|
|
46
|
+
|
|
47
|
+
| 要求 | 细则 |
|
|
48
|
+
| ---- | ---- |
|
|
49
|
+
| 条数 | **每个** `ad-sets` 中花费 >0 的广告组 **各 1 段**(不得合并成 1 句带过) |
|
|
50
|
+
| 字数 | 每段 **≥ 80 字** |
|
|
51
|
+
| 必含 | 组名、花费占比或绝对值、线索数、`costPerResult`、频次;判断「曝光不足 / 频次过高 / 效率领先」 |
|
|
52
|
+
| 数据依据 | `ad-sets-<id>.json` → `adGroups[]` |
|
|
53
|
+
|
|
54
|
+
### 3. `narrative.country`(国家报告)
|
|
55
|
+
|
|
56
|
+
| 要求 | 细则 |
|
|
57
|
+
| ---- | ---- |
|
|
58
|
+
| 字数 | **≥ 80 字** |
|
|
59
|
+
| 必含 | CPL **最低** 与 **最高** 国家名及具体 CPL;中间梯队 1~2 国;花费集中度 |
|
|
60
|
+
| 数据依据 | `country-<id>.json` → `countries[]` |
|
|
61
|
+
|
|
62
|
+
### 4. `narrative.recommendations[]`(固定 4 条标题)
|
|
63
|
+
|
|
64
|
+
**必须恰好 4 条**,`title` 仅限下表枚举;`content` 须**数据驱动**,不得模板空话。
|
|
65
|
+
|
|
66
|
+
| title | content 最低要求(每条 **≥ 150 字**) |
|
|
67
|
+
| ----- | ------------------------------------- |
|
|
68
|
+
| **简化表单问题** | 结合 **高频次 + 高 CPL** 的国家/组(写出名称与数字);建议字段从 N 减到 3;说明预期对 CPL 的影响 |
|
|
69
|
+
| **区域调整** | 按 **具体广告组** 写语言/市场策略(如土/葡/德);引用各组 CPL 对比;写清「加谁、减谁、暂停谁」 |
|
|
70
|
+
| **预算重构** | 给出 **具体比例**(如 4:3:3 或按 spend 占比调整);测试周期(7/14 天);优胜劣汰阈值(CPL > 账户均值 ×1.2 则减 30%) |
|
|
71
|
+
| **素材建议** | 按 **平台**(IG/FB)与 **国家** 分述;素材套数、视频时长、形态;至少点名 1 个高花费低结果创意/版位 |
|
|
72
|
+
|
|
73
|
+
**建议前缀(对齐网站诊断)**:
|
|
74
|
+
|
|
75
|
+
- 问题严重(CPL > 均值 1.3 倍、频次 >2.5、零成效花费 >$50)→ `content` 以 **「强烈建议:」** 开头
|
|
76
|
+
- 其余优化项 → **「推荐优化:」** 开头
|
|
77
|
+
|
|
78
|
+
---
|
|
79
|
+
|
|
80
|
+
## 二、数据驱动补充建议(`supplementaryRecommendations` · 必填)
|
|
81
|
+
|
|
82
|
+
在 4 条固定标题之外,**必须**再写 **7 维清单**,每条 `{ "dimension", "issue", "suggestion" }`:
|
|
83
|
+
|
|
84
|
+
| dimension | issue(发现了什么) | suggestion(怎么办) |
|
|
85
|
+
| --------- | ------------------- | -------------------- |
|
|
86
|
+
| 预算与广告组 | 点名高 `spendPercentage`、差 `costPerResult` 的组 | 降预算 %、暂停、或合并受众 |
|
|
87
|
+
| 平台与版位 | `publisherPlatform` + `platformPosition` 组合 | 减投差版位、加码 winner 平台 |
|
|
88
|
+
| 地域 | 高消耗低结果国家 | geo 排除 / 收窄 / 单独组 |
|
|
89
|
+
| 受众 | 低效 age×gender | 排除或降 bid;扩量高效段 |
|
|
90
|
+
| 创意 | 高花费低 `results` 的 ad | 关停、复制 winner 结构 |
|
|
91
|
+
| 频次与疲劳 | `frequency` >2.5 且转化差 | 扩受众、换创意、降预算 |
|
|
92
|
+
| 接口限制 | 无按日/关键词等 | 一句说明「Meta 接口未提供」,**禁止编造** |
|
|
93
|
+
|
|
94
|
+
每条 `suggestion` **≥ 60 字**,须含 **至少 1 个当次数字或名称**。
|
|
95
|
+
|
|
96
|
+
---
|
|
97
|
+
|
|
98
|
+
## 三、优先级改进计划(`priorityPlan` · HTML 必填 · Excel 推荐)
|
|
99
|
+
|
|
100
|
+
对齐网站诊断 `PriorityPlan`:
|
|
101
|
+
|
|
102
|
+
```jsonc
|
|
103
|
+
{
|
|
104
|
+
"priorityPlan": {
|
|
105
|
+
"high": ["…", "…"], // ≥2 条,本周必须做
|
|
106
|
+
"medium": ["…", "…"], // ≥2 条
|
|
107
|
+
"low": ["…", "…"] // ≥2 条,持续优化
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
每条 **≥ 40 字**,带责任维度(预算/素材/地域等)。
|
|
113
|
+
|
|
114
|
+
---
|
|
115
|
+
|
|
116
|
+
## 四、HTML 默认交付 · 深度分析扩展(必填)
|
|
117
|
+
|
|
118
|
+
用户未指定 Excel 时,除 §一~§三外 **还必须** 填写:
|
|
119
|
+
|
|
120
|
+
| 字段 | 最低要求 |
|
|
121
|
+
| ---- | -------- |
|
|
122
|
+
| `executiveSummary` | **3~5 段**,每段 **≥ 80 字**;解释「为什么」而不只报数;可拆自 `narrative.overall` 但须加深因果 |
|
|
123
|
+
| `healthDiagnosis.lifecyclePhase` | `test-market` / `find-winner` / `scale` 三选一 |
|
|
124
|
+
| `healthDiagnosis.lifecycleVerdict` | **≥ 60 字**,结合总花费与维度分散度 |
|
|
125
|
+
| `healthDiagnosis.fourQuestions` | **恰好 4 张卡片**(钱花得值不值 / 赢在哪 / 输在哪 / 下月重点) |
|
|
126
|
+
| 每张 `fourQuestions[]` | `verdict` + `evidence` **≥2 条**(含数字)+ `action` **≥ 40 字** |
|
|
127
|
+
| `healthDiagnosis.scorecard` | **≥ 6 行**(平台、国家、广告组、受众等分项红绿灯) |
|
|
128
|
+
| `sections.platform.insight` | **≥ 200 字** |
|
|
129
|
+
| `sections.country.insight` | **≥ 200 字** |
|
|
130
|
+
| `sections.adSets.insight` | **≥ 200 字** |
|
|
131
|
+
| `sections.audience` | `goldenProfile` **≥3 条** + `antiProfile` **≥2 条** + `insight` **≥ 150 字** |
|
|
132
|
+
| `sections.landingPage.rows` | **≥ 3 行**(心理阻碍 / 数据信号 / 推演 / 优先级) |
|
|
133
|
+
| `abTests` | **≥ 3 个**实验(变量、假设、成功标准) |
|
|
134
|
+
| `actionChecklist` | `today` **≥2**、`thisWeek` **≥3**、`thisMonth` **≥3** 条可执行项 |
|
|
135
|
+
|
|
136
|
+
**禁止**:HTML 中大量「(待 Agent 撰写)」占位;交付前 Read HTML 确认无空节。
|
|
137
|
+
|
|
138
|
+
---
|
|
139
|
+
|
|
140
|
+
## 五、撰写流程(对齐 P8)
|
|
141
|
+
|
|
142
|
+
1. **拉数**:`facebook-analysis` + `--json-out`(默认 5 维)。
|
|
143
|
+
2. **脚本读盘**:聚合 KPI、Top/Bottom 国家、平台、受众、广告组(禁止 Read 业务 JSON 进对话)。
|
|
144
|
+
3. **先 outline 后 JSON**:列出将引用的数字与 7 维建议要点,再写 `meta-period-report.json`。
|
|
145
|
+
4. **渲染**:`facebook-analysis render`(HTML)或脚本写 xlsx(Excel)。
|
|
146
|
+
5. **Read 终稿**:对照 `deliverable-preflight.md` § P4-FB。
|
|
147
|
+
|
|
148
|
+
---
|
|
149
|
+
|
|
150
|
+
## 六、自检清单(Agent 交付前勾选)
|
|
151
|
+
|
|
152
|
+
- [ ] `narrative.recommendations` 4 条,每条 content ≥150 字且含真实数字
|
|
153
|
+
- [ ] `supplementaryRecommendations` 7 维齐全
|
|
154
|
+
- [ ] `priorityPlan` high/medium/low 各 ≥2 条
|
|
155
|
+
- [ ] HTML:`executiveSummary` ≥3 段、`fourQuestions` =4、`scorecard` ≥6 行
|
|
156
|
+
- [ ] HTML:`sections.platform/country/adSets.insight` 各 ≥200 字
|
|
157
|
+
- [ ] HTML:`actionChecklist` 三列非空;`abTests` ≥3
|
|
158
|
+
- [ ] Excel:总数据 Sheet 叙事块 4 节齐全,无 1 句话敷衍
|
|
159
|
+
- [ ] 未编造 Meta 无接口维度(按日、关键词等)
|
|
160
|
+
|
|
161
|
+
---
|
|
162
|
+
|
|
163
|
+
## 相关文档
|
|
164
|
+
|
|
165
|
+
- `report-templates/meta-period-report.md` — 流程与章节
|
|
166
|
+
- `report-templates/meta-period-report-excel.md` — Excel 五 Sheet
|
|
167
|
+
- `references/analytics/facebook-analysis-guide.md` — API 字段
|
|
168
|
+
- `references/core/deliverable-preflight.md` — 交付审阅
|
|
169
|
+
- `assets/website-diagnosis-rules.md` — 网站诊断(对照参考)
|
|
@@ -60,8 +60,9 @@
|
|
|
60
60
|
},
|
|
61
61
|
"recommendations": {
|
|
62
62
|
"type": "array",
|
|
63
|
-
"description": "优化建议固定 4
|
|
64
|
-
"minItems":
|
|
63
|
+
"description": "优化建议固定 4 条;每条 content ≥150 字且引用当次数据(见 meta-period-report-rules.md)",
|
|
64
|
+
"minItems": 4,
|
|
65
|
+
"maxItems": 4,
|
|
65
66
|
"items": {
|
|
66
67
|
"type": "object",
|
|
67
68
|
"required": ["title", "content"],
|
|
@@ -70,12 +71,39 @@
|
|
|
70
71
|
"type": "string",
|
|
71
72
|
"enum": ["简化表单问题", "区域调整", "预算重构", "素材建议"]
|
|
72
73
|
},
|
|
73
|
-
"content": {
|
|
74
|
+
"content": {
|
|
75
|
+
"type": "string",
|
|
76
|
+
"description": "≥150 字;严重问题以「强烈建议:」开头,其余以「推荐优化:」开头"
|
|
77
|
+
}
|
|
74
78
|
}
|
|
75
79
|
}
|
|
76
80
|
}
|
|
77
81
|
}
|
|
78
82
|
},
|
|
83
|
+
"supplementaryRecommendations": {
|
|
84
|
+
"type": "array",
|
|
85
|
+
"description": "7 维数据驱动补充建议(预算/平台/地域/受众/创意/频次/接口限制);每条 suggestion ≥60 字",
|
|
86
|
+
"minItems": 7,
|
|
87
|
+
"maxItems": 7,
|
|
88
|
+
"items": {
|
|
89
|
+
"type": "object",
|
|
90
|
+
"required": ["dimension", "issue", "suggestion"],
|
|
91
|
+
"properties": {
|
|
92
|
+
"dimension": { "type": "string" },
|
|
93
|
+
"issue": { "type": "string" },
|
|
94
|
+
"suggestion": { "type": "string" }
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
},
|
|
98
|
+
"priorityPlan": {
|
|
99
|
+
"type": "object",
|
|
100
|
+
"description": "高/中/低优先级改进计划(对齐网站诊断 PriorityPlan)",
|
|
101
|
+
"properties": {
|
|
102
|
+
"high": { "type": "array", "items": { "type": "string" }, "minItems": 2 },
|
|
103
|
+
"medium": { "type": "array", "items": { "type": "string" }, "minItems": 2 },
|
|
104
|
+
"low": { "type": "array", "items": { "type": "string" }, "minItems": 2 }
|
|
105
|
+
}
|
|
106
|
+
},
|
|
79
107
|
"tables": {
|
|
80
108
|
"type": "object",
|
|
81
109
|
"description": "Sheet2~5 表格数据;可省略,由 --snapshot-dir 合并",
|
|
@@ -106,11 +134,12 @@
|
|
|
106
134
|
"executiveSummary": {
|
|
107
135
|
"type": "array",
|
|
108
136
|
"items": { "type": "string" },
|
|
109
|
-
"
|
|
137
|
+
"minItems": 3,
|
|
138
|
+
"description": "HTML 默认交付必填:3~5 段执行摘要,每段 ≥80 字;可深化 narrative.overall"
|
|
110
139
|
},
|
|
111
140
|
"healthDiagnosis": {
|
|
112
141
|
"type": "object",
|
|
113
|
-
"description": "HTML
|
|
142
|
+
"description": "HTML 默认交付必填:四问卡片 + 红绿灯表(Excel 不含)",
|
|
114
143
|
"properties": {
|
|
115
144
|
"lifecyclePhase": { "enum": ["test-market", "find-winner", "scale"] },
|
|
116
145
|
"lifecycleVerdict": { "type": "string" },
|