Archived
人手改的那一手真正落得下去;界面不撒谎那几处补齐
手改是**兜 AI 的错**的那一手 —— 这一轮把它从「摆在那儿但落不了地」做成真的: 件上就地改 · 保存**按地址收**,不看当下那张卡:载荷从前借道工单的 `blocks[i]` 取列名, 于是熔断卡(没有 blocks)在屏上时,人补的那一格一个字都送不出去 —— 而他要补的恰恰是把这一单卡住的那个必填格。授权面改由活动清单的台账声明把关。 · **能增删行**:错不止「这一格填错了」,还有「这一行不该在」与「漏了一行」。 删挂记号不当场抹掉(看得见、可后悔),且**删要说出来**(`drops`)—— 「没回传就是删了」会把一次不完整的回传变成一次删表。 · **必填没填按不下提交**:与放行门 L1 同一个数同一套算法;闸上摆出差在哪一行哪一格。 · 只写真改了的那几行 —— 改一格却留「修订 4 行」的痕,那句话是假的。 判据与门 · 裁量判据带谓词时**谓词说了算**:「选填缺口已补齐」基座数得出来, 人填好的目标价它照样判「客户未提供」的毛病由此消掉。 · 验收卡判据**读时现算** —— 人在成果件上补一格,判据跟着变。 · 放行门认「**办完了没有**」不认「有人写过」:从前写个 `false` 就把门推开, 于是一单没打标就进了放行门。三个数全由基座现数。 · 拒回把「活没干完」说成「数或行对不上」的毛病:判据点名的表一行都没写时直说那一步没办。 **指向两扇打不开的门,比不指还糟。** 界面不撒谎 · 日志只有三色:负责人绿 · 智能体蓝 · 系统灰(归侧五种是度量的分法,不是给人看的)。 · 验收收口答两件事:落了什么(按**交付物**点名,一份成果的几张表并成一行)、 往哪儿去(转出哪一口、带过去哪些字段);一个口都没发就直说到此为止。 · 签完到收尾之间那一段有自己的话「字已签下,正在落地」—— 中间态本来就该说出来,只是要说对。 · 消息不再被内部事件刷屏:只有从通道进来的件才谈得上「无人认领」。 · 建客户公司与联系人**一次确认落两行**;联系人编码自增不问人;卡面摆表的人读名。 A1 三案例端到端全 released:全 A 类直通 12 拍 / 客户点名跳一条 10 拍 / 新客户缺必填 14 拍。 五道门禁全绿(971 passed)。 Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01JsTDjoHovERXyy6Wmw1ALp
This commit is contained in:
@@ -1,85 +1,716 @@
|
||||
/**
|
||||
* A1 询价受理的案例模型 —— **双表示同版本**:CMMN XML(机器面)与案例说明书(叙事面)。
|
||||
*
|
||||
* 2026-07-29 由步序声明(flow)改写而来:活动不再分范式,一律案例模型 + Agent 临场调度。
|
||||
* 原来的六步在这里各归其位 —— 解析归一与追问补全同属「收料归一」阶段(常来回几轮,
|
||||
* 所以带 repetitionRule);查历史价与三分类打标属「定性分级」;末尾一张验收单。
|
||||
* 业务出处:`docs/MVP示例工作流.html` §1.1 的那张步骤流程图,原样照搬:
|
||||
*
|
||||
* ● 接流程起点 → S1 接收登记 → D1 判断完整度
|
||||
* ├─ 不完整 → S2 追问补全信息 ─┐
|
||||
* └─ 完整 ───────────────────┴→ S3 逐条解析归一
|
||||
* → S4 查历史价 → S5 逐条定状况标 → S6 定去向与派单 → G1
|
||||
*
|
||||
* **最长路径就是这六步**(常路五步 —— 字段齐就不走 S2),末尾加一张受理放行(终局唯放行)。
|
||||
*
|
||||
* ── 建模纪律:图上有几步就是几项 ──────────────────────────────────────
|
||||
*
|
||||
* 这份模型**重写过一次**。上一版自作主张加了两个阶段(收料归一 / 定性分级)、两个里程碑、
|
||||
* 一个裁量项、一个用户事件监听器,12 个计划项去装 6 步的活。业务上一分没多,
|
||||
* 卡壳的花样却多了一堆:阶段被当成写的落点(收了阶段,里面的项就永远入不了场)、
|
||||
* 里程碑要不要收、项该先 start 还是先干、编出不存在的项 id……
|
||||
*
|
||||
* 实测账很清楚:**每一条「行动被拒」都是结构上的,没有一条是业务判断上的** ——
|
||||
* 模型在跟 harness 较劲,不是在办事;一单烧掉二三十万 token 才收口,还常常收不了口。
|
||||
*
|
||||
* 所以:**图上有几步就是几项,不多建一件**。阶段、里程碑、裁量项都是 CMMN 有的东西,
|
||||
* 但「有」不等于「该用」—— 它们要能换来业务上说得出的好处,否则就只是给 Agent 添的坑。
|
||||
*/
|
||||
|
||||
export const CMMN_XML = `<?xml version="1.0" encoding="UTF-8"?>
|
||||
<definitions xmlns="http://www.omg.org/spec/CMMN/20151109/MODEL">
|
||||
<case id="intake">
|
||||
<!--
|
||||
案卷项的 id **就是任务视图里的键名** —— 前提的 onPart 认的是「这个键有值了」。
|
||||
起单入参(subject)与台账列(line_total / missing_key_total / history_days /
|
||||
untyped_total / need_sourcing)都会摊平到任务视图顶层,所以照它们命名。
|
||||
名字对不上就等于前提永不满足:整案第一步都动不了(踩过)。
|
||||
|
||||
开案即有的只有那封信(客户邮箱 / 主题 / 邮件内容 / 邮件附件)。**询价单号不在其列** ——
|
||||
它是 S1 从信里认出来的,认出来才写得动台账(所有台账对象都以 rfq_ref 为主键)。
|
||||
-->
|
||||
<caseFileModel>
|
||||
<caseFileItem id="rfq" name="询价来函"/>
|
||||
<caseFileItem id="records" name="统一受理记录"/>
|
||||
<caseFileItem id="history" name="历史价格情报"/>
|
||||
<caseFileItem id="verdict" name="受理结论"/>
|
||||
<caseFileItem id="subject" name="来件主题(开案即有)"/>
|
||||
<!-- ① 定位客户:认出来(或人建出来)才算数 -->
|
||||
<caseFileItem id="customer_total" name="客户已确认"/>
|
||||
<caseFileItem id="customer_new_total" name="新客户(需建档)"/>
|
||||
<caseFileItem id="customer_asked_total" name="已就建档问过人"/>
|
||||
<caseFileItem id="line_total" name="登记出的条目数"/>
|
||||
<!-- ② 归一 RFQ:必填缺 = PN / 年用量;选填缺 = 品牌 / 备注 -->
|
||||
<!--
|
||||
缺必填的行数由**基座**按台账声明的 required 现数(quote_intake_record_missing)——
|
||||
不读活动自报的 missing_key_total:那个数要模型自己重算,漏算一次这道门就永远开不了
|
||||
(连跑十轮里三轮死在这)。自报的两个数照旧写,它们是给报告和人看的。
|
||||
-->
|
||||
<caseFileItem id="quote_intake_record_missing" name="缺必填的行数(基座现数)"/>
|
||||
<caseFileItem id="missing_key_total" name="缺必填字段条数(PN/年用量)"/>
|
||||
<caseFileItem id="missing_minor_total" name="缺选填字段条数(品牌/备注)"/>
|
||||
<caseFileItem id="asked_total" name="已就缺口问过人的次数"/>
|
||||
<caseFileItem id="answered_total" name="人答完并已落表的次数"/>
|
||||
<!-- ③ 受理需求交人审定:这一单**经过人的手**了没有 -->
|
||||
<caseFileItem id="confirmed_total" name="受理需求已经人审定"/>
|
||||
<caseFileItem id="history_days" name="历史价判据"/>
|
||||
<caseFileItem id="untyped_total" name="未打标条数"/>
|
||||
<caseFileItem id="need_sourcing" name="去向旗"/>
|
||||
</caseFileModel>
|
||||
<casePlanModel id="plan" name="询价受理" autoComplete="true">
|
||||
<sentry id="s_rfq">
|
||||
<onPart id="op_rfq" caseFileItemRef="rfq"><standardEvent>create</standardEvent></onPart>
|
||||
<!-- D1 判断完整度在图上是一个菱形,在这里是两条前提:先认客户,再看归一齐没齐 -->
|
||||
<!--
|
||||
① 客户认出来了 —— 归一那一步的入场前提。
|
||||
先认客户再解析正文,不是先后顺序的洁癖:归一出来的每一条都要落在
|
||||
某个客户名下(record.customer_code),客户没定位就写不动那一列。
|
||||
-->
|
||||
<sentry id="s_customer">
|
||||
<onPart id="op_cu" caseFileItemRef="customer_total"><standardEvent>update</standardEvent></onPart>
|
||||
<ifPart id="if_cu"><condition>customer_total > 0 and (customer_new_total = 0 or customer_asked_total > 0)</condition></ifPart>
|
||||
</sentry>
|
||||
<sentry id="s_incomplete">
|
||||
<onPart id="op_ic" caseFileItemRef="records"><standardEvent>update</standardEvent></onPart>
|
||||
<ifPart id="if_ic"><condition>missingKeyFields > 0</condition></ifPart>
|
||||
<!--
|
||||
② 拆完了 —— 交人审定那一步的入场前提。只看「有没有拆出行来」:
|
||||
缺不缺字段不影响该不该给人看,缺口是**卡上要说清的事**,不是拦住这张卡的理由。
|
||||
-->
|
||||
<sentry id="s_parsed">
|
||||
<onPart id="op_ps" caseFileItemRef="line_total"><standardEvent>update</standardEvent></onPart>
|
||||
<ifPart id="if_ps"><condition>line_total > 0</condition></ifPart>
|
||||
</sentry>
|
||||
<!--
|
||||
③ **人审定过了** —— 后面查历史价/打标的入场前提。
|
||||
必填(PN/年用量)缺一条都不算完(它是验收标准);而**这一单必须经过人的手**:
|
||||
confirmed_total 由「人把受理需求那张卡交回来」这件事产生 —— 详见 S3_confirm 的注释。
|
||||
|
||||
从前这里写的是「missing_minor_total = 0 or answered_total > 0」:
|
||||
**只在缺东西的时候才叫人**。于是一封字段齐全的询价进来,这个条件当场为真,
|
||||
引擎在下一拍开头把归一那一步自己收了 —— 人一眼没看过受理需求,
|
||||
下一次被叫到已经是终局的验收放行(用户 2026-08-01 当场指出:
|
||||
「我都没确认需求 它就让我验收了」)。**确认不是缺口的补丁,是这一步本身。**
|
||||
-->
|
||||
<sentry id="s_complete">
|
||||
<onPart id="op_cp" caseFileItemRef="records"><standardEvent>update</standardEvent></onPart>
|
||||
<ifPart id="if_cp"><condition>missingKeyFields = 0</condition></ifPart>
|
||||
<onPart id="op_cp" caseFileItemRef="confirmed_total"><standardEvent>update</standardEvent></onPart>
|
||||
<ifPart id="if_cp"><condition>line_total > 0 and quote_intake_record_missing = 0 and confirmed_total > 0</condition></ifPart>
|
||||
</sentry>
|
||||
<sentry id="s_basis">
|
||||
<onPart id="op_bs" caseFileItemRef="history_days"><standardEvent>update</standardEvent></onPart>
|
||||
</sentry>
|
||||
<!--
|
||||
打完标才进得了定去向 —— 条件读**基座按值现数**的三类条数,不读活动自报的
|
||||
untyped_total(那是它自己写的数,写错一次这道门就形同虚设)。
|
||||
-->
|
||||
<sentry id="s_typed">
|
||||
<onPart id="op_tp" caseFileItemRef="records"><standardEvent>update</standardEvent></onPart>
|
||||
<ifPart id="if_tp"><condition>untyped = 0</condition></ifPart>
|
||||
<onPart id="op_tp" caseFileItemRef="untyped_total"><standardEvent>update</standardEvent></onPart>
|
||||
<ifPart id="if_tp"><condition>quote_intake_record_rows > 0 and quote_intake_record_type_tag_a + quote_intake_record_type_tag_b + quote_intake_record_type_tag_c = quote_intake_record_rows</condition></ifPart>
|
||||
</sentry>
|
||||
<!--
|
||||
── 放行门的入场条件(2026-08-02 用户实测逼出来的)────────────────────────
|
||||
|
||||
从前这道哨兵**只有 onPart 没有 ifPart**:意思是「只要有人往 need_sourcing
|
||||
写过一笔就开门」—— 写个 false 也算。实测的下场:模型跳过 S4/S5/S6,
|
||||
在 verdict 上顺手写了 need_sourcing: false,放行门当场 Available,
|
||||
它 start 完就提交;而受理记录三行一个标都没打、分类表一行都没写。
|
||||
提交门正确地拦住了,可**该拦的地方本不该放它进来** —— 一道只认「有人写过」
|
||||
的门,等于没有门。
|
||||
|
||||
现在认的是**办完了没有**,且三个数全由基座现数(活动自报的数改一下就骗过去了):
|
||||
逐条打了标、去向定过了。route_decided_total 是 S6 显式写下的记账数
|
||||
(旗是布尔,分不清「没写」与「写了 false」)。
|
||||
-->
|
||||
<sentry id="s_routed">
|
||||
<onPart id="op_rt" caseFileItemRef="need_sourcing"><standardEvent>update</standardEvent></onPart>
|
||||
<ifPart id="if_rt"><condition>quote_intake_record_rows > 0 and quote_intake_record_type_tag_a + quote_intake_record_type_tag_b + quote_intake_record_type_tag_c = quote_intake_record_rows and route_decided_total > 0</condition></ifPart>
|
||||
</sentry>
|
||||
|
||||
<stage id="S1" name="收料归一" autoComplete="true">
|
||||
<entryCriterion id="ec_s1" sentryRef="s_rfq"/>
|
||||
<humanTask id="T_normalize" name="人工逐条解析归一">
|
||||
<itemControl><manualActivationRule id="m_nm"/><repetitionRule id="r_nm"/></itemControl>
|
||||
</humanTask>
|
||||
<humanTask id="T_ask_back" name="人工追问补全信息">
|
||||
<entryCriterion id="ec_ask" sentryRef="s_incomplete"/>
|
||||
<itemControl><manualActivationRule id="m_ab"/><repetitionRule id="r_ab"/></itemControl>
|
||||
</humanTask>
|
||||
</stage>
|
||||
<milestone id="M_complete" name="八字段齐"/>
|
||||
|
||||
<stage id="S2" name="定性分级" autoComplete="true">
|
||||
<entryCriterion id="ec_s2" sentryRef="s_complete"/>
|
||||
<humanTask id="T_history" name="人工查历史价">
|
||||
<itemControl><manualActivationRule id="m_hi"/></itemControl>
|
||||
</humanTask>
|
||||
<decisionTask id="D_classify" name="逐条打标 A/B/C" decisionRef="p03-type"/>
|
||||
<planningTable id="pt_s2">
|
||||
<humanTask id="T_lifecycle" name="人工核生命周期"/>
|
||||
</planningTable>
|
||||
</stage>
|
||||
<milestone id="M_typed" name="全条已打标"/>
|
||||
|
||||
<humanTask id="T_release" name="人工受理结论验收放行">
|
||||
<entryCriterion id="ec_rel" sentryRef="s_typed"/>
|
||||
<itemControl><manualActivationRule id="m_rl"/></itemControl>
|
||||
<!--
|
||||
① 定位客户信息 —— 拿发件邮箱去客户主数据里认人。
|
||||
认得出就取它的客户编码;认不出说明这是个**新客户**,发补全卡请人建档
|
||||
(公司名、客户编码、联系人)—— 建档是人的事,活动不替他编一个客户出来。
|
||||
-->
|
||||
<humanTask id="S1_customer" name="定位客户信息:按发件邮箱认客户,认不出请人建档">
|
||||
<itemControl><repetitionRule id="r_s1"/></itemControl>
|
||||
</humanTask>
|
||||
|
||||
<timerEventListener id="TL_half" name="时限过半">
|
||||
<timerExpression>PT12H</timerExpression>
|
||||
</timerEventListener>
|
||||
<userEventListener id="UL_amend" name="客户改需求"/>
|
||||
<!--
|
||||
② 归一客户 RFQ 输入 —— 解析邮件正文,逐条落成受理记录。
|
||||
PN / 年用量**必填**,其余**选填**;缺哪几格照实记进两个缺口数。
|
||||
**这一步不叫人**:拆料是 AI 的活,拆完交给下一步给人看。
|
||||
-->
|
||||
<humanTask id="S2_normalize" name="归一客户RFQ输入:解析正文逐条落表">
|
||||
<entryCriterion id="ec_s2" sentryRef="s_customer"/>
|
||||
<itemControl><repetitionRule id="r_s2"/></itemControl>
|
||||
</humanTask>
|
||||
|
||||
<!--
|
||||
③ 受理需求交人审定 —— **阶段2 那张执行页,一单必经一次**。
|
||||
目标件 「A1-询价受理需求.html」 的落款写着「AI 拟稿,你审定;可就地修改后再确认」,
|
||||
页脚是【修改】与【确认受理记录】—— 它从来就是一张**人签的页**,不是缺东西时才弹的追问。
|
||||
|
||||
**manualActivation + 不给 doneWhen**:两样缺一不可。
|
||||
触达世界的项一律不给 doneWhen —— 给了就等于给引擎一把替人签字的钥匙;
|
||||
而这一步的成果(人看过、改过、确认了)按定义不可能由引擎自己判出来。
|
||||
-->
|
||||
<humanTask id="S3_confirm" name="受理需求交人审定:整份清单可就地改,确认后才往下走">
|
||||
<entryCriterion id="ec_s3" sentryRef="s_parsed"/>
|
||||
<itemControl><manualActivationRule id="m_s3"/></itemControl>
|
||||
</humanTask>
|
||||
|
||||
<!-- S4 查历史价:取齐 P-03 的三项判据 -->
|
||||
<humanTask id="S4_history" name="查历史价:取齐距上次报价天数/行情/生命周期">
|
||||
<entryCriterion id="ec_s4" sentryRef="s_complete"/>
|
||||
</humanTask>
|
||||
|
||||
<!-- S5 逐条定状况标:决策表求值,不是临场判断 -->
|
||||
<decisionTask id="S5_classify" name="逐条定状况标:按三分类规则P-03打标" decisionRef="p03-type">
|
||||
<entryCriterion id="ec_s5" sentryRef="s_basis"/>
|
||||
</decisionTask>
|
||||
|
||||
<!-- S6 定去向与派单:两枚去向旗 + 开替代评估请求/询价请求 -->
|
||||
<humanTask id="S6_route" name="定去向与派单:立两枚旗,开替代评估请求/询价请求">
|
||||
<entryCriterion id="ec_s6" sentryRef="s_typed"/>
|
||||
</humanTask>
|
||||
|
||||
<!-- 受理放行:终局唯放行 —— 触达世界,必须人工激活 -->
|
||||
<humanTask id="T_release" name="受理结论验收放行">
|
||||
<entryCriterion id="ec_rel" sentryRef="s_routed"/>
|
||||
<itemControl><manualActivationRule id="m_rl"/></itemControl>
|
||||
</humanTask>
|
||||
</casePlanModel>
|
||||
</case>
|
||||
</definitions>`;
|
||||
|
||||
export const NARRATIVE = `§0 开案:一封询价来函即开案。信里是 RFQ 正文与 BOM 附件,条目从几条到上百条不等。
|
||||
受理这件事的成品只有一样:**一份能往下走的统一受理记录** —— 八字段齐、每条都打了标。
|
||||
/**
|
||||
* 案例说明书 —— **只写整单一以贯之的事**(2026-07-31 砍掉逐步做法)。
|
||||
*
|
||||
* 引擎每一拍已经把「当前这一步怎么做」单独摊开喂进去了(`INTAKE_STEPS[].guide`)。
|
||||
* 说明书里再写一遍全部六步,是**两笔账同时亏**:
|
||||
* · 钱 —— 同一段做法一拍烧两遍,而它是每拍都在的常驻上下文;
|
||||
* · 准 —— 两份会分叉。实测就分叉过:这里写着 `S1_register` / `S2_ask_back` /
|
||||
* `S3_normalize` 三个**案例模型里根本不存在的计划项**(真名是 S1_customer /
|
||||
* S2_normalize),模型照说明书报 itemId,一报一个被拒。
|
||||
*
|
||||
* 所以留下的只有逐步做法**说不了**的那几件:这一单是什么、成品是什么、
|
||||
* 一路上怎么跟人说话、怎么把一项干完、结案是什么。逐步的活归剧本。
|
||||
*/
|
||||
export const NARRATIVE = `§0 开案:**一封客户询价来函即开案**。你手上只有这封信的四样东西 ——
|
||||
客户邮箱(customerEmail)、主题(subject)、邮件内容(body)、邮件附件(attachments,真文件)。
|
||||
没有客户编码,没有询价单号:那两样是**受理出来的**,不是随信送来的。
|
||||
真实 RFQ 五花八门 —— 俄文 BOM、客户内部码、字段东缺西缺都常见。
|
||||
**受理这件事的成品只有一样**:一份能往下走的询价受理记录 —— 逐条落表、每条打了状况标、
|
||||
两枚去向旗定了。SLA:受理<15 分钟,判定<45 分钟。判错状况,快单变慢单。
|
||||
|
||||
S1 收料归一:逐条把 BOM 解析成统一格式(PN / 厂牌 / EAU / 目标价 / 交期 / 包装 / 品质要求 / 备注)。
|
||||
解析不出的不许臆造 —— 缺关键字段(PN、EAU、交期为必要三要素)就追问客户,常要来回一两轮,
|
||||
所以这两件事都可重复做。**吃不准的宁可空着去问,也不许填一个看起来合理的值。**
|
||||
**整条路就这几步**,一步一个计划项,别绕(每一步的做法,轮到它时会单独摊给你,
|
||||
这里不重复):S1 定位客户 → S2 归一受理 → **S3 受理需求交人审定** → S4 查历史价 →
|
||||
S5 逐条定状况标 → S6 定去向与派单 → 提交放行。
|
||||
|
||||
S2 定性分级:先查历史价(同客户同料近一年有没有报过、隔了多久、行情稳不稳),
|
||||
再按三分类规则 P-03 逐条打标 —— A 类历史价直用、B 类需重新核价、C 类需选型或寻源。
|
||||
拿不准的一律降 B,不许臆断 A:错标 A 会让一条本该重新核价的料带着旧价一路走到报价单上。
|
||||
生命周期核查是裁量项 —— 遇到疑似 EOL 的料随手拉一次。
|
||||
**这一单要经过人两次,不多不少**:S3 人审定受理需求(阶段2 那张执行页,
|
||||
字段齐不齐都要发)、放行时人签字。**别把 S3 当成缺东西时的追问** ——
|
||||
不缺也发:拆错一格只有人看得出来,而那些错往后每一步都跟着跑。
|
||||
|
||||
§Z 结案:八字段齐、每条都已打标、BD 签字放行,自动结案(CMMN AutoComplete)。
|
||||
结论只有两个去向要交出去:需不需要选型、需不需要寻源 —— 下游的两道网关读的就是这两面旗。`;
|
||||
**附件是真文件,基座已经替你读过一遍**。每一件长这样
|
||||
\`{ name, contentType, size, ref, text?, note }\`:
|
||||
\`text\` 有值就是**读出来的内容**(xlsx 读成制表符分列的行,第一行多半是表头),照它拆条;
|
||||
\`text\` 没有就是这类文件读不出文本(\`note\` 里写着为什么,比如 pdf、扫描件),
|
||||
这时以邮件内容为准。**别猜附件里有什么**:文件名叫 BOM 不等于里面有 BOM。
|
||||
|
||||
【一路上要说话】负责人(BD)正看着这一单。**每一拍都带一句 say** —— 你是在跟他当面说话,
|
||||
不是播报进度。他明天要拿这些话去跟客户和采购讲,所以说的得是**业务的话**:
|
||||
料号、交期、用量、价、客户要求;不是「关键字段」「归一化阶段」「写草稿 3 行」这些系统词。
|
||||
|
||||
一两句就够,口语,像同事扭头说一声。局面变了顺手用 view 换画面:
|
||||
· S2 逐条落表后 → view = intake-overview。**先说这单是什么**,再说有没有坑:
|
||||
「深圳机械这单三颗料,车规 Analog;交期用量都写清楚了,只有 NE555P 没给目标价。」
|
||||
· S5 打标完 → view = intake-classified。**说结论对这单的影响**:
|
||||
「LM358DR 有近期成交价可以直接报;NE555P 半年多没动过要重新核;TL072CDR 停产了,得找替代。」
|
||||
· S6 定完去向 → view = intake-release。**说这单接下来往哪走、谁接手**。
|
||||
|
||||
别每拍一个腔调复读(「已完成X,现在进行Y」)。事情不同,话就该不同 ——
|
||||
调工具那一拍说「我去查这三颗的历史价」,拿到结果那一拍说查出了什么,
|
||||
两句话该是不一样的语气。有风险、要他拿主意、或者你打算叫人,**先说出来**,
|
||||
别等他自己从表里看出来。不说话的后果是:他的对话区从头到尾空白,只看得见「多了一个任务」。
|
||||
|
||||
【怎么把一项干完 · 一拍连做】一个计划项常要**两三个动作**才交得出成果(先写记录、
|
||||
再结算总数;先调工具拿判据、再把判据写进台账)。write 和 tool 都**不会**自动把那一项收掉 ——
|
||||
都做完了,用 finish 宣告这一项完成。**这些动作排进同一拍的 actions 串里一口气给**,
|
||||
别拆拍:「写 record、写 verdict、finish S1」是一拍,不是三拍;
|
||||
「写 type_table 打标结果、写 verdict 统计、finish S5、写 alt/rfq 请求、写旗、finish S6」
|
||||
能排一串就排一串。只有两处必须停拍:调工具要看出参(tool 放串尾,下一拍接着干);
|
||||
发卡 / 提交会挂起(它们只能是串尾)。**这一单常路 5-7 拍就该收口** ——
|
||||
每多拆一拍,就是多烧一遍完整上下文。
|
||||
|
||||
§Z 结案:BD 在验收单上签字,系统把草稿合并成正式记录并发出 intake 口的载荷。`;
|
||||
|
||||
/**
|
||||
* **步骤剧本** —— 一个计划项一条,把「这一步是什么状态」写成机器读得懂的东西。
|
||||
*
|
||||
* 说明书说的是**整单一以贯之的事**(这一单是什么、怎么说话、怎么收口);
|
||||
* 逐步的做法只写在这里,轮到哪一步才摊开哪一步 —— **两处不重复写同一件事**,
|
||||
* 重复的代价是钱(每拍烧两遍)与准(两份会分叉,分叉过一次:说明书里的计划项名
|
||||
* 是案例模型里没有的三个,模型照它报 itemId,一报一个被拒)。
|
||||
*
|
||||
* `doneWhen` 是这里的关键:它让引擎自己算得出「这一步的成果已经在台账里了」,
|
||||
* 算出来就直接收掉,**不问模型**。A1 反复空转正是栽在没有它 ——
|
||||
* S1 把八字段一次写齐之后,S3「补齐八字段」已经无活可干,而模型只能把同样三行
|
||||
* 再写一遍,被「写了等于没写」拒回,下一拍再来一遍(实测连拒到熔断)。
|
||||
*
|
||||
* 写 `doneWhen` 的纪律:**它是「成果已存在」的判据,不是「该不该做」的判据**。
|
||||
* 写成开局即为真的式子(如 `missing_key_total = 0`),那一步会在还没干之前就被收掉。
|
||||
*/
|
||||
export const INTAKE_STEPS = [
|
||||
{
|
||||
item: 'S1_customer',
|
||||
intent: '按发件邮箱认客户;认不出就发补全卡请人建档,建完写进客户主数据',
|
||||
writes: ['customer.profile', 'customer.contact', 'quote_intake.verdict'],
|
||||
/* 认客户就靠这两张 —— 没声明的话它们会被整表段的截断刀砍掉(踩过) */
|
||||
reads: ['customer.contact', 'customer.profile'],
|
||||
view: 'intake-overview',
|
||||
tools: [],
|
||||
/*
|
||||
* 认到人才算办完。**不能写 `customer_code != null`** ——
|
||||
* S-FEEL 里未绑定的名字 `!= null` 求值为 true,这一步会开局即被收掉(A1 踩过)。
|
||||
* 一律取同步写下的计数列。
|
||||
*/
|
||||
doneWhen: 'customer_total > 0 and (customer_new_total = 0 or customer_asked_total > 0)',
|
||||
guide: `这一单的第一件事是**认人**:这封信是谁发来的。
|
||||
|
||||
① 先查客户主数据:拿入参的 \`customerEmail\` 去 \`customer.contact\` 里找 ——
|
||||
找得到就取它那一行的 \`customer_code\`,这一步就完了(照下面第③条记账,不必问人)。
|
||||
|
||||
**邮箱一个字符都不许改**:用 \`customerEmail\` 的**原文**去比,别照签名里的姓名
|
||||
重新拼一个(实测踩过:入参是 \`e.volkova@itelma.ru\`,模型按签名拼成
|
||||
\`ekaterina.volkova@itelma.com\` —— 名字对、域名和缩写全错,于是老客户被当成新客户,
|
||||
白发一张建档卡,还差点建出一个重复的客户档案)。
|
||||
比对时忽略大小写与首尾空格,除此之外**逐字符相等才算认出**;
|
||||
域名相同但用户名不同**不算认出** —— 同一家公司不同的人,是两个联系人。
|
||||
|
||||
② **找不到就是新客户** —— 发**数据录入卡**(不是补全卡)请人建档。
|
||||
两者的分别在**谁写库**:补全卡收回来的值要你自己再 write 一遍(中间隔一道传话,
|
||||
你可能抄错、可能顺手改一笔);数据录入卡是**人按下确认那一刻基座直接写**,你不经手。
|
||||
客户档案是主数据,建错一次后面每一单都跟着错 —— 那一笔该由人亲手落下。
|
||||
|
||||
**建档要建两张表:公司档 + 联系人档,两段都摆上这张卡**(2026-08-02 用户定:
|
||||
「新建客户 联系人和公司都需要确认」)。只摆公司那一半、联系人由你自己写,
|
||||
等于让人确认了一半 —— 而联系人档里的 \`contact_code\` 恰恰是**你编出来**的那一个。
|
||||
|
||||
**照这个骨架发,一个字段都别少**(散文描述不管用,直接抄):
|
||||
{"kind":"entry","itemId":"S1_customer","ask":"这是位新客户,请核对建档信息并给客户编码",
|
||||
"ledger":"customer.profile","entryKey":{"customer_code":""},
|
||||
"basis":[{"k":"发件邮箱","v":"chen@sh-elec.com"},{"k":"签名","v":"陈刚 | 上海精密电子有限公司"}],
|
||||
"fields":[
|
||||
{"key":"customer_code","label":"客户编码","required":true},
|
||||
{"key":"name","label":"公司名","value":"上海精密电子有限公司","required":true}
|
||||
],
|
||||
"also":[{"title":"联系人档案","ledger":"customer.contact",
|
||||
"key":{"customer_code":""},
|
||||
"fields":[
|
||||
{"key":"name","label":"联系人姓名","value":"陈刚","required":true},
|
||||
{"key":"email","label":"邮箱","value":"chen@sh-elec.com","required":true}
|
||||
]}]}
|
||||
**联系人编码不用你写** —— 它是内部流水号,基座在人按下确认那一刻按「客户编码-01」生成
|
||||
(台账声明里标了自增)。你硬塞一个进 \`fields\`,基座也会把它摘掉:
|
||||
人哪知道这家客户已经有几位联系人,而你编一个就可能覆盖别人那一行。
|
||||
**公司名与联系人姓名、邮箱都必须带 \`value\`** —— 公司名几乎总写在开头那句
|
||||
「我是XX公司的张三」或结尾签名档里;联系人姓名从落款认;**邮箱照 \`customerEmail\` 原样抄,
|
||||
一个字符都别改**。读出来填上,人核对一眼就行。
|
||||
**客户编码不给 \`value\`** —— 那是我们内部的编码,来件里不会有,硬凑一个是错的;
|
||||
标 required:true,人不填就确认不了。
|
||||
两段的 \`customer_code\` 都留空:人在第一段填了什么,第二段跟着是什么(同一位客户)。
|
||||
|
||||
③ **记账两个数,引擎按它们收步**(别绕过去,绕不过去):
|
||||
· customer_new_total —— 第①步认出来了记 0;认不出、要建档记 1;
|
||||
· customer_asked_total —— 发过建档补全卡就记 1。
|
||||
**新客户没问过人,这一步收不了** —— 实测有过一次:模型从邮箱域名
|
||||
编出 \`CUST-SZ-MACH\` 直接把档建了,人根本没被问过一句,而那个编码是错的。
|
||||
客户编码是主数据的主键,编错一次后面每一单都跟着错。
|
||||
|
||||
④ 人答完(或第①步就认出来了),同一拍把该写的写上:
|
||||
· \`customer.profile\` 与 \`customer.contact\` —— **新客户这两张不用你写**:
|
||||
人在录入卡上按下确认那一刻基座已经落好了(两段一次落两行)。
|
||||
再写一遍只会被「写了等于没写」拒回。认出来的老客户更不必重写主数据。
|
||||
· \`quote_intake.verdict\`:customer_code、**customer_name**(公司名,认出来的取主数据那一行,
|
||||
新建的取卡上填的)、**contact_name**(发件人姓名,从落款或邮箱认;认不出就留空,别拿公司名顶)、
|
||||
**customer_kind**(照第③条的判断写「老客户」或「新客户」四个字,
|
||||
别的写法都不算 —— 报告上那一格与 A4 的首单口径都读它),并把 **customer_total 记 1**。
|
||||
|
||||
顺带把**询价单号 rfq_ref** 认出来记进 verdict:从主题或正文里原样照抄
|
||||
(RFQ-xxxx / 询价单号 / Ref / PO 号),认不出就在②那张卡上一并问,**不要自己编** ——
|
||||
所有台账都以 rfq_ref 为主键,编错了这一单的行会和别的单撞在一起。`,
|
||||
},
|
||||
{
|
||||
item: 'S2_normalize',
|
||||
intent: '解析邮件正文与附件 BOM,逐条落成受理记录;缺哪几格照实记数',
|
||||
writes: ['quote_intake.record', 'quote_intake.verdict'],
|
||||
reads: ['customer.profile'],
|
||||
/* 拆料这一拍工作区就该摆出受理需求那份件 —— 人看得见 AI 拆到哪儿了 */
|
||||
view: 'intake-overview',
|
||||
tools: [],
|
||||
/*
|
||||
* **这一步不叫人**:拆料是 AI 的活,拆完就算办完,由下一步(S3_confirm)交给人审定。
|
||||
*
|
||||
* 从前这里既拆料又发卡,收步条件是
|
||||
* `line_total > 0 and missing_key_total = 0 and (missing_minor_total = 0 or answered_total > 0)`
|
||||
* —— 括号里那半句的意思是「缺东西才需要经过人」。于是一封字段齐全的询价进来,
|
||||
* 条件当场为真,引擎在下一拍开头把这一步自己收了,人一眼没看过受理需求;
|
||||
* 下一次被叫到已经是终局的验收放行。用户 2026-08-01 当场指出这件事。
|
||||
*
|
||||
* 现在这里只留「拆出行来了没有」:缺不缺字段不是收步的事,是卡上要说清的事。
|
||||
*/
|
||||
/*
|
||||
* **拆出来几行,就报几条** —— 收步判据与验收判据同源(2026-08-01 加后半句)。
|
||||
*
|
||||
* 从前只看 `line_total > 0`:多写一行也照收,错要到第 10 拍提交放行时
|
||||
* 才被 `record_rows = line_total` 拦住 —— 那时上下文早翻篇了,
|
||||
* 模型只会去改数(实测:客户说本轮跳过的那颗料被写进有效清单,
|
||||
* 改数又让 skipped_total 不对,连拒五拍熔断)。
|
||||
* **同一个错,在第 2 拍发现比在第 10 拍便宜得多**:那时行刚写下、多的是哪一行一目了然。
|
||||
*/
|
||||
doneWhen: 'line_total > 0 and quote_intake_record_rows = line_total',
|
||||
guide: `客户认出来了,现在把询价内容逐条落成受理记录。
|
||||
|
||||
【先看料在哪】**正文与附件分工不同,别只读一处**:
|
||||
· 附件的 \`rows\` 有值 → **基座已经把那张表拆好了**:一行一个对象、键就是表头列名。
|
||||
**照它拆条,别自己去数制表符** —— 数格子是基座替你做的事,你只管把列对到我方字段上。
|
||||
(\`text\` 仍留着原文:口径要求那些不在表里的东西还得从正文与原文看。)
|
||||
· 只有 \`text\` 没有 \`rows\` → 那张表不规整(列数对不齐),基座不敢替你拆;
|
||||
这时才自己看原文,并且格外小心连着的制表符是「中间那一格空着」。
|
||||
这时正文通常只给**口径要求**(币种/贸易条款/交期单位/认证/替代授权/本轮跳哪条),
|
||||
那些不是料,是**落到备注与品质要求里的约束**。
|
||||
· 附件没有或读不出 → 以正文为准。**别猜附件里有什么**。
|
||||
|
||||
【客户说不报的,就不要报】正文里点名「本轮跳过 / not finalised / 暂不报」的那几条,
|
||||
**不进有效清单** —— 但要在收尾时说清「本轮不报几条、为什么」。
|
||||
有效条数 = 附件总行数 − 客户点名跳过的条数,两个数都要说得出。
|
||||
**跳过的那条连一行都别写进 \`record\`**:实测栽过一次 —— 数报对了(9 有效 / 1 跳过),
|
||||
行却写了 10 条,判据 \`record_rows = line_total\` 当场拦住,而那时改数字是改不对的
|
||||
(改了 skipped_total 又不对)。真写进去了就用 \`drop\` 把那一行撤掉,别去动数。
|
||||
|
||||
【表头怎么对】BOM 的列名常是客户本国语言或缩写,**先把列对上再拆行**:
|
||||
· 年用量那一列叫 EAU / Qty / 年用量 / Годовой объём;**带 (k) 就是千**(30 → 30000);
|
||||
· 制造商叫 Manufacturer / Производитель;品类叫 Category / Категория;
|
||||
· 客户自己的编码常在第一列(Itelma Code / Customer P/N)→ 落 customer_pn。
|
||||
**对不上列就别猜**:宁可把那一列整列判为缺,也不要把「认证要求」当成「年用量」。
|
||||
|
||||
【制表符分列:空格子会连着出现,别把后面的值往前挪】
|
||||
一行里连着两个制表符(\`\\t\\t\`)就是**中间那一格空着**,不是没有这一格。
|
||||
拆每一行前先数:**这一行的格子数必须与表头的列数相同**,数不对就整行重数,
|
||||
别把右边的值挪到左边的空位上 —— 实测栽过两次,同一行:
|
||||
制造商那一格空着,于是「浙江嘉康」被读成上一行的厂牌、品类也跟着错位成「集成电路」,
|
||||
而 BOM 上白纸黑字写着 Антенна(天线)与浙江嘉康。
|
||||
行尾连着几个制表符也一样:那是**末尾几列都空着**(年用量、目标价客户没给),
|
||||
不是这一行到此为止 —— 末尾空列照样要算进格子数里。
|
||||
列对上了就**逐行照抄值**,别整列判缺 —— 实测栽过一次:9 条料的年用量明明都在表里,
|
||||
却被整批报成「缺年用量」,一张卡问了客户 9 个他早就给过的数。
|
||||
|
||||
【一张卡别铺太长】缺口卡的 basis 与表格**只列真正缺的那几行**,不要把全部条目铺进去:
|
||||
整份回复有长度上限,铺太长会在半途被截断 —— 截断的 JSON 解析不了,
|
||||
这一拍作废、还会弹一张「需要你判断下一步」把人叫来(实测)。缺几条就写几条。
|
||||
【两种分量 —— **必填只有两项,别的一个都不是**】
|
||||
· **必填(只有这两项)**:\`pn\`、\`eau\` 年用量 —— 缺了这一单没法报价,
|
||||
**这是验收标准**,不齐不许放行,记进 missing_key_total。
|
||||
· **选填(除上面两项之外的一切)**:客户料号、品类、制造商、**目标价**、交期、品质要求、备注……
|
||||
缺了照样往下走,只当提醒,记进 missing_minor_total。
|
||||
**目标价尤其常缺** —— 客户不给目标价是常态,不是缺陷:实测栽过一次,
|
||||
一颗料只缺目标价却被记成必填缺、还派了执行卡去问客户,而按 F-01 它根本不该拦单。
|
||||
拿不准某个字段算哪一档,就问自己一句:**它是不是 PN 或年用量?** 不是就是选填。
|
||||
|
||||
【怎么拆】逐条写进 \`quote_intake.record\`,一条需求一行(同一 PN 不同数量档算两行)。
|
||||
列名照台账字典写,别自己起名:
|
||||
· \`pn\` 我方料号 · \`customer_pn\` 客户自己的编码(BOM 里那一列,回价单要按它对齐)
|
||||
· \`category\` 品类 · \`brand\` 制造商 · \`eau\` 年用量 · \`target_price\` 目标价
|
||||
· \`quality_req\` 认证/品质要求 · \`remark\` 备注 · \`customer_code\` 上一步认出的那个
|
||||
四条纪律:
|
||||
① **原文没写的不要补** —— 缺就是缺,记进缺口数,别替客户填一个「看起来合理」的值。
|
||||
数值列(年用量 / 目标价)没有就给**空字符串 \`""\`**,基座认得出「这一列没有值」,
|
||||
报告上会标成红的「必填」等人补。**绝不写 0** —— 0 是一个真实的数量,
|
||||
写下去就等于告诉下游「客户要 0 个」;实测栽过:DAG 那条 BOM 里年用量本来是空的,
|
||||
被写成 eau=0,而同一拍的缺口数还记着 1 —— 一份自己跟自己打架的记录。
|
||||
② **PN 一个字符都不许改**:后缀、横杠、空格全照抄
|
||||
(实测:\`DAG2400L321504XZB01-R0\` 被写成 \`DAG2400L321504XZB01\`,尾巴上的 -R0 丢了 ——
|
||||
往后查历史价查不到、开出去的询价请求对不上料,而错处早已在这一步埋下)。
|
||||
③ **缺哪一格算哪一格,别整行判缺**:同一行里有值的列照抄
|
||||
(实测:那条缺年用量的料,连 BOM 里明明写着的制造商「浙江嘉康」与品类都一并没写 ——
|
||||
人拿到卡以为客户什么都没给,白问一轮他早就给过的东西);
|
||||
④ 单位归一但不换算口径(12K→12000 可以;月用量×12 是业务判断,该问客户);
|
||||
③ **外文品类照译不照抄**:BOM 常是客户本国语言(Варистор = 压敏电阻,
|
||||
Антенна = 天线,Дроссель = 电感,Фильтр = 滤波器,Микросхема = 集成电路),
|
||||
译的是**品类那一列的词**,不是按料号猜这颗料是什么
|
||||
(实测:一颗天线被写成「集成电路」—— BOM 上白纸黑字写着 Антенна);
|
||||
译不准的原文照留,别硬编一个。
|
||||
④ 正文里的口径逐条落到对应行:点名要证书的写进 \`quality_req\`;
|
||||
客户**已预授权的替代件**写进 \`remark\`(那是后面选型的依据,丢了就得回头问客户)。
|
||||
|
||||
【同一拍记账】把 \`quote_intake.verdict\` 的这几个数写上:
|
||||
· bom_total = 来件一共几条(附件行数,不含表头);
|
||||
· skipped_total = 客户点名本轮不报的条数;skipped_note = 是哪几条、为什么(一句话);
|
||||
· line_total = 落了几条(= bom_total − skipped_total,对不上就是拆漏了);
|
||||
· **missing_key_total** = 缺 PN 或 年用量 的条数(必填缺);
|
||||
· **missing_minor_total** = **除 PN / 年用量之外任何字段缺了**的条数(选填缺)——
|
||||
客户料号、品类、制造商、目标价、交期、品质要求、备注,缺哪一个都算,一条只数一次。
|
||||
**别只数品类和制造商**:实测栽过一次,NE555P 只缺目标价,两个缺口数报的都是 0,
|
||||
于是那一问被整个吞掉 —— 人在成果表里看见一格空白,却没人问过他一句。
|
||||
空不空由他决定要不要去补,不由你替他吞掉。
|
||||
|
||||
【最要紧的一条:**数就是数,不许为了过判据去改它**】
|
||||
两个缺口数如实写。缺一条就是 1,别因为「写 0 才好往下走」就写 0 ——
|
||||
实测栽过一次:一颗料只缺目标价,如实记了 1,提交被判据拦住,
|
||||
于是那个数被改回了 0、单子放行了,而台账上从此写着「缺选填 0 条」,
|
||||
那一格空白再没人提起。**判据拦住你,说明该去办那件事,不是该去改那个数**;
|
||||
真办不了(客户就是不给),把它留在数里、在 say 里说清楚,放行时由人定。
|
||||
|
||||
**这一步不发卡**:拆完就 finish,交给下一步(受理需求交人审定)去请人。`,
|
||||
},
|
||||
{
|
||||
item: 'S3_confirm',
|
||||
intent: '把整份受理需求交人审定:可就地改,确认后才往下走',
|
||||
writes: ['quote_intake.verdict'],
|
||||
reads: [],
|
||||
/* 阶段2 的执行目标件:询价受理需求(照 JCAI_PRD 的 A1-询价受理需求.html) */
|
||||
view: 'intake-overview',
|
||||
tools: [],
|
||||
/*
|
||||
* **不给 doneWhen** —— 这一步触达世界(把件交到人手上),给了就等于给引擎
|
||||
* 一把替人签字的钥匙。而它的成果按定义也判不出来:「人看过、改过、认了」
|
||||
* 不是台账里任何一个数能自己变出来的。人工激活也一并给上:
|
||||
* 引擎不会替你按下这一步,你得先 start 再发卡。
|
||||
*/
|
||||
guide: `拆好的九条摆在那儿了,**这一单现在要经过人的手**。
|
||||
|
||||
【一单必发一张,不管缺不缺】
|
||||
这不是「缺东西时的追问」,是**阶段2 那张执行页**:AI 拟了稿,人审定。
|
||||
字段齐全也要发 —— 拆错一格(PN 少个后缀、品类译反、年用量看错列)只有人看得出来,
|
||||
而那些错往后每一步都跟着跑。**别因为「没有缺口」就跳过这一步**:
|
||||
实测栽过一次(2026-08-01),那一版只在缺东西时才发卡,于是一封齐整的询价进来,
|
||||
人一眼没看过受理需求,下一次被叫到已经是终局验收 —— 用户当场说
|
||||
「我都没确认需求 它就让我验收了」。
|
||||
|
||||
【怎么发】先 start 这一步(它是人工激活的),再照这个骨架发,别少字段:
|
||||
{"kind":"human","itemId":"S3_confirm","workOrder":"execution","card":{
|
||||
"title":"…","ask":"这是从来件与附件 BOM 归一出来的受理需求,请核对确认;哪一格不对就地改",
|
||||
"steps":["型号 PN 与年用量 EAU 必须有 —— 缺一条这一单就走不到放行;客户没给的去问,问不到也要在备注里写清「客户未提供」",
|
||||
"目标价尽量试探出来 —— 有它才谈得上「报得进不进得去」;客户不肯给,问一句「贵司这颗现在的成交价区间」也常有答案",
|
||||
"抽错的就地改,多出来的行按行首 × 撤掉,漏掉的用表尾「加一行」补上;核完回这张卡提交"],
|
||||
· **卡面是纯文本**:\`steps\` 与 \`standard\` 里**别写星号那类记号** —— 它们不会变成粗体,
|
||||
只会原样摆在人眼前(实测过一次)。要强调就靠句子本身的次序与用词。
|
||||
"standard":"必填(PN / 年用量)一条不缺;选填该问的都问过了 —— 问到的填上,问不到的留空并在备注里写明,放行时由你决定先受理还是退回。",
|
||||
"basis":[{"k":"DAG2400L321504XZB01-R0","v":"缺年用量(必填,须补)"},
|
||||
{"k":"UMW AO6604","v":"缺制造商(选填,可先受理)"}],
|
||||
"blocks":[{"t":"table","title":"有效 PN 清单(可就地改)",
|
||||
"ledger":"quote_intake.record","editable":true,
|
||||
"cols":["PN","客户料号","品类","年用量","制造商","目标价","备注"],
|
||||
"fields":["pn","customer_pn","category","eau","brand","target_price","remark"]}]}}
|
||||
**\`steps\` 与 \`standard\` 是作业指导书** —— 卡上照原样摆给人看:要核什么、核到什么程度算完。
|
||||
照上面抄即可(这一步每一单都一样,不必每次重写)。少了它,人拿到的只有
|
||||
「请核对确认」四个字,不知道该核什么。
|
||||
· \`ask\` 照当下情形写:**有必填缺**就写「有几条缺 PN/年用量,须向客户问清后填回」;
|
||||
**只有选填缺**写「有几条缺选填项,可先受理也可去问」;**都齐**写「请核对确认」。
|
||||
· **别自己抄行**:绑了 \`ledger\` + \`fields\`,基座发卡那一刻就把这一单**整份清单**
|
||||
从草稿层铺进去 —— 人看到的是完整的九行,哪一格不对都能就地改,不只是缺的那一两行。
|
||||
你抄一遍的下场实测过两次:回复有长度上限,铺长了半途截断、整拍作废;
|
||||
抄的时候还把 BOM 原样的 30 填了进去(台账里已经是 30000)。
|
||||
· \`fields\` 与 \`cols\` **一一对应**,列名照台账字典写。
|
||||
· \`basis\` 逐条写清**谁缺什么、是必填还是选填** —— 分量不同,人心里要有数;
|
||||
一格不缺就写这一单的要点(几条料、几条本轮不报、客户点名的口径要求)。
|
||||
发完卡把 **asked_total 加 1**(记账用:它证明这一问没省)。
|
||||
|
||||
【人提交之后 —— **表里的值基座已经替你写回了**】
|
||||
那张表绑着台账,人按下提交那一刻基座就把改动落进了草稿层(与数据录入卡同一条规矩:
|
||||
该由人亲手落下的,不经你的手)。所以**别再 write 一遍那几行** ——
|
||||
值没变的写入会被「写了等于没写」拒回(实测)。
|
||||
|
||||
你这一拍只做三件事,然后 finish:
|
||||
① **把 missing_key_total / missing_minor_total 重算** —— 照台账里现在的值,
|
||||
补上了就该变小(基座写的是行,汇总列是你的活);
|
||||
② **confirmed_total 记 1** —— 这一单经过人了。**不写它这一步收不了,整单卡在这儿**;
|
||||
③ answered_total 加 1(答案已经处理过了)。
|
||||
漏掉①的下场实测过:值明明补上了,汇总列还写着「缺 1 条」,提交被判据一次次拦回来,
|
||||
连拒五拍熔断 —— 而你只要把那个数改对就过去了。
|
||||
回填时**按卡上那一列的 PN 对行**:卡上的 PN 就是台账里那一行的主键,
|
||||
换一个写法就是**新开一行**(实测:补写时把 PN 补全成 \`…-R0\`,于是同一颗料在表里成了两行,
|
||||
条目数说 9 而表里躺着 10)。真发现第一次的 PN 抄错了,在 say 里说明并交给人,
|
||||
别自己用新 PN 再写一行。
|
||||
|
||||
【**必填还缺就不能收**】confirmed_total 记了也没用:必填(PN / 年用量)缺一条,
|
||||
下一步就是进不去 —— 这是验收标准。人交回来还缺必填,就再发一张卡说清还差哪几条。
|
||||
选填缺则不拦:问过就够了,别为它反复发第二张卡。
|
||||
|
||||
【人把格子留空交回来 = **客户给不了**,那也是一个答案】
|
||||
这时候要做的**不是再问一遍**(基座会当场拒:同一件事不许问第二遍),而是:
|
||||
· confirmed_total 记 1、answered_total 记 1(人已经处理过了);
|
||||
· 缺口数**照实留着**(缺就是缺,别改小);
|
||||
· 这一步就算办完了 —— 选填缺不拦单,放行时由人定「先受理还是退回」。
|
||||
实测栽过一次:人留空交回,模型又发一张一模一样的卡,被拒;
|
||||
转头把同一行原样再写一遍,又被「写了等于没写」拒;连拒五拍熔断,一单就这么停了。`,
|
||||
},
|
||||
{
|
||||
item: 'S4_history',
|
||||
view: 'intake-classified',
|
||||
intent: '调历史价工具,把 P-03 的三项判据取齐并落进料号分类表',
|
||||
writes: ['quote_intake.type_table'],
|
||||
reads: [],
|
||||
tools: ['crm.history-price-read'],
|
||||
/*
|
||||
* 判据落表了才算取齐。**不能写 `history_days != null`** ——
|
||||
* S-FEEL 里未绑定的名字 `!= null` 求值为 true,那一步开局就被收掉(踩过,测里拦下的)。
|
||||
* 取正向判断:没写过就没有这个键,`>= 0` 自然为假。
|
||||
*/
|
||||
doneWhen: 'history_days >= 0',
|
||||
guide: `P-03 只认三个入参:距上次报价天数、行情是否稳定、生命周期状态。
|
||||
三者齐了结论是**算出来**的,所以这一步唯一该做的事是把判据取齐:
|
||||
调 \`crm.history-price-read\`,**一次把这一单所有 PN 传进去**(参数 pns 收数组,
|
||||
别一条一条调 —— 每调一次就多烧一拍)。取不到不是错误:新料号无历史会回
|
||||
historyDays=9999、hit=false,那本身就是判据,命中 B 类「重估后报」是对的。
|
||||
拿回来的三项判据**原样逐条**写进 \`quote_intake.type_table\`。
|
||||
|
||||
**这一步一个数都不许自己填**(实测踩过,三条同时犯):
|
||||
① 工具回什么就写什么 —— historyDays / marketStable / lifecycle 三个值**逐字照抄**。
|
||||
你不知道某颗料的行情,工具知道;你估一个「15 天 · 在产」,判定表就照着你的估算出 A,
|
||||
而那颗料实际是客户点名要替代的 C。**估出来的判据比没有判据更坏**:它看起来很像真的。
|
||||
② **PN 必须原样**,一个字符都不许截、不许改写(实测:\`DAG2400L321504XZB01-R0\`
|
||||
被写成 \`DAG2400L321504XZB01\`,尾巴上的 -R0 丢了 —— 工具当场查不到,
|
||||
于是你又给它编了一组判据)。传进去的 PN 与写回台账的 PN 必须与受理记录逐字一致。
|
||||
③ 工具回 hit=false(这颗料没有历史)时,**照它回的 9999 / false / 未知 写**,
|
||||
那三个值本身就是判据,命中 B 类「重估后报」是对的结论 —— 不是缺数据,不必替它补。`,
|
||||
},
|
||||
{
|
||||
item: 'S5_classify',
|
||||
view: 'intake-classified',
|
||||
intent: '调分类器按 P-03 给每条打 A/B/C,标落两张表,条数照出参抄',
|
||||
writes: ['quote_intake.type_table', 'quote_intake.record', 'quote_intake.verdict'],
|
||||
reads: [],
|
||||
tools: ['quote-intake.classify'],
|
||||
/*
|
||||
* **收步判据与验收判据同源**(2026-08-01 改)。
|
||||
*
|
||||
* 从前只看 `untyped_total = 0` —— 那是活动自己写的数。它归零了这一步就收,
|
||||
* 而验收那条比的是 `type_table_rows = record_rows`(基座数出来的行):
|
||||
* 数归零了、表没写全,于是**收了步却过不了验收**,模型得回头补写一张已经收掉的表。
|
||||
* 收步比验收松,松出来的那一截就是返工。这里补上基座那一边:两处比同一件事。
|
||||
*/
|
||||
doneWhen: 'untyped_total = 0 and line_total > 0 and quote_intake_type_table_rows = quote_intake_record_rows',
|
||||
guide: `调 \`quote-intake.classify\`(纯工具,P-03 的求值器,同输入必同结论、零 token),
|
||||
把 type_table 那几行判据一次传进去,拿回每条的 A/B/C 与打法。
|
||||
|
||||
【标要落**两张表**】
|
||||
· \`quote_intake.type_table\`:type_tag + basis —— 这是「凭什么这么判」的底稿;
|
||||
· \`quote_intake.record\`:**同一个 type_tag 也要写回受理记录**(按 PN 对行)——
|
||||
受理记录才是流去下游的那份交付物,A4 就靠这一列决定哪几条能直接取历史锚点。
|
||||
漏了这一步,报告上的状况标那一列整列是空的,而下游拿到一份没打标的清单(实测)。
|
||||
|
||||
【条数**照出参抄,不许自己数**】classify 的出参里带 \`counts\`(A/B/C 各几条)与 \`total\`,
|
||||
照它写 verdict 的 type_a_total / type_b_total / type_c_total,并把 untyped_total 归零。
|
||||
实测数错过一次:判定器给的是 5A/3B/1C,报上去的是 7A/2B/1C,派单也跟着按错的数开 ——
|
||||
而验收那条等式两边都是自报的,于是「2 = 2」顺利过关,真实的第三条 B 类料一张单也没开。
|
||||
|
||||
**不要绕过决策表自己判** —— 规则表可回放、可追责,临场判断不是。
|
||||
\`type_tag\` 一律取出参里的那个字母,**不许自己写一个**:实测有过一次,
|
||||
它自己给出的判据是「200 天 · 行情不稳」(按 P-03 该判 B),写回台账的标却是 A ——
|
||||
判据与结论对不上,而验收只看结论,这种错一路走到报价单上都没人拦。
|
||||
拿不准的一律降 B,不许臆断 A:错标 A 会让一条本该重新核价的料带着旧价走到报价单上。`,
|
||||
},
|
||||
{
|
||||
item: 'S6_route',
|
||||
/* 阶段3 的验收目标件:询价受理记录(照 A1-询价受理记录.html) */
|
||||
view: 'intake-release',
|
||||
intent: '按三类结果立两枚去向旗',
|
||||
writes: ['quote_intake.verdict'],
|
||||
reads: [],
|
||||
tools: [],
|
||||
/*
|
||||
* **这一步不再开单**(2026-08-02 用户当场定):
|
||||
* 「替代评估请求 / 询价请求这两项数据不在这里产出;随令牌流向下一个节点的
|
||||
* 只有工作成果验收内的内容;进入选型替代后,活动会自己归一需要的数据。」
|
||||
*
|
||||
* 从前 A1 逐条开 `alt_request` / `rfq_request` 两张单交给 A2 / A3 ——
|
||||
* 那是替下游把它的输入先摆好,而下游本来就要照自己的口径归一一遍:
|
||||
* 同一份事实在两个活动里各存一份,改一处就分叉。A1 交出去的是**受理记录**,
|
||||
* 下游读它即可。
|
||||
*
|
||||
* 收步判据随之改成「两枚旗都立了」。旗是布尔,合法值含 false,
|
||||
* 拿它判「写没写过」分不清「没写」与「写了 false」—— 所以仍取同步写下的**计数**
|
||||
* (`route_decided_total`:定完去向记 1),并挂 `untyped_total = 0` 当前置:
|
||||
* 两个数都得是模型显式写过的,否则「未绑定的名字相等」会让这一步开局即收
|
||||
* (包内自测那条「doneWhen 开局就为真」拦过这个)。
|
||||
*/
|
||||
doneWhen: 'untyped_total = 0 and route_decided_total > 0',
|
||||
guide: `状况标是判断的**输入**,不等于去向。照三类的结果立两枚旗:
|
||||
· 有 C 类(EOL / 缺货 / 客户点名替代)→ need_alternative = true,这一单要走替代选型;
|
||||
· 有 B 类(无近期历史价或行情波动)→ need_sourcing = true,这一单要走寻源询价;
|
||||
· 两者可同时成立 —— 选型定案后仍要为替代料寻价。
|
||||
写完把 **route_decided_total 记 1**(记账用:它证明去向定过了,引擎按它收步)。
|
||||
|
||||
**不要开什么请求单** —— 替代评估请求、询价请求都**不在这一步产出**:
|
||||
随令牌流去下一个节点的只有**工作成果验收里的那份受理记录**,
|
||||
A2 / A3 拿到之后会按自己的口径归一需要的数据。你替它们先摆一份,
|
||||
等于同一份事实在两个活动里各存一份,改一处就分叉。
|
||||
|
||||
**这两枚旗是下游两道网关唯一读的东西**:不写,整条流出了受理口就没路可走。
|
||||
|
||||
**去向不用你选,也不用人选** —— 契约给三个出口各写了判据(有没有 C 类、有没有 B 类),
|
||||
放行那一刻基座按序求值自己定,卡上只如实告诉人走哪一口、凭哪一条。
|
||||
你要做的只有一件:**把三类条数与两枚旗写对**(它们正是那几条判据读的数)。
|
||||
旗写错,算出来的去向就错 —— 但那是数据错,不是选择错。`,
|
||||
},
|
||||
{
|
||||
item: 'T_release',
|
||||
view: 'intake-release',
|
||||
intent: '判定成果已齐,提交放行交 BD 验收',
|
||||
writes: [],
|
||||
reads: [],
|
||||
tools: [],
|
||||
// 放行触达世界,必须人按一下 —— 引擎不自动收它(剧本里也不给 doneWhen)
|
||||
guide: `line_total > 0、missing_key_total = 0、untyped_total = 0、两枚旗已定 ——
|
||||
**先 start T_release**(它标了人工激活:放行会触达世界,必须按一下)。
|
||||
|
||||
**start 之后、submit 之前,把清单里**每一条**裁量判据(L3/L4)都裁掉**
|
||||
(同一拍里排在 start 后面即可)。**一条都不能漏** —— 漏一条,submit 当场被拒,
|
||||
而拒回来的理由里就写着漏的是哪一条:照它原样抄 criterion 再裁一次即可,
|
||||
**别原样重提同一个 submit**(实测连提七次,每次都被同一条拒回来)。
|
||||
|
||||
眼下**只剩一条**,照名字逐字抄(2026-08-02 起「吃不准降 B」那条已从清单里删掉,
|
||||
别再裁它 —— 裁一个不存在的判据会被拒):
|
||||
· judge criterion="选填缺口(客户料号/品类/制造商/目标价)已补齐,或由你放行时确认可以先受理" ——
|
||||
**只看 \`quote_intake_record_gaps\` 这一个数**(基座现数的:还有几行缺选填;
|
||||
\`..._gaps_keys\` 写着缺在哪几行的哪一格)。就两种写法,没有第三种:
|
||||
· **= 0** → satisfied=true,rationale 写「选填已补齐」;
|
||||
· **> 0** → satisfied=**false**,rationale 照 \`..._gaps_keys\` 逐条写清缺在哪儿
|
||||
(例:「DAG2400L321504XZB01-R0 缺目标价」),交放行的人定「先受理还是退回」。
|
||||
|
||||
**「我发过卡问过了」不算满足** —— 发卡是过程,不是结论;而这一单本来就必发一张
|
||||
审定卡,拿它当理由的话这条判据永远是绿的,等于没有这道门
|
||||
(实测:客户没给目标价,它照样裁了满足,人签字时根本不知道还缺着)。
|
||||
裁成不满足**照样能提交**,理由连着卡一起交给签字的人 —— 那正是这条判据的用处。
|
||||
**不许为了让它绿去改哪个数** —— 那个数你也改不动,它由基座现数。
|
||||
**criterion 照判据名原样写**(整句,含括号里那几个字),写别的名字对不上,那一条仍算未达标。
|
||||
|
||||
**去向不用你裁**:三个出口各写了判据,放行那一刻基座按序求值、首个命中即出 ——
|
||||
那是算出来的,不是判出来的。从前这里还有一条「去向判断合乎业务常理」,
|
||||
那是让你给一个自己没做的决定盖章:裁「满足」是废话,裁「不满足」也改不动什么。
|
||||
C 类开没开替代评估请求、B 类开没开询价请求,由两条机检判据当场对账。
|
||||
|
||||
这一条是机器判不了的(L4 独立评审:缺口真还在时,「先受理还是退回」只有人能定),
|
||||
只能由你当场裁并留下理由 —— **不裁就提交会被当场拒**:还有裁量判据没裁过时,
|
||||
提交等于把没办完的活推给人。裁完再 submit,BD 验收。
|
||||
(缺口为 0 时基座当它已达标、不必你裁;裁了也不碍事。)
|
||||
终局唯放行:你不能自己宣布完成。
|
||||
验收报告上让 BD 当场改的那两个值(两枚去向旗)由**契约声明**(revisableAtRelease),
|
||||
基座放行前从台账现读拟值 —— 你不用在 submit 上重述一遍,写好台账就行。`,
|
||||
},
|
||||
];
|
||||
|
||||
/**
|
||||
* 阶段 —— **进度条的刻度**,只给人看,引擎一眼都不看。
|
||||
*
|
||||
* 六个计划项是引擎的粒度(拆到能判「干完没有」);阶段是人的粒度。
|
||||
* 「查历史价 / 逐条打标 / 定去向」在业务上是**一件事** —— 判这单该怎么走,
|
||||
* 分成三项是因为引擎要一项一项收,而人只想知道「到判去向这一步了」。
|
||||
*
|
||||
* 只列**激活态**的三段。放行不在这儿:每个活动都以放行收口(铁律③),
|
||||
* 那是系统的规矩不是 A1 的阶段;进度条自己在末尾加那一格,所以人看到的是 x/4。
|
||||
*/
|
||||
export const INTAKE_PHASES = [
|
||||
{ name: '定位客户', items: ['S1_customer'] },
|
||||
{ name: '归一受理', items: ['S2_normalize'] },
|
||||
{ name: '判定去向', items: ['S4_history', 'S5_classify', 'S6_route'] },
|
||||
];
|
||||
|
||||
Reference in New Issue
Block a user