From 9161a2013c391050c6a816281b93aeb147a3e102 Mon Sep 17 00:00:00 2001 From: xiangsilian Date: Fri, 11 Sep 2026 23:45:15 +0800 Subject: [PATCH] =?UTF-8?q?fix(chat):=20TTS=20=E6=9C=97=E8=AF=BB=E5=87=80?= =?UTF-8?q?=E5=8C=96=E2=80=94=E2=80=94=E4=B8=8D=E5=BF=B5=20Markdown/?= =?UTF-8?q?=E8=8B=B1=E6=96=87=E6=A0=87=E7=82=B9/=E8=A1=A8=E6=83=85?= =?UTF-8?q?=E7=AC=A6=E5=8F=B7=EF=BC=88=E4=B8=AD=E6=96=87=E5=81=9C=E9=A1=BF?= =?UTF-8?q?=E6=A0=87=E7=82=B9=E4=BF=9D=E7=95=99=E5=81=9A=E8=87=AA=E7=84=B6?= =?UTF-8?q?=E5=81=9C=E9=A1=BF=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/main/ets/components/f4/TtsEngine.ets | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/app/entry/src/main/ets/components/f4/TtsEngine.ets b/app/entry/src/main/ets/components/f4/TtsEngine.ets index 0159ebb..bac4735 100644 --- a/app/entry/src/main/ets/components/f4/TtsEngine.ets +++ b/app/entry/src/main/ets/components/f4/TtsEngine.ets @@ -8,6 +8,8 @@ * - 不依赖 queueMode:官方 API 文档(0=flush)与 Codelabs(0=排队)说法 * 互相矛盾,新朗读前显式 engine.stop() 打断,语义自持 * - 播报状态回调 onStateChanged:页面据此复位 🔊 图标 + * - V3:speak 前 sanitizeForSpeech 净化——不念 Markdown/英文标点/表情符号 + * (中文停顿标点保留做自然停顿) * * 模型:offline 中文引擎(online: 1,系统内置);模拟器通常无此服务,须真机。 * 日志 tag MEIDA_TTS:hdc shell hilog | grep MEIDA_TTS @@ -18,6 +20,18 @@ import { textToSpeech } from '@kit.CoreSpeechKit' const SEGMENT_MAX_CHARS: number = 400 const SEGMENT_FALLBACK_MS: number = 150000 +/** + * 朗读净化(V3):剥掉会被引擎念出来的符号——Markdown 标记(**、#、- 列表符)、 + * 英文标点、表情等,只留中英文、数字与中文停顿标点(,。;:!?、……靠它们做自然停顿, + * 引擎不会逐字念)。顾问回答被要求不写 Markdown(agent_chat v1),这里是双保险。 + */ +function sanitizeForSpeech(text: string): string { + return text + .replace(/[^一-龥a-zA-Z0-9\s。,、;:!?]/g, ' ') + .replace(/\s+/g, ' ') + .trim() +} + /** 播报状态变化回调(speaking=false 表示本轮朗读完全结束或被停止)。 */ export type TtsStateChanged = (speaking: boolean) => void @@ -78,7 +92,7 @@ export class TtsEngine { * 由调用方决定是否提示(自动朗读静默、手动点击提示)。 */ async speak(text: string): Promise { - const trimmed: string = text.trim() + const trimmed: string = sanitizeForSpeech(text) if (trimmed.length === 0) { return }