weaver-work-cli 0.1.28 → 0.1.29

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.
Files changed (40) hide show
  1. package/dist/shortcuts/invoice/host.js +2 -1
  2. package/dist/shortcuts/invoice/manifest.js +2 -1
  3. package/dist/shortcuts/invoice/operations/issuing-qys.js +132 -36
  4. package/dist/shortcuts/workflow/index.js +3 -0
  5. package/dist/shortcuts/workflow/operations/read.js +13 -6
  6. package/dist/shortcuts/workflow/operations/registry.js +2 -0
  7. package/dist/shortcuts/workflow/operations/todoSmartConvert/businessRules.js +331 -0
  8. package/dist/shortcuts/workflow/operations/todoSmartConvert/detailFieldRules.js +133 -0
  9. package/dist/shortcuts/workflow/operations/todoSmartConvert/summaryCache.js +326 -0
  10. package/dist/shortcuts/workflow/operations/todoSmartConvert/todoDetailCache.js +113 -0
  11. package/dist/shortcuts/workflow/operations/todoSmartConvert/todoDetailFetcher.js +259 -0
  12. package/dist/shortcuts/workflow/operations/todoSmartConvert/todoListRows.js +64 -0
  13. package/dist/shortcuts/workflow/operations/todoSmartConvert/todoOutputPlanner.js +28 -0
  14. package/dist/shortcuts/workflow/operations/todoSmartConvert/todoSmartConvert.aggregate.js +70 -0
  15. package/dist/shortcuts/workflow/operations/todoSmartConvert/todoSmartConvert.collect.js +267 -0
  16. package/dist/shortcuts/workflow/operations/todoSmartConvert/todoSmartConvert.core.js +392 -0
  17. package/dist/shortcuts/workflow/operations/todoSmartConvert/todoSmartConvert.detail.js +165 -0
  18. package/dist/shortcuts/workflow/operations/todoSmartConvert/todoSmartConvert.group.js +448 -0
  19. package/dist/shortcuts/workflow/operations/todoSmartConvert/todoSmartConvert.groupSummary.js +348 -0
  20. package/dist/shortcuts/workflow/operations/todoSmartConvert/todoSmartConvert.js +222 -0
  21. package/dist/shortcuts/workflow/operations/todoSmartConvert/todoSmartConvert.pipeline.js +66 -0
  22. package/dist/shortcuts/workflow/operations/todoSmartConvert/todoSmartConvert.smartGroup.js +502 -0
  23. package/dist/shortcuts/workflow/operations/todoSmartConvert/todoSmartConvert.summary.js +540 -0
  24. package/docs/_catalog.md +1 -0
  25. package/docs/invoice.md +3 -3
  26. package/docs/todo-smart-convert/README.md +171 -0
  27. package/docs/todo-smart-convert/data-contract.md +236 -0
  28. package/docs/todo-smart-convert/mock/ai-todo-query-target.json +209 -0
  29. package/docs/todo-smart-convert/subgroup-rules.md +245 -0
  30. package/package.json +1 -1
  31. package/scripts/package-skill.mjs +1 -1
  32. package/skill-template/domains/yepiaotong.md +2 -2
  33. package/skills/weaver-e10-workflow/SKILL.md +23 -1
  34. package/skills/weaver-e10-workflow/references/todo-smart-convert.md +282 -0
  35. package/skills/weaver-e10-workflow/scripts/todo-summary.cjs +695 -0
  36. package/skills/weaver-e10-yepiaotong/SKILL.md +6 -6
  37. package/skills/weaver-e10-yepiaotong/references/invoice-issuing-examples.md +10 -7
  38. package/skills/weaver-e10-yepiaotong/references/invoice-issuing-form-fill-rules.md +41 -14
  39. package/skills/weaver-e10-yepiaotong/references/invoice-issuing-workflow.md +54 -26
  40. package/skills/weaver-e10-yepiaotong/references/source-manifest.json +2 -2
@@ -0,0 +1,245 @@
1
+ # 二次分组(subgroup)规则
2
+
3
+ > 范围:`smartGroup` 阶段里把「工作流分组」再切成「汇总项 + 单条」的全部规则
4
+ > 源码:`todoSmartConvert.smartGroup.ts`、`businessRules.ts`、`todoSmartConvert.group.ts`
5
+ > 核对日期:2026-10-27(ai二次分组改造后定稿:删除 `other` 汇总项、<3 条硬门槛不再被计划覆盖、意图表驱动、详情缓存)
6
+
7
+ ## 0. 一句话
8
+
9
+ 大组是否做 ai二次分组、怎么分,由 **「业务类型」+「用户意图是否适用本组」+「有没有给本组计划」+「组内条数门槛」** 四个因素决定;CLI 负责判定与兜底,AI 只提供「子组怎么切、叫什么名」。
10
+
11
+ **没有计划就不产汇总项**——只有三类确定性汇总项(请假阈值组 / 财务阈值组 / 客户问题支持过期组)例外,其余一律平铺。
12
+
13
+ ## 1. 门槛与硬约束
14
+
15
+ | 规则 | 值 / 行为 |
16
+ |---|---|
17
+ | 最小条数 | `GROUP_MIN_ITEMS_FOR_SUBGROUP = 3`(`group.ts`) |
18
+ | 不足 3 条 | **一律平铺单条**,不产生任何汇总项;**即使给了 `aiGroups` 也忽略**并写 warning(硬门槛不再被计划覆盖) |
19
+ | 优先组 | 不做 ai二次分组;`aiGroups` 里出现 `groupId:"priority"` ⇒ 硬失败 `ai_group_priority_unsupported` |
20
+ | 计划作用范围 | 只作用于本组;本组是否采纳还受「业务类型 + 意图是否适用」限制(见 §2) |
21
+ | 全局开关已取消 | `aiGroups: []` 等价于「没传」,不再有单独的「兼容校验路径」 |
22
+ | 叠加视图影响 | 优先条目仍算进它所属工作流分组的条数,所以「原本 2 条 + 1 条超时」的组现在算 3 条,**会跨过门槛** |
23
+ | 空 `groupFocus` | 只影响「计划是否被采纳」;不影响 `<3` 门槛与确定性规则 |
24
+
25
+ ## 2. 决策顺序(每个非优先组,严格按代码顺序)
26
+
27
+ ```
28
+ kind = detectBusinessKind(group.title) // 组名判定,见 §5
29
+ groupHasFocus = hasFocus && focusAppliesToKind(groupFocus, kind) // 意图表,见 §4
30
+ plansAllowed = groupHasFocus || kind === 'internal-message' // 计划是否可能被采纳
31
+
32
+ D0 优先组 ─────────────────────────────────▶ 全部单条;defaultExpanded=true;结束
33
+ D1 满意度类 ───────────────────────────────▶ 全部单条;summary 用标题兜底(不调 AI);忽略计划 + warning
34
+ D2 组内 <3 条 ─────────────────────────────▶ 全部平铺;忽略计划 + warning(硬门槛)
35
+ D3 计划不被采纳(kind 非内部留言 且 意图不适用)
36
+ ───────────────────────────────────▶ 全部平铺 + warning(区分「意图不适用」与「本类型默认不分组」两种文案)
37
+ D4 计划被采纳但本组一条计划都没有
38
+ 内部留言 ─────────────────────────▶ **硬失败 `ai_groups_required`**(没有平铺这个第三态,必须补计划)
39
+ 其他类型 ─────────────────────────▶ warning「意图适用但模型未给本组计划,按无意图处理」→ 落到 D5
40
+ D5 无生效计划 ⇒ 只有三类确定性汇总项会产汇总项:
41
+ leave / finance ─────────────────────▶ 阈值组(≤1天 / ≤100元;**有命中才建**)+ 其余平铺
42
+ issue-support ───────────────────────▶ 「已过期及今天即将过期的流程」(**有命中才建**)+ 其余平铺
43
+ other ───────────────────────────────▶ 全部平铺(写一条 warning 说明原因)
44
+ (internal-message 到不了这里:D4 已硬失败)
45
+ D6 有生效计划 ⇒ 按计划切(见 §6):
46
+ a) 保留标题(1天以内/100元以内)逐条核阈值,超阈值剔出转平铺 + 点名
47
+ b) 子组 <2 条 ⇒ 解散,条目转平铺
48
+ c) 漏分条目 ⇒ 平铺(**不造 other**)
49
+ D7 排序:无 focus → 条数降序(稳定排序);有 focus → 沿用 AI 给的顺序
50
+ D8 编号:id = `${groupId}-${排序后下标+1}`
51
+ ```
52
+
53
+ > **结构恒定**:`displayItems` 只有两种形态——真子组(`type:"group"`)或平铺条目(`type:"request"`)。
54
+ > 不再出现「与父组同名」的整组汇总项,也不再出现 `other` 兜底汇总项。
55
+
56
+ ## 3. 行为矩阵(最容易 review 错的地方)
57
+
58
+ | 本组情况 | 无计划 · 无 focus | 无计划 · 意图适用 | 有本组计划 · 采纳 | 有本组计划 · 不采纳 |
59
+ |---|---|---|---|---|
60
+ | 优先组 | 全单条(置顶展开) | 同左 | 硬失败 | 硬失败 |
61
+ | 满意度类 | 全单条 + 标题摘要 | 全单条 + 标题摘要 | 全单条(忽略计划 + warning) | 同左 |
62
+ | **任意类型 <3 条** | 平铺 | 平铺 | **平铺(硬门槛,忽略计划)** | 平铺 |
63
+ | 内部留言 ≥3 条 | **必须给计划**:按流程标题分(缺计划 → `ai_groups_required`) | **必须给计划**:按侧重点+详情分 | **按计划细分** | —(总是采纳) |
64
+ | 请假/财务 ≥3 条 | 阈值组 + 其余平铺 | 按计划细分 | 按计划细分(保留标题仍在核阈值) | 阈值组 + 其余平铺 |
65
+ | 客户问题支持 ≥3 条 | 过期组 + 其余平铺 | 按计划细分 | 按计划细分 | 过期组 + 其余平铺 |
66
+ | 其他类 ≥3 条 | 平铺 + warning | 按计划细分 | 按计划细分 | **平铺**(本类型无 focus 时忽略计划) |
67
+
68
+ > **只有内部留言(无 focus)和「意图适用本组的任意类型」会消费 `aiGroups`**;其他类在无 focus 时给了计划也会被忽略并写 warning。
69
+
70
+ ## 4. 意图表 `INTENT_RULES`(`businessRules.ts`)
71
+
72
+ 用户意图不是「随便一句中文」,而是**按维度分类**的:专属意图只作用于对应业务类型,通用意图只作用于内部留言与其他类。
73
+
74
+ | id | 标签 | 命中词(正则片段) | 作用类型 `kinds` |
75
+ |---|---|---|---|
76
+ | `leave` | 请假维度 | 请假/休假/年假/调休/病假/事假/婚假/产假/陪产/丧假/探亲/工伤/哺乳/假别/假期/假种/天数 | `leave` |
77
+ | `finance` | 财务维度 | 报销/费用/金额/发票/差旅/借款/付款/请款/科目/票据/对公/核销 | `finance` |
78
+ | `issue-support` | 问题支持维度 | 问题/模块/处理阶段/客户问题/工单/支持 | `issue-support` |
79
+ | `generic` | 通用维度 | 客户/发起人/创建人/提交人/时间/日期/紧急/等级/优先级/主题/标题/类型/类别/状态/节点/内容 | `internal-message`、`other` |
80
+
81
+ 判定规则(`intentAppliesToKind`,顺序不可换):
82
+
83
+ 1. `groupFocus` 为空,或本组是满意度类 ⇒ **不适用**(= 无意图);
84
+ 2. 先看**专属意图**(`leave`/`finance`/`issue-support`):任一命中 ⇒ **只按专属的 `kinds` 判**,通用词同时出现也忽略;
85
+ 3. 专属都没命中才看通用意图;
86
+ 4. 都不命中 ⇒ **视为没有意图**:不报错、不喂 AI,按该类型的确定性规则处理。
87
+
88
+ > 推论:`"按费用类型"` 对财务类适用、对请假类**不**适用(专属「财务维度」不包含 `leave`);
89
+ > `"按客户"` 对内部留言/其他类适用、对请假/财务/问题支持**不**适用。
90
+ > 给错类型的意图不会硬失败,只会退回确定性规则并写 warning。
91
+
92
+ ## 5. 业务类型判定
93
+
94
+ 按**组名(= workflowName)**判定,顺序不可换(客户问题支持精确名优先、满意度次之,避免「费用报销满意度调查」被归为财务):
95
+
96
+ | 类型 | 判据 | 无计划时的分组行为 | 组摘要 / 单条摘要 |
97
+ |---|---|---|---|
98
+ | `issue-support` | **精确名**:`Ecology客户问题支持解决流程`、`E10客户问题支持解决流程` | 「已过期及今天即将过期的流程」一组,其余平铺;无命中则全平铺 | 过期组 = CLI 固定模板 |
99
+ | `satisfaction` | `满意度\|满意调查\|评价调查\|服务评价\|问卷调查` | 全部单条 | 单条 `summary` **= 条目标题**(不调 AI) |
100
+ | `internal-message` | `INTERNAL_MESSAGE_WORKFLOW_PATTERN`(内部留言/内部消息/留言板/意见建议…) | **平铺单条**(有计划才细分) | 计划子组留空交 AI |
101
+ | `leave` | 请假/休假/年假/调休/病假/事假/婚假/产假/陪产/丧假/探亲假/工伤假/哺乳假/公休 | ≤1 天合并,其余平铺 | 阈值组 = CLI 固定模板 |
102
+ | `finance` | 报销/费用/付款/请款/借款/发票/差旅/对公/资金/核销 | 单笔 ≤100 元合并,其余平铺 | 阈值组 = CLI 固定模板(金额区间/合计/费用类数) |
103
+ | `other` | 兜底 | **平铺单条** | 无汇总项 |
104
+
105
+ 阈值:`LEAVE_DAY_THRESHOLD = 1`、`FINANCE_AMOUNT_THRESHOLD = 100`;标题固定 `1天以内` / `100元以内`。
106
+
107
+ **指标(天数/金额)只来自流程详情数据**(`RowMetrics`),取不到时该条**平铺并点名**,不猜、不硬凑进阈值组。
108
+ 财务组一条金额都取不到时,组摘要退回「标题清单」模板,而不是编一个 `¥0 ~ ¥0`。
109
+
110
+ ## 6. `aiGroups` 的字段校验与切分细节
111
+
112
+ ### 6.1 入参校验(`readAiGroups`)
113
+
114
+ | 检查 | 失败 |
115
+ |---|---|
116
+ | 必须是数组 | `ai_groups_invalid` |
117
+ | 每项只允许 `groupId` / `title` / `requestIds` / `seq` | `ai_groups_invalid`(列出多余字段) |
118
+ | `groupId` / `title` 必须是非空字符串(19 位 ID 用数字会失真) | `ai_groups_invalid` |
119
+ | `groupId` 必须存在于本次分组结果 | `ai_group_unknown_group`(回传可用列表) |
120
+ | `groupId` 不能是 `priority` | `ai_group_priority_unsupported` |
121
+ | `requestIds` 与 `seq` 二选一,不能同时给、也不能都空 | `ai_groups_invalid` |
122
+ | `seq` 只接受 ≥1 的整数 | `ai_groups_invalid` |
123
+ | `seq` 越界 | `ai_group_seq_out_of_range` |
124
+ | `requestId` 不属于该组 | `ai_group_unknown_request` |
125
+ | 同一条被分进两个子组 | `ai_group_duplicate_request` |
126
+
127
+ `seq` 口径:**组内 1-based 下标**,顺序 = `group.requestDatas` 顺序(与 `collect` 的 `groupingBrief[].items[].idx` 同源)。叠加视图后优先条目也在该数组里,序号顺延。
128
+
129
+ ### 6.2 切分后的三道兜底
130
+
131
+ | 兜底 | 行为 |
132
+ |---|---|
133
+ | **保留标题核阈值** | 请假/财务组里,模型拿 `1天以内` / `100元以内` 当子组名时,逐条核 `metrics`;超阈值的**剔出→平铺**并点名(`不满足阈值` warning)。其它标题不核 |
134
+ | **子组 <2 解散** | 子组只剩 1 条不成立,解散后条目平铺;被解散的在同一条 warning 里报数量 |
135
+ | **漏分平铺** | 没被 `used` 标记的条目全部平铺,**不建 `other` 汇总项**,写 warning 报数量 |
136
+
137
+ > 三道兜底都不丢数据:任何条目要么在子组里,要么出现在 `displayItems` 的单条里。
138
+
139
+ ## 7. 排序与编号
140
+
141
+ - **无 focus**:子组按条数降序;`sort` 稳定,同条数保持 AI 给的先后。
142
+ - **有 focus**:完全沿用 AI 数组顺序。
143
+ - **编号**:`id = 父组id-序号`,序号 = **排序后**的下标(从 1 起)。因此条数变化导致排序变动时,`id` 会跟着变(已知问题,见 §11)。
144
+
145
+ ## 8. 组摘要 / 单条摘要由谁写
146
+
147
+ | 路径 | `summary` |
148
+ |---|---|
149
+ | 阈值组(`1天以内` / `100元以内`) | 组摘要 = CLI 固定模板,**即使本轮传了 `aiGroups` 也照写** |
150
+ | 客户问题支持过期组 | 组摘要 = CLI 固定模板 |
151
+ | AI 计划产生的子组(含请假/财务里自定义标题的子组) | 组摘要先留空串,由 `groupSummaryContext` → AI → `groupSummary` 回填 |
152
+ | 其他类 / 内部留言(无计划) | 无汇总项,不存在组摘要 |
153
+ | 满意度类单条 | **单条 summary = 条目标题**(确定性,不调 AI,保证非空) |
154
+ | 其余单条 | 由 `summary` 阶段 AI 写;详情为空时标题兜底 |
155
+
156
+ ### 8.1 三套固定模板原文(`businessRules.ts`)
157
+
158
+ 公共尾巴:`可展开逐条查看或直接汇总处理`(CLI 固定文案与喂模型的 `stats.tail` 共用同一个常量)
159
+
160
+ | 类型 | 模板 |
161
+ |---|---|
162
+ | 请假 | `共{N}条,涵盖{M}类假别(事假、病假、年假等),由A、B、C等发起。{尾巴}` |
163
+ | 财务 | `单笔 ¥{min} ~ ¥{max},合计 {红字}¥{sum}{/红字},涵盖{M}类费用(差旅、餐饮等),由A、B、C等发起。{尾巴}` |
164
+ | 问题支持 | `含 {N} 条已过期或今日到期的问题流程,由 A、B 等 N 人发起,其余 {M} 条按单条展示,{尾巴}` |
165
+
166
+ 三条口径(2026-10-27 用户定):
167
+
168
+ 1. **类别(假别/费用类别)去重后最多列 3 个,没列完才补「等」**——列全了不加「等」。
169
+ 一个类别都取不到时只说「1类假别」/「1类费用」,不编名字。
170
+ 2. **发起人超过 2 人才补「等」**(1~2 人把名字列全),最多列 3 个。
171
+ 3. 财务合计用**纯文本** `¥{金额}`,**不加任何 HTML 标签**(2026-10-27 用户要求;此前用 `<span style="color:#d9363e;font-weight:700">` 做红字,但载荷内联进聊天消息时会被当真实标签解析、吞掉后续内容)。
172
+
173
+ > 费用类别只来自**流程详情字段**(`费用类型`/`费用类别`/`报销类型`…)或**标题关键词**兜底。
174
+ > 租户表单若没有这类字段、标题也没有关键词,就只会输出「1类费用」——不猜。
175
+
176
+ ### 8.2 组摘要不缓存(规则)
177
+
178
+ 组摘要**每轮都重新生成**:组内条目变动频繁,按组缓存必然出现「文案说 3 条、卡片是 5 条」。
179
+ 缓存的只有单条摘要(`requestId` 粒度)。
180
+
181
+ ## 9. 错误码
182
+
183
+ `ai_groups_invalid` / `ai_group_unknown_group` / `ai_group_priority_unsupported` / `ai_group_seq_out_of_range` / `ai_group_unknown_request` / `ai_group_duplicate_request` / **`ai_groups_required`**
184
+
185
+ > `ai_groups_required`(2026-10-27):**内部留言 ≥3 条却没有本组计划**时硬失败(`retryAllowed=true`),报错里给出 `groupId` 与「1..N」可用序号,并说明默认维度是**流程标题**。
186
+ > `ai_groups_missing_group` **已删除**:**非内部留言**的类型「意图适用却没给计划」= 视为无意图 + warning(内部留言不走这条路)。
187
+
188
+ ## 10. 真实数据核对(近一年 77 条 / 17 组,2026-10-27)
189
+
190
+ | 组 | 条数 | 结果 |
191
+ |---|---|---|
192
+ | 公司重要通知 | 32 | **全部平铺**(其他类无 focus ⇒ 忽略计划/无计划都不分) |
193
+ | 安全问题处理工单 | 11 | **全部平铺** |
194
+ | 契约锁满意度调查反馈 / 满意度调查反馈 | 5 / 5 | 全平铺,`summary` = 标题 |
195
+ | 泛微请假流程 | 3 | `1天以内`(2) + 平铺 1 |
196
+ | 总部费用报销 | 4 | `100元以内`(2) + 平铺 2 |
197
+ | 内部留言 | 2 | <3 硬门槛 ⇒ 平铺 |
198
+ | 其余 11 组 | 各 1~5 | 平铺 |
199
+
200
+ 汇总项只剩 `1天以内(2)`、`100元以内(2)`;空摘要 0;`totalCount = 77`,Σ`count = 98`(差值 21 = 优先组的叠加条数)。
201
+
202
+ ## 11. review 关注点
203
+
204
+ ### 11.0 ✅ 2026-10-27(ai二次分组改造收口)
205
+
206
+ | 项 | 旧行为 | 新行为 |
207
+ |---|---|---|
208
+ | AI 漏分 | 并入/新建 `other` 汇总项(标题就是英文 `other`) | **全部平铺**,不造 `other` |
209
+ | 组内 <3 条但有计划 | 计划覆盖门槛,照分 | **硬门槛优先**,忽略计划 + warning |
210
+ | 意图适用却没给本组计划 | 硬失败 `ai_groups_missing_group` | **视为无意图** + warning,走确定性规则 |
211
+ | 意图判定 | 「没命中关键词就算通用」 | **意图表 `INTENT_RULES`**:专属只作用对应类型,通用只作用内部留言/其他 |
212
+ | 子组只剩 1 条 | 保留 | **解散 → 平铺** |
213
+ | 摘要命中 ⇒ 跳过详情 | 跳过(导致阈值指标拿不到,10.2 复发) | **摘要缓存只省 AI,不省详情**(详情缓存按 `requestId__lastFeedBackDateTime` 命中则零请求) |
214
+ | `formatInternalMessageOverflowSummary` | 残留导出 + 调用 | 已删(含 `SMART_UNCOVERED_TITLE` 的全部消费点) |
215
+
216
+ ### 11.1 ✅ 已修(2026-10-26):阈值组内容校验曾是死代码
217
+
218
+ 原条件用了 `explicitEmptyPlans`(`aiGroups` 长度为 0),却写在 `groupPlans.length > 0` 的分支里,**永远不成立**:
219
+ 3 条 500/1000/1500 元的报销,`aiGroups:[{title:"100元以内",…}]` 照样输出 `count=3` 且无 warning。
220
+ **修法**:条件改成 `plan.title === 保留标题`,模型用了保留标题才逐条核阈值。
221
+
222
+ ### 11.2 ✅ 已修(2026-10-26):阈值分组被「详情全空」误吞
223
+
224
+ 触发条件:本轮传了 `aiGroups` + 该组全部命中摘要缓存(⇒ 没拉详情)⇒ `fact === undefined` 被当成「详情为空」⇒ 整组走「沿用父组名整组展示」,阈值分支没机会执行。
225
+ **修法**:排除 `cachedIds`、阈值分支提前;2026-10-27 进一步改成「摘要命中不再跳过详情」(详情缓存兜住请求量),从根上消除这类判据混用。
226
+
227
+ ### 11.3 ⚠️ 满意度组的计划被静默忽略 → 已改为写 warning
228
+
229
+ `D1` 在其它判定之前 `return`,现在会明确写两条:`满意度类不做 ai二次分组,已忽略 aiGroups` + `summary 由标题兜底(不调 AI)`。
230
+
231
+ ### 11.4 ✅ 已修(2026-10-26):`aiGroups: []` 不再硬失败
232
+
233
+ 空数组与「未传」语义统一(都走业务类型确定性规则);原先的非阈值类硬失败已不存在。
234
+
235
+ ### 11.5 子组 `id` 不稳定
236
+
237
+ 编号基于**排序后**的下标,条数一变编号就漂移。如果前端拿 `id` 做展开状态记忆或 diff key,会出现「内容没变但 id 变了」。
238
+
239
+ ### 11.6 `other` 是英文标题 —— 已随 `other` 汇总项一起删除
240
+
241
+ `SMART_UNCOVERED_TITLE` 不再进入最终结构,卡片上不会再出现英文 `other`。
242
+
243
+ ### 11.7 门槛与叠加视图的相互作用
244
+
245
+ 优先条目也算进所属工作流组的条数,原本 2 条、含 1 条紧急的组现在算 3 条 —— 会**从平铺变成可二次分组**。近一年数据实测符合预期(`totalCount 77` / `Σcount 98`)。
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "weaver-work-cli",
3
- "version": "0.1.28",
3
+ "version": "0.1.29",
4
4
  "description": "面向泛微工作工具链的可扩展 TypeScript CLI 基础框架(E10 业票通等能力)",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -77,7 +77,7 @@ function shouldSkip(entry, relativePath) {
77
77
  if (entry.name === '.DS_Store' || entry.name.startsWith('.')) return true
78
78
  if (relativePath === 'product.json') return true
79
79
  if (relativePath === 'references/source-manifest.json') return true
80
- if (entry.isDirectory() && ['assets', 'node_modules', 'scripts'].includes(entry.name)) return true
80
+ if (entry.isDirectory() && ['cache', 'assets', 'node_modules', 'scripts'].includes(entry.name)) return true
81
81
  return false
82
82
  }
83
83
 
@@ -14,7 +14,7 @@
14
14
  - `invoice-update.md`:编辑发票的 `prepare -> apply` 写入协议。
15
15
  - `invoice-delete.md`:删除发票的 `prepare -> apply` 写入协议。
16
16
  - `invoice-reim.md`:发票报销自动填单的数据准备;报销单创建/修改调用 `weaver-e10-workflow` 的 `workflow.createForm` / `workflow.createRequest` / `workflow.edit*`。
17
- - `invoice-issuing-workflow.md`:AI 开票主链路——从契约锁合同台账按合同号分组,调用 `weaver-e10-workflow` 的 `workflow.createForm` / `workflow.createRequest` 发起开票流程草稿。
17
+ - `invoice-issuing-workflow.md`:AI 开票主链路——获取待开票合同数据(含契约锁、泛微及其他合同)后按合同号分组,按核算公司选择开票流程(上海亘岩网络科技有限公司走契约锁开票流程;泛微网络、泛微软件、上海田亩、成都泛微、泛微国际、北京泛微、浙江泛微走合同收款发票开具及快递申请;为空或不在名单里时先问用户;退票、冲红、红冲走合同开票冲红流程),调用 `weaver-e10-workflow` 的 `workflow.createForm` / `workflow.createRequest` 发起草稿。
18
18
  - `invoice-issuing-form-fill-rules.md`:开票流程填单规则,用户没说的字段按默认值填(如本次开票金额 = 总金额)。
19
19
  - `invoice-issuing-qys-ledger.md`:兜底链路,用户明确要求时按台账行直接开票或存待开台账。
20
20
  - `invoice-issuing.md`:兜底链路,用户明确要求时按口述字段直接开票或存台账的 payload 确认链。
@@ -27,7 +27,7 @@ Agent 调优重点:
27
27
  - 写操作只接受 CLI 生成的 continuation,不手工构造或复用。
28
28
  - `validate=false` 只能说明跳过查验,不能描述为查验通过。
29
29
  - 报销单创建、报销单修改、开票和存台账必须先展示摘要并取得明确确认;报销单按 `workflow.createRequest` 的 `{main, details}` 结构组装 `data`。
30
- - 开票默认按契约锁台账发起开票流程草稿(`action` 固定 `draft`),明细每行只传金额(含税)与台账数量,其余列由合同号联动带出,Agent 不做金额计算;只有用户明确说"直接开票 / 不走流程 / 存待开台账"才调开票接口。
30
+ - 开票先获取待开票合同数据(含契约锁、泛微及其他合同),再发起流程草稿(`action` 固定 `draft`)。销售方取台账「核算公司」,名称必须完全相同:「上海亘岩网络科技有限公司」用契约锁开票流程;泛微网络、泛微软件、上海田亩、成都泛微、泛微国际、北京泛微、浙江泛微用「合同收款发票开具及快递申请」;为空或不在名单里时先问用户;退票、冲红、红冲用「合同开票冲红流程」。明细每行只传金额(含税),其余列由合同号联动带出,Agent 不做金额计算。CLI 已无直接开票接口。
31
31
  - 直接开票时从用户输入、附件或对话上下文提取购方字段;当前资料包没有客户档案查询 operation,资料不足时追问用户补齐。
32
32
  - 当前资料包没有红字发票红冲接口,`invoice.red.*` 不属于可用 operation。
33
33
  - 结果为 `partial/write_uncertain` 时停止,不自动重放写请求。
@@ -92,17 +92,39 @@ cliHelp: "weaver-work-cli workflow --help"
92
92
 
93
93
  **判定规则:** 用户核心对象是流程实例(request)或流程发起表单时走本 Skill。要发起某个流程用 `workflow.createForm`(它内部解析当前用户**可发起**的工作流,解析不出唯一目标时直接给出候选);查询"我有哪些待办"用 `workflow.search`。涉及"姓名转人员 ID / 组织架构 / 部门岗位"时,人员与组织解析交给 `weaver-e10-hrm`,本 Skill 只在操作流程时消费其返回的人员 ID。
94
94
 
95
+ **独立分支例外(先判这个,再判上面的常规规则):** 用户要跨多条流程做汇总、摘要或金额合计时,直接使用只读 operation `workflow.todoSmartConvert`,并只读取 [`todo-smart-convert.md`](references/todo-smart-convert.md)。
96
+
97
+ **本分支术语(沟通、改造、写文档一律用这套说法):**
98
+
99
+ | 名词 | 含义 |
100
+ | --- | --- |
101
+ | `workflowName` | 工作流名称。**第一次分组 = 按 `workflowName` 分组**,得到的每组叫「工作流分组」 |
102
+ | **ai二次分组** | 工作流分组之后、在组内再切子组的过程。**只有这一步需要 AI 计划(`aiGroups`)** |
103
+ | **原始列表数据** | `/getTodoListData` 返回的待办条目集合,整条链路的唯一数据源 |
104
+ | **流程标题** | 条目字段 `requestNameTitle`;没有流程详情数据时 AI 只能靠它判断语义 |
105
+ | **流程详情数据** | `/getWorkflowRequest` 返回的表单字段 + 基础信息(分组素材 / 摘要素材 / 阈值指标都取它) |
106
+ | **requestDatas** | 工作流分组的条目集合(汇总项内部也用同名数组);**平铺输出到 `displayItems[]` 的 `type:"request"` 单条**,不是留在 `requestDatas` |
107
+ | `requestId` | 请求 ID。`getTodoListData` 返回的每条数据对应一个,回灌摘要、定位条目都以它为主键 |
108
+
109
+ 不带限定语时说「分组」= 按 `workflowName` 的第一次分组;说「二次分组」= ai二次分组。
110
+
111
+ - 不要先调用 `workflow.search`,也不要套用它的分组或 `finalMarkdown` 输出口径。
112
+ - 阶段顺序、`aiGroups`(仅在需要语义侧重点时生成)与回灌、摘要规则和最终输出均以该 reference 为准;若省略 `aiGroups`,按 reference 的确定性规则继续执行;AI 计划必须基于本轮 `collect` 结果。
113
+ - 本分支只读;提交、置已读、催办、共享、转发、退回等写操作仍走 `workflow.batch*`。
114
+ - 最终交付**不区分宿主/agent 类型**,固定两份、缺一不可:① 回复里**直接内联完整的 `<browsertool>...</browsertool>` 载荷**(标签成对,`params.data` = 完整三键);② **同时**落一份**不带标签的纯 JSON 文件**(同一份载荷)并用 `present_files` 打开。超长时先提示条数与体积再输出,不要改成分组拆块。
115
+
95
116
  ## 选哪个命令
96
117
 
97
118
  | 想做什么 | 命令 | 按需读取 reference |
98
119
  | --- | --- | --- |
99
120
  | 查看可用 operation、字段和风险等级、输出契约 | `workflow schema` | [`workflow-agent-entry.md`](references/workflow-agent-entry.md) |
100
- | 查询待办/已办/我发起/全部流程,按条件筛选,待办智能分组取数 | `workflow run workflow.search` | [`workflow-query.md`](references/workflow-query.md) |
121
+ | 常规流程列表查询与筛选(含查询分组,不做跨条目汇总) | `workflow run workflow.search` | [`workflow-query.md`](references/workflow-query.md) |
101
122
  | 待办整体分组计数(智能分组第二步;须先确认 `oaContext.smartGrouping === true`) | `workflow run workflow.todoStat` | [`workflow-query.md`](references/workflow-query.md) |
102
123
  | 查看流程完整详情、生成摘要、打开流程、查签字意见/流转日志 | `workflow run workflow.detail` / `workflow.summary` / `workflow.open` / `workflow.requestLog` | [`workflow-view.md`](references/workflow-view.md) |
103
124
  | 打开 OA 页面(待办/已办/草稿/监控/创建/查看) | `workflow run workflow.pageOpen` | [`workflow-view.md`](references/workflow-view.md) |
104
125
  | 单条流程操作:同意/提交/退回/转办/委托/转发/抄送/加审/收回/结束/征询/删除/催办 | `workflow run workflow.operatePrepare` / `workflow.operateApply` | [`workflow-operate.md`](references/workflow-operate.md) |
105
126
  | 批量提交 / 置已读 / 催办 / 共享 / 转发 / 退回(均为单命令,一次调用完成) | `workflow run workflow.batchSubmit` / `workflow.batchRead` / `workflow.batchSupervise` / `workflow.batchShare` / `workflow.batchForward` / `workflow.batchReject` | [`workflow-batch.md`](references/workflow-batch.md) |
127
+ | 跨条目汇总待办 / 待办摘要 / 待办金额合计(workflow 内独立分支) | `workflow run workflow.todoSmartConvert` | [`todo-smart-convert.md`](references/todo-smart-convert.md) |
106
128
  | 单条流程共享(人/部门/分部/群组/角色/岗位/所有人) | `workflow run workflow.sharePrepare` / `workflow.shareApply` | [`workflow-share.md`](references/workflow-share.md) |
107
129
  | 发起/创建流程、填单、存草稿 | `workflow run workflow.createForm` / `workflow.createRequest` | [`workflow-create.md`](references/workflow-create.md) |
108
130
  | 修改已存在流程的表单内容 | `workflow run workflow.editLoad` / `workflow.editPrepare` / `workflow.editApply` | [`workflow-edit.md`](references/workflow-edit.md) |
@@ -0,0 +1,282 @@
1
+ # todoSmartConvert:待办智能汇总
2
+
3
+ `workflow.todoSmartConvert` 是 workflow 模块的只读 operation,用于跨条目取数、首次分组、校验 AI 分组计划、按需取详情和回填单条摘要。固定的组摘要由 CLI 生成,普通列表查询使用 `workflow.search`;批量提交、置已读、催办、共享、转发、退回使用 `workflow.batch*`;不要把这些链路混用。
4
+
5
+ ## 0. 术语(改代码、写文档、对话都用这套说法)
6
+
7
+ | 名词 | 含义 | 对应实现 |
8
+ | --- | --- | --- |
9
+ | `workflowName` | 工作流名称 | 大组的 `title` 就是它 |
10
+ | **第一次分组** | 按 `workflowName` 把条目分到各个工作流;得到的每个组叫**工作流分组** | `runGroup` → `wfIdGroup[]` |
11
+ | **ai二次分组** | 在工作流分组**内部**再切子组的过程;**只有这一步需要 AI 计划** | `smartGroup` + `aiGroups` |
12
+ | **原始列表数据** | `/getTodoListData` 返回的待办条目集合,**整条链路的唯一数据源**(每条对应一个 `requestId`) | `collect` 的 `items[]` |
13
+ | **流程标题** | 条目标题 `requestNameTitle`;没有**流程详情数据**时,AI 只能靠它做语义判断,也是摘要的兜底文案 | `requestData.requestNameTitle` |
14
+ | **流程详情数据** | `/getWorkflowRequest` 返回的表单字段 + 基础信息;用于 a) 分组素材 b) 摘要素材 c) 阈值指标(请假天数/报销金额/到期时间) | `DetailFact { context, metrics }` |
15
+ | **requestDatas** | 一个工作流分组内的条目集合;既是大组自身的条目容器,也是汇总项(`type:"group"`)内部的条目容器 | `wfIdGroup[].requestDatas` / `displayItems[group].requestDatas` |
16
+ | `requestId` | 请求 ID;`getTodoListData` 返回的每条数据对应一个,回灌摘要与定位条目都以它为主键 | `requestData.requestId` |
17
+ | `getWorkflowRequest` | 返回**流程详情数据**(表单字段 + 基础信息) | `endpoints.ts` 的 `REQUEST_DETAIL` |
18
+
19
+ 约定:不带限定语说「分组」= 第一次分组;说「二次分组」= **ai二次分组**。产出物里 `displayItems[].type === 'group'` 的即 ai二次分组的结果;**平铺**指条目作为 `displayItems[]` 里 `type:"request"` 的单条输出(不是留在 `requestDatas` 里)。
20
+
21
+ ## 1. 调用链
22
+
23
+ | 阶段 | 作用 |
24
+ | --- | --- |
25
+ | `collect` | 调列表接口取数,并生成首次分组和 `groupingBrief` |
26
+ | `group` | 对已传入的 `items` 重新做首次分组 |
27
+ | `smartGroup` | 校验 `aiGroups`,组装最终三键结构 |
28
+ | `summaryContext` | 拉详情并生成单条摘要素材 |
29
+ | `summary` | 回填 `aiSummaries`,可同时生成组摘要素材 |
30
+ | `groupSummaryContext` | 生成汇总项摘要素材 |
31
+ | `groupSummary` | 回填 `aiGroupSummaries` |
32
+ | `aggregate` | 兼容旧调用的本地去重、分组和金额合计 |
33
+
34
+ 推荐调用顺序:
35
+
36
+ ```text
37
+ collect
38
+ -> smartGroup(可传 withDetailContext:true)
39
+ -> summary(可传 withGroupSummaryContext:true)
40
+ -> groupSummary(仅存在待回填组摘要时)
41
+ ```
42
+
43
+ 未传 `stage` 时,没有 `items` 进入 `collect`;传入 `items` 进入兼容的本地汇总;回灌带有最终结构的数据时由 CLI 推断后续阶段。显式 `stage` 优先。
44
+
45
+ 每次模型调用由调用方把 AI 耗时追加到 `skillTiming`;CLI 会把当前阶段耗时追加到 `meta.timing.steps`。不要为没有执行的 AI 阶段填入耗时。
46
+
47
+ ## 2. 取数和详情
48
+
49
+ ### 取数
50
+
51
+ - 默认 `category=todo`,只调用对应分类的列表接口;待办接口为 `getTodoListData`。collect 不调用总数、分组统计或详情接口。
52
+ - 时间范围一次传入,通过 `current` 递增翻页。默认 `limit=200`,最大 `limit=500`;单页最多 100 条,最多 5 页。`pageSize` 是兼容参数,实际大小由 `limit` 推导。
53
+ - 达到 limit、页内没有新增数据或返回空页时停止;跨页按 `requestId` 去重。超过上限会在 warnings 中说明。
54
+ - 只使用响应中的 `displayData`,不把技术行当作回退数据。长 ID 始终按字符串处理。
55
+ - `items` 只保留稳定业务字段;`workflowName`、接收时间、金额等辅助字段只用于本地分组、指标和兼容 `aggregate`,不会泄漏到最终 `requestData`。
56
+ - collect 会刷新本地查询快照,但本 operation 不执行写操作。
57
+
58
+ ### 详情和缓存
59
+
60
+ - 详情请求固定使用 `isReadRequest:false`,默认并发 10、单条超时 10 秒;单条失败不会拖垮整批。
61
+ - 详情字段先清洗 HTML/CSS,再应用业务字段规则;正文全量传给模型,不做固定字数截断。
62
+ - **两个持久化缓存**,key 都是 `requestId__lastFeedBackDateTime`,有效期 7 天、清理间隔 3 天:
63
+ - 详情缓存:正文 / 阈值指标 / 分组字段,**详情为空也写**;
64
+ - 摘要缓存:单条摘要文本。
65
+ 记录里带 `lastFeedBackDateTime` 就是为了这个 key:**流程有新反馈 ⇒ key 变 ⇒ 缓存自然失效**,不需要另设版本号。
66
+ - 详情取数时机:`collect` 只为「用户给了 `groupFocus` 且意图适用于本组」的组预取字段;`smartGroup` 对全部条目走一次「缓存优先」的取数(命中零请求)。因此**冷缓存下详情请求集中在 `smartGroup`,`summary` 阶段通常 0 请求**。
67
+ - 摘要缓存**只省 AI、不省详情**:阈值指标只认流程详情数据,所以命中摘要的条目仍会查一次详情缓存;摘要为空的条目用标题兜底。
68
+
69
+ ## 3. 首次分组
70
+
71
+ - `groupBy` 支持 `workflow`、`urgency`、`date`、`none`,默认 `workflow`。
72
+ - 优先组固定为 `id=priority`、标题“建议优先处理的流程”,排在第一位。超时条目必入;没有级别 2 时才使用级别 1,级别 0 不因等级入选。
73
+ - 优先组是**叠加视图**,不是筛选:入选条目**仍保留在各自的工作流分组里**(优先组只是置顶入口)。因此 `Σcount` 会大于 `totalCount`,**`totalCount` 一律按去重条数计(= collect 取到的全部条数),不做分组求和**。
74
+ - 普通组保持接口返回的首次出现顺序。只有调用方明确给出排序意图时才传 `groupOrder`;`topN` 只影响中间 Markdown,不裁剪最终数组。
75
+ - `groupingBrief` 用组内 1-based `idx` 标识条目。AI 生成 `aiGroups` 时优先用 `seq`,不要手抄长 `requestId`。
76
+
77
+ ## 4. `smartGroup` 和 `aiGroups`
78
+
79
+ `aiGroups` 是扁平数组,每项只允许 `groupId`、`title`、`requestIds` 或 `seq`:
80
+
81
+ ```json
82
+ [
83
+ { "groupId": "workflow-id", "title": "安全通知", "seq": [1, 3] },
84
+ { "groupId": "workflow-id", "title": "制度通知", "seq": [2, 4] }
85
+ ]
86
+ ```
87
+
88
+ - `requestIds` 与 `seq` 二选一,不能同时出现,也不能都为空。
89
+ - 优先组不可二次分组。
90
+ - 组内少于 3 条时**一律平铺单条**,不产生任何汇总项——阈值组(1天以内 / 100元以内)、问题支持过期组、内部留言整组都不例外;**即使给了 `aiGroups` 也忽略**(CLI 写 warning)。这条是硬门槛,没有例外。
91
+ - 组内至少 3 条时,完全省略 `aiGroups` 走 CLI 确定性分组;显式传入后,未知 `groupId`、未知 `requestId`、重复分配和越界序号直接失败(**仅在本组计划会被采纳时校验**:意图适用于本类型,或本组是内部留言)。
92
+ - `aiGroups` 不是必填,**而且只有两种情形该给**:
93
+ - ① 用户给了侧重点(`groupFocus`),且侧重点**按意图表适用于本组类型**;
94
+ - ② 本组是**内部留言** —— 而且**≥3 条时是必须给**:没有侧重点(或侧重点不适用)就按**流程标题**分组,缺计划直接报 `ai_groups_required`。
95
+ 其他类(`other`)在无侧重点时**不要给**:给了也会被忽略,且该组**全部平铺单条**(会写 warning);请假/财务/客户问题支持按各自的阈值 / 过期规则分,不需要计划。
96
+ - **意图按表判定,不是「随便一句中文都算」**(`INTENT_RULES`):专属意图(请假维度 / 财务维度 / 问题支持维度)只作用于对应类型;通用意图(客户 / 发起人 / 时间 / 紧急 / 标题主题 / 类型…)只作用于内部留言与其他类。意图不适用于本组时,CLI 按「没有意图」处理并写 warning,**不会硬失败**。
97
+ - 意图适用于本组、却没给本组计划 ⇒ 按「没有意图」处理(确定性规则),不再报 `ai_groups_missing_group`。
98
+ **例外:内部留言 ≥3 条没有这个退路**——必须给计划(按标题或按侧重点),缺计划报 `ai_groups_required`,并把可用 `groupId` 与 `1..N` 序号回传。
99
+ - 无 `groupFocus` 时,子组按条数降序;有 `groupFocus` 时,子组沿用 AI 数组顺序。侧重点只作用于匹配的业务类型。
100
+ - 请假类和财务类在无侧重点时使用 CLI 阈值兜底:分别合并不超过 1 天、不超过 100 元的条目,其余单条;**指标只取流程详情字段**,取不到就平铺并点名,不猜。
101
+ - **阈值分组与「传不传 `aiGroups`」无关**:只要本组没拿到生效计划、且没有匹配的侧重点,就始终按阈值分组。阈值组的 `summary` 由 CLI 固定生成,即使本轮传了 `aiGroups` 也不会留空——**不要把阈值组当成需要 AI 写摘要的组**。
102
+ - `1天以内` / `100元以内` 是**保留标题**:模型把它们当子组名时,CLI 会逐条核阈值,超阈值的条目被剔出、按单条展示并写 warning。
103
+ - **客户问题支持类**(`workflowName` **精确等于** `Ecology客户问题支持解决流程` / `E10客户问题支持解决流程`,不含 new / 明细 / 重庆机构 / FWSX-老客户):
104
+ - **无侧重点**:CLI 固定产出一个汇总项「已过期及今天即将过期的流程」(判定 `overdue || expiringToday`),其余按单条展示;**一条都没命中就不产这个子组**,全部单条。
105
+ - **有侧重点**:`collect` 会为这类流程额外拉一次详情,把字段键值放进 `groupingBrief.items[].fields`(最多 8 个字段、每个值截 80 字),模型按意图指向的字段值分桶(如"按问题小分类"→ 用 `问题所属模块` 的取值做子组名)。
106
+ - `groupingBrief.items[]` 带 `overdue` / `expiringToday` / `userOverTime`,**不要自己解析「11-30到期」这类文案**。
107
+ - **默认不二次分组**:没有生效计划时,只有两类会产汇总项——请假/财务的阈值组、客户问题支持类的过期组;**其他一律平铺单条**,不造「与父组同名」的整组汇总项,也**不造 `other`**。满意度类全单条,`summary` 用条目标题兜底(不调 AI、不为空)。AI 路径的漏分、子组只剩 1 条、保留标题里超阈值的条目,**一律落成单条**(不丢数据)并写 warnings。
108
+
109
+ 最终业务对象只能有三个键:
110
+
111
+ ```json
112
+ {
113
+ "smartSummaryTitle": "您当前有 N 条待办,清单如下:",
114
+ "totalCount": 0,
115
+ "wfIdGroup": []
116
+ }
117
+ ```
118
+
119
+ 每个大组包含 `id`、`topPriority`、`title`、`count`、`defaultExpanded`、`displayItems`。`displayItems` 是以下两种对象之一:
120
+
121
+ ```json
122
+ { "type": "request", "requestData": {} }
123
+ ```
124
+
125
+ ```json
126
+ {
127
+ "type": "group",
128
+ "id": "workflow-id-1",
129
+ "title": "子组名称",
130
+ "count": 0,
131
+ "summary": "",
132
+ "defaultExpanded": false,
133
+ "requestDatas": []
134
+ }
135
+ ```
136
+
137
+ `totalCount` = **按 `requestId` 去重后的条数**(= collect 取到的全部条数),**不是** `wfIdGroup[].count` 之和:优先组是叠加视图,Σ`count` 会大于 `totalCount`。只有优先组默认展开;普通组先放汇总项,再放单条。漏分、缓存命中、详情失败等诊断信息放在信封或 `meta`,不放进业务对象。
138
+
139
+ ## 5. 摘要阶段
140
+
141
+ ### 单条摘要
142
+
143
+ - `summaryContext` 输出 `summaryBatches`,每个批次带组信息、条目标题、清洗后的 `context` 和 `contextAvailable`。
144
+ - 批次不跨组;默认按 24,000 字或 100 条切分。摘要默认建议 50 字,可用 `summaryMaxChars` 覆盖。
145
+ - 调用方只提交本轮有效的 `aiSummaries:[{requestId,summary}]`。未知或重复 ID 失败;漏写保留空串并告警。CLI 不截断或改写摘要。
146
+
147
+ ### 组摘要
148
+
149
+ - 确定性路径下 `smartGroup` 只为请假、财务、客户问题支持三类产固定组摘要;其他类与内部留言没有汇总项,也就不存在组摘要。显式 AI 分组路径的组摘要先为空,由 `groupSummaryContext` / `groupSummary` 回填。
150
+ - 组摘要素材只包含标题、发起人和单条摘要,不重复拉详情。组摘要默认建议 100 字,可用 `groupSummaryMaxChars` 覆盖。
151
+ - 回填使用汇总项自身的 id(例如 `workflow-id-1`),不是父组 id。未知或重复 ID 失败,漏写保留空串并告警。
152
+
153
+ ## 6. 输出和兼容字段
154
+
155
+ - 最终业务数组完整返回,不因输出大小裁剪或改写;warnings 可在 operation 信封和 `meta.warnings` 中出现,其他诊断字段只放在 `meta`,不混入业务对象。
156
+ - `detailPrefetch`、`pageSize` 和旧拼写 `smartSmmaryTitle` 仅为兼容,新调用使用当前字段。
157
+ - 复杂批次可以使用仓库提供的 `scripts/todo-summary.cjs`,但脚本只是 CLI 编排器,不能绕过本 operation 的校验和输出契约。
158
+
159
+ ### 6.1 交付形态:内联 `<browsertool>` + 另存无标签 JSON(硬规则)
160
+
161
+ **用户 2026-10-27 定稿:不区分宿主与 agent 类型**(小e / WorkBuddy / Codex / 终端全部同一套规则),交付**固定两份、缺一不可**:
162
+
163
+ - **① 回复正文里直接内联完整的 `<browsertool>...</browsertool>` 块**(不省略、不用路径代替)+ 各阶段耗时。
164
+ - **② 同时另存一份不带标签的纯 JSON 文件**(内容 = 同一个载荷对象),路径 `~/Downloads/待办汇总-<范围>-<条数>条-<YYYYMMDD>.json`,落盘后立刻用 `present_files` 打开。
165
+ - 标签**必须成对**:`<browsertool>` 开头、`</browsertool>` 结尾;漏标签或只贴裸 JSON 都算交付失败。
166
+ - `params.data` 的值就是最终三键对象(`smartSummaryTitle` / `totalCount` / `wfIdGroup`)**原样**:不裁剪、不改字段名、不再外包一层。
167
+ - 载荷里除 `params.data` 外**不要**再带 `schemaVersion` / `ok` / `operation` / `meta` / `warnings` 等信封字段。
168
+ - 外框字段全部固定,只有 `src` 与 `params.data` 会变:
169
+
170
+ ```text
171
+ <browsertool>
172
+ {
173
+ "version": "1.0",
174
+ "callId": "call-1",
175
+ "toolId": "todo_summary",
176
+ "arguments": {
177
+ "type": "e10",
178
+ "title": "待办摘要汇总",
179
+ "src": "<当前认证环境的 baseUrl>/workflow/list/todo",
180
+ "sdkRenderType": "drawer",
181
+ "sdkOptions": {
182
+ "compName": "ui.CorsComponent",
183
+ "routerType": "MemoryRouter",
184
+ "routerProps_pc": { "initialEntries": ["/"] },
185
+ "routerProps_mobile": { "initialEntries": ["/"] },
186
+ "params_pc": { "app": "@weapp/workflow", "compName": "Comp.WflTodoSummary", "clientType": "pc" },
187
+ "params_mobile": { "app": "@weapp/workflow", "compName": "Comp.WflTodoSummary", "clientType": "mobile" },
188
+ "params": { "data": { } }
189
+ }
190
+ }
191
+ }
192
+ </browsertool>
193
+ ```
194
+
195
+ - `src` 取**当前认证环境**的 `baseUrl`(`weaver-work-cli --json auth status` 的 `baseUrl`)拼 `/workflow/list/todo`,不要写死某个租户。脚本已用 `state.json` 缓存 `baseUrl`,同一轮流水线不会重复启动 CLI。
196
+ - `params_pc` / `params_mobile` 必须放在 `sdkOptions` 内、与 `routerProps_pc/mobile` 同级,**不要放进 `arguments` 层**。
197
+ - `app` 的值按原始字符写 **`@weapp/workflow`**(`@` 后面**没有**斜杠,共 15 个字符):**不要写成 `@//weapp/workflow`、不要转义成 `@\/\/weapp\/workflow`、也不要加前导空格**——带前导空格会导致 app 解析失败(用户 2026-10-27 以截图校正)。
198
+ - 脚本 `exportFinal` 已在 stdout 的 `[JSON_BEGIN]`/`[JSON_END]` 之间直接输出**带标签的整块载荷**,原样粘贴即可;交付时**要传 `--file`**,它会另存一份**不带标签的纯 JSON**(同一载荷)供 `present_files` 打开。
199
+ - 体积见 §6.2:**不能按组拆成多个 `<browsertool>` 块**(拆开就不是合法载荷);超限时先在正文说明条数与预计体积,建议缩小范围,用户坚持再照做。
200
+
201
+
202
+ 交付约束:**不改写条目字段、不做体积裁剪**(载荷里必须是完整明细)。
203
+
204
+ #### 6.1.3 耗时口径(只在回复里用文字报出,不产出 md)
205
+
206
+ 用户 2026-10-27 要求:**取消「耗时说明 md」这份交付物**,交付物只有那一块 `<browsertool>` 载荷;耗时仍照常统计,但只在回复正文里用文字给出。下面两个口径要分开报,**不要把两个数混成一个**:
207
+
208
+ | 口径 | 取法 | 含义 |
209
+ | --- | --- | --- |
210
+ | 墙钟 | `/usr/bin/time -p` 包住整次调用 | 含 node 启动 + 模块加载 + 业务 |
211
+ | CLI 内部 | `meta.timing.steps[].durationMs` | 只含业务(请求 + 计算) |
212
+
213
+ - **请求级埋点已移除**(2026-10-27 用户定:`internal/e10/requestTiming.ts` 模块及 `client.ts` / `request-runtime.ts` 的接线一并删除,`meta.timing` 现在只有 `startedAt / finishedAt / stage / steps / durationMs`)。不要再读 `meta.timing.requests`,也不要写 `WEAVER_WORK_CLI_TIMING=1`。
214
+ - 常见误读:`collect` 的墙钟含进程启动(沙箱下 5~7s),真实取数只有 0.2~0.3s。脚本已分列输出 `collect 内部 / 含进程启动`。
215
+ - **不落任何耗时文件**:`--timing-report` 只在控制台打两行「耗时摘要」(总墙钟+业务占比、以及一条"只统计 CLI 链路"的提示),不再写 md、不要 `present_files` 它;回复里用文字给出总墙钟与真实业务耗时可。
216
+
217
+ **端到端要分四层报,不要只报一个数**(实测 2026-10-26 近一年:端到端 4m58s,而 CLI 只占 23s):
218
+
219
+ | 层 | 典型量级 | 来源 |
220
+ | --- | --- | --- |
221
+ | CLI 内部(业务) | 1~3s | `meta.timing.steps` |
222
+ | CLI 墙钟 | 20~25s(3 次进程 × 5~7s 启动) | `/usr/bin/time` |
223
+ | 脚本与工具 | 2~3s | 探查命令、写文件 |
224
+ | **模型往返** | **200~270s** | 每次工具结果后 20~35s |
225
+
226
+ ⇒ 端到端提速最有效的不是优化 CLI,而是**减少往返次数**:
227
+
228
+ - 用 `run --days N --limit N --out DIR --file --timing-report` 一次跑完 `prep + build`(无待写摘要时连 `finish` 一起),别一步步手动跑。
229
+ - **有侧重点(`--focus`)时的最短路径是 4 轮**,多一轮就多 5~7s:
230
+ ```
231
+ R1 prep --days N --focus "请假类型、报销类型" # 输出组/行/序号,以及 collect 已预取的「请假类型 / 金额 / 费用说明」
232
+ R2 写 aiGroups.json && build --plan aiGroups.json # 同一条命令里完成
233
+ R3 写 aiSummaries.json && finish --summaries … # 输出组摘要素材
234
+ R4 写 aiGroupSummaries.json && finish --group-summaries … # 同一条命令内回填单条 + 组摘要并导出
235
+ ```
236
+ - `prep` 的每行后面会带 `‹字段=值›`(最多 3 个、相同值只留一个):**据此分桶,不要再自己去翻详情缓存**。
237
+ - `--group-summaries` 让 `finish` 在同一次调用里跑完 `summary` + `groupSummary`,省掉「读完素材再单独调一次 groupSummary」那一轮。
238
+ - **启动开销的真相(2026-10-27 实测)**:宿主给每个 node 子进程注入 `NODE_OPTIONS=--require=…/node-language-shim.cjs`,它对**每次 fs 读取**加 ~10ms 审计;本 CLI 启动要读 387 个模块 ⇒ 每次 ~4s(负载高时 25s)。
239
+ `todo-summary.cjs` 启动 CLI 时**只摘掉 shim 这一项**(`cliEnv()`),实测 `--version` 4.96s→0.16s、`workflow schema` 7.23s→0.19s、脚本 spawn 6.46s→0.56s。
240
+ 需要保留 shim 时设 `WEAVER_WORK_CLI_KEEP_NODE_SHIM=1`;手工单次调用想提速可用 `env -u NODE_OPTIONS weaver-work-cli …`。
241
+ - **不要把工具改造夹进跑数流程**:脚本/规则有问题先按业务跑完(或单独一轮改完),别边跑边改——实测一次任务里这样做掉了 6 轮、约 40s。
242
+ - 加了 `--timing-report` 就**不要**再自己 `node -e` 反复查中间产物——每次探查等于一次额外往返(≈30s)。
243
+ - 交付回复里同时给「CLI 执行 X 秒 / 端到端 Y 秒(含模型推理)」,只报其中一个都算口径错误。
244
+
245
+ ### 6.2 载荷体积
246
+
247
+ 本分支**不走**公共输出层的 24 KB 落盘投影:`workflow/index.ts` 对 `workflow.todoSmartConvert` 显式传 `persist: false`(注释写明「业务契约要求直接返回完整 `requestDatas`/`displayItems`;禁止公共输出层按 24 KB 预算递归折叠该分支」)。因此不会产生 `refId` / `resultFile`,也不会被折叠或裁剪,体积全部由调用方承担。
248
+
249
+ - 体积按 **约 700 B/条** 估算:1 条 ≈ 750 B,30 条 ≈ 21 KB,70 条 ≈ 49 KB,300 条 ≈ 210 KB。
250
+ - **唯一交付路径(不区分宿主)**:组件要求**一个**块内联完整 `data`,所以**不能按组拆块**(拆开就不是合法载荷);超过约 80 KB(≳110 条)时先在正文说明条数与预计体积,并建议缩小范围或排除某个流程,用户坚持再照做。实测 320 条 = 176 KB,已超出单条消息容量。
251
+ - **其他客户端路径**:落文件即可,体积不受对话限制;只有回复里的说明文字要短。
252
+
253
+ ## 7. 常用命令
254
+
255
+ macOS/Linux:
256
+
257
+ ```bash
258
+ weaver-work-cli --json workflow run workflow.todoSmartConvert --input-json '{"stage":"collect","limit":100}'
259
+ ```
260
+
261
+ Windows PowerShell:
262
+
263
+ ```powershell
264
+ weaver-work-cli --json workflow run workflow.todoSmartConvert --input-json '{"stage":"collect","limit":100}'
265
+ ```
266
+
267
+ 大 payload 使用 `--input <path>` 或受支持的输入回灌方式,内容必须来自上一阶段返回值,不要手工重算 `wfIdGroup`。
268
+
269
+ ## 8. 错误处理
270
+
271
+ | 错误 | 处理 |
272
+ | --- | --- |
273
+ | `stage_invalid` / `input_unknown_key` | 按错误中的允许字段修正 |
274
+ | `items_required` / `items_invalid` / `items_too_many` | 先执行 `collect`,再回灌原始 `items` |
275
+ | `ai_groups_invalid` / `ai_group_*` | 检查 `groupId`、`title`、`seq`/`requestIds` 和覆盖范围 |
276
+ | `final_groups_required` / `wfidgroup_invalid` | 原样回灌上一步 `wfIdGroup` |
277
+ | `ai_summaries_required` / `ai_summary_*` | 只提交本轮有效 `requestId`,不能重复 |
278
+ | `ai_group_summaries_required` / `ai_group_summary_*` | 使用汇总项 id,不使用父组 id |
279
+ | `forbidden_endpoint` | 只允许列表取数接口,检查是否误用了辅助分页接口 |
280
+ | `authentication` / `network` | 按共享 skill 的认证和失败规则处理,不盲目重试 |
281
+
282
+ 最终交付固定两份:**回复里内联的 `<browsertool>` 载荷** + **同名的不带标签纯 JSON 文件**(`present_files` 打开),耗时以文字写在回复里;不要用 Markdown 报告或摘要清单替代载荷,也不要再产出「耗时说明 md」这类附加文件(见 §6.1、§6.1.3)。