xiaoyuan-assistant 0.5.45

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,1214 @@
1
+ # 小园智能助手完整业务流程
2
+
3
+ > 文档用途:记录“小园”从用户输入到页面操作、数据读取、数据分析、语音播报的完整业务链路,以及每个版本的重要设计变更。
4
+ >
5
+ > **维护规则:后续每次升级 SDK,都必须把本次版本的新增/修改内容追加到本文档最下方,不删除历史记录。**
6
+
7
+ ---
8
+
9
+ ## 一、项目定位
10
+
11
+ “小园”是一个嵌入 Vue3 + Vite 大屏项目的智能助手。它的核心目标不是替代大屏原有业务代码,而是在原有业务之上增加一层自然语言控制能力:
12
+
13
+ ```text
14
+ 用户说人话
15
+
16
+ AI理解用户意图
17
+
18
+ 映射到当前大屏真实 Function / DataSource
19
+
20
+ 按用户表达顺序执行
21
+
22
+ 需要时读取最新页面数据
23
+
24
+ 需要分析时再次调用 AI 进行深度分析
25
+
26
+ 字幕 + 语音播报结果
27
+ ```
28
+
29
+ 设计原则:
30
+
31
+ 1. 业务大屏尽量不重构。
32
+ 2. 页面操作使用 `data-ai-function` 声明能力。
33
+ 3. 页面具体实例使用 `data-ai-param` 标记目标参数。
34
+ 4. `data-ai-description` 描述这个能力是什么,帮助 AI 理解何时应该调用。
35
+ 5. 用户不需要知道 Function 名称,只需要说自然语言。
36
+ 6. AI 负责“听懂人话并选择 Function/参数”,SDK 负责“执行”。
37
+ 7. 多步操作严格按顺序执行,前一步完成后才执行下一步。
38
+ 8. 如果下一步页面 DOM 尚未加载,SDK 等待目标 DOM 出现后再执行。
39
+ 9. 普通页面操作优先追求速度,不做深度推理。
40
+ 10. 只有明确的数据分析、研判、趋势判断等任务才进入深度分析流程。
41
+ 11. 小园不会向最终用户展示 Function、Tool、DataSource 等内部技术细节。
42
+
43
+ ---
44
+
45
+ ## 二、宿主项目接入
46
+
47
+ ### 2.1 main.js
48
+
49
+ 宿主项目只需要安装插件并配置 AI 模型信息:
50
+
51
+ ```js
52
+ import { createApp } from 'vue'
53
+ import App from './App.vue'
54
+ import router from './router'
55
+ import Xiaoyuan from '@example/xiaoyuan-assistant'
56
+ import '@example/xiaoyuan-assistant/style.css'
57
+
58
+ const app = createApp(App)
59
+
60
+ app.use(router)
61
+
62
+ app.use(Xiaoyuan, {
63
+ model: 'THUDM/GLM-Z1-9B-0414',
64
+ aiUrl: 'https://api.siliconflow.cn/v1/chat/completions',
65
+ apiKey: '你的 API Key',
66
+ wakeWord: '你好小园',
67
+ enableWakeWord: true,
68
+ enableTTS: true
69
+ })
70
+
71
+ app.mount('#app')
72
+ ```
73
+
74
+ 对外重点配置:
75
+
76
+ - `model`:模型名称。
77
+ - `aiUrl`:模型请求地址。
78
+ - `apiKey`:模型调用密钥。
79
+
80
+ 执行参数、thinking 策略、超时、队列等尽量由 SDK 内部统一管理。
81
+
82
+ ### 2.2 App.vue
83
+
84
+ 小园组件挂在 `App.vue` 顶层,因此路由切换后小园仍然存在:
85
+
86
+ ```vue
87
+ <template>
88
+ <router-view />
89
+ <XiaoyuanAssistant />
90
+ </template>
91
+ ```
92
+
93
+ ---
94
+
95
+ ## 三、页面操作能力协议
96
+
97
+ ### 3.1 最小 DOM 协议
98
+
99
+ 页面只需要在可操作 DOM 上增加三个属性:
100
+
101
+ ```vue
102
+ <div
103
+ data-ai-function="changeYear"
104
+ :data-ai-param="year"
105
+ data-ai-description="切换当前大屏时间年份"
106
+ @click="changeYear(year)"
107
+ >
108
+ {{ year }}
109
+ </div>
110
+ ```
111
+
112
+ 含义:
113
+
114
+ ```text
115
+ data-ai-function
116
+
117
+ AI 要调用哪个能力
118
+
119
+ data-ai-description
120
+
121
+ 告诉 AI 这个能力是什么、什么时候调用
122
+
123
+ data-ai-param
124
+
125
+ 告诉 SDK 当前 Function 对应的具体实例
126
+ ```
127
+
128
+ ### 3.2 v-for 场景
129
+
130
+ 例如年份:
131
+
132
+ ```vue
133
+ <div
134
+ v-for="year in yearList"
135
+ :key="year"
136
+ data-ai-function="changeYear"
137
+ :data-ai-param="year"
138
+ data-ai-description="切换当前大屏时间年份"
139
+ @click="changeYear(year)"
140
+ >
141
+ {{ year }}
142
+ </div>
143
+ ```
144
+
145
+ 渲染后的页面相当于:
146
+
147
+ ```html
148
+ <div
149
+ data-ai-function="changeYear"
150
+ data-ai-param="2023"
151
+ data-ai-description="切换当前大屏时间年份"
152
+ >
153
+ 2023
154
+ </div>
155
+ ```
156
+
157
+ 用户说:
158
+
159
+ > “帮我切换到2023年。”
160
+
161
+ AI 应理解为:
162
+
163
+ ```json
164
+ {
165
+ "function": "changeYear",
166
+ "params": {
167
+ "value": "2023"
168
+ }
169
+ }
170
+ ```
171
+
172
+ SDK 根据 `data-ai-function + data-ai-param` 精确定位,不依赖页面可见文字,也不需要遍历所有 DOM 猜测。
173
+
174
+ ### 3.3 菜单场景
175
+
176
+ ```vue
177
+ <div
178
+ v-for="item in menuList"
179
+ :key="item.id"
180
+ data-ai-function="handleMenuClick"
181
+ :data-ai-param="item.name"
182
+ data-ai-description="切换大屏菜单到指定业务模块"
183
+ @click="handleMenuClick(item)"
184
+ >
185
+ {{ item.name }}
186
+ </div>
187
+ ```
188
+
189
+ 用户说:
190
+
191
+ > “切换到集成监管。”
192
+
193
+ AI 负责理解:
194
+
195
+ ```text
196
+ Function = handleMenuClick
197
+ 参数 = 集成监管
198
+ ```
199
+
200
+ SDK 再定位:
201
+
202
+ ```css
203
+ [data-ai-function="handleMenuClick"][data-ai-param="集成监管"]
204
+ ```
205
+
206
+ 最后执行原有 DOM click,从而继续走业务已有的 `@click` 逻辑。
207
+
208
+ ---
209
+
210
+ ## 四、自然语言理解与 Planner
211
+
212
+ 小园不是要求用户输入 Function 名称,而是:
213
+
214
+ ```text
215
+ 用户自然语言
216
+
217
+ Planner AI
218
+
219
+ 理解用户意图
220
+
221
+ 查当前大屏能力描述
222
+
223
+ 找到最匹配的 Function
224
+
225
+ 提取参数
226
+ ```
227
+
228
+ 例如用户说:
229
+
230
+ > “帮我切换到集成监管并且切换到2023年份。”
231
+
232
+ Planner 应返回类似:
233
+
234
+ ```json
235
+ {
236
+ "type": "workflow",
237
+ "steps": [
238
+ {
239
+ "type": "action",
240
+ "function": "handleMenuClick",
241
+ "params": {
242
+ "value": "集成监管"
243
+ }
244
+ },
245
+ {
246
+ "type": "action",
247
+ "function": "changeYear",
248
+ "params": {
249
+ "value": "2023"
250
+ }
251
+ }
252
+ ]
253
+ }
254
+ ```
255
+
256
+ 用户的一句话包含多个连续动作时,视为多个独立指令;每个动作都必须拆成独立 Step,并保持原始顺序。
257
+
258
+ ---
259
+
260
+ ## 五、多步指令执行流程
261
+
262
+ 这是“小园”最核心的执行流程:
263
+
264
+ ```text
265
+ 用户一句话
266
+
267
+ Planner AI 一次性拆分任务
268
+
269
+ Step 1
270
+
271
+ 立即执行 Step 1
272
+
273
+ Step 1 完成
274
+
275
+ 播报 Step 1 完成内容
276
+
277
+ 取出 Step 2
278
+
279
+ 等待 Step 2 对应 DOM / 页面能力出现
280
+
281
+ 执行 Step 2
282
+
283
+ 播报 Step 2 完成内容
284
+
285
+ ……
286
+
287
+ 全部完成
288
+ ```
289
+
290
+ ### 5.1 页面切换后的 DOM 等待
291
+
292
+ 例如:
293
+
294
+ ```text
295
+ ① 切换到“集成监管”
296
+
297
+ 旧页面销毁
298
+
299
+ 新页面开始渲染
300
+
301
+ [data-ai-function="changeYear"] 暂时不存在
302
+
303
+ SDK 等待
304
+
305
+ DOM 出现
306
+
307
+ 定位 data-ai-param="2023"
308
+
309
+ 执行 click()
310
+ ```
311
+
312
+ 这里等待的是**下一步目标 DOM 的出现**,而不是重新扫描整个页面去猜测意图。
313
+
314
+ ---
315
+
316
+ ## 六、执行播报规则
317
+
318
+ ### 6.1 收到指令
319
+
320
+ 收到用户有效指令后,先播报:
321
+
322
+ > 收到指令,请您稍等。
323
+
324
+ 不重复播报用户刚才说的问题。
325
+
326
+ ### 6.2 多步指令
327
+
328
+ 例如:
329
+
330
+ > “切换到物联监测,并切换到2023年。”
331
+
332
+ 流程:
333
+
334
+ ```text
335
+ 收到指令,请您稍等。
336
+
337
+ 正在切换到物联监测。
338
+
339
+ 已切换到物联监测页面。
340
+
341
+ 接下来切换时间年份为2023。
342
+
343
+ 已切换时间年份为2023。
344
+ ```
345
+
346
+ 最终不使用“已完成相关操作”这种无业务含义的泛化播报。
347
+
348
+ ### 6.3 单步指令
349
+
350
+ 例如:
351
+
352
+ > “切换到物联监测。”
353
+
354
+ 播报:
355
+
356
+ ```text
357
+ 收到指令,请您稍等。
358
+ 正在切换到物联监测。
359
+ 已切换到物联监测页面。
360
+ ```
361
+
362
+ ### 6.4 分析任务
363
+
364
+ 进入数据分析前:
365
+
366
+ > “前面的页面操作已完成,接下来开始分析相关数据。”
367
+
368
+ 分析完成后只播报最终分析结果,不暴露 Function、DataSource、Tool 等内部细节。
369
+
370
+ ---
371
+
372
+ ## 七、数据注册流程
373
+
374
+ 页面业务数据不通过 DOM 获取,而是通过 DataSource 注册给小园。
375
+
376
+ 例如土壤墒情:
377
+
378
+ ```js
379
+ xiaoyuan.registerData({
380
+ name: 'soilData',
381
+ description: '当前页面墒情监测数据,用于分析土壤水分、温度和养分情况',
382
+ schema: {
383
+ depth: {
384
+ type: 'number',
385
+ description: '土壤深度,单位cm'
386
+ },
387
+ temperature: {
388
+ type: 'number',
389
+ description: '土壤温度,单位℃'
390
+ },
391
+ humidity: {
392
+ type: 'number',
393
+ description: '土壤湿度,单位%'
394
+ },
395
+ nitrogen: {
396
+ type: 'number',
397
+ description: '土壤氮含量,单位ppm'
398
+ },
399
+ phosphorus: {
400
+ type: 'number',
401
+ description: '土壤磷含量,单位ppm'
402
+ },
403
+ potassium: {
404
+ type: 'number',
405
+ description: '土壤钾含量,单位ppm'
406
+ },
407
+ salinity: {
408
+ type: 'number',
409
+ description: '土壤盐分,单位mg/L'
410
+ }
411
+ },
412
+ get: () => tableData.value
413
+ })
414
+ ```
415
+
416
+ `get()` 每次读取实时值,所以接口刷新后的 `tableData.value` 可以直接被分析流程使用。
417
+
418
+ ---
419
+
420
+ ## 八、分析任务流程
421
+
422
+ 用户:
423
+
424
+ > “切换到集成监管,并且切换2023年数据,然后分析一下土壤数据信息。”
425
+
426
+ Planner 应识别成:
427
+
428
+ ```text
429
+ Step 1:切换菜单
430
+ Step 2:切换年份
431
+ Step 3:分析土壤数据
432
+ ```
433
+
434
+ 执行顺序:
435
+
436
+ ```text
437
+ Step 1
438
+
439
+ 等待完成
440
+
441
+ Step 2
442
+
443
+ 等待完成
444
+
445
+ Step 3 开始
446
+ ```
447
+
448
+ **Step 3 绝不能提前读取土壤数据。**
449
+
450
+ 进入分析步骤后才:
451
+
452
+ ```text
453
+ 获取当前最新 soilData
454
+
455
+ 整理实际数据
456
+
457
+ Analyzer AI
458
+
459
+ 深度思考
460
+
461
+ 输出分析结果
462
+ ```
463
+
464
+ 因此分析到的是:
465
+
466
+ > **页面真正切换完成后的当前年份、当前页面数据。**
467
+
468
+ ---
469
+
470
+ ## 九、Planner 与 Analyzer 分工
471
+
472
+ ### 9.1 Planner
473
+
474
+ 职责只有:
475
+
476
+ ```text
477
+ 人话
478
+
479
+ Function
480
+
481
+ 参数
482
+
483
+ 执行步骤
484
+
485
+ DataSource 请求意图
486
+ ```
487
+
488
+ 执行类请求不开启深度思考,以性能优先。
489
+
490
+ ### 9.2 Analyzer
491
+
492
+ 只有出现明确分析需求才调用第二次 AI,例如:
493
+
494
+ - 分析土壤情况。
495
+ - 分析玉米长势。
496
+ - 根据气象和墒情判断风险。
497
+ - 对多数据源进行综合判断。
498
+ - 生成趋势、风险、建议。
499
+
500
+ Analyzer 使用更高的思考能力,并允许更长输出。
501
+
502
+ ---
503
+
504
+ ## 十、性能设计原则
505
+
506
+ ### 10.1 AI 请求次数
507
+
508
+ 普通操作:
509
+
510
+ ```text
511
+ 一次 Planner
512
+
513
+ 执行
514
+ ```
515
+
516
+ 普通多步操作:
517
+
518
+ ```text
519
+ 一次 Planner
520
+
521
+ 多个本地 Step 串行执行
522
+ ```
523
+
524
+ 不要因为有多个动作而为每个动作重新请求 AI。
525
+
526
+ 分析操作:
527
+
528
+ ```text
529
+ 一次 Planner
530
+
531
+ 执行所有前置动作
532
+
533
+ 获取最新数据
534
+
535
+ 一次 Analyzer
536
+ ```
537
+
538
+ ### 10.2 DOM 查询
539
+
540
+ 优先使用:
541
+
542
+ ```css
543
+ [data-ai-function="函数名"][data-ai-param="参数值"]
544
+ ```
545
+
546
+ 不要每次遍历整个页面所有 DOM、读取大量 `innerText` 再猜测目标。
547
+
548
+ ### 10.3 上下文体积
549
+
550
+ Planner 不需要完整发送所有数据 schema;只需要能力名称、描述、必要参数等轻量信息。
551
+
552
+ Analyzer 只发送本次分析需要的 DataSource 和真实数据。
553
+
554
+ ### 10.4 TTS
555
+
556
+ 语音播报使用队列,但不能阻塞业务执行。播报和页面操作解耦。
557
+
558
+ ---
559
+
560
+ ## 十一、语音唤醒流程
561
+
562
+ 支持:
563
+
564
+ ```text
565
+ “你好小园”
566
+ ```
567
+
568
+ 唤醒后:
569
+
570
+ ```text
571
+ 收起的小园图标
572
+
573
+ 展开
574
+
575
+ 进入待命状态
576
+
577
+ 等待语音指令
578
+ ```
579
+
580
+ 同时支持手动输入文本。
581
+
582
+ 完整链路:
583
+
584
+ ```text
585
+ 用户唤醒
586
+
587
+ 语音识别
588
+
589
+ 得到自然语言指令
590
+
591
+ Planner
592
+
593
+ Action Queue / Analysis
594
+
595
+ 字幕 + 语音
596
+ ```
597
+
598
+ ---
599
+
600
+ ## 十二、错误处理原则
601
+
602
+ 1. Planner 超时:明确提示用户当前指令暂时没有解析成功,不进入无限等待。
603
+ 2. Function 不存在:停止当前步骤并提示具体业务目标未找到。
604
+ 3. 目标 DOM 尚未出现:等待动态 DOM;超过上限后失败并结束当前任务。
605
+ 4. 页面切换失败:停止后续依赖当前页面的步骤,避免继续错误执行。
606
+ 5. 数据读取失败:分析步骤不允许使用旧数据冒充新数据,应明确提示数据读取失败。
607
+ 6. Analyzer 失败:保留前面的页面操作结果,并单独说明分析没有完成。
608
+ 7. TTS 失败:不影响页面操作和字幕结果。
609
+
610
+ ---
611
+
612
+ ## 十三、典型完整业务案例
613
+
614
+ ### 案例 A:只切换菜单
615
+
616
+ 用户:
617
+
618
+ > 你好小园,切换到物联监测。
619
+
620
+ 流程:
621
+
622
+ ```text
623
+ 语音识别
624
+
625
+ Planner
626
+
627
+ handleMenuClick + 物联监测
628
+
629
+ 找到目标 DOM
630
+
631
+ click
632
+
633
+ 完成
634
+
635
+ 播报“已切换到物联监测页面。”
636
+ ```
637
+
638
+ ### 案例 B:菜单 + 年份
639
+
640
+ 用户:
641
+
642
+ > 你好小园,切换到集成监管,并切换到2023年。
643
+
644
+ 流程:
645
+
646
+ ```text
647
+ Planner
648
+
649
+ Step1 handleMenuClick(集成监管)
650
+ Step2 changeYear(2023)
651
+
652
+ 立即 Step1
653
+
654
+ 等待页面变化
655
+
656
+ 等待 Step2 的 DOM
657
+
658
+ click Step2
659
+
660
+ 分别播报每一步完成结果
661
+ ```
662
+
663
+ ### 案例 C:菜单 + 年份 + 分析
664
+
665
+ 用户:
666
+
667
+ > 你好小园,切换到集成监管,并切换2023年数据,然后分析一下土壤数据信息。
668
+
669
+ 流程:
670
+
671
+ ```text
672
+ Planner
673
+
674
+ Step1 handleMenuClick(集成监管)
675
+ Step2 changeYear(2023)
676
+ Step3 analysis(soilData)
677
+
678
+ 执行 Step1
679
+
680
+ 等待页面
681
+
682
+ 执行 Step2
683
+
684
+ 等待完成
685
+
686
+ 获取最新 soilData
687
+
688
+ Analyzer AI
689
+
690
+ 输出分析结果
691
+
692
+ 字幕 + TTS
693
+ ```
694
+
695
+ ---
696
+
697
+ ## 十四、版本更新记录
698
+
699
+ > 后续每次更新必须继续追加在本节最下方,不修改历史条目。
700
+
701
+ ### v0.4.7
702
+
703
+ - 建立 Planner → Executor → Analyzer 三阶段结构。
704
+ - 普通页面操作只进行一次 Planner AI 请求,不做深度思考。
705
+ - 多步任务由 SDK 本地队列串行执行。
706
+ - 分析任务等前置页面操作全部完成后,再读取最新数据并调用第二次 Analyzer AI。
707
+ - `data-ai-function + data-ai-param + data-ai-description` 固化为页面操作协议。
708
+ - DOM 能力按 Function 聚合,减少发送给模型的上下文体积。
709
+ - Planner 阶段不发送完整 DataSource schema。
710
+ - Analyzer 阶段只发送实际使用的数据源和 schema。
711
+ - TTS 改为非阻塞队列。
712
+ - Planner、分析、DOM 等待均增加超时保护,避免长时间假死。
713
+ - 模型、请求地址、API Key 由宿主项目显式传入。
714
+
715
+
716
+ ### v0.4.8 性能优化追加
717
+ - Planner 只负责“听懂人话 → Function/参数/分析步骤”,执行指令关闭思考。
718
+ - 纯页面操作不携带完整数据源上下文;只有包含分析语义时才向 Planner 提供 DataSource 名称和描述。
719
+ - Planner 使用精简 JSON 输出,减少请求输入和输出长度。
720
+ - Analyzer 继续独立调用并开启推理,确保分析质量不被性能优化影响。
721
+
722
+ ---
723
+
724
+ ## v0.4.9 性能优化记录(2026-09-14)
725
+
726
+ 1. Planner 改为流式输出步骤:模型一旦输出完整的单步 JSON,SDK 立即将该步骤加入串行执行队列,不再等待整个规划响应结束。
727
+ 2. 执行步骤与 Planner 请求并行推进:第一个页面操作拿到后立即执行,后续步骤继续等待模型生成并自动排队。
728
+ 3. Planner 使用极简 NDJSON 协议,每行一个 step,减少解析与等待开销。
729
+ 4. 页面能力清单增加短期缓存,减少每次指令的重复 DOM 扫描。
730
+ 5. Planner 不再默认携带 DataSource 全量 schema;只有检测到分析意图时才携带数据源描述。
731
+ 6. 执行阶段保持 `data-ai-function + data-ai-param` 精确定位,DOM 未出现时使用 MutationObserver + 低频轮询等待。
732
+ 7. 分析仍单独调用 AI,并开启推理;分析请求的 thinking budget 下调为 4096,减少不必要的长思考。
733
+ 8. 新增流式 Planner 失败后的结构化结果兜底解析,不再因为单步提前到达而重复发起第二次 Planner 请求。
734
+ 9. 目标:让“切换菜单 / 年份 / 图层”等操作尽快开始执行,把等待集中在必要的模型解析或真实页面渲染阶段。
735
+
736
+
737
+ ### v0.5.0 性能与稳定性优化(2026-09-14)
738
+ - AI 请求不再设置 SDK 级超时,避免模型响应较慢时被前端强制中断。
739
+ - 不设置整体任务时间上限,连续任务按队列自然完成;仅 DOM 等待保留有限时长,防止页面能力永久不存在时无限等待。
740
+ - Planner 提示词进一步精简,只保留必须的 Function/DataSource 选择与顺序拆分规则。
741
+ - 执行阶段保持关闭 thinking;仅分析阶段开启推理。
742
+ - 用户体验目标:先尽快拿到规划并执行,后续步骤排队;需要分析时再进入独立分析请求。
743
+
744
+
745
+ ## v0.5.1 - 2026-09-14
746
+ - 调整为“先路由,再执行/分析”的核心流程:第一次 AI 只负责理解人话、判断 action / analysis / chat,并提取 Function 与参数。
747
+ - 纯页面操作只进行一次轻量 AI 请求,解析完成后直接由 JavaScript/DOM 执行,不再二次调用 AI。
748
+ - 数据分析任务仍分两阶段:先执行全部前置页面操作,再读取最新 DataSource,最后调用第二次 AI 深度分析。
749
+ - 进一步精简 Planner 提示词与输出长度,降低指令解析延迟。
750
+ - 明确 `data-ai-description` 用于语义理解、`data-ai-function` 用于能力标识、`data-ai-param` 用于具体目标实例。
751
+ - 保持多步任务按用户原始顺序串行执行,后续步骤等待当前页面 DOM 准备完成后再执行。
752
+
753
+ ## v0.5.2 性能优化与本地指令执行
754
+
755
+ 本版本进一步将“页面操作”与“AI分析”解耦:
756
+
757
+ 1. 用户输入后,SDK 先本地判断是否存在分析意图。
758
+ 2. 如果是纯页面操作:使用 JS 本地解析用户人话,依据 `data-ai-description` 理解功能,依据 `data-ai-param` 确定目标参数,直接生成 action 队列;不请求 AI。
759
+ 3. 多个页面操作按用户原顺序逐个执行;当前步骤完成后才执行下一步。若页面切换导致下一步 DOM 暂时不存在,则等待对应 `data-ai-function + data-ai-param` 出现后继续。
760
+ 4. 如果本地解析无法高置信判断,或者用户包含分析、研判、预测等需求,再调用 AI Planner。
761
+ 5. 数据分析仍由 Analyzer AI 处理,只有真正需要分析时才消耗 AI 推理能力。
762
+
763
+ ### 推荐页面标记
764
+
765
+ ```html
766
+ <div
767
+ data-ai-function="changeYear"
768
+ data-ai-param="2023"
769
+ data-ai-description="切换当前大屏时间年份"
770
+ >
771
+ 2023
772
+ </div>
773
+ ```
774
+
775
+ SDK 会优先本地识别“切换到2023年”这类人话并直接执行,不再等待模型返回 Function。
776
+
777
+
778
+ ## v0.5.3
779
+
780
+ - 调整播报策略:收到指令后仅播报“收到指令,请您稍等”,执行过程中不再语音播报“正在切换/接下来/正在分析”等中间过程。
781
+ - 每个操作完成后直接播报具体完成结果,例如“已切换到物联监测页面”。
782
+ - 分析开始阶段不再进行中途语音播报,分析完成后直接播报最终分析结果。
783
+ - 字幕仍保留等待状态,但不展示内部 Function / 方法名。
784
+
785
+ ## v0.5.4 业务流程补充
786
+
787
+ ### 典型工作流
788
+ “切换到集成监管,并且切换2023年数据,然后在分析一下土壤数据信息”
789
+
790
+ ### 本地拆分
791
+ 1. action:切换菜单,目标“集成监管”。
792
+ 2. action:切换年份,目标“2023”。
793
+ 3. analysis:分析土壤数据,优先匹配土壤/墒情相关 DataSource。
794
+
795
+ ### 执行规则
796
+ - 第1步立即由 JS 执行。
797
+ - 第1步完成后执行第2步;如果对应 DOM 尚未渲染,等待 `data-ai-function + data-ai-param` 出现。
798
+ - 第2步完成后再读取当前页面的土壤数据。
799
+ - 第3步使用最新土壤数据调用 Analyzer AI。
800
+ - 分析步骤不播报中间“已分析”提示,只播报最终分析结果。
801
+
802
+
803
+ ## v0.5.5 业务流程变更
804
+
805
+ ### 本地优先的执行原则
806
+
807
+ 用户输入后,先由本地 JS 按连接词拆分为独立步骤。对于页面操作,不再调用 Planner AI;程序直接根据 `data-ai-function`、`data-ai-param` 和 `data-ai-description` 选择对应能力并等待当前 DOM 出现后执行。
808
+
809
+ ### 混合指令
810
+
811
+ 例如:`切换到集成监管,并且切换2023年数据,然后在分析一下土壤数据信息` 会被本地拆为:
812
+
813
+ 1. `handleMenuClick` → `集成监管`
814
+ 2. `changeYear` → `2023`
815
+ 3. `analysis` → `soilData`
816
+
817
+ 前两步完全由 JS 执行;第三步等前两步完成后读取最新数据,再调用 Analyzer AI。
818
+
819
+ ### AI 回退
820
+
821
+ 只有本地无法明确判断某个非分析操作时,才回退到 Planner AI;避免普通按钮点击、菜单切换、年份切换等简单操作产生额外模型延迟。
822
+
823
+
824
+ ## v0.5.6 更新
825
+
826
+ ### 每一步的就绪重试
827
+ - 多步任务中的每一个 action 或 analysis step 都独立做资源就绪检查。
828
+ - DOM/页面操作:首次立即查找;找不到后等待 5 秒再重试,最多额外重试 2 次。
829
+ - 数据分析:数据源首次读取失败或为空时,等待 5 秒重试,最多额外重试 2 次。
830
+ - 两轮重试仍未拿到数据时,不让整个任务卡死,继续进入后续流程,由后续步骤决定是否还能完成。
831
+
832
+ ### 分析开始播报
833
+ - 当多步指令进入 AI 分析步骤时,执行前播报“正在分析***,请稍等。”。
834
+ - 分析过程中不再播报内部方法、数据源或中间执行细节。
835
+
836
+
837
+ ## v0.5.7 业务流程更新
838
+
839
+ ### 多步任务节奏
840
+ - 一个用户指令被本地 JS 拆分成多个步骤后,第一步立即开始。
841
+ - 第一步完成后,下一步不会立即执行,而是固定等待 5 秒。
842
+ - 每个后续步骤执行前,都遵循同样的 5 秒间隔。
843
+ - 5 秒间隔用于等待前一步触发的路由切换、Vue DOM 渲染、接口请求和页面状态更新。
844
+
845
+ ### 步骤就绪重试
846
+ - DOM 步骤首次立即查找;未找到时等待 3 秒,再重试一次;仍未找到再等待 3 秒进行第二次重试。
847
+ - 数据分析步骤的数据源首次立即读取;数据为空/失败时按照同样的 3 秒 + 3 秒节奏重试。
848
+ - 两次重试仍无数据或 DOM 时,当前步骤按失败/缺失结果结束,队列不会因为无限等待而卡死。
849
+
850
+
851
+ ## v0.5.8 - 多步骤页面加载与执行规则
852
+
853
+ - 多步骤之间固定保留 5 秒缓冲,给路由、接口和组件渲染留出时间。
854
+ - 每一步先立即查找目标 DOM;如果未出现,每 3 秒重试一次,最多 2 次,避免第一个页面操作后第二步永远卡住。
855
+ - 不再让单步 DOM 等待本身先阻塞 5 秒。
856
+ - 对年份等明确语义,在前一页面还未销毁/后一页面尚未挂载时,也可以先生成 `changeYear` + 年份参数,真正执行时等待目标 DOM。
857
+ - 小园的播报回调不再阻塞命令队列,语音播放不会卡住后续页面操作。
858
+
859
+ ## v0.5.9 业务流程更新
860
+
861
+ ### 连续无连接词自然语言拆分
862
+ - 本地 JS 不再只依赖“然后、并且、再”等连接词拆分。
863
+ - 会读取当前页面已经声明的 `data-ai-function` 和 `data-ai-param`,把参数值作为“能力锚点”。
864
+ - 例如:`切换到物联监测切换到集成监管时间切换到2024` 会拆为:
865
+ 1. 切换到物联监测
866
+ 2. 切换到集成监管
867
+ 3. 时间切换到2024
868
+ - 如果后一页面 DOM 尚未加载,步骤仍然保留,执行阶段再等待目标 `data-ai-function + data-ai-param` 出现。
869
+ - 本次调整不引入额外 AI 请求,仍遵循本地 JS 优先拆分和执行、只有分析任务才调用 AI 的原则。
870
+
871
+
872
+ ## v0.5.10 更新
873
+ 本版本强化本地 JS 指令拆分:即使用户连续说“切换到物联监测,切换到基地资源切换到集成监管时间切换到2024,查看一下土壤墒情数据”,SDK 也会根据已注册的 `data-ai-function + data-ai-param` 能力锚点拆成独立动作,并把最后的数据查看语句识别为分析步骤;执行阶段继续按顺序等待页面状态,只有分析步骤调用 AI。
874
+
875
+ ## v0.5.11 本次优化
876
+
877
+ “查看一下土壤墒情数据”属于数据查看/分析请求,即使用户没有说“分析”二字,也需要先获取当前页面的土壤数据,再交给分析 AI 生成可播报结果。
878
+
879
+ 本版本将“查看、获取、看看、看一下、查询、了解、读取、展示”与“数据、信息、墒情、土壤、气象、湿度、水分、温度、养分、氮、磷、钾、盐分、玉米、长势、病虫害”等组合识别为数据请求。
880
+
881
+ ## v0.5.12 更新
882
+ - UI 图标加强隔离,避免宿主大屏全局 SVG 样式影响小园。
883
+ - 语音输入链路增加麦克风权限检查和单次识别/全局唤醒互斥,减少“语音识别没有成功”的连续报错。
884
+ - 唤醒词触发后自动进入一次命令聆听,用户不需要再次点击麦克风。
885
+ - 唤醒后的命令聆听优先于欢迎语播报,避免小园自己的声音被 ASR 当作用户指令或导致识别失败。
886
+
887
+ ## v0.5.13 - 内置浏览器与页面能力
888
+
889
+ 小园现在内置一组与业务页面无关的通用操作,开发业务大屏时无需再额外添加 DOM 标记:
890
+
891
+ - `refreshPage`:刷新当前页面。
892
+ - `goBack`:返回上一个浏览历史页面。
893
+ - `goForward`:前往下一个浏览历史页面。
894
+ - `openDataCenter`:在当前地址上追加 `console` 查询参数,并重新进入该地址。
895
+ - `scrollPageTop`:滚动到页面顶部。
896
+ - `scrollPageBottom`:滚动到页面底部。
897
+
898
+ 这些能力通过 Function Registry 暴露给 Planner/本地执行器。与业务 DOM Function 不同,内置方法直接调用浏览器 JS API,不需要查询 DOM。
899
+
900
+ `openDataCenter` 示例:
901
+
902
+ `https://example.com/dashboard?year=2024#view`
903
+
904
+ 会变为:
905
+
906
+ `https://example.com/dashboard?year=2024&console#view`
907
+
908
+ 已有其它 query 参数会保留;如果已经存在 `console` 参数,会更新为无值形式后重新加载页面。
909
+
910
+ ## v0.5.14
911
+
912
+ - 修复内置页面操作的本地自然语言识别。
913
+ - 新增“返回上一个页面 / 后退 / 返回上一页”等 `goBack` 别名。
914
+ - 新增“前进 / 下一页”等 `goForward` 别名。
915
+ - 新增刷新页面、打开数据中台、滚动到顶部/底部等内置能力的自然语言别名。
916
+ - 内置方法优先由 JS 本地识别并直接执行,不需要调用 AI。
917
+
918
+
919
+
920
+ ## v0.5.15 更新
921
+ - 用户输入先分流为页面操作、数据分析、普通闲聊。
922
+ - 页面操作优先本地 JS,不调用 AI。
923
+ - 普通闲聊直接调用轻量 Chat AI。
924
+ - 天气问询优先使用已注册 weather/气象数据源;没有实时数据源时不编造。
925
+
926
+ ## v0.5.16 路由策略更新
927
+
928
+ 1. 用户输入先由本地 JS `parseLocalCommands()` 判断。
929
+ 2. 能识别为页面 action:直接执行,不调用 Planner AI。
930
+ 3. 能识别为数据分析:读取对应 DataSource,再调用 Analyzer AI。
931
+ 4. 完全无法识别为 action/analysis:直接进入 Chat AI,回答普通问题。
932
+ 5. 混合输入:先执行本地已识别步骤,剩余文本直接交给 Chat AI。
933
+ 6. 不再为了回答普通问题先调用 Planner AI。
934
+
935
+ ## v0.5.17 更新:新问题优先播报
936
+ 1. 用户提交新问题时,立即停止上一条正在播放的 TTS。
937
+ 2. 清空上一条 TTS 队列,新的指令拥有最新播报优先级。
938
+ 3. 每条指令生成独立 session;旧 session 即使异步完成,也不会再覆盖新问题的字幕或语音。
939
+ 4. 用户可以在上一条任务仍处理时继续提交文字问题;旧业务动作不主动中断,避免破坏页面状态,但旧结果不再播报。
940
+ 5. 新问题仍按现有本地指令 / 数据分析 / Chat 路由执行。
941
+
942
+
943
+ ## v0.5.18:FreeTTS 女声播报
944
+
945
+ TTS 增加 FreeTTS Provider。配置 `tts.provider = "freetts"` 后,小园使用 `zh-CN-XiaoxiaoNeural` 中文女声生成 MP3 并播放;未配置 Key 或 FreeTTS 请求失败时自动回退浏览器原生 TTS,避免影响指令执行。
946
+
947
+ 注意:FreeTTS 开发者 API 当前文档要求免费 API Key,浏览器直连默认没有 CORS 头,生产环境建议通过自有后端代理调用。
948
+
949
+
950
+ ## v0.5.19
951
+ - FreeTTS 播放链路:POST /api/v1/tts 获取 file_id → GET /api/audio/{file_id} 获取 MP3 → 优先 Web Audio 解码播放。
952
+ - 播放前在用户点击/发送/麦克风交互中调用 unlockTTS,先恢复 AudioContext,再异步等待 FreeTTS 网络请求。
953
+ - 当 Web Audio 不可用时回退 HTMLAudioElement。
954
+ - file_id 缓存不再永久保存,超过约 50 分钟重新生成,避免 FreeTTS 1 小时文件窗口内的边界过期问题。
955
+
956
+
957
+ ## v0.5.20 变更
958
+
959
+ 1. FreeTTS CORS 处理
960
+ - 浏览器不再直接 POST 到 `freetts.org`。
961
+ - 新增 `xiaoyuanVitePlugin()`,在宿主项目正在运行的 Vite dev server 内注册轻量同源代理。
962
+ - 代理收到浏览器请求后,由 Node 侧请求 FreeTTS,读取 `file_id`,再下载 MP3,最终只把音频返回给浏览器。
963
+
964
+ 2. 使用方式
965
+ - `vite.config.js` 增加 `xiaoyuanVitePlugin()`。
966
+ - `main.js` 原有 `tts.provider='freetts'` 配置保持不变。
967
+ - 浏览器请求路径默认变成 `/__xiaoyuan/freetts/`,不再触发对 `freetts.org` 的 CORS 预检。
968
+
969
+ 3. 架构边界
970
+ - 纯浏览器 NPM 组件无法自行启动 Node 进程,因此“小园内部代理”实现为挂载在宿主 Vite dev server 中的内置插件;无需用户自己再写一个 Express 服务。
971
+ - 生产部署时应把同样的代理能力放到实际的服务端/Nginx/Node 服务中。
972
+
973
+
974
+ ## v0.5.21:FreeTTS 结尾水印裁剪
975
+
976
+ FreeTTS 免费 API 返回的音频末尾可能追加 `Generated with FreeTTS.org`。小园不再按固定秒数粗暴裁剪,而是利用同一个 `file_id` 对应的 SRT 时间轴:
977
+
978
+ 1. POST `/api/v1/tts` 获取 `file_id`。
979
+ 2. 通过 `/api/audio/{file_id}` 下载完整 MP3。
980
+ 3. 通过 `/api/srt/{file_id}` 获取与语音同步的逐词时间戳。
981
+ 4. 找到最后一条真实用户文本对应的结束时间。
982
+ 5. 额外保留约 120ms 尾音后,用 Node 侧 `ffmpeg` 重新封装 MP3。
983
+ 6. 浏览器最终只播放裁剪后的音频。
984
+
985
+ 这样不会把固定的 2 秒/3 秒写死,短文本和长文本都按实际语音长度裁剪。SRT 或 ffmpeg 不可用时自动回退原始音频。
986
+
987
+ ## v0.5.22:TTS 切换为合我意
988
+
989
+ 1. 用户触发小园播报后,TTS 不再访问 FreeTTS。
990
+ 2. 浏览器默认请求同源 `/__xiaoyuan/hewoyi-tts`。
991
+ 3. Vite/Node 服务端代理收到请求后,将参数转换为合我意 TTS 的 GET 查询参数:`key`、`text`、`voice`、`format`、`speed`、`model`、`type`。
992
+ 4. 上游地址固定为 `https://api.hewoyi.com/api/ai/audio/speech`,请求类型为 GET。
993
+ 5. 代理将上游返回内容原样转回浏览器;如果上游直接返回音频则直接播放,如果返回 JSON 音频地址/音频字段,前端会继续解析并下载实际音频。
994
+ 6. 默认中文女声:`zh-CN-XiaoyiNeural`;默认格式:`mp3`;默认类型:`speech`。
995
+ 7. FreeTTS 原有 `file_id / SRT / ffmpeg` 水印裁剪逻辑全部从默认 TTS 链路中移除。
996
+ 8. 原有 TTS 打断机制、Web Audio 优先、HTMLAudio fallback 继续保留。
997
+
998
+
999
+
1000
+ ## v0.5.23 TTS 链路修复记录
1001
+
1002
+ 1. 前端仍只访问同源 `/__xiaoyuan/hewoyi-tts`。
1003
+ 2. 宿主 Vite 必须注册 `xiaoyuanVitePlugin()`,否则该本地代理不会存在,浏览器无法到达合我意接口。
1004
+ 3. Vite 代理收到请求后调用 `https://api.hewoyi.com/api/ai/audio/speech`。
1005
+ 4. 合我意接口当前文档声明返回 `application/json`;代理会递归寻找音频 URL / Base64。
1006
+ 5. 如果找到外部音频 URL,则由 Vite 服务端二次下载并以 `audio/*` 返回浏览器,避免二次跨域。
1007
+ 6. 前端增加 TTS 请求、响应、音频大小日志,便于定位“完全没有调用接口”的问题。
1008
+
1009
+
1010
+ ## v0.5.24:合我意 TTS 直连
1011
+
1012
+ 1. 小园接收到需要播报的文本后,直接调用 `tts.apiUrl`。
1013
+ 2. 默认地址为 `https://api.hewoyi.com/api/ai/audio/speech`。
1014
+ 3. 请求方法保持 GET,参数为 `key / text / voice / format / speed / model / type`。
1015
+ 4. 不再使用 `/__xiaoyuan/hewoyi-tts`,不再要求宿主项目安装或注册 `xiaoyuanVitePlugin()`。
1016
+ 5. 如果接口直接返回音频,则直接播放;如果返回 JSON,则按音频 URL / Base64 等字段解析后播放。
1017
+ 6. TTS 的打断、队列、Web Audio 优先和 HTML Audio 回退继续保留。
1018
+
1019
+
1020
+ ## v0.5.26 TTS 返回 HTML 音频标签适配
1021
+ 合我意 `speech` 接口当前可能返回 HTML:`<audio controls><source src="..." type="audio/mpeg"></audio>`。小园收到响应后先识别 HTML,再从 `source[src]` 或 `audio[src]` 提取真实音频 URL。提取成功后直接使用浏览器 `HTMLAudioElement` 播放该 URL,不再先把该 URL `fetch` 成 Blob 再交给 WebAudio,从而避免音频资源本身没有 CORS 头时被二次跨域拦截。只有直接 URL 播放失败时,才尝试 CORS `fetch` + Blob 作为兜底。
1022
+
1023
+ ## v0.5.26:TTS 前置门禁流程
1024
+ 1. 用户提交文字或语音指令后,小园先创建本次 `commandSessionId` 并停止上一条播报。
1025
+ 2. 在任何 AI 请求、页面函数执行、DOM 点击、数据读取之前,先执行强制 TTS:`speech.speak('收到指令,请您稍等。', { providerOnly: true })`。
1026
+ 3. 强制 TTS 必须完成“请求 speech 接口 → 取得音频地址/音频 → 浏览器开始并完成播放”。
1027
+ 4. 只有强制 TTS 成功完成后,才进入 `manager.run(commandText)`,后续才允许执行本地指令、数据分析或 Chat AI。
1028
+ 5. 强制 TTS 失败时,不再继续执行本次指令,直接提示用户 TTS 失败,避免出现“页面已经切换但提示音还没出来”的情况。
1029
+ 6. 前置播报使用 Provider-only 模式,不回退到浏览器 `speechSynthesis`,确保前置门禁严格使用用户配置的 TTS 服务。
1030
+ 7. 完成前置 TTS 后,原有每个步骤的结果播报、分析前提示以及新指令打断机制保持不变。
1031
+
1032
+
1033
+ ## v0.5.27 前置 TTS 放行时机调整
1034
+
1035
+ 新的单次指令执行顺序:
1036
+
1037
+ 1. 接收用户指令。
1038
+ 2. 停止上一条旧播报并创建新的 command session。
1039
+ 3. 优先调用第三方 TTS,生成“收到指令,请您稍等。”。
1040
+ 4. 必须成功拿到音频,并确认浏览器已经启动播放。
1041
+ 5. **一旦播放启动,立即进入后续流程,不等待整段音频播完。**
1042
+ 6. 执行 `manager.run`,包括本地 DOM Function、页面操作、数据读取以及必要的 AI 分析。
1043
+ 7. 当前 TTS 继续自然播放;只有新的用户指令到来时,才按原有打断机制停止旧语音。
1044
+
1045
+ 失败门禁:如果 TTS 请求失败、没有得到可播放音频、或浏览器拒绝启动播放,则本次 `manager.run` 不执行。
1046
+
1047
+ ## v0.5.28 TTS 与多步骤执行时序
1048
+ 1. 用户提交问题后,先创建 command session 并停止上一条语音。
1049
+ 2. 第 1 步先请求“收到指令,请您稍等。”的第三方 TTS。
1050
+ 3. TTS 返回可播放音频后,创建/启动 HTMLAudio。
1051
+ 4. **不以 `audio.play()` Promise resolve 作为放行条件,而是等待媒体触发 `playing` 事件。**
1052
+ 5. 一旦触发 `playing`,立即执行第 1 步,不等待语音播放完成。
1053
+ 6. 如果指令被本地拆分成多个步骤,第 2 步及之后的每一步,在 5 秒步骤间隔结束后,先重新请求 TTS。
1054
+ 7. 后续步骤同样必须等到对应音频真正触发 `playing` 后,才能点击菜单、调用 Function、读取数据或启动分析。
1055
+ 8. 因此严格保证“语音已经开始播报”发生在“当前步骤真正执行”之前。
1056
+ 9. TTS 请求失败、没有音频或无法进入 `playing` 状态时,不执行当前步骤。
1057
+
1058
+
1059
+
1060
+ ## v0.5.29 多指令逐步播报与执行
1061
+
1062
+ 多指令不再使用“先执行、后播报”,而是严格按当前步骤逐步门禁:
1063
+
1064
+ 1. 用户输入整句指令。
1065
+ 2. 先请求一次 `收到指令,请您稍等。` TTS;音频真正开始 `playing` 后才进入第一步。
1066
+ 3. 对第一步生成专属播报,例如菜单:`已切换到物联监测菜单。`;年份:`已切换到2025年。`。
1067
+ 4. 第一步专属 TTS 请求成功,并且音频真正开始播放后,立即执行第一步。
1068
+ 5. 第一步执行完成后等待 3 秒。
1069
+ 6. 再处理第二步:先请求第二步专属 TTS,等第二步音频进入 `playing` 后立即执行第二步。
1070
+ 7. 每一步都重复“专属 TTS -> playing -> 执行 -> 完成 -> 等待 3 秒 -> 下一步”。
1071
+ 8. 不等待音频播放结束;只等待音频真正开始播放。
1072
+ 9. 不再在 `onStep` 里重复播报步骤文案,避免同一步语音播放两次。
1073
+
1074
+ 示例:
1075
+
1076
+ `切换到物联监测,切换2025年`
1077
+
1078
+ `收到指令,请您稍等。` -> playing -> `已切换到物联监测菜单。` -> playing -> 执行菜单切换 -> 完成 -> 等待 3 秒 -> `已切换到2025年。` -> playing -> 执行年份切换。
1079
+
1080
+ ## v0.5.30 更新:聊天打字机 + TTS 自动兼容
1081
+
1082
+ ### 1. 聊天消息展示
1083
+ - 用户输入消息立即显示。
1084
+ - 小园回复消息采用打字机效果逐字/逐段展示,避免长文本一次性整块出现。
1085
+ - TTS 与聊天文字展示相互独立:TTS 播放无需等待打字机结束。
1086
+
1087
+ ### 2. TTS 双层兼容
1088
+ 执行前置 TTS 或分步 TTS 时,优先调用配置的第三方 TTS Provider。
1089
+ - 第三方 TTS 请求成功并真正开始播放:立即放行当前业务步骤。
1090
+ - 第三方 TTS 请求失败、音频解析失败、音频无法启动:自动切换浏览器 `speechSynthesis`。
1091
+ - 浏览器 TTS 真正触发 `SpeechSynthesisUtterance.onstart` 后立即放行,不等待 `onend`。
1092
+ - 只有第三方 TTS 和浏览器原生 TTS 都不可用时,当前步骤才失败并阻止后续业务调用。
1093
+
1094
+ ### 3. 多步骤指令保持原规则
1095
+ `切换菜单,切换年份` 等多步骤指令仍按“本步骤 TTS 请求 → TTS 真正开始播放 → 执行当前步骤 → 当前步骤完成 → 等待 3 秒 → 下一步骤”的顺序执行。
1096
+
1097
+
1098
+ ## v0.5.31 更新:修复聊天打字机响应式问题
1099
+ - 根因:消息对象先通过 `messages.value.push(message)` 加入响应式数组后,Vue 会对数组内对象进行 Proxy 包装;此前计时器持续修改的是 `push` 前保存的原始 `message` 对象,可能不会触发视图更新。
1100
+ - 修复:记录消息索引,打字机每次更新都读取 `messages.value[index]` 后修改其 `displayText`,确保 Vue 视图实时刷新。
1101
+ - 行为:用户消息立即出现;小园消息从第一个字符开始逐步显示;自动跟随滚动;展示完成后结束 typing 状态。
1102
+
1103
+ ## v0.5.32:固定前置提示音本地化
1104
+
1105
+ 固定文案 `收到指令,请您稍等。` 不再每次用户输入都调用 TTS 接口。
1106
+
1107
+ 流程:
1108
+
1109
+ 1. 安装 NPM 包时执行 `scripts/cache-received-tts.mjs`。
1110
+ 2. 脚本请求合我意 TTS,解析接口返回的 `<audio><source src="...">`,再下载真实 MP3。
1111
+ 3. MP3 保存为 `src/assets/received-command.mp3`,同时将 `received-command.local.js` 标记为已就绪。
1112
+ 4. 用户每次输入新指令时,小园直接使用本地 MP3 播放,不再请求网络 TTS。
1113
+ 5. 如果本地 MP3 播放失败,才回退到网络 TTS;如果网络 TTS 仍不可用,则由 `SpeechService` 自动回退浏览器原生语音。
1114
+ 6. 只有后续动态播报(如“已切换到物联监测菜单”“已切换到2025年”)继续按照原有逻辑实时请求 TTS。
1115
+
1116
+ 因此固定前置提示音在正常安装缓存成功的情况下,不产生每次指令的 speech 网络请求。
1117
+
1118
+
1119
+ ### v0.5.33 指令拆分修复
1120
+ 连续自然语言中,年份锚点必须包含完整的“2025年”而不是只标记“2025”,否则剩余的“年”会被拼入下一条分析指令。现在拆分顺序稳定为:菜单操作 → 年份操作 → 数据分析。
1121
+
1122
+
1123
+ ## v0.5.34
1124
+ - 优化多指令分析语句拆分:识别“分析一下/查看一下/查询一下”等前置口语动词并从 analysis instruction 中剥离,避免生成“正在分析分析一下土壤数据”。
1125
+ - 保留“切换2025年”作为完整年份 action,不污染后续分析片段。
1126
+ - 分析 TTS 文案统一按分析主题生成,如“正在分析土壤数据,请稍等。”。
1127
+
1128
+
1129
+ ## v0.5.35 流程优化
1130
+
1131
+ ### AI 分析结果的特殊门禁
1132
+ 当工作流包含 AI 数据分析时:
1133
+
1134
+ 1. 先播报“正在分析XXX,请稍等”,该提示不阻塞数据获取与 AI 推理。
1135
+ 2. 数据源并行获取完成后调用 AI 分析。
1136
+ 3. AI 返回最终分析结果后,必须调用 TTS 并等待整段音频播放完成。
1137
+ 4. 只有最终分析结果 `onended` 后,才允许进入下一条指令。
1138
+ 5. 因此“分析一下土壤数据,然后切换2024年”不会在分析结果还没播完时提前切换年份。
1139
+
1140
+ ### 性能策略
1141
+ 普通页面操作仍采用“音频进入 playing 即执行”的快速门禁,不等待音频播放结束。
1142
+ AI 分析开始提示音与数据获取/模型分析并行;多个数据源采用 `Promise.all` 并行获取。
1143
+ 分析结果播报是唯一需要等待完整播放结束的 TTS 阶段,因为它直接决定下一步是否可以执行。
1144
+
1145
+ ### 普通操作性能
1146
+ 普通 Function/DOM 执行后的渲染保护从默认 8 秒降到 200ms;真正的流程节奏由 3 秒步骤间隔和 3 秒重试承担,避免每一步额外浪费 8 秒。
1147
+
1148
+
1149
+ ## v0.5.36
1150
+
1151
+ - “收到指令,请您稍等。”改为始终优先使用本地 MP3,不再为这条固定提示语发起网络 TTS 请求。
1152
+ - 预加载并复用本地 Audio 实例,降低首播偶发不出声的问题。
1153
+ - 本地音频播放失败时自动降级为浏览器原生 speechSynthesis,不再回退到网络 TTS。
1154
+ - 保留普通动态播报使用合我意 TTS。
1155
+
1156
+ ## v0.5.37 本地前置提示音稳定性
1157
+ 收到用户指令后,"收到指令,请您稍等"继续保持本地音频优先,不走网络 TTS。为解决语音识别返回结果后已经脱离用户手势导致 HTMLAudio 播放偶发失败,小园在用户点击发送/麦克风以及开始语音识别阶段先解锁 AudioContext 并预解码本地 MP3;真正收到指令文本后直接启动已解码 AudioBuffer。AudioBuffer 启动成功即视为前置播报开始,后续指令流程立即放行,不等待完整语音播放。若本地 AudioBuffer 失败,才回退浏览器原生 speechSynthesis。
1158
+
1159
+
1160
+ ## v0.5.38:前置提示音按 voice 本地缓存
1161
+ 1. NPM 包不再内置 `received-command.mp3`,不占用发布包体积。
1162
+ 2. `收到指令,请您稍等。` 在浏览器 IndexedDB 中按 `voice` 独立保存。
1163
+ 3. 第一次使用某个 voice 且本地没有缓存时,正常请求合我意 TTS;音频成功播放后异步保存到本地。
1164
+ 4. 后续同 voice 直接读取本地缓存,不再调用 speech 接口。
1165
+ 5. 用户切换 voice 后会查询新的 voice 缓存,不会复用旧 voice 的前置提示音。
1166
+ 6. 本地缓存不存在、读取失败或首次 TTS 失败时,保留浏览器原生 TTS 兜底。
1167
+ 7. 该缓存是浏览器端持久缓存,不会修改宿主项目源码目录。
1168
+
1169
+
1170
+ ### v0.5.39 语音资源策略
1171
+
1172
+ `收到指令,请您稍等。` 的前置语音改为支持“包内置 + 浏览器缓存 + 网络首次生成”的三级资源策略:
1173
+
1174
+ 1. 如果当前 voice 在 NPM 包中存在内置 MP3,直接使用静态资源,不发起 TTS 网络请求。
1175
+ 2. 包内没有对应 voice 时,读取 IndexedDB 中按 voice 隔离的缓存。
1176
+ 3. 包内和 IndexedDB 都没有时,第一次调用合我意 speech 接口,成功后播放并异步写入 IndexedDB。
1177
+ 4. TTS 失败时继续使用浏览器原生 `speechSynthesis` 兼容播报。
1178
+
1179
+ 当前包内置:`zh-CN-XiaoyiNeural`。
1180
+
1181
+ ## v0.5.40 本地前置提示音稳定性
1182
+ - “收到指令,请您稍等”命中 NPM 包内置 MP3 时,固定优先用 HTMLAudioElement 播放。
1183
+ - 必须收到 `playing` 事件,并保持约 120ms 后才返回成功,避免下一步 TTS 立即 `stop()` 导致提示音还未真正输出到扬声器就被切断。
1184
+ - 普通动态 TTS 不改变原有快速策略;仅本地固定提示音增加极短的启动保护窗口。
1185
+
1186
+
1187
+ ## v0.5.41
1188
+
1189
+ - 修复多步骤指令中“收到指令,请您稍等”本地 TTS 被后续步骤 TTS 的 `stop()` 提前打断的问题。
1190
+ - “收到指令,请您稍等”使用独立 Audio 通道;新步骤 TTS 不会停止该提示音。
1191
+ - 只有新的整条用户指令触发全局打断时,才会停止前置提示音。
1192
+ - 本地提示音仍保持“真正触发 `playing` 后立即放行后续流程”,不等待播放结束。
1193
+
1194
+
1195
+ ## v0.5.42 流程稳定性与性能
1196
+ - 多步骤执行采用异常隔离:单个 hook 或播报异常不会直接终止整个流程。
1197
+ - TTS 在前置门禁中快速重试,仍失败时由 SpeechService 自动降级浏览器原生 TTS。
1198
+ - AI 分析开始提示不再重复发起一次 TTS 请求;分析结果仍必须完整播报结束后再进入下一步。
1199
+ - TTS HTTP 请求增加 7 秒超时,避免单次网络异常无限等待。
1200
+ - DOM/FUNCTION 仍采用既有 3 秒重试策略;渲染等待缩短到 120ms,提升页面切换速度。
1201
+
1202
+
1203
+ ## v0.5.43:固定收到指令提示异步化
1204
+ 收到指令后的固定提示音采用独立异步通道,仅作为并行提示,不占用主流程。主流程不等待本地音频加载、播放或网络首次缓存;有本地 voice 音频时直接播放,没有时后台请求一次并缓存。普通 TTS 的停止逻辑不主动停止该独立提示音。
1205
+
1206
+
1207
+ ## v0.5.44 数据中台开关
1208
+ - 打开数据中台:当前地址追加 `console` 参数并刷新页面。
1209
+ - 关闭数据中台:当前地址删除 `console` 参数并刷新页面。
1210
+ - 关闭动作不改变其他 query 参数和 hash。
1211
+
1212
+ ## v0.5.45:新增 XiaoxiaoNeural 本地前置提示音
1213
+
1214
+ 小园现在内置 `zh-CN-XiaoxiaoNeural` 的“收到指令,请您稍等”MP3。当前 voice 命中内置资源时直接播放本地文件;未命中时再查询对应 voice 的 IndexedDB 缓存,仍未命中才请求远程 TTS。