dsh-erpbot 0.1.0 → 0.1.1

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.
@@ -0,0 +1,205 @@
1
+ # Bot AGENTS.md 内容规格
2
+
3
+ > 状态:v3(2026-11-13)— 描述**当前已落地**的 9 段单文件结构,并吸收「Bot 视角评审」结论与「技能优先检索」决策。
4
+ > 适用范围:`D:\erpBot\finance|funds|forecast|tax\AGENTS.md`(四个业务 Bot 的工作区根身份文件)。
5
+ > 用途:改 `bot-identity.js`(Host 生成)与 `src/workbench/index.js`(客户端重写)的唯一依据;评审一份 Bot 身份文件是否合格时逐条对照。
6
+
7
+ ---
8
+
9
+ ## 0. 一句话结论
10
+
11
+ **AGENTS.md = 身份 + 纪律 + 指针 + 技能优先检索范围。** 不是知识库、不是提示词、不是配置备份。
12
+ 落地 9 段:身份、回答顺序、数字可信、红线、挂起纪律、越界转介、**已绑定技能(优先检索)**、用户偏好、自检。
13
+ 严禁写 5 类:业务数字、密钥路径、技能全文、与全局 AGENTS.md 冲突的授权、易变的状态。
14
+
15
+ ---
16
+
17
+ ## 1. 性质:它是被宿主注入的约束文件
18
+
19
+ | 事实 | 后果 |
20
+ |---|---|
21
+ | DSH 在 `D:\erpBot\<bot>` 工作区开会话时,把该目录 `AGENTS.md` 连同 `~/.dsh/AGENTS.md` 一起注入(`instructionFileCandidates: AGENTS.md / CLAUDE.md`) | 内容**每次对话都占 token**,必须短;它是常驻约束,不是一次性寒暄 |
22
+ | 它与用户全局 `AGENTS.md` **同层叠加**,不做覆盖 | 只能写「本 Bot 特有」的,重复全局规则=制造冲突源 |
23
+ | 它随用户改偏好被**整体重写**(客户端 `persist` → Host 落盘) | 任何手工编辑都会被下次保存抹掉 → 手改内容必须进生成函数 |
24
+ | 技能清单与身份同文件(v3 决定) | 绑定技能是「优先检索范围」,必须随身份一起注入,才能参与每轮检索判断 |
25
+
26
+ **体积上限:AGENTS.md ≤ 3.5 KB。** 超过说明写进了知识(该进 `SKILL.md` 或知识库)。
27
+
28
+ ---
29
+
30
+ ## 2. 必备内容清单(AGENTS.md 9 段)
31
+
32
+ > 「判定标准」一栏是可核对的说法;写不出判定标准的段落属于没写清。
33
+
34
+ | # | 段落 | 必须写什么 | 判定标准 | 现状 |
35
+ |---|---|---|---|---|
36
+ | 1 | **身份** | 角色、职责、语气、边界、不适用 五项 | 五项齐全;「语气」只描述说话方式,「边界」只描述动作禁令,「不适用」只列范围外科目——三者互不渗漏 | ✅ 已落地(评审见 §9) |
37
+ | 2 | **回答顺序** | 「遇到 X 类问题先给 A、再给 B」 | 每类场景写清「先给什么、后给什么」;不含「要专业」这类不可验证的话 | ✅ 已落地 |
38
+ | 3 | **数字可信** | 口径、时点、数据集、证据四件套 | 四项各一句硬要求;版本指认用「口径版本 + 生效时点」,不得用「当前」 | ✅ 已落地 |
39
+ | 4 | **红线** | 禁止动作,按「结构上不做 / 只能走通道」两组 | 每条写「禁什么 + 该走哪条通道 / 谁来批」;无通道的红线写「拒绝并说明」 | ✅ 已落地 |
40
+ | 5 | **挂起纪律** | 不确定时的标准动作 | 写明触发条件 + 唯一出路(转人工裁定);不猜、不静默重试 | ✅ 已落地 |
41
+ | 6 | **越界转介** | 越界时指向哪个 Bot | 路由表覆盖其余三个 Bot;每条写「指向谁 + 他能回答什么」 | ✅ 已落地 |
42
+ | 7 | **已绑定技能(优先检索)** | 已绑定技能清单 + 三层检索规则 | 清单与实际 `bot.json.skills` 一致;明确「先命中绑定、未命中回退全局」;有「不得自行发明技能名」明文 | ✅ 已落地(v3 新增) |
43
+ | 8 | **用户偏好** | 回复语气、默认时间口径 | 取值是中文可读标签而非内部 id(`rigorous` → `严谨 · 审计`) | ✅ 已落地 |
44
+ | 9 | **交付前自检** | 交付前自查项 | ≤ 5 条且可核对(报数字,不说「已完成」) | ✅ 已落地 |
45
+
46
+ ---
47
+
48
+ ## 3. 第 7 段:技能优先检索(核心决策)
49
+
50
+ **目的**:给 Bot 一个基础的绑定技能清单,优先在这份小清单里检索,命中不了再回退到 DSH 全局 skill 目录。两层都保留,窄层优先。
51
+
52
+ **三层检索规则**(写进文件,原文):
53
+
54
+ ```markdown
55
+ 规则:
56
+ 1. 回答前,先判断任务是否命中以上技能;命中则加载对应 skill 并按其 SKILL.md 执行。
57
+ 2. 未命中时,才回退到本会话全局 skill 目录(<available_skills>)检索。
58
+ 3. 不得自行发明技能名;加载技能不改变身份与口径。
59
+ ```
60
+
61
+ **为什么是「指令引导」而非「scope 限制」**:
62
+ - DSH 的 skill 检索是「全局 catalog + 按需加载」(`dsh-tool-skill`),模型看到 `<available_skills>` 全集。
63
+ - `ctx.skills.register()` 能做 per-layer 技能注册,但那是「窄范围**取代**全局」,与「保留全局兜底」矛盾。
64
+ - 因此用指令引导模型「先绑定清单、再全局目录」,两层共存,DSH 的全局检索机制不被动。
65
+
66
+ ---
67
+
68
+ ## 4. 四个 Bot 的差异化部分
69
+
70
+ 同构骨架,只换「身份 / 回答顺序 / 越界转介」三块;其余段落(数字可信、红线、挂起纪律、自检)逐字一致——共性纪律只维护一份,避免四处漂移。
71
+
72
+ | Bot | 场景下先给什么 | 典型不适用项 | 越界优先指向 |
73
+ |---|---|---|---|
74
+ | 账房 `finance` | 结论(平/不平、几步、能否出)→ 口径 → 证据路径(报表→底稿→账簿→凭证) | 外币折算与汇兑损益、合并抵消与权益法 | 钱不够 → 司库;未来走势 → 军师;税 → 税官 |
75
+ | 司库 `funds` | 可用余额与安全垫判断 → 时点 → 受限与未达 | 外币资金、融资性保函定价 | 账务处理 → 账房;资金计划预测 → 军师;涉税 → 税官 |
76
+ | 军师 `forecast` | 区间与假设版本 → 结论 → 敏感项(明确区分预测/承诺) | 年度以上战略规划、合并口径预测 | 已发生事实 → 账房/司库;申报 → 税官 |
77
+ | 税官 `tax` | 金额与截止日 → 预计口径 → 未勾选红线(预计与确定分开说) | 关税与进出口退税、跨地区汇总纳税 | 账务与资金 → 账房/司库;预算 → 军师 |
78
+
79
+ ---
80
+
81
+ ## 5. 明确不要写的内容(5 条反例)
82
+
83
+ | 不要写 | 为什么 | 该放哪 |
84
+ |---|---|---|
85
+ | 具体业务数字、余额、当期报表值 | 写进去即过期,且会被当「依据」引用,制造第二个真相源 | 数据集 / KPI payload |
86
+ | 技能全文、SOP 长文、政策条文 | 挤占每次对话上下文,与 `SKILL.md` 双份维护必然漂移 | `~/.dsh/skills/<name>/SKILL.md` |
87
+ | 密钥、连接串、账号、真实内外网地址 | 该文件随工作区可被任意会话读到 | 凭据存储 / MCP 通道配置 |
88
+ | 与 `~/.dsh/AGENTS.md` 重复或冲突的跨区授权 | 同层叠加,后写的不覆盖先写的 | 全局 `AGENTS.md` |
89
+ | 可变状态(当前会话 id、上次运行结果、待办数) | 每次重写都会覆盖,与真实状态不同步 | `bot.json` / `localStorage` |
90
+
91
+ ---
92
+
93
+ ## 6. 文件分工(避免重复)
94
+
95
+ | 文件 | 管什么 | 谁改 | 注入时机 |
96
+ |---|---|---|---|
97
+ | `~/.dsh/AGENTS.md` | 跨工作区通用的协作授权与检索约定 | 用户 | 所有会话 |
98
+ | `D:\erpBot\<bot>\AGENTS.md` | **本 Bot 的身份、纪律、边界、技能优先检索范围** | 插件生成(身份只读 + 偏好可改 + 技能随绑定) | 该 Bot 每次对话 |
99
+ | `D:\erpBot\<bot>\bot.json` | 机器可读的技能名列表 + 更新时间 | 插件生成 | 界面回读 / 幂等写入 |
100
+ | `~/.dsh/skills/<name>/SKILL.md` | **怎么干一件事**的操作手册 | 技能作者 | 技能被调用时 |
101
+
102
+ 一句话:AGENTS.md 说「我是谁、我不许干什么、我优先会什么」,SKILL.md 说「这件事怎么干」,全局 AGENTS.md 说「在哪都能怎么配合」。
103
+
104
+ ---
105
+
106
+ ## 7. 模板骨架(`buildAgentsDoc()` 目标输出)
107
+
108
+ ```markdown
109
+ <!-- schema: erp.bot.agents.v3 -->
110
+
111
+ # erpBot · <名称>(<id>)
112
+
113
+ > <角色 zh> | <角色 en>
114
+
115
+ ## 1. 身份(Identity)
116
+ - 角色(Role):<一句话定位>
117
+ - 职责(Duties):<解释哪些事实>
118
+ - 语气(Tone):<怎么说话,只描述方式,不含禁令>
119
+ - 边界(Boundaries):<不能做什么,只列动作禁令>
120
+ - 不适用(Not applicable):<范围外科目>(越界请转介,不要硬答)
121
+
122
+ ## 2. 回答顺序(Scene prompt)
123
+ <遇到 X 类问题先给 A,再给 B>
124
+
125
+ ## 3. 数字可信(Digital trust — 硬要求)
126
+ - 口径:每个数字必须能说出计算定义与生效版本。
127
+ - 时点:至少报数据观测时点;跨期/跨口径时再分别说明业务发生时点与财务确认时点。
128
+ - 数据集:说明数字来自哪份只读数据集;不直连 ERP、不在前端重算业务数。
129
+ - 证据:结论必须能给出凭据/回执编号或明确写「无依据」,无依据即不下结论。
130
+
131
+ ## 4. 红线(Red lines)
132
+ **不做(结构上无通道)**:不直写 ERP、不代为审批/签批/申报、不勾选确认、不改口径、不覆盖历史版本。
133
+ **只能走通道**:反写仅经受控写通道并留回执;调账由待办发起;改口径走版本变更流程。
134
+ 违反以上任一条时:拒绝执行 + 说明该走哪条通道 + 指出谁能批。
135
+
136
+ ## 5. 不确定怎么办(Suspension discipline)
137
+ 判不准 / 版本指认失败 / 口径冲突 / 同一动作重复到达但载荷不同 → 一律挂起转人工裁定:
138
+ 写明挂起点、缺什么、谁能定;不猜测、不用默认值填空、不静默重试。
139
+
140
+ ## 6. 越界转介(Cross-boundary routing)
141
+ - <话题> → <Bot 名>(<id>)
142
+
143
+ ## 7. 已绑定 DSH 技能(Bound skills — 优先检索)
144
+ - `<skill>`
145
+ - `<skill>`
146
+ 规则:
147
+ 1. 回答前,先判断任务是否命中以上技能;命中则加载对应 skill 并按其 SKILL.md 执行。
148
+ 2. 未命中时,才回退到本会话全局 skill 目录(<available_skills>)检索。
149
+ 3. 不得自行发明技能名;加载技能不改变身份与口径。
150
+
151
+ ## 8. 用户偏好(Preferences)
152
+ - 回复语气:严谨 · 审计 | 简洁 · 结论先行 | 亲和 · 解释型
153
+ - 默认时间口径:<期间/按问题判断>
154
+
155
+ ## 9. 交付前自检(Self-check before delivery)
156
+ - [ ] 每个数字都说了口径与时点?
157
+ - [ ] 每个结论都有依据或明确写了「无依据」?
158
+ - [ ] 有没有越过红线、有没有替人批?
159
+ - [ ] 拿不准的是否已挂起而不是猜?
160
+ - [ ] 是否越界到别的 Bot 的领域?
161
+ ```
162
+
163
+ ---
164
+
165
+ ## 8. 校验与自举
166
+
167
+ 1. **一致性**:`scripts/check-bot-identity.mjs` 断言 Host(`bot-identity.js`)与客户端(`src/bot-data.js`)的身份字段逐字一致。
168
+ 2. **自举**:Host 挂载时 `scaffoldAllBots()` 为四个 Bot 补齐缺失文件,幂等;`scripts/scaffold-bots.mjs` 可手动触发。
169
+ 3. **迁移**:`AGENTS.md` 首行 schema 标记(`erp.bot.agents.v3`)不符或缺失即重写;旧 v2(8 段)自动升到 v3(9 段)。
170
+ 4. **验收**:以 §2 的「判定标准」逐条打勾;报「9 段中 X 段通过」,不写「已完成」。
171
+
172
+ ---
173
+
174
+ ## 9. Bot 视角评审结论(2026-11-13)
175
+
176
+ 从「Bot 每次对话读到这份文件会怎么理解」的角度审视,7 个可改进点(已处理状态见 §10):
177
+
178
+ | # | 结论 | 严重度 | 状态 |
179
+ |---|---|---|---|
180
+ | 1 | 「角色」是「职责」的口号化,无增量信息,纯占 token | 中 | 待定(角色用于页面头像副标) |
181
+ | 2 | 「语气」里混入了禁令(forecast「不替业务拍板」等),弱化约束力 | 高 | 待改 |
182
+ | 3 | `replyTone` 默认「严谨·审计」是 finance 的审计口吻,套给 funds/tax 与自身语气冲突 | 高 | 待改 |
183
+ | 4 | 数字可信「时点」硬性要求三时点区分,多数查询只需观测时点 | 中 | 待改 |
184
+ | 5 | 越界转介只有「指向谁」没有「他能回答什么」 | 中 | 待改 |
185
+ | 6 | 红线与边界重叠 | 低 | 已缓解 |
186
+ | 7 | 「不适用」(范围外)与「边界」(动作禁令)混在同一段 | 低 | 已缓解 |
187
+
188
+ **结论 2/3 是实质缺陷**:直接影响 Bot 行为。其余是颗粒度问题。
189
+
190
+ ---
191
+
192
+ ## 10. 待决项(影响后续实现)
193
+
194
+ 1. **评审结论 2/3/4/5 的落地**(语气去禁令、replyTone 默认空值、时点放宽、越界转介补能力说明)——需要改 `bot-data.js` / `bot-identity.js` 的身份数据。
195
+ 2. **页面呈现**:配置页当前渲染内存中的身份常量,而非文件实际内容。呈现决策见下一轮。
196
+
197
+ ---
198
+
199
+ ## 11. 变更记录
200
+
201
+ | 日期 | 变更 |
202
+ |---|---|
203
+ | 2026-11-12 | v1 初版,逆向整理为 9 段规格(含技能契约) |
204
+ | 2026-11-13 | v2:id 改名 cai/zi/tui/shui → finance/funds/forecast/tax;9 段→8 段(技能契约拆到 SKILLS.md);现状列全绿;新增 Bot 视角评审 |
205
+ | 2026-11-13 | v3:**技能契约合并回 AGENTS.md 第 7 段**(撤销 SKILLS.md 拆分),加入「优先检索 + 全局兜底」三层规则;8 段→9 段;schema v2→v3;明确「指令引导而非 scope 限制」的决策依据 |
@@ -0,0 +1,161 @@
1
+ # Bot 团队协作规格(Team Mode)
2
+
3
+ > 状态:v1(2026-11-13)— 团队模式的**方案设计 + UI 交互 + 技术映射**,未开始实现。
4
+ > 决策基线:方案 A(导航栏新增「团队模式」入口 = 启动一个默认开 Team 的 root 会话),四 Bot 作为子 agent 被委派。
5
+ > 原则:**引擎、任务板、委派、消息、下指令全部用 DSH 标准功能,erpbot 只做「入口」和「镜像展示」两层外壳。**
6
+
7
+ ---
8
+
9
+ ## 0. 一句话结论
10
+
11
+ 团队模式 = 用户点导航栏「团队模式」→ 开一个 `defaultEnabled: true` 的 root 会话 → root 把任务委派给四个财务 Bot(各自 agent preset)→ 任务板实时投影 → 用户随时 `send_message` 下指令。DSH 负责所有协作逻辑,erpbot 负责「入口按钮」和「只读镜像页」。
12
+
13
+ ---
14
+
15
+ ## 1. 技术映射(读 DSH 源码确认)
16
+
17
+ | 需求 | DSH 标准能力 | 依据 | erpbot 要做的事 |
18
+ |---|---|---|---|
19
+ | 导航栏加「团队模式」 | 插件 slot 注册 | erpbot 现有 `nav.js` rail | 加一个 `team` 导航项 |
20
+ | 进入即自动开团队 | `dsh-agent-team` 的 `defaultEnabled: true` | agent-team README §Use | 配置该行,或通过 Host 路由触发 |
21
+ | 团队内四个 Bot | `subagent` + `agentPreset` | tool-subagent README §child preset | 把 4 个身份做成 4 个 preset |
22
+ | 动态交互 + 结果 | `agentTeam` projection 实时投影 | agent-team README §browser projection | 镜像显示,只读 |
23
+ | 随时下指令 | `send_message`(定向任意成员) | tool-subagent-control | 页面上加「对某 Bot 下指令」输入 |
24
+
25
+ ### 1.1 关键机制清单
26
+
27
+ | 机制 | DSH 包 | 作用 |
28
+ |---|---|---|
29
+ | 团队引擎 | `dsh-agent-team` | 持久任务板 + CAS 认领 + 依赖 DAG |
30
+ | 任务工具 | `dsh-tool-agent-team` | `team_task_create/list/get/update` 四工具 |
31
+ | 委派 | `dsh-tool-subagent` | `subagent`/`subagent_fork`,用 `agentPreset` 指定角色 |
32
+ | 消息/发现 | `dsh-tool-subagent-control` | `send_message`/`list_agents`/`wait_agent`/`interrupt_agent` |
33
+ | 身份 | `dsh-agent-preset` | 每个 Bot 一个 preset,persona 承载身份四件套 |
34
+
35
+ ---
36
+
37
+ ## 2. 四 Bot 的 agent preset 映射
38
+
39
+ 复用 `bot-data.js` 已定义的身份四件套,映射成 4 个 preset。身份数据是唯一真相源,用 `check-bot-identity.mjs` 扩展做一致性校验。
40
+
41
+ | preset id | Bot | persona(来自 bot-data) | 域工具 |
42
+ |---|---|---|---|
43
+ | `erp-finance` | 账房 | 角色/职责/语气/边界/不适用/场景 | 凭证、账簿、关账、报表 |
44
+ | `erp-funds` | 司库 | 同上 | 账户、收付、对账、资金计划 |
45
+ | `erp-forecast` | 军师 | 同上 | 预算、预测、情景、差异归因 |
46
+ | `erp-tax` | 税官 | 同上 | 发票、勾选、申报、税负风险 |
47
+
48
+ > 注:AGENTS.md(单 Bot 对话页)与 agent preset(团队子 agent)**并存**,都源自 `bot-data.js`。单 Bot 页走 AGENTS.md 注入,团队子 agent 走 preset persona,两条身份链路用同一份数据,一致性脚本兜底。
49
+
50
+ ---
51
+
52
+ ## 3. UI 交互设计
53
+
54
+ ### 3.1 导航栏
55
+
56
+ ```
57
+ 左栏 rail 现有 6 项(总览 / 四 Bot / 底座)之下,新增一项:
58
+ ┌──────────────┐
59
+ │ 团队模式 👥 │ ← 新增
60
+ └──────────────┘
61
+ ```
62
+
63
+ 点击 = 启动一个团队 root 会话(`defaultEnabled: true`),不改变现有单 Bot 页。
64
+
65
+ ### 3.2 团队模式页布局
66
+
67
+ ```
68
+ ┌─────────────────────────────────────────────────────┐
69
+ │ 顶部:团队状态条(谁在线 / 任务进行中数 / 完成数) │
70
+ ├──────────────────────┬──────────────────────────────┤
71
+ │ 左栏:成员列表 │ 右栏:任务板(镜像 DSH 投影) │
72
+ │ ├ lead(调度者) │ ├ 月末关账 ⏳ 账房 │
73
+ │ ├ finance 账房 ● │ ├ 银行对账 ✅ 司库 │
74
+ │ ├ funds 司库 ● │ ├ 进项核票 ⏳ 税官 │
75
+ │ ├ forecast 军师 ● │ └ 资金缺口预测 ⬜ 未认领 │
76
+ │ └ tax 税官 ● │ │
77
+ ├──────────────────────┴──────────────────────────────┤
78
+ │ 底部:指令输入框 [对某 Bot 下指令] [发送] │
79
+ └─────────────────────────────────────────────────────┘
80
+ ```
81
+
82
+ ### 3.3 三大交互区
83
+
84
+ | 区 | 交互 | 数据源 | 可写性 |
85
+ |---|---|---|---|
86
+ | **成员列表** | 点成员 = 切到它的对话(复用现有切页) | `list_agents` 镜像 | 只读 |
87
+ | **任务板** | 看 subject/status/owner/依赖,点开看详情 | `agentTeam` projection 镜像 | 只读(agent 用工具改) |
88
+ | **指令输入** | 选目标 Bot + 输入 → `send_message` 定向 | 用户输入 | 写(触发 DSH 消息) |
89
+
90
+ ### 3.4 动态交互的体现
91
+
92
+ - 任务状态徽标:`就绪=灰 / 阻塞=橙 / 进行中=蓝 / 完成=绿`,随 projection 实时刷新。
93
+ - 成员状态点:在线/运行中/空闲,镜像 `chatStates` 现有机制。
94
+ - 用户下指令后,任务板出现对应 task,成员状态点变「运行中」,完成变绿——全程 DSH 驱动,erpbot 只重绘。
95
+
96
+ ---
97
+
98
+ ## 4. 一次完整协作的时序
99
+
100
+ ```mermaid
101
+ sequenceDiagram
102
+ participant U as 用户
103
+ participant P as 团队模式页(erpbot)
104
+ participant R as root 会话(DSH)
105
+ participant F as 账房子agent
106
+ participant T as 税官子agent
107
+
108
+ U->>P: 点导航「团队模式」
109
+ P->>R: 开 defaultEnabled root 会话
110
+ U->>P: 底部输入「做月末关账」
111
+ P->>R: send_message 给 lead
112
+ R->>R: 判断跨域 → 拆 2 任务(核账 / 核票)
113
+ R->>F: subagent 委派 核账(erp-finance preset)
114
+ R->>T: subagent 委派 核票(erp-tax preset)
115
+ F-->>R: completion notice
116
+ T-->>R: completion notice
117
+ R->>P: 任务板投影更新(全部完成)
118
+ P->>U: 镜像页实时显示 ✅
119
+ ```
120
+
121
+ ---
122
+
123
+ ## 5. 触发规则(root 的判断逻辑,写进 persona)
124
+
125
+ root 的调度 persona 里写三条规则:
126
+
127
+ 1. **单域直答**:任务只属于一个 Bot 的域,root 直接委派给该 Bot,不开多任务。
128
+ 2. **跨域并行**:多个 Bot 各干各的、互不依赖 → `team_task` 挂多个任务并行。
129
+ 3. **跨域串行**:后序依赖前序结果 → `team_task` + `blocked_by` 声明依赖链。
130
+
131
+ ---
132
+
133
+ ## 6. 实施顺序(骨架先行,方案 B 已定)
134
+
135
+ | 阶段 | 内容 | 依赖 |
136
+ |---|---|---|
137
+ | 1 | 4 个 agent preset(身份映射) | 无 |
138
+ | 2 | Host 挂 `defaultEnabled: true` 团队配置 | 阶段 1 |
139
+ | 3 | 导航栏「团队模式」入口(开 root 会话) | 阶段 2 |
140
+ | 4 | 团队模式页 UI(成员列表 + 镜像任务板 + 指令输入) | 阶段 2 |
141
+ | 5 | 接通真实数据(MCP → payload) | 阶段 1-4 完成后 |
142
+
143
+ > 阶段 1-4 用静态 payload 即可跑通流程骨架(方案 B 已定的「骨架先行」),阶段 5 让协作真正算对数。
144
+
145
+ ---
146
+
147
+ ## 7. 待验证的技术前提
148
+
149
+ | 前提 | 风险 | 验证方式 |
150
+ |---|---|---|
151
+ | erpbot 组件能读到 `agentTeam` projection | 中 | 试 `useProjection('agentTeam')` 或 slot props |
152
+ | 4 preset 能被 root 的 subagent 工具选中 | 低 | 读 `allowedAgentPresets` 配置 |
153
+ | `defaultEnabled` 对「导航点开的会话」生效 | 低 | 阶段 2 实测 |
154
+
155
+ ---
156
+
157
+ ## 8. 变更记录
158
+
159
+ | 日期 | 变更 |
160
+ |---|---|
161
+ | 2026-11-13 | v1 初版:方案 A 定稿,四 Bot preset 映射 + UI 交互 + 时序 + 实施顺序 |
@@ -0,0 +1,262 @@
1
+ # dsh-erpbot 数据契约
2
+
3
+ > 状态:v1(2026-11-12)— PAGES payload 与渲染器的权威字段契约。
4
+ > 用途:改数据、加页面、加渲染器时的唯一字段依据。
5
+ > 权威性:金额单位规则、i18n key 规范是硬约束;代码与本文冲突时以本文为准并修代码。
6
+
7
+ ---
8
+
9
+ ## 1. 总览结构
10
+
11
+ `PAGES` 是一个对象,键是页面 id。两种页面形态:
12
+
13
+ ```text
14
+ PAGES = {
15
+ overview: { kpis, core, sub }, // 概览页:卡片矩阵
16
+ cai: { title, kpis, sections }, // 工作台页:KPI + 段工作台
17
+ zi/tui/shui: { title, kpis, sections },
18
+ base: { title, kpis, sections }, // 底座单页:4 KPI + 8 清单项,无页内 tab
19
+ }
20
+ ```
21
+
22
+ 判定规则(代码 `shell/index.js` 的 `ErpBotPanel`):
23
+
24
+ - 有 `sections` → 走 `SectionShell`(四个 KPI + 段工作台)
25
+ - 有 `todo` → `TodoPage`
26
+ - 否则 → 概览式 `kpis` + `core` + `sub` 卡片布局
27
+
28
+ `base` 为单页(无 tab);`#/erp/shu` / `#/erp/guan` 是落到 `base` 的深链别名,不新增页面键、不产生第二个页面。
29
+
30
+ ---
31
+
32
+ ## 2. 概览页 `overview`
33
+
34
+ ### 2.1 `kpis`(4 张统计卡,简单形态)
35
+
36
+ > 概览页 KPI 用 `labelKey` / `subKey`(字典键),与工作台页的 `label` / `sub`(内联 {zh,en})**字段名不同**,勿混。
37
+
38
+ ```js
39
+ { labelKey: 'ov.todo', value: '12', subKey: 'ov.todoSub', trend: 'up', pct: '+3', spark: [3,4,5,6] }
40
+ ```
41
+
42
+ | 字段 | 类型 | 说明 |
43
+ |---|---|---|
44
+ | `labelKey` | string | 字典键(`t()` 解析) |
45
+ | `value` | string | 主数字 |
46
+ | `subKey` | string | 副标签字典键 |
47
+ | `trend` | `'up'` \| `'down'` | 涨跌方向 |
48
+ | `pct` | string | 环比文案 |
49
+ | `spark` | number[] | 迷你折线数据 |
50
+
51
+ ### 2.2 `core`(核心区卡片)
52
+
53
+ 两种 kind:`focus`(块轮播)、`trend`(趋势图)。
54
+
55
+ **focus**(账/资/推/税 四块轮播):
56
+
57
+ ```js
58
+ { kind:'focus', titleKey:'ov.blocks', items:[
59
+ { key:'nav.cai', value:'1,284', subKey:'focus.cai.sub', timeKey:'focus.time.past',
60
+ pct:67, page:'cai', icon:'doc', unitKey:'blocks.unit.vouchers', period:'2026-11',
61
+ chart:{ type:'bars'|'line'|'donut', data:[...], total?, totalLabelKey? },
62
+ cells:[{ k:'cai.diff', v:'0.00' }, ...] }
63
+ ] }
64
+ ```
65
+
66
+ **trend**(收支趋势):
67
+
68
+ ```js
69
+ { kind:'trend', titleKey:'ov.flow', periodKey:'chart.period',
70
+ legend:[{ key, color, value }], foot:[{ spanKey, value | valueKey }] }
71
+ ```
72
+
73
+ ### 2.3 `sub`(底部三卡)
74
+
75
+ 三种 kind:`donut`、`aging`、`timeline`,字段见 §5 对应渲染器(走 `CardView`)。
76
+
77
+ ---
78
+
79
+ ## 3. 工作台页 `{ title, kpis, sections }`
80
+
81
+ ```js
82
+ {
83
+ title: { zh:'财 · 过去', en:'Books · Past' },
84
+ kpis: [ /* 4 个 KPI,见 §4 */ ],
85
+ sections: [ /* 段:底座 8 项,其余工作台页 6 项,见 §5 */ ],
86
+ }
87
+ ```
88
+
89
+ ---
90
+
91
+ ## 4. KPI 对象(工作台页 `kpis[]`)
92
+
93
+ 每张卡渲染由 `kpi.kind` 决定(代码 `workbench/index.js` 的 `PageKpi`)。
94
+
95
+ ### 4.1 通用字段
96
+
97
+ | 字段 | 类型 | 必填 | 说明 |
98
+ |---|---|---|---|
99
+ | `kind` | 7 种变体之一 | 是 | 见 §4.2 |
100
+ | `label` | `{zh,en}` | 是 | 卡标题 |
101
+ | `value` | string | 是 | 主数字(金额用 `¥` 精确元) |
102
+ | `sub` | `{zh,en}` | 是 | 完整口径(弹窗显示) |
103
+ | `cardSub` | `{zh,en}` | 是 | 卡上一行短口径 |
104
+ | `trend` | `'up'` \| `'down'` | 否 | 环比方向 |
105
+ | `pct` | string | 否 | 环比文案 |
106
+ | `spark` | number[] | 否 | 迷你折线(≥2 点才显示) |
107
+ | `detail` | object | 是 | 弹窗明细,见 §4.3 |
108
+
109
+ ### 4.2 七种变体专有字段
110
+
111
+ | kind | 专有字段 | 卡上形态 |
112
+ |---|---|---|
113
+ | `value` | — | 大数字 + 迷你折线 + 环比 |
114
+ | `split` | `parts: [{value, pct}]` | 大数字 + 3 段占比条 |
115
+ | `stack` | `parts: [{value, pct}]` | 大数字 + 3 段构成条(无图例) |
116
+ | `gauge` | `ratio: number`(0–100)、`caption: {zh,en}` | 大数字 + 环形进度 |
117
+ | `countdown` | `progress: number`(0–100) | 大数字 + 进度条 + 截止日(口径行) |
118
+ | `status` | `state`、`badge: {zh,en}`、`lines: [{label,value}]` | 大数字 + 状态徽标 + 清单摘要 |
119
+ | `delta` | `note: {zh,en}` | 带符号差异 + 迷你折线,note 收进弹窗 |
120
+
121
+ ### 4.3 `detail` 对象(弹窗)
122
+
123
+ ```js
124
+ detail: {
125
+ formula: { zh, en }, // 统计口径,必须能算
126
+ breakdown: [ { label:{zh,en}, value:string, pct:number }, ... ], // 构成明细
127
+ updatedAt: '2026-11-11 09:12', // 更新时点
128
+ report: { page:'cai' }, // 可选:跳转的完整报表页(渲染器已支持,当前数据未启用)
129
+ }
130
+ ```
131
+
132
+ - `breakdown` 缺省时回退 `kpi.parts`(split/stack)→ `kpi.lines`(status)。
133
+ - `pct` 可显式给;缺省时按行金额自动推算(`metricDetailOf` 的 `rowAmount`)。
134
+
135
+ ---
136
+
137
+ ## 5. 段 `sections[]`
138
+
139
+ 每个段是一个「左栏条目 + 右侧主内容」单元。
140
+
141
+ ### 5.1 通用字段
142
+
143
+ | 字段 | 类型 | 必填 | 说明 |
144
+ |---|---|---|---|
145
+ | `icon` | string | 是 | 左栏图标(`ICONS` 里的名字) |
146
+ | `label` | `{zh,en}` | 是 | 左栏条目名 |
147
+ | `kind` | 12 种渲染器之一 | 是 | 见 §5.2 |
148
+ | `pill` | `{zh,en}` | 否 | 左栏数量徽标 |
149
+ | `summary` | `[{label, value, tone}]` | 否 | 右栏顶部 1–3 个摘要块 |
150
+ | `data` | 按 kind | 是 | 主内容数据 |
151
+ | `alt` | `{kind, label, data}` | 否 | 图表/清单双视图(见 §5.3) |
152
+ | `foot` | `{zh,en}` | 否 | 右栏底部口径落款 |
153
+
154
+ `summary` 项:`{ label:{zh,en}, value: string|{zh,en}, tone:'ok'|'warn'|'off' }`。
155
+
156
+ ### 5.2 十二种渲染器的 `data` 形状
157
+
158
+ | kind | `data` 形状 |
159
+ |---|---|
160
+ | `list` | `{ rows: [{ name:{zh,en}, desc:{zh,en}, badge:{zh,en}, tone, meta:{zh,en} }] }` |
161
+ | `table` | `{ columns:[{ label:{zh,en}, align? }], rows:[[cell...]], total?:[cell...] }`;cell 可为 `string` 或 `{zh,en}` |
162
+ | `flow` | `{ rows: [{ name:{zh,en}, desc:{zh,en}, badge:{zh,en}, dir:'in'\|'out', amount }] }` |
163
+ | `cards` | `{ items: [{ label:{zh,en}, value, sub:{zh,en}, tone }] }` |
164
+ | `steps` | `{ done:number, total:number, items: [{ name:{zh,en}, desc:{zh,en}, state:'done'\|'active'\|'todo'\|'off', meta:{zh,en} }] }` |
165
+ | `checks` | `{ items: [{ name:{zh,en}, desc:{zh,en}, state:'ok'\|'warn'\|'error', meta:{zh,en} }] }` |
166
+ | `bars` | `{ items: [{ label:{zh,en}, valueText:string, pct:number, tone }] }` |
167
+ | `chart` | `{ series:number[], labels?:[{zh,en}], caption?:{zh,en}, mode?:'bars'\|'line' }` |
168
+ | `donut` | `{ items: [{ label:{zh,en}, value, pct:number }], total }` |
169
+ | `timeline` | `{ items: [{ when:{zh,en}, title:{zh,en}, desc:{zh,en}, tone }] }` |
170
+ | `scenarios` | 复用 `table` 的 `data` 形状(渲染器 `PaneTable`,加 `scn` 类) |
171
+ | `empty` | 无 data;旧 `rows` 结构自动降级到此 |
172
+
173
+ ### 5.3 `alt` 双视图
174
+
175
+ `chart` / `donut` / `bars` 三类可带 `alt`,右栏标题右侧出现「图表 / 清单」切换:
176
+
177
+ ```js
178
+ alt: { kind:'list'|'table', label:{zh,en}, data:{ /* 对应 kind 的 data */ } }
179
+ ```
180
+
181
+ - 图表视图看趋势,清单视图看同一口径明细行,合计对平。
182
+ - 切段时自动回到图表视图。
183
+
184
+ ---
185
+
186
+ ## 6. 金额规则(硬约束)
187
+
188
+ **payload 只存精确元**:`¥` + 千分位 + 两位小数(如 `¥8,450,000.00`),**禁止** `万/亿/K/M/B` 进数据。
189
+
190
+ **显示层换算**(代码 `metric-format.js` 的 `moneyParts` / `compactMoney`):
191
+
192
+ | 量级 | 中文 | English |
193
+ |---|---|---|
194
+ | ≥ 1 亿 / ≥ 1B | `¥1.26亿` | `¥1.26B` |
195
+ | ≥ 1 万 / ≥ 1M | `¥845万`、`¥1,200万` | `¥8.45M` |
196
+ | ≥ 10K(仅 en) | — | `¥643.5K` |
197
+ | < 1 万 | 保留精确元 `¥8,640.00` | 同 |
198
+
199
+ 规则:
200
+
201
+ - 小数点后最多两位、去尾零;溢出自动进位(`99,999,999` → `1亿`)。
202
+ - 浮点单位只用于读数:图标 title、弹窗「精确金额」行、构成明细、`detail.formula` 仍给到分。
203
+ - 解析正则:`^([+\-]?)¥([\d,]+)(?:\.(\d{1,2}))?$`(`moneyAmount`)。
204
+ - 改单位规则只改 `metric-format.js`,别改数据。
205
+
206
+ ---
207
+
208
+ ## 7. i18n 规范
209
+
210
+ ### 7.1 两种取值方式
211
+
212
+ | 方式 | 用法 | 场景 |
213
+ |---|---|---|
214
+ | `t(key)` | 字典键,点分小写(`nav.erp`、`chart.axisK`) | 全局复用文案 |
215
+ | `L({zh,en})` | 内联双语对象 | 页面数据里的局部文案 |
216
+
217
+ ### 7.2 硬约束
218
+
219
+ - `zh` / `en` 字典 key 集合**必须成对一致**,缺一则漏译。
220
+ - 组件内**禁止**裸写中文或英文(README/文档注释除外)。
221
+ - 字典分层:`nav.*` `chart.*` `ai.*` `kpi.*` `metric.*` `page.*` `focus.*` `feed.*` `books.*` `reports.*` `ds.*` `exp.*` `tx.*` `stat.*` 等。
222
+ - 短口径(`cardSub`)、完整口径(`sub`)、标题(`label`)都走内联 `{zh,en}`。
223
+
224
+ ---
225
+
226
+ ## 8. 枚举汇总
227
+
228
+ | 枚举 | 取值 |
229
+ |---|---|
230
+ | KPI `kind` | `value` `split` `stack` `gauge` `countdown` `status` `delta` |
231
+ | 段 `kind` | `list` `table` `flow` `cards` `steps` `checks` `bars` `chart` `donut` `timeline` `scenarios` `empty` |
232
+ | `trend` | `up` `down` |
233
+ | `tone`(summary/badge/list/cards) | `ok` `warn` `off` |
234
+ | `state`(checks) | `ok` `warn` `error` |
235
+ | `state`(steps) | `done` `active` `todo` `off` |
236
+ | `dir`(flow) | `in` `out` |
237
+ | 图标名 | `ICONS` 字典的键(`grid` `wallet` `card` `swap` `chart` `doc` `users` `list` `pie` `search` `pulse` `bell` `up-right` `right` `left` `chev` `more` `plus` `close` `trend-up` `trend-down` `spark` `settings`) |
238
+
239
+ ---
240
+
241
+ ## 9. 加一页 / 加一渲染器的改动清单
242
+
243
+ **加一页**(三处,代码见 `src/nav.js`、`src/i18n/index.js`、`src/pages-data.js`):
244
+
245
+ 1. `NAV` 加一条 `{ id, icon, key }`;
246
+ 2. `ZH` + `EN` 各加一条 `nav.<id>`;
247
+ 3. `PAGES` 加同 id 的配置(有 `sections` 自动走工作台)。
248
+
249
+ 页面 id 必须全小写字母(`readHashPage` 用 `#/erp/([a-z]+)` 解析)。
250
+
251
+ **加一渲染器**(两处):
252
+
253
+ 1. `src/workbench/index.js` 的 `renderPane` 加一个 `case`;
254
+ 2. 本文 §5.2 补一行 `data` 形状契约。
255
+
256
+ ---
257
+
258
+ ## 10. 契约变更记录
259
+
260
+ | 日期 | 变更 |
261
+ |---|---|
262
+ | 2026-11-12 | 初版,从 `pages-data.js` 逆向整理 |