适用范围:/set-ads-goalskill、ads-goal-evaluatoragent、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。 标注约定:✅ = 已在仓库/真实抓包中逐字核实;⚠️ = 需研发或产品确认,不得当既成事实实现;❌ = 已被证伪的旧说法(保留以防重新引入)。
skill 要做的三件事:自动取数 → 只问取不到的 → 落盘。 自动取数的边界由领星/星商 MCP 决定;问人的边界由「数据源里根本没有这个事实」决定;落盘的边界由 workspace_operational_goal.schema.json 决定 —— 目前第三条最窄,是本轮最先要修的地方(见 §4.4)。
这是唯一的字段清单。前后端、skill、evaluator 均以本表 key 为准。 key 用小驼峰;落盘时的 snake_case 键名见 §4。 共 38 个字段 = known 22 / inferred 6 / missing 10。
| key | 标签 | 分组 | 类型 | 来源(简) | askPolicy | |
|---|---|---|---|---|---|---|
asin | 主 ASIN 与站点 | 对象 | object | product_meta.json | never | |
currentTargets | 磁盘上的现有目标 | 目标状态 | object | targets/current_period.json | never | |
price | 售价 | 对象 | object | get_product_listing → promotion_status.current_price(字符串) | never | |
reviews | 评分与评论数 | 对象 | object | get_product_listing → listing.rating / listing.review_count | never | |
currency | 币种一致性 | 对象 | object | get_orders.currency_code / economics.json / 库存 currency | never | |
dataWindow | 数据窗口与截止时间 | 对象 | object | 调用参数 + payload 顶层 data_retrieved_at | never | |
adPerf30 | 近 30 天广告大盘 | 广告表现 | object | get_campaign_metrics.summary | never | |
cpoNow | 当前单均获客成本 | 广告表现 | number\ | null | 同上,spend ÷ orders | never |
spendPace | 日均花费与历史单日上限 | 广告表现 | object | list_keywords_placement_hourly_with_config / campaigns-daily | never | |
adStructure | 广告结构规模 | 广告表现 | object | 各 list 工具的 total_* | never | |
budgetCaps | 各组日预算与广告位溢价 | 广告表现 | array + total | list_campaigns_config.data[] | never | |
spendMix | 分组花费 / 单量 / CPO | 广告表现 | array | get_campaign_metrics.metrics[] | never | |
wasteSpend | 0 转化搜索词花费 | 广告表现 | object | list_search_terms_with_config | never | |
bidUtilization | 出价利用率 | 广告表现 | object | list_keywords_by_campaign_with_config(bid 数值型) | never | |
budgetChangeLog | 区间内预算/竞价变更史 | 广告表现 | array | list_campaign_operations_with_config | never | |
funnel30 | 30 天流量漏斗 | 流量结构 | object | get_campaign_metrics.summary | never | |
impressionShare | 搜索顶部展示份额 | 流量结构 | number\ | null | 星商 get_target_exposure;领星不可得 | never |
placementGap | 广告位转化差 | 流量结构 | array | list_placement_profile | never | |
groupFunnel | 分组点击与转化率 | 流量结构 | array | get_campaign_metrics.metrics[] 行内 clicks/orders/cvr | never | |
fbaStock | FBA 可用库存 | 库存 | object | listing.inventory_level.fba_warehouse.breakdown.available | never | |
inboundStock | 在途 / 未上架库存(领星可得部分) | 库存 | object | 同上 in_transit/transferring/receiving ⚠️ | never | |
sellRate | 日均动销(单/件双口径) | 库存 | object | get_orders + get_asin_sales | never |
| key | 标签 | 分组 | 类型 | 推导自 | 置信度 | |
|---|---|---|---|---|---|---|
fillRate | 预算消耗率 | 广告表现 | array | spendMix ÷ (budgetCaps × 天数),用 budgetChangeLog 校正 | 中 | |
weekVsMonth | 近 7 天 vs 30 天转化率 | 流量结构 | object | get_campaign_metrics 两次调用 | 中 | |
daysOfSupply | 可售天数 | 库存 | number\ | null | fbaStock.availableUnits ÷ sellRate.unitsPerDay | 高 |
stockHealth | 库存健康度 | 产品背景 | enum | daysOfSupply vs 周期剩余天数 | 中 | |
adDependency | 广告依赖度 | 产品背景 | number\ | null | 广告订单 ÷ 店铺总订单 | 中 |
maturity | 产品成熟度 | 产品背景 | enum | reviews.reviewCount + 30 天订单量 | 低(阈值未定义,默认返回 unknown) |
| key | 标签 | 分组 | 权重 | askPolicy | 条件(摘要) |
|---|---|---|---|---|---|
task | 本周期运营任务 | 运营意图 | 必须 | always | — |
goalPeriod | 目标周期 | 目标状态 | 重要 | conditional | 今天不是月初 3 天内 |
inbound | 在途 / 未上架库存 | 库存 | 重要 | conditional | 目标总需求件数 > FBA 可用件数(分母口径 ⚠️) |
cost | 产品成本区间 | 利润 | 重要 | conditional | task ∈ {profit, clear} |
promo | 促销与折扣计划 | 运营意图 | 重要 | defaultOnly | 用户主动点名 |
guard | 运营护栏 | 运营意图 | 可选 | defaultOnly | 用户主动点名 |
brandTerm | 品牌词归属 | 竞争环境 | 可选 | defaultOnly | 用户主动点名 |
lossCap | 花费天花板 | 利润 | 可选 | defaultOnly | 用户主动点名 |
season | 大促与上新节点 | 运营意图 | 可选 | defaultOnly | 用户主动点名 |
positioning | 价格定位 | 竞争环境 | 可选 | defaultOnly | 用户主动点名 |
⚠️ key 撞名:原型里positioning的askid 是"price",与 known 字段price同名。契约统一用field.k作 key,不要沿用 ask id。
spend vs spends —— processed MCP payload 一律 spend(单数);只有 raw_response 与后端原始行用 spends(复数)。星商路由的 keywords 行转化率键是 conversion_rate,领星是 cvr。跨 provider 必须做键名兼容表。acos: 146.1 是百分数量级;后端 lingxing/campaigns.ts:66-78 normalizeAcos 把 >1 的值 ÷100 转成 0..1;而目标 schema 的 target_acos_pct 是「20 表示 20%」。三处口径不同,转换点必须显式。null,不返回 0 —— 与 load-workspace-kpi.ts deriveMetrics 保持一致(sales<=0 → acos=null,clicks=0 → cvr/cpc=null),前端才能降级成「—」。get_campaign_metrics length 默认 25(真实账户 total_count 可达 244);list_search_terms_with_config 默认 250;list_campaign_operations_with_config 默认只有 10。不显式翻页会静默截断。list_campaigns_config 真实抓包 data[0] 是账户合计行(campaign_id/campaign_name/state 全 null,daily_budget: "7623.00"),len(data)=245 而 total_count=244。asinproducts/<nickname>/product_meta.json;构造代码 apps/backend/scripts/seed-amazon-ad-workspace.ts:2404-2441。键:product_nickname / marketplace / store / store_profile_id / asin_family / primary_asin / primary_asin_history / category / lifecycle_stage / mcp_namespaces / schema_version。路由表 apps/backend/vendor/luckee-ads-skills/system/mcp-data-routing.json(18 条 routing,字段 asins / mcp_server / mcp_tool_prefix / quick_lookup / tool_index)。set-ads-goal/SKILL.md Step 1(遍历 products/ → 读 product_meta.json → 单产品直接用,多产品才问)。asin_family 是 对象数组 [{asin,label,active}],不是 string[] —— 取子 ASIN 必须 .map(x => x.asin)。mcp_namespaces 是 {asin: 工具前缀} 映射,不是数组。get_product_listing 工具描述明确「只接受子 ASIN,不要传父 ASIN」。resolveOrdersIdentifier 优先级 vc_asin > parent_asin > sku > asin(snapshot.ts:188-220,LUC-534)。广告口径与订单口径必须用同一标识符,否则 adDependency 直接失真。currentTargetsproducts/<nickname>/targets/current_period.json;回读 load-workspace-targets.ts:49-62(json 优先、md 兜底)+ parse-operational-goal-json.ts:120。skill 侧强制读取清单见 set-ads-goal/SKILL.md Step 3。isSeedOnly = period_id.endsWith('-seed') && source === 'agent_proposal';configured = !isSeedOnly && 至少一个真实数值;targetState = (ACoS/TACoS、日预算、月订单) 任一有真实数字 ? 'existing_targets' : 'empty_targets'。hard_constraints.md 里的「(待 operator 填写)」占位符不算目标缺失。priceapps/backend/vendor/luckee-ads-skills/products/HT02/ads/portfolios/212098658419429/history/2026-06-24_diag-2026-06-24-pipeline-001/_raw_mcp/listing.json。listing 下只有 title / rating / review_count / promotion_status / fulfillment_method / inventory_level / price_changes 七个键。价格在 promotion_status.current_price = "$9.99"(字符串,带货币符号),list_price = "",discount_percentage = null,price_changes = null。后端等价路径 snapshot.ts:565 fetchProductListing。parseFloat;交叉校验 订单均价 = get_orders.total_sale_total ÷ total_orders。price / list_price / discount_percentage 不存在。❌ price_changes 不含价格历史(实测 null,工具描述标为「预留」)。结论:「过去 30 天挂没挂券」还原不了,promo 必须问人。reviewslisting.json:listing.rating = 4.7、listing.review_count = 790(顶层)。后端等价 amazon/ads-mcp.ts:254-255。rating_distribution 工具描述里承诺但真实 payload 里不存在 → 必须声明为 optional/nullable。只有快照没有序列 → 不出评论增速、不出上架月数。入口别选错:amazon-asin-client.ts:14-18 的 AmazonProductSnapshot 只有 title/imageUrl/listPriceCents/currency。currencyget_orders.currency_code(真实抓包 orders_30d.json);economics.json 由 seed 写死 currency:"USD"(seed-amazon-ad-workspace.ts:2068-2073);库存 cost 的 currency 实测是 "¥"。*_usd。取数时必须断言 ordersCurrencyCode === 'USD',否则整卡降级。库存成本的 ¥ 是明确的混币来源,本轮不出利润结论时也不要混算。dataWindowstart_date/end_date 决定(所有领星报表工具都必填)。asOf 两条真实来源:① processed payload 顶层自带 data_retrieved_at(listing.json / campaign_metrics_*.json / keywords_*.json 均有);② 本地缓存路径取 workspace_ads_sync_state.last_sync_at(db/schema.ts:2986-3006,列名 ads_status/ads_covered_start/ads_covered_end/orders_status/orders_covered_start/orders_covered_end/last_sync_at/last_error)。asOf 优先 payload.data_retrieved_at。ads* / orders* 两组覆盖状态可能一边 ok 一边 error(schema 注释明写 partial_data 场景),窗口取交集。adPerf30products/MF04/ads/portfolios/193653298327955/history/2026-06-24_ai-keyword-post-bid-1d/_raw_mcp/campaign_metrics_2026-06-23.json:summary{total_campaigns,total_impressions,total_clicks,total_spend,total_sales,total_orders,overall_ctr,overall_cvr,overall_cpc,overall_acos,overall_roas} + metrics[] + data_retrieved_at。summary.total_* / overall_*,不要自己求和。acos = spend ÷ adSales × 100、roas = adSales ÷ spend,与 load-workspace-kpi.ts deriveMetrics 同口径。cpoNowadPerf30 同一次调用;keywords / search_terms / placement 行本身也直接回传 cpa。cpoNow = spend30 ÷ adOrders30;adOrders30 = 0 → null(不返回 Infinity)。cpoNow ÷ price ≈ 大盘 ACoS 不是等式(原型 31.4% vs 31.2%,差 0.2pt,源自广告销售额归因残差)。工程实现必须给容差,建议 ±0.5pt,不能写成断言。spendPacelist_keywords_placement_hourly_with_config(工具描述明写每条含 report_date(YYYY-MM-DD) 与 hour(0-23),可只传 campaign_id,length 默认 250);② analyze_keyword_performance(granularity="day")(keyword_id 必填);③ 星商 get_campaign_space_exposure / get_target_exposure 行自带 date(真实抓包 luckee-ads-eval/cases/b0gy4ngg8x-stale-broad-pause-001/input/snapshot_baseline/raw/placements_7d.json);④ 后端 postLingxing("campaigns-daily") → snapshot.ts:391 + DB 表 workspace_ads_daily(schema.ts:2927-2962)。avgDailyUsd = spend30 ÷ 天数;maxDailyUsd = max(按 report_date 聚合后的日花费);evidenceCeilingUsd = maxDailyUsd。maxDailyUsd 是下界,coverage 必须标 keyword_only。全口径日序列仍需 campaigns-daily 或本地表(且 lingxing 的 campaign_key 是哨兵 "__total__",没有「某活动某天」维度)。adStructurecampaigns = list_campaigns_config.total_count 或 summary.total_campaigns;keywords = list_keywords_by_campaign_with_config.summary.total_keywords(campaign_id 必填,需循环);searchTermRows = list_search_terms_with_config.summary.total_search_terms;placementRows = list_placement_profile 行数。adGroups 与 negatives 无干净的一次性接口,可得性分 provider:ad_group_* 键(真实抓包 HT02/.../keywords_28578561337364.json),只能对 list_search_terms_with_config.ad_group_name 去重;星商 keywords 行带 ad_group_id/ad_group_name(真实抓包 products/B0FFT1JQ9T/.../keywords_by_campaign_84264226427264.json)。postLingxing("negative-keywords") → snapshot.ts:134(campaign_id 必填否则抛 400);星商有 get_campaign_negative_keywords。budgetCapsHT02/.../_raw_mcp/campaign_config.json:{success, data[245], total_count:244, message, platform:"lingxing", raw_response},行内键 profile_id / portfolio_id / campaign_id / state / start_date / end_date / targeting_type / daily_budget / portfolio_name / campaign_name / bid_type / placement_adjustment{PP,ROS,TOS}。后端等价 snapshot.ts:411;DTO lingxing/campaigns.ts:22。totalDailyBudgetUsd = Σ parseFloat(daily_budget),先剔除 data[0] 哨兵行、只累加 state === 'enabled'。data[0] 是账户合计哨兵行 —— 不剔除就把合计再加一遍。daily_budget 是字符串 "24.00"。placement_adjustment 的值是字符串 "20%" / "None%",未设置时是字面量 "None%",直接 parseFloat 得 NaN。state,不叫 status(工具描述里没提这个键,抓包里才有)。spendMixcampaign_metrics_*.json 的 metrics[] 逐 campaign 行。不传 campaign_id 即返回全部活动。cpoUsd = spend ÷ orders(orders=0 → null);组间差异用 cpo 比值表达。mock-data.js:568-571 是 mock 补丁)。另:写文档举例时别把两个窗口的数混着用 —— 「auto 12.56 ÷ exact 6.48 = 1.94 倍」是 7 天口径,30 天口径是 auto $12.36 / exact $6.40 = 1.93 倍。wasteSpendHT02/.../_raw_mcp/search_terms.json:summary{...} + search_terms[{search_term,target_text,match_type,campaign_name,ad_group_name,portfolio_name,bid,impressions,clicks,ctr,cpc,spend,sales,direct_sales,indirect_sales,orders,acos,roas,cpa,cvr}]。后端 snapshot.ts:100(length 默认 250)。orders == 0 && clicks ≥ 最小点击阈值;totalSpendUsd = Σ spend(单数键);sharePctOfTotal = totalSpendUsd ÷ spend30 × 100。cpa,折合单量不用自己除。bidUtilizationHT02/.../_raw_mcp/keywords_28578561337364.json(领星路由):行键 keyword_id / keyword_text / match_type / status / campaign_id / campaign_name / **bid**(number, 2.1) / bidding_strategy / impressions / clicks / ctr / cpc / spend / sales / orders / acos / roas / cpa / cvr;summary 另有 average_bid。后端 snapshot.ts:71。weightedBidUsd = Σ(bid × clicks) ÷ Σclicks;actualCpcUsd = Σspend ÷ Σclicks;utilizationPct = actualCpcUsd ÷ weightedBidUsd × 100;Σclicks = 0 → null。bid。跨 provider 转化率键名兼容见 §2.0 第 1 条。推论保留:利用率接近 100% 说明只能靠提价提量。budgetChangeLoglist_campaign_operations_with_config(返回 time / change_type(budget_change, bid_type_change) / campaign_id / campaign_name / user_name / from_source / budget_change{old_value,new_value,change_amount} / bid_type_change{old_value,new_value});后端 postLingxing("campaign-operations") → snapshot.ts:429。fillRate / budgetCaps 两个「快照当区间用」问题的唯一修正来源。funnel30get_campaign_metrics.summary:total_impressions / total_clicks / total_orders / overall_ctr / overall_cvr / overall_cpc。后端 load-workspace-kpi.ts::aggregateLingxingRows(注释明写 mapped DTO 会丢 impressions/clicks,要从原始行自算)。deriveMetrics 完全同式。impressionShareget_target_exposure 真实抓包 luckee-ads-eval/cases/b0gy4ngg8x-stale-broad-pause-001/input/snapshot_baseline/raw/target_exposure_7d.json,items[] 含 top_of_search_impression_share: 0.54(同行带 date/campaign_id/keyword_id/keyword_bid/bid/campaign_budget_amount)。路由映射 shared/scripts/mcp_intent_resolver.py;下游契约 skills/snapshot-capturer/SKILL.md:120-123(keywords.jsonl 含 tos_share/ros_share/pp_share);schema shared/schemas/observation_snapshot.schema.json。null,不做任何份额类外推。luckee2.0 子目录导致的误判。口径注意:它是搜索顶部份额、关键词级、0..1 分数,不是原型写的 campaign 级百分比 38%。placementGapHT02/.../_raw_mcp/placements.json(领星原始 shape {successful, data[], recordsFiltered, req_id}):行内含 placement / name / key / clicks / impressions / orders / spends / sales / cpc / cpa / ctr / cvr / acos / roas / percentage / strategy / bidding / default_bid / bid / daily_budget / campaign_state / targeting_type / campaign_id / campaign_name / portfolio_id。placement 枚举(TOP OF SEARCH ON-AMAZON / DETAIL PAGE ON-AMAZON / OTHER ON-AMAZON / OFF AMAZON)已在工具 schema 确认。后端 snapshot.ts:158。cvr/cpc/cpa 直接读;溢价性价比 = (ToS CVR ÷ 商品页 CVR) ÷ (ToS CPC ÷ 商品页 CPC),>1 则加 ToS 溢价数学上为正。spends(复数),processed shape 用 spend —— 取数前必须确定 processed 参数。percentage/strategy 是当前竞价调整快照,与 budgetCaps.placement_adjustment 同源可交叉核对,同样是带 % 的字符串。行数少、OFF AMAZON 常为 0,先过 clicks 阈值。groupFunnelclicks / orders / cvr。cvr,或 orders ÷ clicks × 100 复核。mock-data.js:597-601 是 mock 补丁)。clicks < 100 的组标 sampleSufficient=false 并降权。fbaStocklisting.inventory_level:total_inventory{quantity, cost, currency:"¥", distribution{fba_percentage,local_percentage,overseas_percentage}};fba_warehouse{total_quantity:1911, total_cost, currency, breakdown{available{quantity:1886}, in_transit, transferring, waiting{quantity:5}, receiving}};local_warehouse / overseas_warehouse 同构。后端 snapshot.ts:565 内的 postLingxing("inventory-stocks", {profile_id, asin})。availableUnits = fba_warehouse.breakdown.available.quantity;fbaTotalUnits = fba_warehouse.total_quantity。total_quantity(1911)≠ available(1886)。断货测算必须用 available。 cost 的 currency 是 ¥ 而售价是 USD,不要混算。inboundStock ⚠️listing.inventory_level:fba_warehouse.breakdown.in_transit(「标发在途(已发往FBA仓,尚未接收)」)/ transferring(「调仓中(FBA仓间转移)」)/ receiving(「入库中(FBA仓正在接收)」);overseas_warehouse.breakdown.transit(「在途库存」);local_warehouse.breakdown.under_review(「调货在途」)/ pending_order(「待到货量」)/ waiting_arrival(「待下单量」)。Amazon 直连侧另有 amazon/ads-mcp.ts:302-314 的 inbound_working_quantity / inbound_shipped_quantity / inbound_receiving_quantity。breakdown.quantity。覆盖「已发往 FBA / 调仓中 / 入库中 / 海外仓在途」,不覆盖 ERP 采购在途(已下单未到港)。DATA_GAPS「领星无在途接口」和给定的数据边界直接冲突。见 §6.1。在确认前:自动取到的部分照实展示,同时仍按「采购在途未知」保守处理并在结论里标注。sellRateHT02/.../_raw_mcp/orders_30d.json:{success, metric, identifier_type, identifier, date_range, date_list, daily_orders(date→值 字典), total_orders, avg_daily_orders, daily_sale_total, total_sale_total, avg_daily_sale_total, currency_code};units_30d.json(get_asin_sales):{success, metric:"store_sales_units", ..., daily_units, total_units, avg_daily_units}。后端 snapshot.ts:254 fetchStoreOrders(含 no-store-orders 兜底与 SKU→ASIN 二次尝试)/ snapshot.ts:281 fetchAsinSales;落库表 daily_store_orders(schema.ts:2963-2980)。totalOrdersPerDay = avg_daily_orders;unitsPerDay = avg_daily_units;adOrdersPerDay = 广告 orders ÷ 天数;organicOrdersPerDay = 前者 − 后者(估算)。daily_orders / daily_units 是 date→值 字典)。payload 顶层的 identifier_type / identifier 让你能在运行时断言广告侧与订单侧用的是同一标识符 —— adDependency 的口径风险从「警告」升级为「可检测」。| key | 算式 | 置信度 | caveat | ||
|---|---|---|---|---|---|
fillRate | spend_window ÷ (parseFloat(dailyBudget) × days) × 100;dailyBudget 缺失或 0 → null | 中 | 分母是当前快照,区间内调过预算就失真 → 必须用 budgetChangeLog 校正。>100% 是 Amazon 允许的单日超投按月封顶,不是数据错误。区间聚合推不出「每天都触顶」 | ||
weekVsMonth | deltaPp = last7CvrPct − last30CvrPct;样本不足或 ` | deltaPp | 低于噪声阈值 → insufficient` | 中 | 噪声阈值在仓库任何地方都没有定义(已全量 grep)→ 见 §6.6。目前只能返回 insufficient |
daysOfSupply | availableUnits ÷ unitsPerDay;周期缺口件数 = 周期天数 × unitsPerDay − availableUnits | 高 | 三套算式冲突,必须拍板,见 §6.8。且建立在「在途 = 0」假设上,展示时必须带假设标注 | ||
stockHealth | daysOfSupply < 周期剩余天数 → risk,否则 healthy;周期长度不可知 → unknown | 中 | workspace_operational_goal.schema.json 没有周期结束日字段(additionalProperties:false,属性已逐条读完)→「周期剩余天数」磁盘上取不到,只能从 period_id 字符串解析或默认 30 天。该默认值必须显式写进实现,否则判定不可复现。risk 是不上调日预算的唯一依据;用户答了在途要翻转 | ||
adDependency | 广告订单 ÷ 店铺总订单 × 100 | 中 | 归因窗口不一致 → 比值只能作量级判断。>100% 判定为两侧标识符口径不一致,不是数据错误(可用 identifier_type 检测) | ||
maturity | reviewCount ≥ 阈值 && 近 30 天订单稳定 → mature,否则 new;阈值缺失 → unknown | 低 | 阈值未定义(已 grep 确认无「成熟链接」判定常量)→ 在产品给出可写死阈值前,返回 unknown 且不参与任何目标值计算。无 listing 上架日字段,不能给「上架 N 个月」 |
| # | 项 | 现状 | 阻塞什么 |
|---|---|---|---|
| C1 | 在途库存语义 | 真实 payload 里 in_transit/transferring/receiving/overseas transit 有数值和中文释义,与「领星无在途接口」冲突 | inbound 追问是否从 conditional 降级;daysOfSupply / stockHealth / 预算上限全部要重算 |
| C2 | impressionShare provider 边界 | 星商可得、领星不可得 | A2 是否允许工作区间不对称,还是统一按最小公分母(领星)定契约、份额一律不进目标卡 |
| C3 | spendPace 全口径日序列 | 关键词口径可得;auto/商品定位只能走 campaigns-daily 或 DB 表 | skill 运行时能否读本地表 / 调后端路由;否则 evidenceCeiling 只能标「关键词口径下界」 |
| C4 | list_campaigns_config 哨兵行稳定性 | data[0] 全 null + 账户合计;len(data)=245 vs total_count=244 | 用「位置剔除」还是「campaign_id 为 null 则剔除」 |
| C5 | processed / raw 两套键名 | spend/spends、cvr/conversion_rate | 需统一走 processed,并给出跨 provider 键名兼容表 |
| C6 | ctx.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_orders | mcp-data-routing.json 领星条目只注册 5 个 canonical 工具;get_orders / get_asin_sales / get_campaign_negative_keywords 只在星商条目。运行时 lingxing_* server 确实暴露了 get_orders,更像路由表漏登记 | 补登记前,领星产品上 totalOrders30 / adSharePct / promo.gap 全部求不出值 |
AskUserQuestionTool.tsx / prompt.ts)questions:min 1 / max 4(:140-141)options:min 2 / max 4(:58-59):61 + prompt.ts:39)UNIQUENESS_REFINE(:94-111):只校验 question 文本唯一 + 同题内 option label 唯一header 的 12 字符只是 describe 文案(ASK_USER_QUESTION_TOOL_CHIP_WIDTH = 12,prompt.ts:5),zod 上没有 .max(12)。CJK 若按列宽算,6 个中文字就占满 12 列request_user_input(set-ads-goal/SKILL.md 确有此行)// ============================================================
// 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 同轮发出。
explicitTopics 为空时,实际问题数按 task 分流(原型 EXPERT_SCRIPT intro 自己写的是「最多 3 个问题…具体几个取决于你第 1 题怎么答」):
| task | inbound 触发(口径 A:基线 34.7%) | cost 触发 | 月中 goalPeriod | 实际问题数 |
|---|---|---|---|---|
profit | 125/0.347 = 360 > 236 ✓ | ✓ | +1 | 3(月中 4) |
bsr | 163/0.347 = 470 > 236 ✓ | ✗ | +1 | 2(月中 3) |
test | 56/0.347 = 161 ≤ 236 ✗ | ✗ | +1 | 1(月中 2) |
clear | 175/0.347 = 504 > 236 ✓ | ✓ | +1 | 3(月中 4) |
⚠️ 若拍板改用口径 B(任务目标占比),上表 test 变成 56/0.208 = 269 > 236 → 要问,profit 变成 338 —— 「test 不问」这个反例会消失,届时「conditional 是真门控」的论证要重新举例。
skipWhen 速查:
task:无 skipWhen(always)goalPeriod:skipWhen = now.getDate() <= 3inbound:skipWhen = 目标总需求件数 ≤ fbaStock.availableUnitscost:skipWhen = task ∉ {profit, clear}skipWhen = !explicitTopics.includes(key)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 的输入。
| value | label | detail |
|---|---|---|
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 天算的,写盘那一刻口径就错了,且卡片上看不见这个默认。
| value | label | detail |
|---|---|---|
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 天 · 有断货风险」是否成立。
| value | label | detail |
|---|---|---|
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 与安全垫」一行,不改任何写盘值。 跳过则本轮不出利润类结论。
| value | label | detail(已复算) |
|---|---|---|
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% 才等价。
| value | label | detail |
|---|---|---|
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 / 预算 / 订单目标,只改策略生成阶段的调整步长上限。
| value | label | detail |
|---|---|---|
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% 的可达性判断跟着变。
| value | label | detail |
|---|---|---|
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」处理,全程不外推。要给一个不一样的授权吗?
| value | label | detail |
|---|---|---|
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 天,不做分段。周期内有节点吗?
| value | label | detail |
|---|---|---|
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)。
| value | label | detail |
|---|---|---|
midRange ⭐ | 中价位 | 保守系数保持 0.90,四个主力词出价与不答完全一致 |
premium | 高价位 | 只作背景记录,本轮出价一个数都不变;等竞品价格数据源接入后才生效 |
value | 低价位 | 同上,本轮出价不变 |
一句话告知:
• 价格定位 —— 按中价位 · 主流价格段处理;出价公式保守系数固定 0.90,不含定位变量。
| key | 落盘键 | 类型 | 算式 / 来源 | |||
|---|---|---|---|---|---|---|
primaryAsin | primary_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) | |||
periodId | period_id | string | 缺省 ` ${isoMonthInReportingTz(now)}-monthly (write-workspace-targets.ts:167/241)。**禁止 -seed` 结尾**,见 §4.3 陷阱 | |||
effectiveFrom | effective_from | date | 缺省 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_note | enum `profit\ | bsr\ | test\ | clear` | 用户第一问答案 |
advertisingOrdersMonthly | advertising_orders_target(仅 origin/dev, v1.1) | number ≥ 0 | 恒等式一:round(organicOrdersBaseline × p ÷ (1 − p))(mock-data.js:140) | |||
targetCpoUsd | 🚫 无落点 | number ≥ 0 | cpoAt(advertisingOrdersMonthly) —— 不是自由输入,见 §5.2 | |||
targetAcosPct | target_acos_pct | number ≥ 0,最多两位小数 | 恒等式二:targetCpoUsd ÷ price × 100(mock-data.js:143) | |||
targetTacosPct | target_tacos_pct | number ≥ 0,最多两位小数 | 恒等式三(精确恒等):targetAcosPct × adOrderSharePct ÷ 100(mock-data.js:146) | |||
dailyBudgetCapUsd | daily_budget_cap_usd | number ≥ 0 | advertisingOrdersMonthly × targetCpoUsd ÷ 30(mock-data.js:141,145) | |||
monthlyOrders | monthly_orders | integer ≥ 0 | round(organicOrdersBaseline + advertisingOrdersMonthly) = round(organic ÷ (1 − p))。语义 = 店铺总订单(load-workspace-performance.ts:1033/1170/1312 actualMonthlyOrders = orderTotals.totalOrders,:408 label「月销量」) | |||
organicOrdersBaseline | 🚫 无落点 | number | totalOrders30 − adOrders30。恒等式一的分母,不落库就无法复算目标 | |||
budgetEvidenceFlag | 🚫 无落点 | enum `evidence\ | extrapolated` | 两种等价写法:adOrderSharePct > 39 或 dailyBudgetCapUsd > $32.10。等价性已验:planForShare(39)→$31.7 ≤ 32.10;planForShare(40)→$34.4 > 32.10 | ||
statusNote | status_note | string(无约束) | 目前是 operatingRhythm / 占比 / CPO / 证据标记 / 假设五类信息的唯一出口。决定 parse-operational-goal-json.ts:134 的 narrative 与 :122 的 configured 判定 | |||
promotionPaused | promotion_paused | boolean | 用户显式选择。true 时 parse-operational-goal-json.ts:143 把 mainGoal 置为「暂停推广」;随后 load-workspace-targets.ts:141-143 resolveStatus 返回 'paused' | |||
source | source | enum `operator_input\ | agent_proposal` | 见 §4.3 速查表。后端兜底 operator_input(write-workspace-targets.ts:246-247);白名单校验 routes/workspace-targets.ts:265-268 | ||
updatedAt | updated_at | date-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。
fbbd20c2-c813-4859-8a7b-485c37502f91,2026-07-20T08:58:32Z)/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 展示「已生成并保存,可编辑」。products/<product>/targets/proposals/<timestamp>_goal_proposal.json,UI 卡片确认后再写 active。PRO-118 当前状态:In Progress(07-20 08:09 曾置 Done,09:00 被打回,至今未闭)。assignee Guanglun Ren。这条口径至今未实现。
⚠️ 「禁止 skill 自己写盘」在仓里有三处落点,只改一处修不了:
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 门禁),改它是跨仓动作。apps/backend/scripts/seed-amazon-ad-workspace.ts:223(ORCHESTRATOR_GOAL_SECTION_CODEX)。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)。
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 校验。研发要先统一。
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 一份,换会话看不到。/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),不回滚 —— 目标已落库、卡片可能仍显示可编辑,是已知不一致。agent_draft vs confirmed_values 的差集即 dirty-field 标记。source 标记规则 + seed 陷阱| 场景 | 请求 source | 落库 source | 读回 TargetSource |
|---|---|---|---|
| set 入口 agent 直接落库、用户未改 | agent_proposal | agent_proposal | agent_proposal |
| 用户在卡上改了任意一个数再确认 | operator_input | operator_input | operator_direct |
| Goal Setup 表单手填 | operator_input | operator_input | operator_direct |
| 请求里没带 source | — | 兜底 operator_input | operator_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 的表现一模一样。
| # | 缺口 | 代码位置 | 影响 | ||
|---|---|---|---|---|---|
| #1 | adOrderSharePct 完全没有落点 | 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 —— 报告字段,从未进过持久化模型) | ||
| #2 | proposal 卡装不下广告订单目标 | 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 一侧 | ||
| #3 | confirmed_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 | ||
| #5 | targetCpoUsd 无字段 | — | 它是整条推导链的枢纽(share → orders → cpoAt(orders) → acos → tacos/budget)。落库后只能当 status_note 散文;下轮读回必须反解,而 CPO 是分段折线(9 锚点),反解不唯一,必然漂移 | ||
| #6 | operatingRhythm 无枚举字段 | status_note 是无约束 type:"string";parse-operational-goal-json.ts:143 的 mainGoal 只有「暂停推广」/ null 两种取值 | 它是 askPolicy=always 的第一问,切换它整套换掉四个数 —— 不落库等于每轮重问 | ||
| #7 | organicOrdersBaseline 无字段 | — | 恒等式一的分母不落库 → 读回时无法复算、无法解释「为什么是 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 月重新计分 | ||
| #9 | rationale 落 active target 时丢失 | proposal schema 里 required + minLength 1;active target schema 没有该字段。saveWorkspaceTargets 组装 record(write-workspace-targets.ts:238-256)只带 status_note | 确认落库那一刻依据就丢了,只剩 history 里机器拼的一行 | ||
| #10 | monthly_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.jsonl | write-workspace-targets.ts:271, 289 只 append targets/targets_history.jsonl | Mario 第 3 条要求 append products/<product>/operator_inputs.jsonl。读侧已就绪 —— loadTargetHistory 读 products/<nickname>/operator_inputs.jsonl(load-target-history.ts:181-182)—— 只差写侧 | ||
| #13 | set/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 | ||
| #15 | seed 判定陷阱 | parse-operational-goal-json.ts:120 | 见 §4.3 | ||
| #16 | proposal 不在产品树、不跨会话 | 见 §4.2 路径 B 第 2 条 | Mario 提的持久目录未实现 | ||
| #17 | marketplace 只在 proposal 卡 | active target schema 无 | 多站点同 ASIN 的目标无法区分 | ||
| #18 | vendor 副本 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 或删除死副本 | ||
| #19 | dev 上有新迁移文件 | 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 永远读不到。需拍板。
① 广告订单 = 自然订单 × 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),保守:不假设广告能带动自然单。
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)。必须单开分支,否则测款模式算出来的预算会低一半。
const SHARE_EVIDENCE_MAX = 39; // 日花费 ≤ 历史上限 $32.10 对应的占比
const SHARE_MODEL_MAX = 55; // 超过这里不再给数字
p > 55 → planForShare 返回 beyond:true,orders/cpo/spend30/budget/acos/tacos 全为 null,一个数都不出39 < p ≤ 55 → ext:true(外推区)p ≤ 39 → 证据区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 既无依据也不起作用。
TASK_PROFILES)| 口径 | p | orders | cpo | spend30 | budget | ACoS | TACoS | 标记 |
|---|---|---|---|---|---|---|---|---|
| 基线 | 34.66 | 113 ✓ | $7.54 | $852 | $28.40 ✓ | 31.4%¹ | 10.9% ✓ | — |
profit | 37.0 | 125 ✓ | 6.72 ✓(锚点) | 840 | 28.0 ✓ | 28.0 ✓ | 28.0×0.37=10.4 ✓ | 证据区 |
| 证据上沿 | 39.0 | 136 | 6.99 | 950 | 31.7 | 29.1 | 11.3 | 证据区边界 |
bsr | 43.4 | 163 ✓ | 8.28 ✓ | 1349.6 | 45.0 ✓ | 34.5 ✓ | 15.0 ✓ | ext:true |
clear | 45.1 | 175 ✓ | 8.91 ✓ | 1559 | 52.0 ✓ | 37.1(表内 37.2,取整残差) | 16.8 ✓ | ext:true |
test | 20.8 | 56 ✓ | 10.71 ≠ cpoAt(56)≈6.06 | — | 20 | 44.7 | 9.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 注释已写明)。它只影响基线展示,不进目标公式。
28.0×37.0/100 = 10.36 → 10.4,与原型精确吻合。「落库前对齐口径」这条 action item 应删除,否则研发会去修一个不存在的 bug。BUDGET_CURVE 常量」—— 全仓 grep 只在 mock-data.js:75 的一句注释里出现过。真实实现是 CPO_ANCHORS + cpoAt() + planForShare()。planForShare 会打 ext:true。| 既有字段 | 变化 |
|---|---|
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.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.2 | impressionShare 的 provider 不对称:星商 get_target_exposure 回传 top_of_search_impression_share(0..1,关键词×日),领星 per-ASIN MCP 无此字段 | 产品 | A2 是否允许工作区间能力不对称,还是统一按最小公分母(领星)定契约、份额一律不进目标卡 |
| 6.3 | spendPace 全口径日序列:list_keywords_placement_hourly_with_config 只覆盖关键词投放;auto/商品定位需走 postLingxing("campaigns-daily") 或 DB 表 workspace_ads_daily(lingxing 的 campaign_key 固定哨兵 "__total__",无「某活动某天」维度) | 研发 | skill 运行时能否读本地表/调后端路由;否则 evidenceCeiling 只能标「关键词口径下界」 |
| 6.4 | inbound 触发条件的分母:(A) 基线占比 34.7%(原型原样)→ profit 360、test 161 不问;(B) 任务目标占比 TASK_SHARE → profit 338、test 269 也问。只有 (B) 与恒等式自洽,但只有 (A) 有「test 不问」这个反例 | 产品 | 选 (A) 还是 (B) 决定默认路径的问题数分流表,也决定「conditional 是真门控」的论证是否成立 |
| 6.5 | bsr 口径两套数打架:TASK_PROFILES.bsr = 163 单/$45/34.5%,而 planForShare(43.4) = 161 单/$44/34.1% | 产品 | 写码前必须定一套;两套并存会导致卡片数与复算数对不上 |
| 6.6 | weekVsMonth 的噪声阈值 + maturity 的评论数阈值 —— 两者在仓库里都没有任何定义(已全量 grep) | 产品(给可写死常量) | 只能由 LLM 每轮临时拍,同一个品两轮对话可能给出相反结论。在给出常量前,这两个字段不应进入结论 |
| 6.7 | 库存耗尽用「单/天」还是「件/天」:get_orders 给订单数、get_asin_sales 给件数(metric: "store_sales_units"),原型用的是订单数(10.9 单/天) | 研发(建议件/天) | 断货天数与周期缺口件数全部受影响 |
| 6.8 | daysOfSupply 三套算式冲突:① 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.10 | set / evaluate 两条落盘路径未分流,且「禁止 skill 写盘」写在三处(含 pin 住的 submodule) | 产品(定分流规则)+ 研发(跨仓改动) | PRO-118 的原始症状「用户以为已经好了,结果目标没落盘」持续存在 |
| 6.11 | template 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.13 | operator_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 全部求不出值 |
原型
/Users/charliephil/Downloads/luckee2.0-dev/analysis/luckee-prototype-full/mock-data.js — BASE:10-31、SYNC_MANIFEST:34、DATA_GAPS:47、EVAL_DIMENSIONS:54、TASK_PROFILES:73-93、ORGANIC_30D:116、CPO_ANCHORS:117-118、SHARE_EVIDENCE_MAX/SHARE_MODEL_MAX:119-120、cpoAt:122-131、planForShare:133-148、GUARDRAILS:470、GOAL_SCHEMA:539-674、TASK_ORDER_GOAL:678、PREFILL_EXAMPLES:681/Users/charliephil/Downloads/luckee2.0-dev/analysis/luckee-prototype-full/a2-chat.js — EXPERT_SCRIPT:14-56、DEFAULT_NOTICE:56、GUARD_ASK:104、askTriggered:516-532、pendingSchemaAsks:533、nextExpertQuestion:538、finishExpertBackground:571Schema 与落盘
/Users/charliephil/Downloads/luckee2.0-dev/apps/backend/seed-sources/amazon-ad-agent/shared/schemas/workspace_operational_goal.schema.json(canonical)/Users/charliephil/Downloads/luckee2.0-dev/apps/backend/seed-sources/amazon-ad-agent/shared/schemas/workspace_goal_proposal.schema.json/Users/charliephil/Downloads/luckee2.0-dev/apps/backend/vendor/luckee-ads-skills/shared/schemas/workspace_operational_goal.schema.json(漂移死副本)/Users/charliephil/Downloads/luckee2.0-dev/apps/backend/vendor/luckee-ads-skills/shared/schemas/operator_input.schema.json/Users/charliephil/Downloads/luckee2.0-dev/apps/backend/src/workspace/targets/operational-goal-schema.ts + __tests__/operational-goal-schema.test.ts/Users/charliephil/Downloads/luckee2.0-dev/apps/backend/src/workspace/targets/write-workspace-targets.ts/Users/charliephil/Downloads/luckee2.0-dev/apps/backend/src/workspace/targets/parse-operational-goal-json.ts/Users/charliephil/Downloads/luckee2.0-dev/apps/backend/src/workspace/targets/load-workspace-targets.ts/Users/charliephil/Downloads/luckee2.0-dev/apps/backend/src/workspace/targets/confirm-workspace-goal-proposal.ts/Users/charliephil/Downloads/luckee2.0-dev/apps/backend/src/workspace/targets/load-workspace-performance.ts/Users/charliephil/Downloads/luckee2.0-dev/apps/backend/src/workspace/targets/load-target-history.ts/Users/charliephil/Downloads/luckee2.0-dev/apps/backend/src/routes/workspace-targets.ts/Users/charliephil/Downloads/luckee2.0-dev/packages/shared/src/api/envelope.ts/Users/charliephil/Downloads/luckee2.0-dev/apps/backend/src/app.ts(:288 挂载)/Users/charliephil/Downloads/luckee2.0-dev/apps/backend/src/sessions/paths.ts(:120-150)/Users/charliephil/Downloads/luckee2.0-dev/apps/backend/src/agents/mcp-catalog.ts(:103-118)/Users/charliephil/Downloads/luckee2.0-dev/apps/backend/src/worker-manager/workspace-project-materializer.ts/Users/charliephil/Downloads/luckee2.0-dev/apps/backend/src/amazon-ad-seed/protocol-markdown.ts/Users/charliephil/Downloads/luckee2.0-dev/apps/backend/scripts/seed-amazon-ad-workspace.ts取数与 skill
/Users/charliephil/Downloads/luckee2.0-dev/apps/backend/vendor/luckee-ads-skills/system/mcp-data-routing.json/Users/charliephil/Downloads/luckee2.0-dev/apps/backend/vendor/luckee-ads-skills/luckee2.0/pipeline-orchestrator/set-ads-goal/SKILL.md/Users/charliephil/Downloads/luckee2.0-dev/apps/backend/vendor/luckee-ads-skills/luckee2.0/pipeline-orchestrator/input-capturer/SKILL.md/Users/charliephil/Downloads/luckee2.0-dev/apps/backend/seed-sources/amazon-ad-agent/shared/agents/ads-goal-evaluator-prompt.md/Users/charliephil/Downloads/luckee2.0-dev/apps/backend/src/data-sources/data-provider/lingxing/snapshot.ts/Users/charliephil/Downloads/luckee2.0-dev/apps/backend/src/data-sources/data-provider/lingxing/campaigns.ts/Users/charliephil/Downloads/luckee2.0-dev/apps/backend/src/data-sources/data-provider/amazon/ads-mcp.ts/Users/charliephil/Downloads/luckee2.0-dev/apps/backend/src/db/schema.ts(:2927-3006)真实抓包样本(键名以此为准)
.../vendor/luckee-ads-skills/products/HT02/ads/portfolios/212098658419429/history/2026-06-24_diag-2026-06-24-pipeline-001/_raw_mcp/:listing.json / campaign_config.json / placements.json / search_terms.json / keywords_28578561337364.json / orders_30d.json / units_30d.json.../vendor/luckee-ads-skills/products/MF04/ads/portfolios/193653298327955/history/2026-06-24_ai-keyword-post-bid-1d/_raw_mcp/campaign_metrics_2026-06-23.json.../vendor/luckee-ads-skills/luckee-ads-eval/cases/b0gy4ngg8x-stale-broad-pause-001/input/snapshot_baseline/raw/:target_exposure_7d.json / placements_7d.json前端
/Users/charliephil/Downloads/luckee2.0-dev/apps/frontend/src/features/chat/goal-setup-prefill.ts/Users/charliephil/Downloads/luckee2.0-dev/apps/backend/vendor/core-agent-loop-worker/ts_luckee/claude-code/packages/builtin-tools/src/tools/AskUserQuestionTool/AskUserQuestionTool.tsx + prompt.ts本次任务未做任何文件修改(只读)。