/** * A7 情报归档的案例模型 —— **双表示同版本**:CMMN XML(机器面)与案例说明书(叙事面)。 * * 业务出处:`docs/MVP示例工作流.html` §1.6(S1 结果登记 → S2 画像更新 → S3 落地率统计), * 按 A4/A6 同一条形状重建(2026-08-03 重写 @3.0.0): * * 上游(A6 三口之一的载荷 `{rfqRef, customerCode, quoteNo, outcome, rows}`)进来 → * 归一成**情报卡**底表(一颗料一条:报价 / 成交价 / 结果 / 时间)→ **一张执行单** * 交 BD:逐条落**输赢归因**与竞对价(归因那一列 AI 一格都不写)→ 按败价幅度 A-01 * 给复用建议 → **入库口径要人拍板**(决策卡:全量 / 脱敏 / 只留内部)→ 放行归档闭环。 * * 上一版(@2.1.0)六处不合现行口径,这一版整个置换: * ① 入参必填 `rfqRef + customerCode + quoteNo` —— 而宽进严出是全线口径,归一在活动内做; * ② 两条判据读活动自报的 `reason_logged_total` / `profile_updated_total` —— * 「机检只读基座数得出来的数」违反史第七类:那个数模型自己写,写 1 就过; * ③ 台账一个 `required` 都没有 —— 执行卡的闸没牙,人不填也能交卷; * ④ **没有人的判断**:归档全由 AI 自己写完自己放行,而输赢归因恰恰是只有跟单的人 * 才说得出的事(客户到底为什么不下单,系统里没有一个字段答得了); * ⑤ 三张共享域表(`market.price_history` / `customer.profile` / `shared.landing_stats`) * 同批退役,墓志铭在清单里; * ⑥ 那段 `run()` 删掉 —— 案例活动不会调它。 * * 【为什么归档也要叫人】它是全流程最后一站,**这一单之后再没有人会回头看它**。 * 归因写错没有下一道工序纠正,而它会一路流进下一单的判据里 —— * 「价格高」这种由 AI 从「客户没回」推出来的结论,三个月后没人记得它是编的。 * 所以两处人:归因逐条落在执行单上,入库口径一张决策卡拍板。 */ export const CMMN_XML = ` update intel_total > 0 and quote_archive_intel_rows = intel_total and collected_total >= 0 update attributed_total > 0 and quote_archive_intel_rows > 0 and quote_archive_intel_missing = 0 and quote_archive_intel_share_scope_pending > 0 update scope_decided_total > 0 and quote_archive_intel_rows > 0 and quote_archive_intel_missing = 0 and quote_archive_intel_share_scope_pending = 0 and quote_archive_intel_share_scope_full + quote_archive_intel_share_scope_masked + quote_archive_intel_share_scope_internal + quote_archive_intel_share_scope_pending = quote_archive_intel_rows `; /** * 案例说明书 —— **只写整单一以贯之的事**(逐步做法归剧本,两处不重复写同一件事)。 */ export const NARRATIVE = `§0 开案:**一单报价有了结果,把它沉淀成下一单用得上的情报**。来路两种,形状都收(宽进): · **流内转来**:任务数据里有 \`rows\` 数组 —— A6 的报价函行(每条带报价、客户表态与定案), 外加 \`outcome\`(won / lost)说明这一单是成了还是丢了;这是最全的一种来路; · **单飞**:只有一段需求文字,说了哪一单、给谁报的、什么价、结果如何。 **成品只有一样**:一份**情报卡清单** —— 一颗料一条,每条都有**输赢归因**, 每条都定了**入库口径**。人签的不是「归了个档」,是**这一单留下了什么、下一单谁用得上**。 **这一单的价值全在下一单**:今天记下的败因,是下次定价时的判据;今天记下的成交价, 是下次的基准。所以归档不是收尾的仪式,是情报生产。 **「客户没回」不是原因** —— 问不出来就如实写「说不清」,**不许编一个听着合理的理由**: 编出来的原因会一路流进下一单的判据里,而那时已经没人记得它是编的。 **能算的不问人,该人说的不代说**:败价幅度 A-01(价差 → 复用建议)、入库口径建议 A-02 (归因 → 建议档位),两支判定表说了算,你不临场拍;而**输赢归因、竞对价、客户原话 三列是人落的,你一格都不写** —— 客户到底为什么不下单,系统里没有一个字段答得了。 **三步走**:归一情报卡 → 交 BD 逐条归因 → 入库口径拍板 → 放行闭环。 **这一单要经过人两次**:BD 在情报归档工作单上逐条落归因(那张单绑着整份情报卡, 归因缺一条按不下提交 —— 基座的闸看着,他填的格子直接落账); **入库口径另叫他拍一次板**(一张决策卡)。竞对报价与客户身份写进共享情报库之后, 下一单谁都读得到 —— 这条边界不该由系统自己越过。最后 BD 放行签字。 **这是全流程最后一站**:放行之后没有下一个节点,只有一口 \`archived\`(归档闭环)。 去向不必你选,也没得选。 【这一单只有一个岔路口,它长这样 —— 无论哪一拍都照这个发,别自己另编一张】 入库口径要人拍板,那张决策卡**三处写死**: · \`"itemId":"T_scope"\` —— 英文计划项号。**别写中文名「入库口径拍板」**:中文名不是 id, 写了当场被拒(【目标缺口】那一段摆的就是中文名,别照它抄); · \`"ask":" 条情报待定入库口径,按哪一档进库?"\` —— 只把 N 换成条数,**别的字一个都不改**。 换了问法,系统就认不出这是同一张卡:人已经点过的那张会被你再发一遍; · 三个 \`value\`:\`full\`(全量入库)、\`masked\`(脱敏入库)、\`internal\`(只留内部)。 label 与 consequence 可以按本单的数改写,**value 不行**。 **FULL / MASKED / INTERNAL 三个码只有拍板那一步写得出来** —— 归一那一步遇到情报一律落 PENDING,替他写了口径,那张卡就永远不会发,这条边界成了摆设。 拍板之前**别急着发卡也别绕路** —— 归一那一步先把情报卡写完、finish; 下一拍引擎会把拍板那一步的做法摊开给你,那时再发。 【一路上要说话】负责人正看着这一单。**每一拍都带一句 say** —— 说业务的话: 这一单最后怎么样、归了几条情报、下次遇到同类该注意什么;不说系统词。 到了要找人的那一拍,先说清这一轮办完了什么、为什么现在需要他。 【怎么把一项干完 · 一拍连做】write 和 tool 都不会自动收步 —— 做完用 finish 宣告。 动作排进同一拍的 actions 串一口气给;只有发卡 / 提交(挂起)必须停拍。 **这一单常路 6-8 拍就该收口**。 §Z 结案:BD 在验收单上签字,系统把草稿合并成正式情报卡并发出 \`archived\` 那一口 —— 这条工作流就此走到终点。归档做得好不好,不在这一单看得出来, 在下一次报价能不能一眼看到「上次为什么丢」看得出来。`; /** * 步骤剧本 —— 一个计划项一条;`doneWhen` 是「成果已存在」的判据,不是「该不该做」。 * * 三处不给 `doneWhen`:`S2_attribute`(发卡触达真人)、`T_scope`(要人拍板)、 * `T_release`(签下去触达世界)—— 给了等于给引擎一把替人签字的钥匙。 */ export const ARCHIVE_STEPS = [ { item: 'S1_collect', intent: '整份归一成情报卡:逐条承继报价与结果、标上报价日期、口径一律先落待定', writes: ['quote_archive.intel', 'quote_archive.verdict'], /* 上游载荷数组不声明就会被整表段的截断刀砍掉(砍掉的恰是要归一的行) */ reads: ['rows'], view: 'archive-worksheet', tools: [], /* * 归一走完才算办完:行数两边对上(一边是基座现数), * 且 collected_total 写过(S-FEEL 未绑定的名字参与比较为假,所以「没写过」进不来)。 */ doneWhen: 'intel_total > 0 and quote_archive_intel_rows = intel_total and collected_total >= 0', guide: `第一件事:把这一单的**情报卡**落出来 —— 一拍连做,别拆成两拍。 【条目从哪儿来】 · 流内转来(\`rows\` 里的行带 pn / quoted_price / reply_type / deal_status):照抄 —— 报过的价、谈成的价都是既成事实,**一个数字都别改**;带 line_total 这类总数的是结论行, 不是料,别落进情报卡。 · 单飞:照正文归一,列了几颗落几行;正文里说的报价与结果就是要沉淀的那一份。 **按 pn 归并,一颗料一行**;pn 一个字符不许改(它是主键,改一个字就是新开一行)。 【写 \`quote_archive.intel\`,一颗料一行】(rows 收数组) rfq_ref(入参有就原样抄;没有从主题认;认不出才自己造一个,**造出来的必须以 \`ARC-\` 开头** (\`ARC-<主题或第一颗料的型号>\`)。两条硬规矩: **① 整单只有一个单号,不是一颗料一个** —— 它是这一单的案号,情报卡所有行、verdict 那一行 用的都得是同一个串(实测栽过:两颗料造出两个号,一单裂成两单); **② 只从本单来件取材** —— 台账里躺着的 RFQ-xxxx 是别人的单,借它的号等于把两单的行 混进同一个案卷(实测栽过一轮))、pn、customer_code、customer_name、category、brand、eau、 **quoted_price**(我方报过的价,原样抄)、**final_price**(成交了才有;没成交留空)。 另两列**照这个口径落**: · **deal_result** —— 这一条的结果,只许写 \`WON\`(成交)或 \`LOST\`(不成交)两个大写码之一。 上游给了 outcome 就照它:won → WON,lost / revise → LOST; 逐行还带着 deal_status 的,以行上的为准(WON → WON,其余一律 LOST)。 认不出就按 **LOST** 落 —— 归档里不许把说不清的算成成交。 · **quoted_at** —— 报价日期,写成 \`YYYY-MM-DD\`。来件里有报价日就抄它,没有就写今天。 这一格是下一单查「距上次报价多久」的那把尺,别留空。 【**这三列你一格都不写**:cause_tag / competitor_price / evidence】 输赢归因、竞对报的那个价、客户原话 —— 这三样只有跟单的人知道,**系统不替人归因**。 它们是 BD 在工作单上落的;price_gap_band 那一列要等竞对价回来才算得了,这一步也别写。 这不是客气话:一条要你分辨「这行能不能替人填」的规矩会走岔; **「这几列不写」没有分支可走岔。** 【**share_scope 这一列一律写 \`PENDING\`**】入库口径是人在决策卡上拍的, \`FULL\` / \`MASKED\` / \`INTERNAL\` 三个码这一步一个都不许写 —— 替他写了,那张卡就永远不会发。 【同一拍写 \`quote_archive.verdict\`(一行)】 rfq_ref、**quote_no**(报价单号:来件有就抄,没有写 \`Q-\` 开头 + 本单案号的尾段,整单一个)、 customer_code / customer_name(顶层有就抄)、**outcome**(整单结果:有一条 WON 就写 \`won\`, 一条都没有就写 \`lost\`)、**intel_total** = 情报卡行数、**collected_total** = 这一拍归一落表的 条数(0 也要写)、summary 一句人话(几条料、给谁报的、最后成没成)。 写完 finish 这一项。`, }, { item: 'S2_attribute', intent: '一张执行单交 BD:逐条落输赢归因、竞对价与客户原话', writes: ['quote_archive.intel', 'quote_archive.verdict'], reads: ['quote_archive.intel'], view: 'archive-worksheet', tools: [], /* 不给 doneWhen —— 发卡触达真人(manualActivation),引擎不替人按 */ guide: `情报卡落好了,**该 BD 出场了** —— 这是本活动唯一的一张执行单。 输赢的缘由只有跟单的人说得出:客户到底为什么不下单、竞对报了多少、他原话怎么说的。 工作区摆的是**情报归档工作单**:逐颗料一张情报档案卡(报价、结果、客户约束), 归因六档与败价幅度的尺挂在上面,填回区长在同一屏 —— 他在「输赢归因」那一列逐行落。 这一步标了人工激活:发卡就算启动,不必单发 start;引擎也不会替你收 —— 人交回后要你自己 finish。 【照这个骨架发,别少字段】(卡面是纯文本,steps/standard 里别写星号记号) {"kind":"human","itemId":"S2_attribute","workOrder":"execution","card":{ "title":"情报归因:<单号> 共 条待归因", "ask":"这一单的情报卡已按报价与结果归好,报价、成交价与日期都在工作单上。请逐条落输赢归因:输在价格 / 交期 / 认证 / 规格 / 关系,还是说不清;输在价格的把竞对报的那个价也填上,并把客户原话记进依据一栏。", "steps":["归因只有六档,选最主要的那一个:PRICE 价格 · LEADTIME 交期 · CERT 认证资质 · SPEC 规格不匹配 · RELATION 关系或既有供应商 · OTHER 说不清", "「客户没回」不是原因 —— 问不出来就落 OTHER 并在依据里写明「未获反馈」,不要编一个听着合理的理由:它会一路流进下一单的判据里", "归因落 PRICE 的行必须填竞对价:没有那个数,「输在价格」下一单读不出任何东西 —— 缺这一格这张单按不下提交", "依据一栏尽量记客户原话:「你们比 XX 贵两毛」比「价格偏高」值钱得多 —— 下一单定价时看的就是这一句"], "standard":"整份情报卡每一条都有归因,归因落 PRICE 的那几条填了竞对报的价 —— 缺一条这张单按不下提交(系统的闸看着)。", "basis":[{"k":"","v":"<报价 · 成交价 · 结果 · 报价日期>"}]}} · \`basis\` 逐条列本单的料:PN + 报价与结果。几颗写几条,别铺全表。 · **卡上不摆表、不摆格子**(执行卡零输入):整份情报卡就在工作单那份件上, 归因、竞对价、竞对名、依据四列都能就地改、行能撤 —— **别往卡里塞 \`blocks\` 或 \`fields\`**。 · **这张卡带提交闸**:归因(必填列)还空着,他按不下提交 —— 缺几格、缺在哪行, 闸上与件上的红带都写着(与放行门 L1 同一个数同一套算法)。 发完卡这一拍就结束了(发卡挂起)。 【人交回之后 —— 他落的归因基座已经替你写回台账了】这一步你只做三件事,然后 finish: ① **算 price_gap_band**(只给归因是 PRICE 且填了竞对价的那几行):按败价幅度 A-01, 价差 gap =(quoted_price − competitor_price)/ quoted_price,只许写这三个大写码之一: · gap ≤ 0.03 → **SLIGHT**(擦肩而过:下一单同价位再试,输的多半不在价格); · 0.03 < gap ≤ 0.10 → **MODERATE**(有差距但够得着:下一单让到这一档或换替代料降本); · gap > 0.10 → **STRUCTURAL**(结构性价差:下一单别再报同一价位,先走替代选型)。 不是 PRICE 的行、或没有竞对价的行,这一格给空串 \`""\`,**不许写 0 也不许编一个码**。 ② 写 verdict:**attributed_total** = 已落归因的条数(照情报卡行数记); 他在件上撤掉了行的话,**intel_total 跟着改成撤后的实数**。 ③ finish 这一项。 **别在这一拍写入库口径** —— 那是下一步人拍板的事(它有自己的一段做法),这里写了也白写。 情报卡其余列你一列都不用重写:他落的值基座已经落进台账了,重写会被「写了等于没写」拒回。 【这张卡一单只发一次】人交回来之后不许再发同一张卡 —— 必填有提交闸兜着, 卡回来时归因一定是齐的(或那几行已被他撤掉)。`, }, { item: 'T_scope', intent: '入库口径要人定 —— 发一张决策卡,照他选的那一档把待定口径改掉', writes: ['quote_archive.intel', 'quote_archive.verdict'], reads: ['quote_archive.intel'], view: 'archive-roster', tools: [], /* 人工激活 + 要人拍板 —— 不给 doneWhen,引擎不许替人收 */ guide: `**这一单只发一张决策卡。动手之前先在轨迹里找一行 \`[card-reply]\` 带「人选了:」:** · **找到了** —— 跳到下面【人点完之后】那一段,照他选的那一档把那几行的 share_scope 从 PENDING 改掉、写 scope_decided_total、finish。**一个 decide 都不许再发** (实测栽过:人点完了,而这一步又从头念了一遍「发一张决策卡」,同一张卡连发六次, 前两次无事发生、后四次被拒 —— 他点过的那一下就白点了); · **没找到** —— 才轮到下面【发一张决策卡】那一段。 【这一步办完了没有,看一个数】待定口径条数 quote_archive_intel_share_scope_pending: **它归零就是这一步办完了**。PENDING 是过渡态(情报归好了、还没定进哪一档库), 拍板那一下必须把它换成 FULL / MASKED / INTERNAL 之一 —— 三档都是结论。 这个数不归零,放行门开不了;它归零,这一步也就不会再找你。 这一步只在**归因齐了**(基座现数 quote_archive_intel_missing = 0 且人交回过执行单)时入场 —— 读的是人自己填的那一格,不是你判的结论:该不该叫人,不由被叫的那一方说了算。 它触达公司对外的边界(竞对报价与客户身份进了共享库,下一单谁都读得到),标了人工激活: 发卡就算启动,不必单发 start。 工作区这时摆的是**情报归档清单**,整份情报卡与归因分布都在上面,他看得见全局再拍板。 【发一张决策卡,照这个骨架】(选项少于两个基座拒发) **三处一个字都不许改**(与说明书里那一段是同一份): · \`itemId\` 只写英文计划项号 \`T_scope\` —— **别写中文名「入库口径拍板」**, 中文名不是 id,写了当场被拒(实测栽过两拍:「计划项不存在:异议拍板」); 【目标缺口】那一段摆的是中文名,它不是 id,别照它抄。 · \`ask\` 照抄「 条情报待定入库口径,按哪一档进库?」,**只把 N 换成条数** —— 换了问法系统就认不出这是同一张卡,人已经点过的那张会被你再发一遍(实测发出去两张)。 · 三个 \`value\` 照抄 \`full\` / \`masked\` / \`internal\` —— 它们是三档的代号, 换了名字下一段的对应关系就对不上,你自己也不知道人点的是哪一档。 label 与 consequence 可以按本单的数改写,value 不行。 {"kind":"decide","itemId":"T_scope","ask":" 条情报待定入库口径,按哪一档进库?", "options":[ {"value":"full","label":"全量入库(客户与竞对都留名)","consequence":"下一单查这颗料时,客户是谁、竞对是谁、报了多少一目了然 —— 情报最值钱,但客户身份与第三方报价也就此对内公开"}, {"value":"masked","label":"脱敏入库(只留价位与败因)","consequence":"竞对与客户的名字抹掉,价位、归因、日期照留 —— 下一单照样判得了价,追不到是谁说的"}, {"value":"internal","label":"只留内部(不进共享情报库)","consequence":"这一单的情报只在本案卷里躺着,下一单查不到 —— 涉密或客户明说不外传时才选这一档"}], "basis":[{"k":"","v":"归因 <码> · 报价 · 竞对价 · 败价幅度 <码>"}]} · basis 逐条摆本单的料:归因、报价、竞对价、败价幅度 —— 裸卡基座拒发。 · **recommended 按入库口径建议 A-02 标**,只标一项:归因里有 PRICE 的(带着第三方报价) 标 \`masked\`;一条 PRICE 都没有的标 \`full\`。 · 发完这一拍就结束了(发卡挂起)。 【人点完之后】轨迹上那一行长这样:\`[card-reply] …人选了:xxx\`。**照那一档把口径改掉, 别再发第二张卡**,一拍连做 —— **整份情报卡逐行都要动 share_scope**,一条都不许留在 PENDING: · 选 full:每行 share_scope 改 **FULL**;verdict 的 share_policy 写 \`full\`; · 选 masked:每行 share_scope 改 **MASKED**;verdict 的 share_policy 写 \`masked\`; · 选 internal:每行 share_scope 改 **INTERNAL**;verdict 的 share_policy 写 \`internal\`。 然后把 verdict 写全:**scope_decided_total 记 1**、**buyer_profile** 一句客户行为画像 (照这一单看得出来的写:价格敏感 / 决策快慢 / 认证门槛 / 常用条款,是 MVP §1.6 S2 那一步的产物)、 summary 补一句拍板结果,finish 这一项。 改完之后待定口径条数就是 0 了,这一步再也不会找你。`, }, { item: 'T_release', intent: '情报已沉淀、口径已定,提交放行交 BD 验收', writes: [], reads: [], view: 'archive-roster', tools: [], /* 放行触达世界,必须人按一下 —— 引擎不自动收它(剧本里也不给 doneWhen) */ guide: `每条都有归因、口径全定过、待定清零 —— 可以走放行了 (T_release 人工激活:动它就算启动,不必单发 start)。先把裁量判据裁掉,然后 submit,同一拍连做: · judge criterion="情报站得住:每条归因说得出来路、竞对价与客户原话可溯,BD 对这份情报负责" —— 看情报卡里每一行的归因与依据:说得出来路就 satisfied=true; 有行说不出的(比如归因落了 PRICE 却没有客户原话),satisfied=**false**,rationale 逐行写清, 交签字的人定。**裁成不满足照样能提交** —— 理由连着卡一起交给他。 criterion 照判据名**整句原样抄**,写别的名字对不上。 **去向没得选**:这是全流程最后一站,只有一口 \`archived\`(归档闭环),放行那一刻自动发射。 你要做的只是把情报落准,别在提交里手选出口。 终局唯放行:你不能自己宣布完成。`, }, ]; /** * 阶段 —— 进度条的刻度,只给人看。除放行外每个计划项恰属一个阶段; * 放行那一格由基座统一加,人看到的是 x/4。 */ export const ARCHIVE_PHASES = [ { name: '归一情报卡', items: ['S1_collect'] }, { name: '逐条归因', items: ['S2_attribute'] }, { name: '入库口径拍板', items: ['T_scope'] }, ];