@alipay/alipay-aipay 1.6.7 → 1.6.8-beta1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/skill/VERSION +1 -1
- package/skill/references/integration/flow.md +4 -4
- package/skill/references/normal/full-process-routing.md +3 -3
- package/skill/references/onboarding/flow.md +4 -2
- package/skill/references/onboarding/modules/product-sign.md +1 -1
- package/skill/references/onboarding/modules/scripts/mcc.sh +226 -1
- package/skill/references/runtime-entry.md +1 -1
package/package.json
CHANGED
package/skill/VERSION
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
1.6.
|
|
1
|
+
1.6.8-beta1
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
|
|
8
8
|
## 按步读取
|
|
9
9
|
|
|
10
|
-
禁止整份预读,只通过当前 Skill 绝对路径下的统一 runtime 按 marker 加载阶段,本 flow 仍是唯一执行依据:`entry`;项目与技术栈唯一后 `prepare
|
|
10
|
+
禁止整份预读,只通过当前 Skill 绝对路径下的统一 runtime 按 marker 加载阶段,本 flow 仍是唯一执行依据:`entry`;项目与技术栈唯一后 `prepare`;非阻塞方案送达后按步骤 2 完成执行者分流,再由确定的执行者加载 `development --product-type "$PRODUCT_TYPE"`;其后依次加载 `implementation`、`verification`、`acceptance --product-type "$PRODUCT_TYPE"` 和 `checklist --product-type "$PRODUCT_TYPE" --project-path "$PROJECT_PATH"`。统一单命令规则继承 `runtime-entry.md`;用户项目命令仍以已确认项目根为 workdir。未到阶段、无关产品/语言、维护说明和模块不得提前读取,原 flow 保留完整规则供校验。
|
|
11
11
|
|
|
12
12
|
步骤 1 只有 `CLI_ENV:NEEDS_SETUP` 才读 `../normal/alipay-cli-env.md`;步骤 2 只有产品无法唯一确定时才读 `modules/product-decision.md`;沙箱正常路径只执行已封装脚本,协议异常或用户要求解释时才读沙箱指南;步骤 4 只按 `integration_context_runner.mjs` 的当前产品/语言事实读取 SDK、本地契约和示例,不读取接口索引本身;步骤 6 仅按量付费读 A2M 沙箱模块;步骤 8 由 loader 直接附带当前产品适用 checklist,禁止再读取整份 `modules/checklist.md`。
|
|
13
13
|
|
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
|
|
16
16
|
## ⛔ 强制执行要点
|
|
17
17
|
|
|
18
|
-
先确认唯一项目和服务端语言,再在产品确定前执行步骤 1;匿名沙箱不传 `PRODUCT`,缺失时创建、存在时复核,待配置只阻止沙箱依赖动作。步骤 2
|
|
18
|
+
先确认唯一项目和服务端语言,再在产品确定前执行步骤 1;匿名沙箱不传 `PRODUCT`,缺失时创建、存在时复核,待配置只阻止沙箱依赖动作。步骤 2 的完整方案可见送达前不得改代码;送达后按步骤 2 完成执行者分流,再由确定的执行者按同一产品、项目和技术栈开工,不再执行方案 receipt 或项目快照二次校验。`integration_only/full_process` 共用检查器事实和非阻塞 `integration.start.confirm`,一次只开发一个产品。网站支付/APP 支付必须实现下单、查询、退款、退款查询、关闭和异步通知;按量付费只实现 402、验付和履约确认。本流程不主动发起真实生产交易。
|
|
19
19
|
|
|
20
20
|
生产 `appId`、应用公钥、应用私钥和支付宝公钥必须属于同一生产应用,私钥格式必须匹配当前语言,格式转换只用支付宝官方密钥工具。网站支付/APP 支付本地无公网 `notify_url` 时仍实现通知代码,可暂不传地址并用查询验收;生产上线前必须恢复公网通知及验签、幂等、关键字段、`success` 回写和补偿查询,不得提前宣称生产就绪。
|
|
21
21
|
|
|
@@ -203,11 +203,11 @@ node "<SKILL_DIR>/references/normal/scripts/runtime.mjs" integration-message sta
|
|
|
203
203
|
|
|
204
204
|
若 bootstrap 尚未创建正式 run,本命令在任何 Integration 业务 stage 前原子创建 run 和 `clarify_intent`,并输出后续唯一使用的 `RUN_ID` marker;触发本命令的首条消息不计入新 run。已有同组合 active run 时只复用 marker。步骤 3 之后所有能生成 stage 的登记命令都必须携带当前 telemetry context;runner 已生成 stage 后 Agent 禁止重复调用 `telemetry stage`。
|
|
205
205
|
|
|
206
|
-
runner 失败时不得展示手写方案或进入步骤 3。成功时必须取得唯一 `ALIPAY_AIPAY_INTERNAL:CUSTOMER_MESSAGE_DELIVERY_REQUIRED=integration.start.confirm`;缺失或重复都视为失败。stdout 是步骤 2 唯一对客消息;命令后的下一个动作必须把 stdout 作为可见进行中消息原文送达,不得作为最终答复结束当前回合,送达前禁止调用工具。命令面板、折叠输出、内部思考或“已渲染/已展示”说明都不能替代交付;禁止添加 `<...>`
|
|
206
|
+
runner 失败时不得展示手写方案或进入步骤 3。成功时必须取得唯一 `ALIPAY_AIPAY_INTERNAL:CUSTOMER_MESSAGE_DELIVERY_REQUIRED=integration.start.confirm`;缺失或重复都视为失败。stdout 是步骤 2 唯一对客消息;命令后的下一个动作必须把 stdout 作为可见进行中消息原文送达,不得作为最终答复结束当前回合,送达前禁止调用工具。命令面板、折叠输出、内部思考或“已渲染/已展示”说明都不能替代交付;禁止添加 `<...>` 标签、“已输出完整方案”“服务声明已打印”、重复摘要或任何前后缀。送达后同一回合按根意图分流:`full_process` 先按 `../normal/full-process-routing.md` 读取 Worker 协议;满足启用条件时必须创建唯一 Worker,由 Worker 执行 development,主 Agent 不执行;不满足启用条件或根意图为 `integration_only` 时,由当前 Agent 执行 development,不得总结、询问、等待回复、解码内部值或再次渲染方案。
|
|
207
207
|
|
|
208
208
|
- `full_process`:`../normal/full-process-routing.md` 已用同一方案绑定检查器事实、产品、项目、语言/框架和服务声明;送达后按该路由选择唯一 Integration Worker 或当前 Agent 执行,禁止重显或确认。两者都须完成本 flow 全部适用接口、条件和 checklist。用户中断后事实变化则返回 router 重新澄清、扫描和展示。方案仍禁止提前展示 MCC、授权范围、开通材料或 onboarding 待办。
|
|
209
209
|
|
|
210
|
-
|
|
210
|
+
执行者分流完成后,只由确定的执行者通过 development loader 进入步骤 3;loader 只继承 `clarify_intent` 已记录的正式 run 事实,不再单独记录开始态 stage。它不表示收到额外确认回复;不得伪造用户回复。Agent 在收到每条聊天框用户消息时,必须先按 `runtime-entry.md` 的统一前置命令计数,再处理消息;不按 Skill 相关性筛选,`summary` 收口后不再计入当前 run,方案本身不是用户输入。
|
|
211
211
|
|
|
212
212
|
**方案后的上下文锁定**:已有项目保持步骤 1 前已经唯一解析的实际根目录和原有工程结构;新项目必须已经在步骤 1 前按用户明确选择的方式完成初始化并通过构建标识检查。此处不重复准备、初始化或探测项目。路径内容与步骤 1 成功时不一致则停止并重新澄清;只有目标项目或服务端语言变化时返回步骤 1,其他情况直接进入步骤 3。
|
|
213
213
|
|
|
@@ -68,7 +68,7 @@ node "<SKILL_DIR>/references/normal/scripts/runtime.mjs" integration-message con
|
|
|
68
68
|
node "<SKILL_DIR>/references/normal/scripts/runtime.mjs" integration-message locate-projects --base-path "$USER_WORKSPACE_ROOT" --search-input "$SEARCH_INPUT" --format message --input-id "$USER_INPUT_ID"
|
|
69
69
|
```
|
|
70
70
|
|
|
71
|
-
字段齐备后执行唯一 runner;禁止拆开执行项目扫描、`jq` 或 renderer,也不得附加解析。runner stdout 是本动作唯一对客消息;只有取得唯一 `CUSTOMER_MESSAGE_DELIVERY_REQUIRED=integration.start.confirm` 才能继续。命令后的下一个动作必须把 stdout
|
|
71
|
+
字段齐备后执行唯一 runner;禁止拆开执行项目扫描、`jq` 或 renderer,也不得附加解析。runner stdout 是本动作唯一对客消息;只有取得唯一 `CUSTOMER_MESSAGE_DELIVERY_REQUIRED=integration.start.confirm` 才能继续。命令后的下一个动作必须把 stdout 作为可见进行中消息原文送达,不得作为最终答复结束当前回合,送达前禁止调用工具;送达后同一回合先执行下文的 Worker 分流,由确定的执行者加载 development,不得总结、询问、等待回复或再次校验项目快照。用户中断后若方案事实变化,重新澄清、扫描并输出当前方案;静态扫描只决定进入验证,不证明完成。
|
|
72
72
|
|
|
73
73
|
`ROOT_INTENT` 固定为 `full_process`。
|
|
74
74
|
|
|
@@ -78,13 +78,13 @@ node "<SKILL_DIR>/references/normal/scripts/runtime.mjs" integration-message sta
|
|
|
78
78
|
|
|
79
79
|
上面命令失败时不得展示手写确认或进入子流程。成功时必须取得唯一 `ALIPAY_AIPAY_INTERNAL:CUSTOMER_MESSAGE_DELIVERY_REQUIRED=integration.start.confirm`;缺失或重复视为失败。参数必须是当前确定值,禁止直接拼用户输入。runner 校验产品 variant 并固定服务声明;实际范围仍取当前 Integration flow 和 checklist。
|
|
80
80
|
|
|
81
|
-
交接事实必须来自 runner 内同一次 `scan`,不得用描述、自报或历史分类代替;`evidence` 不进 renderer,Onboarding 前不传/猜 `mccCode`。方案 stdout 按 marker
|
|
81
|
+
交接事实必须来自 runner 内同一次 `scan`,不得用描述、自报或历史分类代替;`evidence` 不进 renderer,Onboarding 前不传/猜 `mccCode`。方案 stdout 按 marker 送达后,先读取 `../normal/integration-worker.md`;满足其启用条件时,按协议创建唯一 Worker,由其执行步骤 3 至步骤 8;否则由当前 Agent 执行 `node "<SKILL_DIR>/references/normal/scripts/runtime.mjs" flow integration development --product-type "$PRODUCT_TYPE"` 并串行完成步骤 3 至步骤 8。loader 只加载当前分支规则;业务完成后由登记入口记录完成态 stage。禁止手工 telemetry、解码内部值、重复扫描或重显方案。
|
|
82
82
|
|
|
83
83
|
合法组合必须确定产品、路径/来源、代码状态、语言/框架、服务声明和顺序;发现其他产品时保留。零散信息、`新建` 选择、未选候选或新项目未准备均不能替代完整机器校验。方案送达后仍完整执行 Integration 和 checklist。
|
|
84
84
|
|
|
85
85
|
## 子流程衔接规则
|
|
86
86
|
|
|
87
|
-
-
|
|
87
|
+
- 启动方案送达后先完成上述 Worker 分流,再由确定的执行者进入 Integration 步骤 3;用户中断后若项目、产品、语言或框架变化,重新澄清和扫描并输出当前方案。
|
|
88
88
|
- Integration 先完整执行和校验。只有沙箱待配置,或 READY 且仅剩非阻塞人工待验证项时,保持“部分通过”进入 Onboarding;其他代码、安全或依据缺口停在 Integration,不提前登录或收集开通材料。
|
|
89
89
|
- 阶段间只复用有效的产品、Integration 结果、沙箱状态、人工待验证项和剩余待办,不再询问是否进入 Onboarding;Onboarding 只保留非阻塞方案展示。
|
|
90
90
|
- 授权后分别查询签约、应用和适用服务;分支独立,单分支失败不回滚其他成功,认证/授权不匹配或主体变化全局阻断。一次只收集一个类别,提前材料直接校验且不重复索取。
|
|
@@ -105,13 +105,15 @@ node "<SKILL_DIR>/references/normal/scripts/runtime.mjs" onboarding-message prod
|
|
|
105
105
|
|
|
106
106
|
已有经营描述、二级类目名称或编码时,以原话作为 `MCC_SEARCH_QUERY` 进入唯一 MCC 解析/方案入口。非精确编码时,Agent 应先理解原话;遇到行业俗称、场景表达或目录不一定直接出现的用词时,在首次调用中一并传入 0 到 12 个有原话依据的行业概念、经营对象或同义表达,形成 `MCC_SEMANTIC_TERMS_JSON`;只传概念、不传 MCC 编码,不补原话无依据的行业。没有可靠扩展时省略,不追加用户确认。runner 在完整动态类目内做 NFKC、泛词剥离、标点边界内二/三元词组召回和动态稀有度排序;原始精确编码/完整名称决定性优先,其余原话与 Agent 辅助概念联合评分,语义项按 `0.7` 权重参与,名称证据高于描述,其他候选最多 8 项。评分只用于真实目录召回排序;禁止业务词到 MCC 的硬编码点映射、Agent 全表判断、远程语义或额外网络调用。
|
|
107
107
|
|
|
108
|
+
同一 `run + 用户输入 + 原始 MCC_SEARCH_QUERY + 产品` 的语义扩充最多重试 2 次。首次未提供语义词且结果为 `NONE`,或已提供语义词但结果仍为 `NONE` 且仍有预算时,`mcc.sh` 只输出唯一内部 `MCC_SEMANTIC_RETRY_REQUIRED`,stdout 为空;Agent 必须保留原始 query、复用同一 `USER_INPUT_ID`,根据原话补充此前未使用且有依据的概念后原样重跑本命令,不向用户展示中间无匹配。后续重试提交的语义词必须保留此前语义词并至少新增一个此前未使用的词;未新增或替换原语义词时不消耗额度,直接进入既有无匹配分支。`MCC_SEMANTIC_RETRY_REQUIRED` 的 `attempt` 从 1 到 2,达到 2 后不再产生该 marker;仍为 `NONE` 时输出下方既有无匹配文案并停止。精确 MCC 编码不存在不进入语义重试;网络/CLI 失败仍只走各自既有恢复分支。语义重试计数只保存在当前 run 的私有缓存中,不随 MCC 树缓存 TTL 过期,读写异常按已耗尽处理,禁止通过更换 query、重置参数或重复调用绕过上限。
|
|
109
|
+
|
|
108
110
|
`queryMcc` 树和多候选快照使用默认 1800 秒、本轮 run 私有缓存;无有效 run 不缓存。快照绑定产品、salesCode、原查询、规范化语义词、选择回执、候选树事实及按需取得的资格详情;有效命中原样复用,失效才重查。runner 只接受最多 12 项、单项 2 到 40 字符、总计不超过 240 字符的 JSON 字符串数组并拒绝 MCC 编码;Agent 参数错误时基于原话更正或省略,不要求用户重述。两种 MCP 请求体不变;`batchQueryMcc` 最多一次,只传树内命中且 `needSpecialQual=true` 的候选编码。
|
|
109
111
|
|
|
110
112
|
```bash
|
|
111
113
|
node "<SKILL_DIR>/references/normal/scripts/runtime.mjs" mcc resolve-plan --query "$MCC_SEARCH_QUERY" --product-type "$PRODUCT_TYPE" --root-intent "$ROOT_INTENT" [--semantic-terms-json "$MCC_SEMANTIC_TERMS_JSON"] --input-id "$USER_INPUT_ID" [--telemetry-run-id "$RUN_ID" --telemetry-history-count "$HISTORY_COUNT"]
|
|
112
114
|
```
|
|
113
115
|
|
|
114
|
-
只收唯一 `ALIPAY_AIPAY_INTERNAL:MCC_QUERY_RESULT=SUCCESS` 与 `ALIPAY_AIPAY_INTERNAL:MCC_RESOLUTION=<json
|
|
116
|
+
只收唯一 `ALIPAY_AIPAY_INTERNAL:MCC_QUERY_RESULT=SUCCESS` 与 `ALIPAY_AIPAY_INTERNAL:MCC_RESOLUTION=<json>`。若同时收到唯一 `ALIPAY_AIPAY_INTERNAL:MCC_SEMANTIC_RETRY_REQUIRED=<json>` 且 stdout 为空,不产生对客消息,按上面的规则在同一工作回合自动重跑;不得把该 marker 或中间状态展示给用户。`UNIQUE` 恰一项,并有唯一 `CUSTOMER_MESSAGE_DELIVERY_REQUIRED=onboarding.plan.confirm`、`MCC_CONTEXT_RECEIPT`;先把 stdout 作为可见进行中消息原文送达,随后同一回合进入 `execute`,不等待回复。`MULTIPLE` 送候选后等待;预算耗尽的 `NONE` 送既有无匹配文案后等待用户重新描述。禁止旧 `mcc-search`/`mcc-candidates`、手写候选或近似追问。
|
|
115
117
|
|
|
116
118
|
`MCC_FLOW:RETRY_WITH_NETWORK`:联网后原样重试同一 `mcc resolve-plan`。`CLI_FLOW:RETRY_WITH_LOCAL_FS_PERMISSION` 且 stdout 空:给同命令 CLI 私有状态目录权限后原样重试。失败停 MCC;禁无匹配兜底或进方案/授权/签约。
|
|
117
119
|
|
|
@@ -123,7 +125,7 @@ node "<SKILL_DIR>/references/normal/scripts/runtime.mjs" mcc resolve-plan --quer
|
|
|
123
125
|
|
|
124
126
|
`salesCode`、`scope` 和基础材料只取“产品映射”。方案只由 `mcc resolve-plan` 或已有唯一 MCC 事实时的 `onboarding-message plan-confirm --root-intent "$ROOT_INTENT" --mcc-context-receipt ... [--telemetry-run-id "$RUN_ID" --telemetry-history-count "$HISTORY_COUNT"]` 生成,展示产品/scope、MCC、特殊资质、企业账号边界及商家平台链接。已有 active run 时必须传 telemetry 参数;如果该命令首次创建 run,则尚无 marker,省略参数并由同一进程把本次 plan stage 的 history 固定为 0,命令返回后取得后续使用的 `RUN_ID`。`ROOT_INTENT` 取当前路线根意图,只能是 `onboarding_only` 或 `full_process`。特殊资质只来自 `qualificationGroups`:多个 group 是 OR 关系;同一 group 内多个 qualifications 是 AND 关系;`attaType` 为空或 null 的 qualification 只展示 `qualName`,不生成上传项。禁止补充模板外资格或审核规则。
|
|
125
127
|
|
|
126
|
-
不得保留“先运行旧字符串搜索,无匹配后再临时让 Agent
|
|
128
|
+
不得保留“先运行旧字符串搜索,无匹配后再临时让 Agent 猜”的两阶段流程,或重复询问已知信息;这里的语义重试只允许按上一段登记的两次预算执行,且不增加用户确认。语义概念与原话必须在同一次 `resolve-plan` 中提交并统一校验。唯一事实校验通过、方案 marker 唯一且 stdout 已送达后,执行 `onboarding-execute-ready --product-type "$PRODUCT_TYPE"`;缺值/冲突/非唯一继续澄清,产品/MCC 变更重渲染。`execute` loader 只继承 `clarify_intent` 已记录的正式 run 事实,不再单独记录 stage,不代表新增用户回复,禁手工上报;Agent 在收到每条聊天框用户消息时,必须先按 `runtime-entry.md` 的统一前置命令计数,不按是否与 Skill 相关筛选,`summary` 收口后不再计入当前 run,`full_process` 不重置。
|
|
127
129
|
|
|
128
130
|
<!-- ALIPAY_AIPAY_FLOW:ONBOARDING_ENTRY:END -->
|
|
129
131
|
|
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
|
|
14
14
|
`queryMcc`、签约查询和签约提交使用 `{"request":{...},"ctx":{}}`;`batchQueryMcc` 使用 `{"request":{"mccCodes":[...]}}`。完整 payload 只在脚本中维护;Agent 不从本模块自行构造 MCP 调用。
|
|
15
15
|
|
|
16
|
-
`queryMcc` 成功树按同一 run、产品短时缓存,并逐项提供二级 MCC 编码、名称、描述和布尔型 `needSpecialQual`;任一必需字段缺失、为空或类型异常均失败关闭,不从其他字段回退。非精确编码时,Agent 应先理解用户原话,并在行业俗称或场景表达与目录用词可能不同时于首次调用一并提供有原话依据的行业概念或同义表达;runner 拒绝由 Agent 提供 MCC code
|
|
16
|
+
`queryMcc` 成功树按同一 run、产品短时缓存,并逐项提供二级 MCC 编码、名称、描述和布尔型 `needSpecialQual`;任一必需字段缺失、为空或类型异常均失败关闭,不从其他字段回退。非精确编码时,Agent 应先理解用户原话,并在行业俗称或场景表达与目录用词可能不同时于首次调用一并提供有原话依据的行业概念或同义表达;runner 拒绝由 Agent 提供 MCC code,并只在当前产品动态树内召回和校验。首次未提供语义词且结果为 `NONE`,或已提供语义词但结果仍为 `NONE` 时,由脚本发出 flow 登记的重试 marker,Agent 按 flow 最多完成两次语义扩充重试;后续重试必须保留此前语义词并至少新增一个此前未使用的词,未新增或替换原语义词时不消耗额度,直接回到既有无匹配分支;耗尽后回到 flow 登记的无匹配文案,要求重新描述经营信息。候选确定后,仅把其中 `needSpecialQual=true` 的编码集中交给一次 `batchQueryMcc` 获取特殊资质项;没有此类候选时不调用。多候选的完整树事实和资格详情按 run、产品、原查询、规范化语义词和选择回执缓存,用户选择时携带同一语义词且有效命中则两种 MCC 方法都不重调。缓存默认 1800 秒、无有效 run 不落盘;过期、损坏、符号链接、跨 run、语义词变化或事实哈希不一致时忽略并从完整当前产品目录重查,不修补缓存事实,不做产品级全量预取。
|
|
17
17
|
|
|
18
18
|
## 签约状态
|
|
19
19
|
|
|
@@ -20,6 +20,7 @@ fi
|
|
|
20
20
|
source "${SCRIPT_DIR}/network_retry.sh"
|
|
21
21
|
|
|
22
22
|
ONBOARDING_RUNNER="${SCRIPT_DIR}/../../../normal/scripts/onboarding_message_runner.mjs"
|
|
23
|
+
MCC_SEMANTIC_RETRY_LIMIT=2
|
|
23
24
|
|
|
24
25
|
require_command jq || exit 1
|
|
25
26
|
require_command node || exit 1
|
|
@@ -306,6 +307,203 @@ write_mcc_selection_cache() {
|
|
|
306
307
|
}
|
|
307
308
|
}
|
|
308
309
|
|
|
310
|
+
mcc_semantic_retry_file() {
|
|
311
|
+
local product_type="$1" sales_code="$2" root scope
|
|
312
|
+
root="$(mcc_cache_root)" || return 1
|
|
313
|
+
scope="$(mcc_cache_scope_id)" || return 1
|
|
314
|
+
if [ -e "$root" ] && { [ ! -d "$root" ] || [ -L "$root" ]; }; then
|
|
315
|
+
return 1
|
|
316
|
+
fi
|
|
317
|
+
mkdir -p "$root" 2>/dev/null || return 1
|
|
318
|
+
chmod 700 "$root" 2>/dev/null || true
|
|
319
|
+
printf '%s/%s.%s.%s.semantic-retries.json' \
|
|
320
|
+
"$root" \
|
|
321
|
+
"$(mcc_cache_component "$scope")" \
|
|
322
|
+
"$(mcc_cache_component "$product_type")" \
|
|
323
|
+
"$(mcc_cache_component "$sales_code")"
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
claim_mcc_semantic_retry() {
|
|
327
|
+
local product_type="$1" sales_code="$2" query="$3" input_id="$4" semantic_terms_json="$5"
|
|
328
|
+
local file dir lock entry original_query retry_count next_retry_count now tmp lock_acquired=false
|
|
329
|
+
local current_terms previous_terms missing_previous_terms new_terms
|
|
330
|
+
file="$(mcc_semantic_retry_file "$product_type" "$sales_code")" || return 2
|
|
331
|
+
dir="${file%/*}"
|
|
332
|
+
lock="${file}.lock"
|
|
333
|
+
current_terms="$(printf '%s' "$semantic_terms_json" | jq -ce '
|
|
334
|
+
select(type == "array" and all(.[]; type == "string")) | unique | sort
|
|
335
|
+
' 2>/dev/null)" || return 2
|
|
336
|
+
|
|
337
|
+
# Claim and persist one retry under a bounded private lock. Lock contention
|
|
338
|
+
# is treated as exhausted so a broken local state cannot cause a retry loop.
|
|
339
|
+
for _ in 1 2 3 4 5 6 7 8 9 10; do
|
|
340
|
+
if mkdir "$lock" 2>/dev/null; then
|
|
341
|
+
lock_acquired=true
|
|
342
|
+
break
|
|
343
|
+
fi
|
|
344
|
+
sleep 0.05
|
|
345
|
+
done
|
|
346
|
+
[ "$lock_acquired" = true ] || return 2
|
|
347
|
+
|
|
348
|
+
if [ -e "$file" ]; then
|
|
349
|
+
if [ ! -f "$file" ] || [ -L "$file" ]; then
|
|
350
|
+
rmdir "$lock" 2>/dev/null || true
|
|
351
|
+
return 2
|
|
352
|
+
fi
|
|
353
|
+
now="$(date +%s 2>/dev/null)" || {
|
|
354
|
+
rmdir "$lock" 2>/dev/null || true
|
|
355
|
+
return 2
|
|
356
|
+
}
|
|
357
|
+
entry="$(jq -cr \
|
|
358
|
+
--arg inputId "$input_id" \
|
|
359
|
+
'
|
|
360
|
+
if .schemaVersion == 1
|
|
361
|
+
and ((.entries // null) | type == "array")
|
|
362
|
+
and all(.entries[];
|
|
363
|
+
type == "object"
|
|
364
|
+
and ((.inputId // null) | type == "string")
|
|
365
|
+
and ((.query // null) | type == "string")
|
|
366
|
+
and ((.count // null) | type == "number")
|
|
367
|
+
and (.count >= 0)
|
|
368
|
+
and ((.count | floor) == .count)
|
|
369
|
+
and ((.updatedAtEpoch // null) | type == "number")
|
|
370
|
+
and ((.semanticTerms // []) | type == "array")
|
|
371
|
+
and all((.semanticTerms // [])[]; type == "string")
|
|
372
|
+
)
|
|
373
|
+
then
|
|
374
|
+
([.entries[] |
|
|
375
|
+
select(
|
|
376
|
+
.inputId == $inputId
|
|
377
|
+
) |
|
|
378
|
+
{query, count, updatedAtEpoch, semanticTerms: (.semanticTerms // [])}
|
|
379
|
+
] | sort_by(.updatedAtEpoch) | last) // null
|
|
380
|
+
else
|
|
381
|
+
error("invalid MCC semantic retry state")
|
|
382
|
+
end
|
|
383
|
+
' "$file" 2>/dev/null)" || {
|
|
384
|
+
rmdir "$lock" 2>/dev/null || true
|
|
385
|
+
return 2
|
|
386
|
+
}
|
|
387
|
+
else
|
|
388
|
+
entry='null'
|
|
389
|
+
fi
|
|
390
|
+
|
|
391
|
+
if [ "$entry" = "null" ]; then
|
|
392
|
+
original_query="$query"
|
|
393
|
+
retry_count=0
|
|
394
|
+
previous_terms='[]'
|
|
395
|
+
else
|
|
396
|
+
original_query="$(printf '%s' "$entry" | jq -r '.query // empty' 2>/dev/null)" || original_query=""
|
|
397
|
+
retry_count="$(printf '%s' "$entry" | jq -r '.count // -1' 2>/dev/null)" || retry_count=-1
|
|
398
|
+
previous_terms="$(printf '%s' "$entry" | jq -c '.semanticTerms // []' 2>/dev/null)" || previous_terms=""
|
|
399
|
+
if [ "$original_query" != "$query" ]; then
|
|
400
|
+
rmdir "$lock" 2>/dev/null || true
|
|
401
|
+
return 2
|
|
402
|
+
fi
|
|
403
|
+
[ -n "$previous_terms" ] || {
|
|
404
|
+
rmdir "$lock" 2>/dev/null || true
|
|
405
|
+
return 2
|
|
406
|
+
}
|
|
407
|
+
missing_previous_terms="$(jq -nr \
|
|
408
|
+
--argjson previous "$previous_terms" \
|
|
409
|
+
--argjson current "$current_terms" \
|
|
410
|
+
'($previous - $current) | length' 2>/dev/null)" || {
|
|
411
|
+
rmdir "$lock" 2>/dev/null || true
|
|
412
|
+
return 2
|
|
413
|
+
}
|
|
414
|
+
new_terms="$(jq -nr \
|
|
415
|
+
--argjson previous "$previous_terms" \
|
|
416
|
+
--argjson current "$current_terms" \
|
|
417
|
+
'($current - $previous) | length' 2>/dev/null)" || {
|
|
418
|
+
rmdir "$lock" 2>/dev/null || true
|
|
419
|
+
return 2
|
|
420
|
+
}
|
|
421
|
+
if [ "$missing_previous_terms" -ne 0 ] || [ "$new_terms" -eq 0 ]; then
|
|
422
|
+
# A semantic retry must be cumulative and add at least one new term.
|
|
423
|
+
# Do not consume a retry budget for an unchanged or replaced term set.
|
|
424
|
+
rmdir "$lock" 2>/dev/null || true
|
|
425
|
+
return 3
|
|
426
|
+
fi
|
|
427
|
+
fi
|
|
428
|
+
case "$retry_count" in
|
|
429
|
+
''|*[!0-9]*) retry_count="$MCC_SEMANTIC_RETRY_LIMIT" ;;
|
|
430
|
+
esac
|
|
431
|
+
if [ "$retry_count" -ge "$MCC_SEMANTIC_RETRY_LIMIT" ]; then
|
|
432
|
+
rmdir "$lock" 2>/dev/null || true
|
|
433
|
+
return 2
|
|
434
|
+
fi
|
|
435
|
+
next_retry_count=$((retry_count + 1))
|
|
436
|
+
now="$(date +%s 2>/dev/null)" || {
|
|
437
|
+
rmdir "$lock" 2>/dev/null || true
|
|
438
|
+
return 2
|
|
439
|
+
}
|
|
440
|
+
tmp="$(mktemp "$dir/.mcc-semantic-retries.XXXXXX" 2>/dev/null)" || {
|
|
441
|
+
rmdir "$lock" 2>/dev/null || true
|
|
442
|
+
return 2
|
|
443
|
+
}
|
|
444
|
+
chmod 600 "$tmp" 2>/dev/null || true
|
|
445
|
+
if [ -f "$file" ] && [ ! -L "$file" ]; then
|
|
446
|
+
if ! jq -ce \
|
|
447
|
+
--arg query "$original_query" \
|
|
448
|
+
--arg inputId "$input_id" \
|
|
449
|
+
--argjson count "$next_retry_count" \
|
|
450
|
+
--argjson now "$now" \
|
|
451
|
+
--argjson semanticTerms "$current_terms" '
|
|
452
|
+
if .schemaVersion != 1 or ((.entries // null) | type != "array") then
|
|
453
|
+
error("invalid MCC semantic retry state")
|
|
454
|
+
else
|
|
455
|
+
.entries = (
|
|
456
|
+
[.entries[] | select(.inputId != $inputId)]
|
|
457
|
+
+ [{
|
|
458
|
+
query: $query,
|
|
459
|
+
inputId: $inputId,
|
|
460
|
+
count: $count,
|
|
461
|
+
updatedAtEpoch: $now,
|
|
462
|
+
semanticTerms: $semanticTerms
|
|
463
|
+
}]
|
|
464
|
+
)
|
|
465
|
+
| .entries |= sort_by(.updatedAtEpoch)
|
|
466
|
+
end
|
|
467
|
+
' "$file" >"$tmp" 2>/dev/null; then
|
|
468
|
+
rm -f "$tmp" 2>/dev/null || true
|
|
469
|
+
rmdir "$lock" 2>/dev/null || true
|
|
470
|
+
return 2
|
|
471
|
+
fi
|
|
472
|
+
elif [ -e "$file" ]; then
|
|
473
|
+
rm -f "$tmp" 2>/dev/null || true
|
|
474
|
+
rmdir "$lock" 2>/dev/null || true
|
|
475
|
+
return 2
|
|
476
|
+
else
|
|
477
|
+
if ! jq -cn \
|
|
478
|
+
--arg query "$original_query" \
|
|
479
|
+
--arg inputId "$input_id" \
|
|
480
|
+
--argjson count "$next_retry_count" \
|
|
481
|
+
--argjson now "$now" \
|
|
482
|
+
--argjson semanticTerms "$current_terms" \
|
|
483
|
+
'{
|
|
484
|
+
schemaVersion: 1,
|
|
485
|
+
entries: [{
|
|
486
|
+
query: $query,
|
|
487
|
+
inputId: $inputId,
|
|
488
|
+
count: $count,
|
|
489
|
+
updatedAtEpoch: $now,
|
|
490
|
+
semanticTerms: $semanticTerms
|
|
491
|
+
}]
|
|
492
|
+
}' >"$tmp" 2>/dev/null; then
|
|
493
|
+
rm -f "$tmp" 2>/dev/null || true
|
|
494
|
+
rmdir "$lock" 2>/dev/null || true
|
|
495
|
+
return 2
|
|
496
|
+
fi
|
|
497
|
+
fi
|
|
498
|
+
if ! mv "$tmp" "$file" 2>/dev/null; then
|
|
499
|
+
rm -f "$tmp" 2>/dev/null || true
|
|
500
|
+
rmdir "$lock" 2>/dev/null || true
|
|
501
|
+
return 2
|
|
502
|
+
fi
|
|
503
|
+
rmdir "$lock" 2>/dev/null || true
|
|
504
|
+
printf '%s' "$next_retry_count"
|
|
505
|
+
}
|
|
506
|
+
|
|
309
507
|
get_mcc_tree() {
|
|
310
508
|
local product_type="$1" sales_code="$2" cached tree cached_rows
|
|
311
509
|
cached="$(read_mcc_tree_cache "$product_type" "$sales_code" 2>/dev/null || true)"
|
|
@@ -550,7 +748,7 @@ validate_mcc_selection_cache() {
|
|
|
550
748
|
}
|
|
551
749
|
|
|
552
750
|
run_resolve_plan() {
|
|
553
|
-
local query="" product_type="" root_intent="" semantic_terms_json='[]' select_value="" selection_receipt="" input_id="" sales_code tree rows tree_catalog resolution match_type full_codes candidate_rows qualification_rows details catalog selection_cache="" used_selection_cache=false stale_selection=false resolved_semantic_terms='[]'
|
|
751
|
+
local query="" product_type="" root_intent="" semantic_terms_json='[]' select_value="" selection_receipt="" input_id="" sales_code tree rows tree_catalog resolution match_type semantic_status full_codes candidate_rows qualification_rows details catalog selection_cache="" used_selection_cache=false stale_selection=false resolved_semantic_terms='[]' retry_input_id="" retry_rc=0 next_retry_count=0 retry_reason="" retry_payload=""
|
|
554
752
|
shift
|
|
555
753
|
while [[ $# -gt 0 ]]; do
|
|
556
754
|
case "$1" in
|
|
@@ -630,7 +828,34 @@ run_resolve_plan() {
|
|
|
630
828
|
}
|
|
631
829
|
capture_or_exit resolution run_mcc_match_codes "$query" "$product_type" "$root_intent" "$tree_catalog" "$semantic_terms_json"
|
|
632
830
|
match_type=$(printf '%s' "$resolution" | jq -r '.matchType // ""' 2>/dev/null) || mcc_query_failed
|
|
831
|
+
semantic_status=$(printf '%s' "$resolution" | jq -r '.semanticStatus // ""' 2>/dev/null) || mcc_query_failed
|
|
633
832
|
resolved_semantic_terms=$(printf '%s' "$resolution" | jq -c '.semanticTerms // []' 2>/dev/null) || mcc_query_failed
|
|
833
|
+
if [ "$match_type" = "NONE" ] \
|
|
834
|
+
&& [ "$semantic_status" != "DYNAMIC_CATALOG_CODE_NOT_FOUND" ] \
|
|
835
|
+
&& [ "$semantic_status" != "DYNAMIC_CATALOG_CODE_AMBIGUOUS" ]; then
|
|
836
|
+
retry_input_id="${input_id:-__NO_INPUT_ID__}"
|
|
837
|
+
next_retry_count="$(claim_mcc_semantic_retry \
|
|
838
|
+
"$product_type" "$sales_code" "$query" "$retry_input_id" "$resolved_semantic_terms" 2>/dev/null)"
|
|
839
|
+
retry_rc=$?
|
|
840
|
+
if [ "$retry_rc" -eq 0 ]; then
|
|
841
|
+
if [ "$semantic_terms_json" = "[]" ]; then
|
|
842
|
+
retry_reason="MISSING_SEMANTIC_TERMS"
|
|
843
|
+
else
|
|
844
|
+
retry_reason="NO_MATCH"
|
|
845
|
+
fi
|
|
846
|
+
retry_payload=$(jq -cn \
|
|
847
|
+
--argjson attempt "$next_retry_count" \
|
|
848
|
+
--argjson maxAttempts "$MCC_SEMANTIC_RETRY_LIMIT" \
|
|
849
|
+
--argjson remainingAttempts "$((MCC_SEMANTIC_RETRY_LIMIT - next_retry_count))" \
|
|
850
|
+
--arg reason "$retry_reason" \
|
|
851
|
+
'{attempt: $attempt, maxAttempts: $maxAttempts, remainingAttempts: $remainingAttempts, reason: $reason}') \
|
|
852
|
+
|| mcc_query_failed
|
|
853
|
+
emit_control "MCC_QUERY_RESULT=SUCCESS"
|
|
854
|
+
emit_control "MCC_RESOLUTION=$resolution"
|
|
855
|
+
emit_control "MCC_SEMANTIC_RETRY_REQUIRED=$retry_payload"
|
|
856
|
+
return 0
|
|
857
|
+
fi
|
|
858
|
+
fi
|
|
634
859
|
if [ "$match_type" = "NONE" ]; then
|
|
635
860
|
catalog="$tree_catalog"
|
|
636
861
|
else
|
|
@@ -60,7 +60,7 @@ node "<SKILL_DIR>/references/normal/scripts/runtime.mjs" render <messageId> --va
|
|
|
60
60
|
|
|
61
61
|
每个登记 stage 动作前,Agent 回顾当前可见历史并得到 `HISTORY_COUNT`;只统计本 run `clarify_intent` 后的用户角色消息,无法可靠回顾时使用 `null`。调用能生成 stage 的业务命令时追加 `--telemetry-run-id "$RUN_ID" --telemetry-history-count "$HISTORY_COUNT"`,由统一 gateway 在业务 parser 前剥离并传给唯一 stage 责任方,Agent 不再额外调用 `telemetry stage`。首次在产品/MCC 确认命令内部新建 run 时尚无 marker,该进程使用刚创建的 active run 且本次 history 固定为 0。`c4` 只承载 `user_input_after_clarify_intent`、`history_count`、`root_intent` 和按唯一非 summary event 数计算的 `progress_pct`;正式计数始终取 runtime counter,history 只诊断、不纠偏。
|
|
62
62
|
|
|
63
|
-
三种入口均只在无法确定产品时执行 `product-clarify` runner;产品明确但缺 MCC 用 `mcc-clarify`,且必须携带当前 `ROOT_INTENT`,由 runner 先创建或复用正式 telemetry run。已有经营描述、名称或编码时只执行 `node "<SKILL_DIR>/references/normal/scripts/runtime.mjs" mcc resolve-plan --query "$MCC_SEARCH_QUERY" --product-type "$PRODUCT_TYPE" --root-intent "$ROOT_INTENT" [--semantic-terms-json "$MCC_SEMANTIC_TERMS_JSON"] --input-id "$USER_INPUT_ID"`。`ROOT_INTENT` 缺失或非法时 runner 必须停止,禁止依赖跨进程环境变量静默推断或在没有正式 run 的情况下继续 MCC 查询。非精确编码时,Agent 在首次调用中最多补充 12
|
|
63
|
+
三种入口均只在无法确定产品时执行 `product-clarify` runner;产品明确但缺 MCC 用 `mcc-clarify`,且必须携带当前 `ROOT_INTENT`,由 runner 先创建或复用正式 telemetry run。已有经营描述、名称或编码时只执行 `node "<SKILL_DIR>/references/normal/scripts/runtime.mjs" mcc resolve-plan --query "$MCC_SEARCH_QUERY" --product-type "$PRODUCT_TYPE" --root-intent "$ROOT_INTENT" [--semantic-terms-json "$MCC_SEMANTIC_TERMS_JSON"] --input-id "$USER_INPUT_ID"`。`ROOT_INTENT` 缺失或非法时 runner 必须停止,禁止依赖跨进程环境变量静默推断或在没有正式 run 的情况下继续 MCC 查询。非精确编码时,Agent 在首次调用中最多补充 12 个有原话依据的行业概念、经营对象或同义表达;首次未提供语义词且结果为 `NONE`,或已提供语义词但结果仍为 `NONE` 时,同一 `run + 用户输入 + 原始 query + 产品` 最多再扩充并重试 2 次,复用同一 `USER_INPUT_ID`,每次只增加有原话依据且此前未使用的概念;后续重试提交的语义词必须保留此前语义词并至少新增一个此前未使用的词,未新增或替换原语义词时不消耗额度;达到上限仍为 `NONE` 时必须按既有 no-match 对客消息要求用户重新描述经营信息。只传概念、不传 MCC 编码且不新增确认;禁止通过更换 query、重置参数或重复调用绕过上限,也禁止硬编码映射、全表判断、远程语义或手拼候选。runner 按 Onboarding entry 严格校验当前产品 `queryMcc` 目录、联合排序原话与语义证据,只为命中且 `needSpecialQual=true` 的候选调用一次 `batchQueryMcc`,并复用有效的同 run/产品候选快照。
|
|
64
64
|
|
|
65
65
|
代码开发或完整接入缺项目位置/服务端语言时,使用 `integration.context.required` 或 `integration.project_path.required` 标准消息;框架缺失不触发补问。先区分新建/复用,`PROJECT_INPUT` 保留原话。Agent 确定两类项目的真实 `PROJECT_PATH`,以 `resolve-project --project-path "$PROJECT_PATH" --intent <existing|new>` 交 runner 校验;`PROJECT_INPUT` 不得代替路径。默认新建也先选安全路径并显式传入。现有项目位置未知时先定位候选;路径经 `resolve-project` 后再用 `framework-resolve`。补齐前不初始化、装依赖或改文件。
|
|
66
66
|
|