LUCKEELUC-1485 · 交研发 · 全量契约 ← 返回工作台

A2 专家模式 · 目标 Schema 工程契约 v1.0

交研发FOR ENG
目录
  1. 0. 一句话
  2. 1. 字段总表
  3. 2. 自动取数映射
  4. 3. AskUserQuestion 契约
  5. 4. 输出与落盘
  6. 5. 本轮口径变更:日预算 → 预期广告订单占比
  7. 6. 未决问题(需产品或业务先拍板)
  8. 附录 · 关键文件(绝对路径,全部实读)
适用范围:/set-ads-goal skill、ads-goal-evaluator agent、workspace_goal_proposal 卡片、POST /api/workspaces/:id/targets 落盘链路。 基线代码:本地 dev @ 2763ce07(2026-08-10)。开工前必须 git pull —— origin/dev 已含 PR #1044(LUC-1483),本地 schema 仍是 schema_version const "1.0"、无 advertising_orders_target。 标注约定:✅ = 已在仓库/真实抓包中逐字核实;⚠️ = 需研发或产品确认,不得当既成事实实现;❌ = 已被证伪的旧说法(保留以防重新引入)。

0. 一句话

skill 要做的三件事:自动取数 → 只问取不到的 → 落盘。 自动取数的边界由领星/星商 MCP 决定;问人的边界由「数据源里根本没有这个事实」决定;落盘的边界由 workspace_operational_goal.schema.json 决定 —— 目前第三条最窄,是本轮最先要修的地方(见 §4.4)。


1. 字段总表

这是唯一的字段清单。前后端、skill、evaluator 均以本表 key 为准。 key 用小驼峰;落盘时的 snake_case 键名见 §4。 共 38 个字段 = known 22 / inferred 6 / missing 10。

1.1 known(22)—— 自动取数,不问

key标签分组类型来源(简)askPolicy
asin主 ASIN 与站点对象objectproduct_meta.jsonnever
currentTargets磁盘上的现有目标目标状态objecttargets/current_period.jsonnever
price售价对象objectget_product_listing → promotion_status.current_price(字符串)never
reviews评分与评论数对象objectget_product_listing → listing.rating / listing.review_countnever
currency币种一致性对象objectget_orders.currency_code / economics.json / 库存 currencynever
dataWindow数据窗口与截止时间对象object调用参数 + payload 顶层 data_retrieved_atnever
adPerf30近 30 天广告大盘广告表现objectget_campaign_metrics.summarynever
cpoNow当前单均获客成本广告表现number\null同上,spend ÷ ordersnever
spendPace日均花费与历史单日上限广告表现objectlist_keywords_placement_hourly_with_config / campaigns-dailynever
adStructure广告结构规模广告表现object各 list 工具的 total_*never
budgetCaps各组日预算与广告位溢价广告表现array + totallist_campaigns_config.data[]never
spendMix分组花费 / 单量 / CPO广告表现arrayget_campaign_metrics.metrics[]never
wasteSpend0 转化搜索词花费广告表现objectlist_search_terms_with_confignever
bidUtilization出价利用率广告表现objectlist_keywords_by_campaign_with_config(bid 数值型)never
budgetChangeLog区间内预算/竞价变更史广告表现arraylist_campaign_operations_with_confignever
funnel3030 天流量漏斗流量结构objectget_campaign_metrics.summarynever
impressionShare搜索顶部展示份额流量结构number\null星商 get_target_exposure;领星不可得never
placementGap广告位转化差流量结构arraylist_placement_profilenever
groupFunnel分组点击与转化率流量结构arrayget_campaign_metrics.metrics[] 行内 clicks/orders/cvrnever
fbaStockFBA 可用库存库存objectlisting.inventory_level.fba_warehouse.breakdown.availablenever
inboundStock在途 / 未上架库存(领星可得部分)库存object同上 in_transit/transferring/receiving ⚠️never
sellRate日均动销(单/件双口径)库存objectget_orders + get_asin_salesnever

1.2 inferred(6)—— 由 known 推导,必带算式与置信度

key标签分组类型推导自置信度
fillRate预算消耗率广告表现arrayspendMix ÷ (budgetCaps × 天数),用 budgetChangeLog 校正中
weekVsMonth近 7 天 vs 30 天转化率流量结构objectget_campaign_metrics 两次调用中
daysOfSupply可售天数库存number\nullfbaStock.availableUnits ÷ sellRate.unitsPerDay高
stockHealth库存健康度产品背景enumdaysOfSupply vs 周期剩余天数中
adDependency广告依赖度产品背景number\null广告订单 ÷ 店铺总订单中
maturity产品成熟度产品背景enumreviews.reviewCount + 30 天订单量低(阈值未定义,默认返回 unknown)

1.3 missing(10)—— 只能问人

key标签分组权重askPolicy条件(摘要)
task本周期运营任务运营意图必须always—
goalPeriod目标周期目标状态重要conditional今天不是月初 3 天内
inbound在途 / 未上架库存库存重要conditional目标总需求件数 > FBA 可用件数(分母口径 ⚠️)
cost产品成本区间利润重要conditionaltask ∈ {profit, clear}
promo促销与折扣计划运营意图重要defaultOnly用户主动点名
guard运营护栏运营意图可选defaultOnly用户主动点名
brandTerm品牌词归属竞争环境可选defaultOnly用户主动点名
lossCap花费天花板利润可选defaultOnly用户主动点名
season大促与上新节点运营意图可选defaultOnly用户主动点名
positioning价格定位竞争环境可选defaultOnly用户主动点名
⚠️ key 撞名:原型里 positioning 的 ask id 是 "price",与 known 字段 price 同名。契约统一用 field.k 作 key,不要沿用 ask id。

2. 自动取数映射

2.0 全局取数纪律(先读,否则下面每条都会踩)

  1. spend vs spends —— processed MCP payload 一律 spend(单数);只有 raw_response 与后端原始行用 spends(复数)。星商路由的 keywords 行转化率键是 conversion_rate,领星是 cvr。跨 provider 必须做键名兼容表。
  2. ACoS 单位 —— MCP 真实抓包 acos: 146.1 是百分数量级;后端 lingxing/campaigns.ts:66-78 normalizeAcos 把 >1 的值 ÷100 转成 0..1;而目标 schema 的 target_acos_pct 是「20 表示 20%」。三处口径不同,转换点必须显式。
  3. 缺数返回 null,不返回 0 —— 与 load-workspace-kpi.ts deriveMetrics 保持一致(sales<=0 → acos=null,clicks=0 → cvr/cpc=null),前端才能降级成「—」。
  4. 翻页 —— get_campaign_metrics length 默认 25(真实账户 total_count 可达 244);list_search_terms_with_config 默认 250;list_campaign_operations_with_config 默认只有 10。不显式翻页会静默截断。
  5. 哨兵行 —— list_campaigns_config 真实抓包 data[0] 是账户合计行(campaign_id/campaign_name/state 全 null,daily_budget: "7623.00"),len(data)=245 而 total_count=244。

2.1 known 字段取数明细

asin

currentTargets

price

reviews

currency

dataWindow

adPerf30

cpoNow

spendPace

adStructure

budgetCaps

spendMix

wasteSpend

bidUtilization

budgetChangeLog

funnel30

impressionShare

placementGap

groupFunnel

fbaStock

inboundStock ⚠️

sellRate

2.2 inferred 字段推导明细

key算式置信度caveat
fillRatespend_window ÷ (parseFloat(dailyBudget) × days) × 100;dailyBudget 缺失或 0 → null中分母是当前快照,区间内调过预算就失真 → 必须用 budgetChangeLog 校正。>100% 是 Amazon 允许的单日超投按月封顶,不是数据错误。区间聚合推不出「每天都触顶」
weekVsMonthdeltaPp = last7CvrPct − last30CvrPct;样本不足或 `deltaPp 低于噪声阈值 → insufficient`中噪声阈值在仓库任何地方都没有定义(已全量 grep)→ 见 §6.6。目前只能返回 insufficient
daysOfSupplyavailableUnits ÷ unitsPerDay;周期缺口件数 = 周期天数 × unitsPerDay − availableUnits高三套算式冲突,必须拍板,见 §6.8。且建立在「在途 = 0」假设上,展示时必须带假设标注
stockHealthdaysOfSupply < 周期剩余天数 → risk,否则 healthy;周期长度不可知 → unknown中workspace_operational_goal.schema.json 没有周期结束日字段(additionalProperties:false,属性已逐条读完)→「周期剩余天数」磁盘上取不到,只能从 period_id 字符串解析或默认 30 天。该默认值必须显式写进实现,否则判定不可复现。risk 是不上调日预算的唯一依据;用户答了在途要翻转
adDependency广告订单 ÷ 店铺总订单 × 100中归因窗口不一致 → 比值只能作量级判断。>100% 判定为两侧标识符口径不一致,不是数据错误(可用 identifier_type 检测)
maturityreviewCount ≥ 阈值 && 近 30 天订单稳定 → mature,否则 new;阈值缺失 → unknown低阈值未定义(已 grep 确认无「成熟链接」判定常量)→ 在产品给出可写死阈值前,返回 unknown 且不参与任何目标值计算。无 listing 上架日字段,不能给「上架 N 个月」

2.3 需研发确认(不要混进正表实现)

#项现状阻塞什么
C1在途库存语义真实 payload 里 in_transit/transferring/receiving/overseas transit 有数值和中文释义,与「领星无在途接口」冲突inbound 追问是否从 conditional 降级;daysOfSupply / stockHealth / 预算上限全部要重算
C2impressionShare provider 边界星商可得、领星不可得A2 是否允许工作区间不对称,还是统一按最小公分母(领星)定契约、份额一律不进目标卡
C3spendPace 全口径日序列关键词口径可得;auto/商品定位只能走 campaigns-daily 或 DB 表skill 运行时能否读本地表 / 调后端路由;否则 evidenceCeiling 只能标「关键词口径下界」
C4list_campaigns_config 哨兵行稳定性data[0] 全 null + 账户合计;len(data)=245 vs total_count=244用「位置剔除」还是「campaign_id 为 null 则剔除」
C5processed / raw 两套键名spend/spends、cvr/conversion_rate需统一走 processed,并给出跨 provider 键名兼容表
C6ctx.metrics 的取数时机❌「Step 3 pre-flight 经 MCP 取数」不成立。set-ads-goal/SKILL.md Step 3 只读本地四个文件,不碰 MCP;MCP 取数在 Step 4 的 ads-goal-evaluator整套 shouldAsk 在现有流程里拿不到 metrics,三个 conditional 一个都跑不了。 要落地必须把取数前移到 Step 3 —— 这是一处真实流程改动
C7领星路由表缺 get_ordersmcp-data-routing.json 领星条目只注册 5 个 canonical 工具;get_orders / get_asin_sales / get_campaign_negative_keywords 只在星商条目。运行时 lingxing_* server 确实暴露了 get_orders,更像路由表漏登记补登记前,领星产品上 totalOrders30 / adSharePct / promo.gap 全部求不出值

3. AskUserQuestion 契约

3.1 平台硬约束(✅ 已核实 AskUserQuestionTool.tsx / prompt.ts)

3.2 判定引擎(可直接抄进代码)

// ============================================================
// A2 专家模式 · AskUserQuestion 判定引擎
// 原型参考:analysis/luckee-prototype-full/a2-chat.js askTriggered():516 / pendingSchemaAsks():533
//           analysis/luckee-prototype-full/mock-data.js GOAL_SCHEMA:539 / TASK_ORDER_GOAL:678
// 铁律:conditional 逐字段 switch 写死,禁止字符串 eval / new Function
// ============================================================

// ---- 输入上下文 ----
// ctx.metrics        ⚠️ 见 §2.3 C6:现有流程里 Step 3 不取 MCP,必须先把取数前移
//   listingPrice     ← get_product_listing → price_state          ✅ 领星路由已注册
//   fbaStock         ← get_product_listing → inventory_state      ✅ 领星路由已注册
//   adOrders30 / maxDailySpend
//                    ← list_campaigns_with_date_and_portfolio_with_config ✅ 领星 required
//   totalOrders30 / totalSales30 / adSharePct
//                    ⚠️ 需 get_orders —— 领星路由表未注册(§2.3 C7)
// ctx.answers        ← 本轮已收到的 AskUserQuestion / request_user_input 答案(键用 field.k)
// ctx.explicitTopics ← 用户主动点名的字段 key 列表(取值域 = 本契约的 key,不得混入别名)
// ctx.goal           ← products/<nickname>/targets/current_period.json
// ctx.now            ← 当前时间

const TASK_ORDER_GOAL = { profit: 125, bsr: 163, test: 56, clear: 175 };   // mock-data.js:678
const TASK_SHARE      = { profit: 37.0, bsr: 43.4, test: 20.8, clear: 45.1 }; // TASK_PROFILES[*].adShare
const DEPENDS_ON_TASK = new Set(["inbound", "cost"]);
const WEIGHT_ORDER    = { "必须": 0, "重要": 1, "可选": 2 };

// ---- 规则 0:$ARGUMENTS 提前说过 == 已答 ----
// ⚠️ 暂不实现。set-ads-goal/SKILL.md Step 2 明文:
//   "Do not hard-parse it yourself; pass it verbatim to the evaluator so it can normalize against evidence."
//   且仓内无自由文本→schema 答案的解析实现(goal-setup-prefill.ts 只拼 "/set-ads-goal " 前缀)。
//   原型 PREFILL_EXAMPLES(mock-data.js:681) 是演示常量,其 inbound 取值 "some" 与本契约枚举不同。
//   要落地必须先改 SKILL.md Step 2 并新写 parser。在那之前 shouldAsk 只认 ctx.answers。

function shouldAsk(field, ctx) {
  if (field.state !== "missing") return false;              // 1. 系统确实取不到
  if (ctx.answers[field.key] !== undefined) return false;   // 已答
  if (field.askPolicy === "defaultOnly") {
    return ctx.explicitTopics.includes(field.key);          // 2. 只有主动点名才问(有意偏离原型)
  }
  if (field.askPolicy === "always") return true;
  switch (field.key) {                                      // 3. conditional:写死,不 eval
    case "inbound": {
      // ⚠️ 分母需拍板(§6.4),两种口径结论相反:
      //   (A) 基线占比 ctx.metrics.adSharePct(原型 askTriggered 原样)
      //   (B) 任务目标占比 TASK_SHARE[task]  ← 与恒等式自洽
      const share = USE_TASK_SHARE ? TASK_SHARE[ctx.answers.task] : ctx.metrics.adSharePct;
      const goalOrders = TASK_ORDER_GOAL[ctx.answers.task] ?? ctx.metrics.adOrders30;
      return Math.round(goalOrders / (share / 100)) > ctx.metrics.fbaStock;
    }
    case "cost":
      return ["profit", "clear"].includes(ctx.answers.task); // 与原型逐字一致
    case "goalPeriod":
      return ctx.now.getDate() > 3;                          // 月初 3 天内「本月」≈「完整 30 天」
    default:
      return false;
  }
}
// ⚠️ promo 保持 defaultOnly,不要升级成 conditional:
//   它的 gap 公式依赖 get_orders(领星路由未注册),且 2% 阈值在真实订单数据上几乎必然触发
//   (多件订单 / 运费 / 税 / 退款冲销),会退化成 always,破坏问题预算。

// ---- 分轮:依赖 task 的必须等 task 答完再判 ----
const byWeight = (a, b) => (WEIGHT_ORDER[a.weight] ?? 9) - (WEIGHT_ORDER[b.weight] ?? 9);

function planAskRounds(ctx) {
  const round1 = GOAL_SCHEMA
    .filter(f => shouldAsk(f, ctx) && !DEPENDS_ON_TASK.has(f.key))
    .sort(byWeight);
  if (round1.length) return round1.slice(0, 4);   // 默认 = [task](月中则 [task, goalPeriod])
  const round2 = GOAL_SCHEMA
    .filter(f => shouldAsk(f, ctx))
    .sort(byWeight);
  return round2.slice(0, 4);                       // 默认 = [inbound, cost]
}

// ---- 收敛断言 ----
function assertAskBudget(askedKeys, ctx) {
  const ceiling = 3 + ctx.explicitTopics.length + (ctx.now.getDate() > 3 ? 1 : 0);
  if (askedKeys.length > ceiling) throw new Error(`ask budget exceeded: ${askedKeys.length} > ${ceiling}`);
}

// ---- 未问字段的一句话告知(不发问,但必须披露)----
function buildDefaultNotice(ctx) {
  return GOAL_SCHEMA
    .filter(f => f.state === "missing" && !shouldAsk(f, ctx) && ctx.answers[f.key] === undefined)
    .map(f => `• ${f.label} —— ${f.defaultValue}`);
}

顺序约束(可实现性要求,必须写进实现):inbound 与 cost 的条件都依赖 ctx.answers.task,因此必须排在 task 之后求值,不能与 task 同轮发出。

3.3 默认路径的问题序列

explicitTopics 为空时,实际问题数按 task 分流(原型 EXPERT_SCRIPT intro 自己写的是「最多 3 个问题…具体几个取决于你第 1 题怎么答」):

taskinbound 触发(口径 A:基线 34.7%)cost 触发月中 goalPeriod实际问题数
profit125/0.347 = 360 > 236 ✓✓+13(月中 4)
bsr163/0.347 = 470 > 236 ✓✗+12(月中 3)
test56/0.347 = 161 ≤ 236 ✗✗+11(月中 2)
clear175/0.347 = 504 > 236 ✓✓+13(月中 4)

⚠️ 若拍板改用口径 B(任务目标占比),上表 test 变成 56/0.208 = 269 > 236 → 要问,profit 变成 338 —— 「test 不问」这个反例会消失,届时「conditional 是真门控」的论证要重新举例。

skipWhen 速查:

3.4 missing 字段完整问法(成品文案)


task · 本周期运营任务 — 权重「必须」/ always

问:

这个周期,你想让这个品干什么?数据能算出结构上还剩多少效率空间,算不出你要拿这个空间换什么——选不同任务,目标 ACoS / 日预算 / 月广告订单是整套换掉,不是微调。

为什么问不了自动取:GOAL_SCHEMA 里 task 是 state=missing / askPolicy=always。mcp-data-routing.json 领星条目只注册 5 个 canonical 工具,没有任何字段记录「本周期的经营意图」。

影响:决定三个写盘值的全部取值区间 —— target_acos_pct 28.0%~44.7%、daily_budget_cap_usd $20~$52、monthly_orders 56~175(TASK_PROFILES, mock-data.js:73-101)。同时它是 inbound / cost 两题 condition 的输入。

valuelabeldetail
profit ⭐盈利模式目标 ACoS 28.0% · 日预算 $28(不加钱)· 月广告订单 113 → 125。已复算:$852÷113=$7.54 → 结构重分配后 $6.72,$840÷$6.72=125 单,$6.72÷$23.99=28.0%。planForShare(37) 独立验算得 125 / $6.72 / $840 / $28.0 / 28.0% / 10.4%,六个数全对。$28 在近 30 天实际日花费区间内
bsr冲 BSR 排名目标 ACoS 34.5% · 日预算 $45 · 月广告订单 163。$45 超过近 30 天单日最高 $32.10,163 单是外推值;合并动销 (163+213)÷30=12.5 单/天,236 件约 19 天。⚠️ planForShare(43.4) 给 161 单 / $44 / 34.1%,与 TASK_PROFILES 的 163/$45/34.5% 差 1~2 个单位,写码前需拍板以哪套为准
test测款 / 测词目标 ACoS 44.7% · 日预算 $20 封顶 · 月广告订单 56。TASK_PROFILES.test 标了 offCurve:true —— 不在 CPO 曲线上,不能用 planForShare 验算,只能作独立口径引用。买的是搜索词样本不是订单
clear清库存目标 ACoS 37.2% · 日预算 $52 · 月广告订单 175。合并动销 (175+213)÷30=12.9 单/天,236 件约 18 天清完。$52 同属外推区(ext:true)

默认值:无(always)。⚠️「$ARGUMENTS 里已含意图即视同已答」这条豁免规则要落地必须先改 SKILL.md Step 2,见 §3.2 规则 0。


goalPeriod · 目标周期 — 权重「重要」/ conditional 【本轮新增字段】

问:

这版目标按哪个周期算?我所有数都是 30 天口径;今天是月中的话,「本月剩余」和「完整 30 天」不是一回事。

为什么必须有:workspace_operational_goal.schema.json 的 required 是 [schema_version, primary_asin, period_id, effective_from] —— period_id / effective_from 是必填,但 GOAL_SCHEMA 33 个字段里没有任何一个负责它们(dataWindow 是取数回溯窗口,不是目标周期)。workspace_goal_proposal.schema.json 的 properties 里完全没有 period 相关字段,卡片承载不了周期。

影响:write-workspace-targets.ts:241-242 在缺省时静默填 period_id=当月、effective_from=今天。后果:8/13 确认目标 → effective_from=2026-08-13,但 monthly_orders 是按完整 30 天算的,写盘那一刻口径就错了,且卡片上看不见这个默认。

valuelabeldetail
fullMonth ⭐完整自然月period_id=<YYYY-MM>-monthly,effective_from=下月 1 号。三个数原样成立,不需按天折算
remainingMonth本月剩余effective_from=今天,period_id=<YYYY-MM>-partial。monthly_orders 必须按剩余天数折算后再写盘(例:剩 18 天 → 125×18/30=75),否则卡片与文件口径不一致
rolling30未来 30 天滚动effective_from=今天;period_id 命名规则待确认(schema 只要求 minLength≥1)。三个数原样成立,与 UI 按自然月展示的对账口径需确认

默认值:⚠️ 当前后端行为 = period_id 取当月、effective_from 取今天,且不经过卡片。在改成显式字段前,rationale 里必须写一句「本目标按 <period_id> 计,effective_from=<日期>」。


inbound · 在途 / 未上架库存 — 权重「重要」/ conditional

问:

领星没有在途库存接口,我看不到有没有补货在路上。按你选的口径,这 30 天的总需求超过 FBA 在售的 236 件——这题直接决定我要不要把预算推过 $32.10(近 30 天真实出现过的最高单日花费)。

影响:决定 daily_budget_cap_usd 与 monthly_orders 取守成值还是证据区上沿,以及「可售天数 22 天 · 有断货风险」是否成立。

valuelabeldetail
none ⭐没有在途按 236 件 ÷ 10.9 单/天 = 22 天售罄。日预算锁 $28、monthly_orders 125,周期最后 8 天按断货处理,rationale 写明
partial有,但补不满这周期断货点后移,缺口仍在。日预算仍取守成 $28,不进外推区,125 单不变
full有,够覆盖整周期库存不再是约束,放到证据区上沿。按 planForShare(SHARE_EVIDENCE_MAX=39) 复算:136 单 / CPO $6.99 / 花费 $950 / 日预算 $31.7 / ACoS 29.1%。⚠️ ACoS 会从 28.0% 抬到 29.1%。❌「137 单 / $32」不成立:137 单对应占比 39.14% > 39,按原型定义就是外推
unknown我不确定等同「没有在途」保守处理:22 天、$28、125 单,rationale 额外标注这是假设不是事实

默认值:在途 = 0,可售天数 22 天,日预算不上探,rationale 注明「按 0 在途保守估计」。 ⚠️ 前置修复(否则这题是纯装饰):原型 finishExpertBackground 里 share = risk ? Math.min(p.adShare, 39) : p.adShare,profit 的 adShare=37 < 39,两个分支结果完全一样 —— 照抄原型的话 profit 口径下答不答 inbound 三个数一个都不变。必须把 full 分支显式抬到 39%(得 136 单 / $31.7 / 29.1%)。


cost · 产品成本区间 — 权重「重要」/ conditional

问:

还有一项我拿不到:产品成本。没有它我能给你目标 ACoS,但算不出保本线——也就没法告诉你这个目标到底赚不赚钱。给个区间就行,不用精确数。

影响:只影响目标卡上的「保本 ACoS 与安全垫」一行,不改任何写盘值。 跳过则本轮不出利润类结论。

valuelabeldetail(已复算)
lt5 ⭐低于 $5按上界 $5 + 15% 佣金 $3.60,毛利 $23.99−$5−$3.60=$15.39,保本 ACoS 64.2%。目标 28% 剩 36.2pt 垫,清库存 37.2% 也安全(未计 FBA 配送费,实际更低)
5to10$5 – $10按上界 $10:毛利 $10.39,保本 ACoS 43.3%。目标 28% 剩 15.3pt;清库存 37.2% 只剩 6.1pt,已贴边
gt10$10 以上取 $12 参考:毛利 $8.39,保本 ACoS 35.0%。目标 28% 剩 7.0pt;清库存 37.2% 越过保本线 2.2pt,是零毛利出货
skip先跳过不填成本,本轮不出利润类结论,目标卡标注「未判断是否赚钱」。三个写盘值完全不变

默认值:不问也不填;目标卡标注「未判断是否赚钱」。task ∈ {bsr, test} 时永不触发。 ⚠️ 15% 家居类目佣金是原型的外部假设,仓内没有佣金率数据源 —— 写进 rationale 时必须标成假设。


promo · 促销与折扣计划 — 权重「重要」/ defaultOnly

问(仅在用户主动点名时):

近 30 天订单均价比 listing 价低了约 {gapPct}%,看着像挂过 Coupon 或折扣——领星不回传促销日历,订单行也不单列折后价,所以我判断不了。这段时间挂过折扣吗?

影响:基线客单价是 ACoS 换算的分母。客单价降 10%,同样单量的 ACoS 从 31.2% 抬到 34.7%(+3.5pt,31.2÷0.9=34.67)—— 目标 ACoS 28% 实际要按 31.5% 才等价。

valuelabeldetail
none ⭐没挂过按 listing 价 $23.99 作基线,CVR 6.8%、ACoS 31.2% 直接沿用,不做校正
pastOnly挂过,本周期不挂剔除折扣日后重算客单价与 CVR 再推目标值;剔除后剩余样本不足 14 天则明说样本不够,不给假精确的数
ongoing挂过,本周期还挂按折后客单价重算:客单价每降 10%,target_acos_pct 需抬约 3.5pt 才等价,daily_budget_cap_usd 同步下修以守住同一个 CPO
unsure记不清了按没挂处理走默认基线,rationale 标注「基线可能含折扣期,客单价存在高估风险」

一句话告知(默认文案):

• 促销与折扣计划 —— 按近 30 天没挂、本周期也不挂处理:客单价 $23.99、CVR 6.8% 直接作基线,不做校正。

guard · 运营护栏 — 权重「可选」/ defaultOnly

问:

这一轮的调整步长上限按什么走?(本轮 auto 组预算要从 $8 降到 $5,−37.5%,会撞到「预算单次 ≤20%」那条。)

影响:不改本轮 ACoS / 预算 / 订单目标,只改策略生成阶段的调整步长上限。

valuelabeldetail
keep ⭐单次调价 ≤10%wall mounted shelf 理论价 $0.73($23.99×12.03%×28%×0.90),护栏内本轮只提到 $0.68($0.62→$0.68 = +9.7%),$0.73 留到下一轮。❌ +10.9% 是另一个词 no drill wall shelf($0.55→$0.61)的数
enableBudgetCap再加预算 ≤20%auto 组预算改成 $8 → $6.40(−20%)而不是一次降到 $5,剩余降幅下轮执行
relax本轮放宽上限允许 wall mounted shelf 一次调到 $0.73(+17.3%)、3 tier floating shelf 一次降到 $0.38(−37.0%),记入变更日志。四个主力词理论价 $0.73 / $0.58(维持)/ $0.76 / $0.38

默认值:⚠️ 不能照抄「沿用已启用的两条」 —— 仓内读不到「已启用」这个状态。seed-amazon-ad-workspace.ts:2373-2377 显示 targets/hard_constraints.md seed 出来只有两行 markdown:「每日预算计划:(待 operator 填写)」「每日预算上限:(待 operator 填写)」,连「单次调价 ≤10%」「不动品牌词」这两个概念都不在文件里。原型 GUARDRAILS(mock-data.js:470)四条是原型常量,不是仓内事实。

安全默认(落地前):按「单次调价 ≤10%」执行,并在 rationale 里明写「hard_constraints.md 未填写,本轮按保守步长处理」。

一句话告知:

• 运营护栏 —— hard_constraints.md 未填写,本轮按保守步长处理:单次调价 ≤10%,超出部分拆到下一轮。

brandTerm · 品牌词归属 — 权重「可选」/ defaultOnly

问:

SB 品牌组近 30 天 30 单、ACoS 22.9%。我默认按「这些词是你自投、且可能被人抢」处理,本轮不动它。要改这个假设吗?

影响:决定 SB 组那 30 单算不算广告增量。剔掉后增量 ACoS = $852 ÷ (83 × $23.99) = 42.8%;target_acos_pct 28% 的可达性判断跟着变。

valuelabeldetail
ownDefend ⭐自投防守30 单全部计入广告增量,ACoS 口径维持 31.2%,本轮不碰品牌组
incrementalZero算自然单SB 30 单剔出广告增量,增量 ACoS 从 31.2% 重算到 42.8%,目标 ACoS 能否定在 28% 需重评
notBrand不是品牌词按普通手动组处理,纳入本轮调价范围,SB 组 $6/天预算进重分配池

一句话告知:

• 品牌词归属 —— 按「你自投、且可能被抢」处理:SB 组 30 单全部计入广告增量,本轮不碰品牌组。

lossCap · 花费天花板 — 权重「可选」/ defaultOnly

问:

花费上限我按「日预算 × 30,且单日不超过近 30 天真实出现过的最高值 $32.10」处理,全程不外推。要给一个不一样的授权吗?
valuelabeldetail
evidenceOnly ⭐守在证据区证据区上限是占比 SHARE_EVIDENCE_MAX=39 → 136 单 / 日预算 $31.7 / 周期花费 $950 / ACoS 29.1%。❌「最高 137 单」不成立(对应 39.14%,已越线)。⚠️ $32.10 是单日最高花费,拿它当 30 天日均上限是口径错配(30 天实际日均 $28.40)
allowExtrapolation允许进外推区日预算可到 $45 / $52,对应 163 / 175 单。超出历史单日花费上限,属模型外推,置信度只到「中」
periodTotal我给周期总额⚠️ 需研发补一个反函数:仓内没有 BUDGET_CURVE 这个常量(该名字只出现在 mock-data.js:75 的注释里)。真实实现是 CPO_ANCHORS + cpoAt() + planForShare(),而 planForShare 的自变量是广告订单占比不是预算。要从「周期总额 ÷ 30」反推订单数,必须解 orders × cpoAt(orders) = spend30(单调,可二分求解)—— 这段代码仓内不存在

一句话告知:

• 花费天花板 —— 周期花费上限 = 日预算 × 30,单日不超过历史最高 $32.10,全程不外推。

⚠️ task=bsr/clear 时不加问,改为在 rationale 里一句话声明外推(否则默认路径会变成 4 题)。


season · 大促与上新节点 — 权重「可选」/ defaultOnly

问:

本周期我按「无大促、无上新」处理:日预算平铺 30 天,不做分段。周期内有节点吗?
valuelabeldetail
none ⭐没有节点$28 × 30 天平铺,daily_budget_cap_usd 即每天的上限,周期花费 $840
promoEvent有大促 / 秒杀预算分段:节点期单独给日预算、节点外回落。⚠️ 分段无处落盘 —— schema 只有单一 daily_budget_cap_usd 且 additionalProperties:false,只能写进 rationale
newLaunch有上新 / 改版改版后 CVR 6.8% 的基线失效。本轮目标只按改版前窗口给,并标注改版后需重新取数再定一版

一句话告知:

• 大促与上新节点 —— 按本周期无大促、无上新处理:日预算平铺 30 天,不做分段。

positioning · 价格定位 — 权重「可选」/ defaultOnly

问:

要不要告诉我这个品在价格带里的定位?先说清楚:本轮出价公式是 售价 × 该词 CVR × 目标 ACoS × 0.90,里面没有定位这个变量——你答了也改不动任何一个数。

影响:本轮不影响任何输出值。 出价公式无定位系数,保守系数固定 0.90(逐词复算:$23.99×12.03%×28%=$0.808×0.90=$0.73;四个主力词 $0.73 / $0.58 / $0.76 / $0.38)。

valuelabeldetail
midRange ⭐中价位保守系数保持 0.90,四个主力词出价与不答完全一致
premium高价位只作背景记录,本轮出价一个数都不变;等竞品价格数据源接入后才生效
value低价位同上,本轮出价不变

一句话告知:

• 价格定位 —— 按中价位 · 主流价格段处理;出价公式保守系数固定 0.90,不含定位变量。

4. 输出与落盘

4.1 产出字段与算式

key落盘键类型算式 / 来源
primaryAsinprimary_asin^[A-Z0-9]{10}$从 product_meta.json 读;与 $ARGUMENTS 冲突时以产品目录为准(SKILL.md:47「never change it from $ARGUMENTS or invent a different ASIN」;后端 write-workspace-targets.ts:47 readProductMetaPrimaryAsin)
periodIdperiod_idstring缺省 ` ${isoMonthInReportingTz(now)}-monthly (write-workspace-targets.ts:167/241)。**禁止 -seed` 结尾**,见 §4.3 陷阱
effectiveFromeffective_fromdate缺省 isoDateInReportingTz(now)(Pacific 报表日,:171/242)
adOrderSharePct🚫 无落点number 0–100,且 ≤ 55用户输入。基线 = adOrders30 ÷ totalOrders30 × 100。上限来自 mock-data.js:119-120:SHARE_EVIDENCE_MAX=39 / SHARE_MODEL_MAX=55(planForShare 对 >55 直接 beyond:true 且全部返回 null)
operatingRhythm🚫 只能塞 status_noteenum `profit\bsr\test\clear`用户第一问答案
advertisingOrdersMonthlyadvertising_orders_target(仅 origin/dev, v1.1)number ≥ 0恒等式一:round(organicOrdersBaseline × p ÷ (1 − p))(mock-data.js:140)
targetCpoUsd🚫 无落点number ≥ 0cpoAt(advertisingOrdersMonthly) —— 不是自由输入,见 §5.2
targetAcosPcttarget_acos_pctnumber ≥ 0,最多两位小数恒等式二:targetCpoUsd ÷ price × 100(mock-data.js:143)
targetTacosPcttarget_tacos_pctnumber ≥ 0,最多两位小数恒等式三(精确恒等):targetAcosPct × adOrderSharePct ÷ 100(mock-data.js:146)
dailyBudgetCapUsddaily_budget_cap_usdnumber ≥ 0advertisingOrdersMonthly × targetCpoUsd ÷ 30(mock-data.js:141,145)
monthlyOrdersmonthly_ordersinteger ≥ 0round(organicOrdersBaseline + advertisingOrdersMonthly) = round(organic ÷ (1 − p))。语义 = 店铺总订单(load-workspace-performance.ts:1033/1170/1312 actualMonthlyOrders = orderTotals.totalOrders,:408 label「月销量」)
organicOrdersBaseline🚫 无落点numbertotalOrders30 − adOrders30。恒等式一的分母,不落库就无法复算目标
budgetEvidenceFlag🚫 无落点enum `evidence\extrapolated`两种等价写法:adOrderSharePct > 39 或 dailyBudgetCapUsd > $32.10。等价性已验:planForShare(39)→$31.7 ≤ 32.10;planForShare(40)→$34.4 > 32.10
statusNotestatus_notestring(无约束)目前是 operatingRhythm / 占比 / CPO / 证据标记 / 假设五类信息的唯一出口。决定 parse-operational-goal-json.ts:134 的 narrative 与 :122 的 configured 判定
promotionPausedpromotion_pausedboolean用户显式选择。true 时 parse-operational-goal-json.ts:143 把 mainGoal 置为「暂停推广」;随后 load-workspace-targets.ts:141-143 resolveStatus 返回 'paused'
sourcesourceenum `operator_input\agent_proposal`见 §4.3 速查表。后端兜底 operator_input(write-workspace-targets.ts:246-247);白名单校验 routes/workspace-targets.ts:265-268
updatedAtupdated_atdate-time由 saveWorkspaceTargets 统一写(:243),agent 不要自己填。同一个值作为 history 行的 ts(:277)
rationale仅 proposal 卡string,minLength 1由 evaluator 生成,逐句可追溯到 provenance lock 允许的来源。⚠️ 落到 active target 时被丢弃(§4.4 #9)

⚠️ monthly_orders 取整陷阱:schema 是 type:"integer",但路由用 optionalFiniteNumber(routes/workspace-targets.ts:277-278)、assignOptionalNumber(write-workspace-targets.ts:188-196)原样透传。新恒等式 organic ÷ (1 − p) 几乎必然产生小数(213 ÷ 0.63 = 338.10),直接 POST 会被 AJV 判 422「must be integer」。skill 必须自己 Math.round。(advertising_orders_target.value/min/max 是 type:"number",不受此限。)

⚠️ target_acos_pct 小数位:routes/workspace-targets.ts:240 optionalPercentNumber → hasAtMostTwoDecimalPlaces,超了抛 InvalidSaveInputError。

4.2 两条落盘路径

Mario 的口径(Linear PRO-118 评论 fbbd20c2-c813-4859-8a7b-485c37502f91,2026-07-20T08:58:32Z)

  1. set 入口(/set-ads-goal 这类「设置目标」)→ 直接写 active target:products/<product>/targets/current_period.json + append targets_history.jsonl + append products/<product>/operator_inputs.jsonl,标 source=agent_proposal;UI 展示「已生成并保存,可编辑」。
  2. evaluate 入口(「评估目标 / 这个目标合理吗」)→ 只落 durable proposal 到 products/<product>/targets/proposals/<timestamp>_goal_proposal.json,UI 卡片确认后再写 active。

PRO-118 当前状态:In Progress(07-20 08:09 曾置 Done,09:00 被打回,至今未闭)。assignee Guanglun Ren。这条口径至今未实现。

路径 A —— set 入口(现状与 Mario 口径不一致)

⚠️ 「禁止 skill 自己写盘」在仓里有三处落点,只改一处修不了:

  1. apps/backend/vendor/luckee-ads-skills/luckee2.0/pipeline-orchestrator/set-ads-goal/SKILL.md:139 —「Never write current_period.json / operator_inputs.jsonl / targets_history.jsonl yourself from this skill」;同文件 :17 更硬:「Luckee denies that path unless write_validated_json uses workspace_operational_goal.schema.json」。这是 git submodule(pin 在 CalVer tag,走 skills-bump.yml PR 门禁),改它是跨仓动作。
  2. apps/backend/scripts/seed-amazon-ad-workspace.ts:223(ORCHESTRATOR_GOAL_SECTION_CODEX)。
  3. apps/backend/src/amazon-ad-seed/protocol-markdown.ts:67 / :82 / :83 / :152(中英双份,:82-83 明确写了 PRO-118 的 same-turn commit 规则)。

唯一真正会写盘的实现:apps/backend/src/workspace/targets/write-workspace-targets.ts::saveWorkspaceTargets,入口 POST /api/workspaces/:id/targets(路由 routes/workspace-targets.ts:297,挂载 app.ts:288)。 写序(:265-292):mkdir → 写唯一命名临时文件 .tmp-<pid>-<uuid> → append history → rename 提交;append 失败在 rename 之前中止并 rm 临时文件,两个文件不会劈叉。 history 行 shape:{ts, type:"target_update", source, updated_target, summary},summary 由 buildSummary(:175-186) 机器拼。 它不写 operator_inputs.jsonl(Mario 第 3 条未实现,见 §4.4 #12)。

路径 B —— evaluate 入口

  1. 写 proposal,不碰 active target。实际路径不是 Mario 写的那个,而是 thread 沙箱下的 skill-to-ui/workspace_goal_proposal/<primary_asin>_<marketplace>_<YYYYMMDD>_<HHMMSS>.json(目录常量 WORKSPACE_GOAL_PROPOSAL_DIR 在 confirm-workspace-goal-proposal.ts:9)。

⚠️ 两份种子文件对「怎么写」互相矛盾:set-ads-goal/SKILL.md:119 与 :126-129 强制 mcp__write-validated-json__write_validated_json + schema_path=shared/schemas/workspace_goal_proposal.schema.json,并写明 NOT raw Write/Edit;而 seed-sources/amazon-ad-agent/shared/agents/ads-goal-evaluator-prompt.md(Path 在第 78 行,写法在第 82 行)说「Write it with the Write tool」。只有前者会走 AJV 校验。研发要先统一。

  1. 位于 thread 沙箱 working dir:confirm-workspace-goal-proposal.ts:110-119 走 resolveWorkspacePaths(workspaceId, threadId).workingDir,sessions/paths.ts:129-141 解析成 <home>/workspaces/<id>/worker-home/projects/<projectThreadId>/working_dir —— 每 thread 一份,换会话看不到。
  2. 用户点确认 → 前端 POST /api/workspaces/:id/targets,带 threadId + proposalFilePath(routes/workspace-targets.ts:290-293)。后端顺序(:350 → :377 → :379-403):先 saveWorkspaceTargets 真正落 active target → invalidateWorkspaceOverviewCache → 再 confirmWorkspaceGoalProposal 记账(给卡片 JSON 打 confirmed/confirmed_at/confirmed_values/agent_draft,并对每个曾渲染过该文件的 parent_id 追加 file_monitor 帧,findAllFileMonitorParentIds:64-94)。记账失败只 log.warn(:387/:397),不回滚 —— 目标已落库、卡片可能仍显示可编辑,是已知不一致。
  3. agent_draft vs confirmed_values 的差集即 dirty-field 标记。

4.3 source 标记规则 + seed 陷阱

场景请求 source落库 source读回 TargetSource
set 入口 agent 直接落库、用户未改agent_proposalagent_proposalagent_proposal
用户在卡上改了任意一个数再确认operator_inputoperator_inputoperator_direct
Goal Setup 表单手填operator_inputoperator_inputoperator_direct
请求里没带 source—兜底 operator_inputoperator_direct

(映射见 load-workspace-targets.ts:106-136 inferSource。)

🚨 必须避开的陷阱:parse-operational-goal-json.ts:120 — isSeedOnly = raw.period_id.endsWith("-seed") && raw.source === "agent_proposal" 命中则 :122 configured=false,再经 load-workspace-targets.ts:147-149 resolveStatus 返回 'seed'。 即:路径 A「直接写 active target 且 source=agent_proposal」时,period_id 绝对不能沿用 <date>-seed,必须 YYYY-MM-monthly(write-workspace-targets.ts:167 currentMonthPeriodId 的缺省值就是对的,别覆盖它),否则目标写进去了、看板还是「未设目标」—— 和 PRO-118 的表现一模一样。

4.4 现有 schema 装不下的字段(研发要先修)

#缺口代码位置影响
#1adOrderSharePct 完全没有落点canonical apps/backend/seed-sources/amazon-ad-agent/shared/schemas/workspace_operational_goal.schema.json(additionalProperties:false,properties 已逐条读完);后端镜像 operational-goal-schema.ts:19-83;drift guard __tests__/operational-goal-schema.test.ts:11;WorkspaceTargetsApi.SaveInput(packages/shared/src/api/envelope.ts:1490-1548);workspace_goal_proposal.schema.json本轮新口径的核心主输入无处存。多写一个键必 422。需新增 ad_order_share_pct(number, 0–100,建议 maximum:55)到 operational goal schema + proposal schema + SaveInput + 路由解析。(全仓唯一出现「广告订单占比」的地方是只读参考资料:vendor/luckee-ads-skills/amazon_ads_skills_bundle/ad-goal-evaluator-skill/references/field-requirements.md:148 baseline_ad_order_share、operator-target-derivation.md:32 ad_order_share —— 报告字段,从未进过持久化模型)
#2proposal 卡装不下广告订单目标workspace_goal_proposal.schema.json(dev 上仍无 advertising_orders_target,additionalProperties:false)API 层已能收(routes/workspace-targets.ts:265-330 optionalAdvertisingOrderTarget、envelope.ts:1539、write-workspace-targets.ts:244,254-255),缺的只有卡片这一环。evaluate 路径产出的卡片带不了广告订单目标。LUC-1483 只修了 active target 一侧
#3confirmed_values 装不下对象confirm-workspace-goal-proposal.ts:38-54 buildConfirmedValues 返回 `Record<string, string\number\boolean>`即便 #2 修好,confirmed_values/agent_draft 仍会静默丢掉 advertisingOrdersTarget,dirty-field 比对漏判
#4本地 checkout 落后本地 dev @ 2763ce07;LUC-1483 的 PR #1044 已在 origin/dev(fb93ccf6)本地 operational-goal-schema.ts 仍是 schema_version const "1.0"、无 advertising_orders_target;parse-operational-goal-json.ts:148 仍硬编码 ordersPerDayText: null(dev 上第 189 行起已改为条件表达式)。开工前必须 git pull
#5targetCpoUsd 无字段—它是整条推导链的枢纽(share → orders → cpoAt(orders) → acos → tacos/budget)。落库后只能当 status_note 散文;下轮读回必须反解,而 CPO 是分段折线(9 锚点),反解不唯一,必然漂移
#6operatingRhythm 无枚举字段status_note 是无约束 type:"string";parse-operational-goal-json.ts:143 的 mainGoal 只有「暂停推广」/ null 两种取值它是 askPolicy=always 的第一问,切换它整套换掉四个数 —— 不落库等于每轮重问
#7organicOrdersBaseline 无字段—恒等式一的分母不落库 → 读回时无法复算、无法解释「为什么是 125 单」,也无法判断占比目标是否已因自然单变化而失效
#8周期只能是自然月schema 有 period_id + effective_from,没有周期结束日;resolveGoalMonthPeriodState(load-workspace-performance.ts:194-207)强制 snap 成 ${targetMonth}-01 ~ endOfIsoMonth(targetMonth),且 :201 const targetMonth = configuredMonth < currentMonth ? currentMonth : configuredMonth 会把过期月份静默前滚到当前月滚动 30 天 / 跨月 / 大促分段周期都表达不了;8 月定的目标到 9 月会不声不响按 9 月重新计分
#9rationale 落 active target 时丢失proposal schema 里 required + minLength 1;active target schema 没有该字段。saveWorkspaceTargets 组装 record(write-workspace-targets.ts:238-256)只带 status_note确认落库那一刻依据就丢了,只剩 history 里机器拼的一行
#10monthly_orders 取整schema type:"integer" vs optionalFiniteNumber 透传见 §4.1 取整陷阱,422
#11证据/外推边界与假设无处可落—缺 budget_evidence_flag(或 assumptions 对象)承载:是否越过 $32.10 / 是否超过占比 39 / 在途按 0 计 / 成本未知 / 无促销。这些正是 6 个 defaultOnly 字段的 defaultValue,落库后全部蒸发。复看目标时无法区分「$45 是外推」和「$28 是证据内」
#12不写 operator_inputs.jsonlwrite-workspace-targets.ts:271, 289 只 append targets/targets_history.jsonlMario 第 3 条要求 append products/<product>/operator_inputs.jsonl。读侧已就绪 —— loadTargetHistory 读 products/<nickname>/operator_inputs.jsonl(load-target-history.ts:181-182)—— 只差写侧
#13set/evaluate 未分流三处落点(见 §4.2 路径 A)产品 + 跨仓改动,不是纯后端
#14⚠️ template working_dir vs thread 沙箱saveWorkspaceTargets(:214)调 resolveWorkspaceWorkingDir → agents/mcp-catalog.ts:103-118 优先返回 resolveTemplateWorkingDir;agent 侧 write_validated_json 写 thread 沙箱(sessions/paths.ts:129-141)。workspace-project-materializer.ts 全部同步函数单向 template → thread(:625/:694/:806/:481/:503),无反向同步矛盾未解:仓里三处种子文案(seed-amazon-ad-workspace.ts:223、protocol-markdown.ts:82-83、ads-goal-evaluator-prompt.md:15/94)都把「input-capturer + write_validated_json」写成合法 SoT commit 路径。要么这条被官方祝福的路径本身落不到看板(PRO-118 之外还有 bug),要么存在未找到的回流机制。必须拍板,不能按一半结论设计路径 A
#15seed 判定陷阱parse-operational-goal-json.ts:120见 §4.3
#16proposal 不在产品树、不跨会话见 §4.2 路径 B 第 2 条Mario 提的持久目录未实现
#17marketplace 只在 proposal 卡active target schema 无多站点同 ASIN 的目标无法区分
#18vendor 副本 schema 已漂移apps/backend/vendor/luckee-ads-skills/shared/schemas/workspace_operational_goal.schema.json 仍 const "1.0"、无 advertising_orders_target;target_acos_pct/target_tacos_pct 仍带 "maximum": 100(proposal 同)。drift guard 只锁 seed-sources 那一份运行时不受影响(seed-amazon-ad-workspace.ts:1296-1316 SHARED_VALIDATOR_OVERLAY_FILES 用 seed-sources 版 cp --force 覆盖,注释 :1302 明说是 Luckee-only overlay),但加字段的改动点是 4 处不是 2 处(seed-sources JSON + operational-goal-schema.ts + submodule JSON + envelope.ts:1490 SaveInput);只改 submodule 那份会被 seed 静默覆盖。建议加 lint 或删除死副本
#19dev 上有新迁移文件migrate-legacy-operational-goal.ts、migrate-legacy-operational-goals-in-working-dir.ts(外加 resolve-canonical-operational-goal.ts、canonical-operational-goal-chat-context.ts)做 schema 扩展时这些迁移路径要同步

另:operator_input.schema.json(vendor/luckee-ads-skills/shared/schemas/)的 required = [ts, type, captured_at_cp]、type 枚举含 target_update、未声明 additionalProperties —— 严格说非数值答案可以带类型化键写进 JSONL 而不违反 schema,但没有任何下游会读它,结论(不可结构化回读)仍成立。

⚠️ operator_inputs.jsonl 路径仓内自相矛盾:后端认产品目录根(seed-amazon-ad-workspace.ts:2379-2380 写、load-target-history.ts:181 读 products/<nickname>/operator_inputs.jsonl),而 set-ads-goal/SKILL.md Step 3 与 generate-ads-strategy/SKILL.md:89 写的是 products/<nickname>/targets/operator_inputs.jsonl。照后者写,后端 loadTargetHistory 永远读不到。需拍板。


5. 本轮口径变更:日预算 → 预期广告订单占比

5.1 三条恒等式(全程成立,可交叉验算)

① 广告订单   = 自然订单 × p ÷ (1 − p)        p = adOrderSharePct / 100
② ACoS       = CPO ÷ 售价 × 100
③ TACoS      = ACoS × p                       ← 精确恒等,不是近似

代码出处:mock-data.js:140 / :143 / :146(planForShare)。 自然订单按 30 天固定(ORGANIC_30D = 213 = 326 − 113),保守:不假设广告能带动自然单。

5.2 CPO 不是自由输入,是订单数的函数 ⚠️

const CPO_ANCHORS = [[0,5.60],[94,6.38],[110,6.55],[125,6.72],[137,7.01],
                     [149,7.65],[163,8.28],[175,8.91],[213,10.60]];   // mock-data.js:117-118
// cpoAt(orders): 锚点间线性插值,超出末锚点线性外推(:122-131)

推导方向单向:share → orders → cpo → acos → tacos/budget。p 一变 CPO 就变,不能当常数乘数。 ❌ 把 targetCpoUsd 写成「用户直接给 / 由 ACoS 反推」等于删掉「边际获客成本递增」这条核心建模假设。

例外:test(测款)口径 offCurve:true(mock-data.js:85),CPO $10.71 不在曲线上(cpoAt(56) ≈ $6.06)。必须单开分支,否则测款模式算出来的预算会低一半。

5.3 取值范围 · 证据区 vs 外推区

const SHARE_EVIDENCE_MAX = 39;   // 日花费 ≤ 历史上限 $32.10 对应的占比
const SHARE_MODEL_MAX    = 55;   // 超过这里不再给数字

39 不是拍的:planForShare(39) → 136 单 → CPO $6.99 → 日预算 $31.7 ≤ $32.10;planForShare(40) → 142 单 → $34.4 > $32.10。它精确等于「日预算 ≤ 历史单日最高」那条线的整数占比上界 —— 与 budgetEvidenceFlag 是同一条规则的两种表达。

❌ 不要用「上限 90% / UI 滑杆 90% / p > baseline×1.5 标外推」 —— 那是拍脑袋的,且四个任务口径的目标占比 20.8~45.1 全部落在 55 以内,p→1 发散根本不会发生,夹到 90 既无依据也不起作用。

5.4 四口径全链路验算(全部精确复现 TASK_PROFILES)

口径porderscpospend30budgetACoSTACoS标记
基线34.66113 ✓$7.54$852$28.40 ✓31.4%¹10.9% ✓—
profit37.0125 ✓6.72 ✓(锚点)84028.0 ✓28.0 ✓28.0×0.37=10.4 ✓证据区
证据上沿39.01366.9995031.729.111.3证据区边界
bsr43.4163 ✓8.28 ✓1349.645.0 ✓34.5 ✓15.0 ✓ext:true
clear45.1175 ✓8.91 ✓155952.0 ✓37.1(表内 37.2,取整残差)16.8 ✓ext:true
test20.856 ✓10.71 ≠ cpoAt(56)≈6.06—2044.79.3 ✓offCurve:true

¹ 基线 ACoS 有两个值:BASE.acos = 31.2(852÷2731)与 852÷113÷23.99 = 31.4,差值来自广告销售额 $2,731 vs 113×$23.99=$2,711 的归因残差(mock-data.js:14 注释已写明)。它只影响基线展示,不进目标公式。

5.5 已被证伪、不要重新引入的说法

5.6 对既有字段的影响

既有字段变化
dailyBudgetCapUsd从主输入降级为因变量(orders × cpo ÷ 30)
monthlyOrders语义确认为店铺总订单(= organic + ad),不是广告订单
advertisingOrdersMonthly新的广告侧目标,落 advertising_orders_target(v1.1,仅 origin/dev)
targetAcosPct / targetTacosPct全部由 p 唯一决定,不再独立输入
budgetEvidenceFlag新增派生标记,与 lossCap 的授权边界联动
UI主输入控件从「日预算」改成「广告订单占比」,滑杆区间 [0, 55],> 39 显示外推样式,> 55 不出数字只出说明

6. 未决问题(需产品或业务先拍板)

#问题该问谁不拍板的后果
6.1在途库存口径(本轮最关键)。真实抓包 products/HT02/.../_raw_mcp/listing.json 的 inventory_level.fba_warehouse.breakdown 里,in_transit(「标发在途」)/transferring(「调仓中」)/receiving(「入库中」)、overseas_warehouse.breakdown.transit(「在途库存」)、local_warehouse.breakdown.under_review(「调货在途」)/pending_order(「待到货量」)都是真实存在的键并带中文释义。这与「领星无在途接口」的既定边界直接冲突研发(确认字段真实含义与可靠性)inbound 追问是否降级、daysOfSupply/stockHealth/预算上限是否全部重算,整套追问逻辑取决于此
6.2impressionShare 的 provider 不对称:星商 get_target_exposure 回传 top_of_search_impression_share(0..1,关键词×日),领星 per-ASIN MCP 无此字段产品A2 是否允许工作区间能力不对称,还是统一按最小公分母(领星)定契约、份额一律不进目标卡
6.3spendPace 全口径日序列:list_keywords_placement_hourly_with_config 只覆盖关键词投放;auto/商品定位需走 postLingxing("campaigns-daily") 或 DB 表 workspace_ads_daily(lingxing 的 campaign_key 固定哨兵 "__total__",无「某活动某天」维度)研发skill 运行时能否读本地表/调后端路由;否则 evidenceCeiling 只能标「关键词口径下界」
6.4inbound 触发条件的分母:(A) 基线占比 34.7%(原型原样)→ profit 360、test 161 不问;(B) 任务目标占比 TASK_SHARE → profit 338、test 269 也问。只有 (B) 与恒等式自洽,但只有 (A) 有「test 不问」这个反例产品选 (A) 还是 (B) 决定默认路径的问题数分流表,也决定「conditional 是真门控」的论证是否成立
6.5bsr 口径两套数打架:TASK_PROFILES.bsr = 163 单/$45/34.5%,而 planForShare(43.4) = 161 单/$44/34.1%产品写码前必须定一套;两套并存会导致卡片数与复算数对不上
6.6weekVsMonth 的噪声阈值 + maturity 的评论数阈值 —— 两者在仓库里都没有任何定义(已全量 grep)产品(给可写死常量)只能由 LLM 每轮临时拍,同一个品两轮对话可能给出相反结论。在给出常量前,这两个字段不应进入结论
6.7库存耗尽用「单/天」还是「件/天」:get_orders 给订单数、get_asin_sales 给件数(metric: "store_sales_units"),原型用的是订单数(10.9 单/天)研发(建议件/天)断货天数与周期缺口件数全部受影响
6.8daysOfSupply 三套算式冲突:① 236 ÷ 10.87 = 21.7;② 缺口 30 × 10.87 − 236 = 90 件;③ askTriggered 的 needUnits = TASK_ORDER_GOAL[task] ÷ (adShare/100) = 360。三者互不一致,且 ③ 依赖 task 的答案产品哪一个是正式触发条件;并明确 inbound 必须在 task 之后求值这一顺序约束
6.9护栏的结构化落点:hard_constraints.md seed 内容只有两行「(待 operator 填写)」,「单次调价 ≤10%」「不动品牌词」这两个概念在仓内根本不存在,更没有「已启用」标记产品 + 研发guard 的三个选项落不了盘;默认文案只能说「未填写,按保守步长处理」
6.10set / evaluate 两条落盘路径未分流,且「禁止 skill 写盘」写在三处(含 pin 住的 submodule)产品(定分流规则)+ 研发(跨仓改动)PRO-118 的原始症状「用户以为已经好了,结果目标没落盘」持续存在
6.11template working_dir vs thread 沙箱的回流机制(§4.4 #14):materializer 无反向同步,但三处种子文案把 write_validated_json 写成合法 SoT 路径研发不确认就设计路径 A,可能做出一条永远落不到看板的写盘链路
6.12卡片写法两份文档打架:set-ads-goal/SKILL.md:119/126-129 要求 write_validated_json(NOT raw Write/Edit);ads-goal-evaluator-prompt.md:82 说 Write tool研发只有前者走 AJV 校验;不统一则 proposal 卡可能带着 schema 外的键落地
6.13operator_inputs.jsonl 路径矛盾:后端读写产品目录根,SKILL.md 写 targets/ 子目录研发照 SKILL.md 写,loadTargetHistory 永远读不到这些行
6.14三套 provider 不对齐:本契约按领星写。星商多 get_campaign_space_exposure / get_target_exposure / get_advertised_asin / get_store_asin_info,keywords 行带 ad_group_id/ad_group_name;Amazon 直连多 inbound_* 与 SP-API产品A2 是否只按领星定、其余走降级
6.15领星路由表是否漏登记 get_orders(§2.3 C7)。运行时 lingxing_* server 确实暴露该工具,但 mcp-data-routing.json 领星条目未注册研发补登记前,领星产品上 adSharePct、promo 的 gap 判定、adDependency 全部求不出值

附录 · 关键文件(绝对路径,全部实读)

原型

Schema 与落盘

取数与 skill

真实抓包样本(键名以此为准)

前端

本次任务未做任何文件修改(只读)。