Chinesische Sprachsynthese: Textnormalisierung und Engineering
Dieser Skill stammt von einem KI-Schreibtischhaustier, das täglich spricht (siehe „Quelle und Wartung“ am Ende): Claude-Streaming-Antworten werden Satz für Satz an die lokale TTS-Engine übergeben. Die untenstehenden Regeln sind getestet, die Zahlen sind gemessen.
Ein-Satz-Prinzip: Die Aussprache wird in dieser Schicht konvertiert, die Engine kümmert sich nur um die Phonetik. Wird der konvertierte Text einer anderen Engine übergeben, ändert sich die Aussprache; bei Verwendung eines fertigen TN-Bibliothek verschlechtern sich einige Stellen. Fertige Bibliotheken (wetext, PaddleSpeech) werden zum Vergleich und zur Regelergänzung herangezogen.
Ablauf
Streaming-Text aus dem Modell
→ ① Filtern: Dekorationen, Emojis, Listensymbole entfernen; Codeblöcke, Tabellen, Links durch einen Satz ersetzen (beim Streaming müssen getrennte Token erkannt werden)
→ ② Konvertierung in Aussprache: Zahlen, Zeiten, Einheiten, Symbole, Aussprache-Ersetzungstabelle (beim Streaming an Token-Grenzen festhalten)
→ ③ Satztrennung: Erster Abschnitt bis zum ersten Komma; danach bei mehr als 40 Zeichen am Komma trennen
→ ④ Synthese: Nur etwa 20 Sekunden vorausberechnen; beim Unterbrechen die Inferenz wirklich stoppen
Die Reihenfolge darf nicht geändert werden: Erst Aussprache-Konvertierung, dann Satztrennung. Bei zuerst getrennten Sätzen traten im Test drei Fehler auf:
| Original | Ergebnis nach Satztrennung | Problem |
|---|---|---|
下午14:30开会 | 下午14: / 30开会 | Doppelpunkt als Komma interpretiert, Zeit getrennt |
看这里:https://…/pull/7 就行 | https: / //…/pull/7 就行 | Link in zwei Hälften geteilt, Adresse buchstabiert |
大概3~5天 | 大概35天 | ~ als Dekoration entfernt, gelesen als „fünfunddreißig Tage“ |
Halbbreiter Doppelpunkt : wird nur als Komma behandelt, wenn danach ein Leerzeichen kommt.
Streaming: Token-Grenzen beachten
Ein ganzer Satz kann korrekt konvertiert sein, aber beim Streaming nicht. Bei zeichenweisem Streaming werden „工作了26年“ und „80年代“ als „Er liu nian“ (二六年) bzw. „Ba ling nian dai“ (八零年代) gelesen, weil die Zeichen nach der Zahl noch nicht da sind; -10°C wird als Englisch gelesen.
- Bei Zahlen auf 2 weitere Zeichen warten, bevor sie freigegeben werden; Einheiten auch mit Leerzeichen erkennen („跑了 5 “ zunächst zurückhalten, auf
kmwarten). - Beim Übergang zum nächsten Abschnitt die letzten chinesischen Zeichen des vorherigen Abschnitts mitnehmen (um „了 26 年“ zu erkennen, muss „了“ zuvor gesehen werden).
- Maximal 200 Zeichen zurückhalten, bei Codeblöcken/Tabellen maximal 4000 Zeichen; bei Erreichen des Limits freigeben.
- Jeder Test wird sowohl „Zeichen für Zeichen“ als auch „als ganzer Satz“ durchgeführt; die Ergebnisse müssen identisch sein.
Schnellsuche-Regeln (Details in references/rules.md)
| Kategorie | Beispiel | Aussprache |
|---|---|---|
| Jahr | 2026年, 2026届, 26年见 | Ziffer für Ziffer: èr líng èr liù nián, èr líng èr liù jiè, èr liù nián jiàn |
| Anzahl Jahre | 工作了26年, 80年代, 26年前 | Nach Anzahl: èr shí liù nián, bā shí nián dài, èr shí liù nián qián |
| Nummern | Telefon, SMS-Code, 编号27149, 房间1201 | Ziffer für Ziffer, 1 als „yāo“; Bindestriche und Leerzeichen erzeugen Pause |
| Fünfstellige Zahlen | 95588 / Preis 12999 | Kein Maß, nicht auf 0 endend, nicht vorher Geld → Nummer; sonst nach Anzahl |
| Internet-Slang | 666, 2333, 520 allein / 666个赞, 花了888 | Ziffer für Ziffer / Nach Anzahl (nur eine feste Liste) |
| 2 vor Einheit | 2个, 2L, 2℃ | liǎng ge, liǎng shēng; außer Temperatur: èr shè shì dù |
| Einheit | 60km/h, 3kWh, 5min, 10mg | měi xiǎo shí liù shí gōng lǐ, sān dù diàn, wǔ fēn zhōng, shí háo kè |
| Nicht konvertierte Einheiten | 4g网络, 5A景区, 16GB | Original an Engine übergeben |
| Bereich, Prozent | 3~5%, 3-5km, -1.5~2 | bǎi fēn zhī sān dào wǔ, sān dào wǔ gōng lǐ, fù yī diǎn wǔ dào èr |
| Uhrzeit | 14:30, 13:00, 9:05 | shí sì diǎn bàn, shí sān diǎn, jiǔ diǎn líng wǔ (24-Stunden-Format) |
| Datum, Bruch | 10/26 交 / 有7/12的观众 | shí yuè èr shí liù hào / shí èr fēn zhī qī (nur nach „有, 占, 约“ als Bruch) |
| Symbole | #15, 2x, x3, 100w | shí wǔ hào, liǎng bèi, chéng sān, yī bǎi wàn |
Mit Referenzfällen prüfen
references/golden-cases.json enthält 115 ganze Fälle und 24 Streaming-Fälle, die alle vom Originalprojekt geprüft wurden. scripts/check-tn.mjs testet deine Implementierung damit:
node scripts/check-tn.mjs ./my-tn.mjs # Modul exportiert normalize(text) und (optional) createStream()
node scripts/check-tn.mjs ./my-tn.mjs --only 号码 # Nur eine Gruppe testen
- Beim Vergleich werden Leerzeichen und übliche Satzzeichen ignoriert, nur die Aussprache wird verglichen. Fälle mit
notesind „zur Referenz“ und zählen nicht als Fehler. - Beim Neuschreiben von TN als Abnahmekriterium verwenden; beim Ändern von Regeln einmal ausführen, um Regressionen zu vermeiden.
- Umstrittene Aussprachen (z.B. „dieser Computer 12999“ wird als Nummer behandelt) wurden im Originalprojekt akzeptiert. Wenn dein Produkt andere Aussprachen möchte, ändere die Referenzfälle und begründe die Änderung.
Fehlersuche bei falscher Aussprache
Vier Ursachen: Der Text wurde vor der Aussprache-Konvertierung nicht als solcher erkannt; Mehrlautzeichen wurden vom Modell falsch erraten; die Referenzaufnahme ist fehlerhaft; zufällig (einfach nochmal generieren).
Denselben Satz mit der aktuellen Stimme und zwei anderen Stimmen vorlesen: Alle Stimmen machen denselben Fehler → Aussprache-Ersetzungstabelle ergänzen; nur eine Stimme klingt seltsam → Stimme wechseln. Jedes Mal Originaltext und konvertierte Aussprache in ein Log (jsonl) schreiben, um später nachzuschlagen.
Engineering (Details in references/engines.md)
- First-Byte-Latenz: Der erste Abschnitt wird nur bis zum ersten Komma getrennt. Bei Matcha liegt die Latenz bei 0,65 Sekunden nach dem ersten Zeichen.
- Effektivste Optimierung: Caching. Ein nie synthetisierter Satz benötigt bei CosyVoice 2,6–3 Sekunden für den ersten Block; ein Cache-Treffer dauert 6 ms. Sobald der Nutzer den Mund öffnet, wird ein Fülltext („嗯“, „我想想“) vorab synthetisiert.
- Abbruch muss die Inferenz wirklich stoppen: Wird die vLLM-Verbindung getrennt,
abort_requestaufrufen; sonst wartet der nächste Satz 4,9 Sekunden (nach Implementierung 0,8–1,7 Sekunden). - Englisch wird still ignoriert: Manche Engines sprechen
GitHub,PR,%nicht; vor Engine-Upgrade die englische Aussprache erneut validieren. - Fallback-Quelle: Wenn die Hauptquelle nicht bereit ist oder beim ersten Fehler ausfällt, auf Fallback umschalten; bei Fehlern mitten im Satz nicht wiederholen. Dass der HTTP-Service läuft, bedeutet nicht, dass das Modell geladen ist; bei 503-Fehlern wiederholen.
Quelle und Wartung
- Quelle: Cirra (KI-Schreibtischhaustier, RedCarpG/cirra-deskpet) und sein Sprachdienst cirra-voice, 2026-10 bis 2026-11.
- Code:
sidecar/src/tts/speech/(numbers.ts,units.ts,links.ts,filter.ts,overrides.ts,speech.test.ts),sidecar/src/tts/splitter.ts,sidecar/src/tts/pronunciations.ts. - Dokumentation:
docs/speech.md,docs/speech-tn.md(119 Sätze mit drei Vergleichstabellen),docs/tts.md,docs/voices.md,docs/voice-chat.md,docs/voice-chat-2.md,docs/voice-install.md,docs/perf-checkup.md. - Erfahrungen: Private Repo
lessons/chinese-tts.md.
- Code:
- Ursprung: RedCarpG/redcarpg-skills unter
skills/chinese-tts/. Die Kopie in~/.claude/skills/ist eine installierte Instanz; nicht direkt bearbeiten.references/golden-cases.jsonwurde am 2026-11-10 aus Cirrasspeech.test.tsper Skript extrahiert; keine automatische Synchronisation. - Änderungen vornehmen:
- Im redcarpg-skills-Repo ändern, per Pull Request;
manifest.jsonVersion erhöhen,node scripts/validate.mjsbestehen lassen, dann Tagchinese-tts-v<version>setzen und veröffentlichen; danach lokale Kopie neu installieren. Zuerstlessons/chinese-tts.mdauf neue Einträge prüfen. - Wenn Cirras TN-Regeln sich ändern, die Referenzfälle neu extrahieren (Extraktionsmethode: Aus
speech.test.tsdie GruppereadNumbersund die „Streaming“-Gruppe der Paare[Original, Aussprache]lesen) und mit Cirrassidecar/dist/tts/einen Adapter bereitstellen, dercheck-tn.mjsausführt; alle Tests sollten bestehen. - Die Engine-Zahlen (Latenz, Echtzeitfaktor, GPU-Speicher) wurden im Oktober 2026 auf einem Computer mit 8 GB GPU-Speicher und CPU ohne VNNI gemessen; bei anderer Hardware neu messen.
- Im redcarpg-skills-Repo ändern, per Pull Request;
- Änderungsprotokoll:
- 2026-11-12 · 1.0.0, in redcarpg-skills öffentlich veröffentlicht. Lokale absolute Pfade entfernt, Quellprojekt als Link angegeben; Inhalt unverändert.
- 2026-11-10 · Cirra · Erstveröffentlichung: Ablauf und Reihenfolge, Streaming-Grenzen, Regelschnellsuche und Details, Fehlersuche bei falscher Aussprache, Engine-Engineering; 139 Referenzfälle und
check-tn.mjs(mit Cirras Implementierung getestet, alle bestanden); zwei Test-Prompts inevals/.