/* =========================================================
   Curriculum data — 9 modules / 29 chapters
   ---------------------------------------------------------
   Metadata only (bilingual). The teaching content ("解释")
   for each chapter lives in content/<id>.<lang>.md and is
   fetched on demand by the chapter page. `viz` names an
   interactive bench ("声控台") from viz.jsx / viz2.jsx /
   viz3.jsx / viz4.jsx; the code listings live in code.jsx /
   code2.jsx (looked up by chapter id). `props` lists the key
   concepts the chapter leans on.

   Domain: AI voice customer service for a massage / wellness
   business (按摩 · 推拿 · 足疗 · 养生) — from the shop's
   phone line and WeChat inbox down to ASR models, TTS
   vocoders, latency budgets, platform bills and compliance.
   ========================================================= */

const MODULES = [
  {
    id: "m1", arch: "m1-arch", code: "BZ", accent: "primary", level: 1,
    zh: "生意场景与那本客服账", en: "The Business, and the Customer-Service Ledger",
    tagline: { zh: "AI 客服不是一个技术项目,是一笔账:漏掉的电话值多少钱,决定了你该投多少钱去接它。", en: "Voice AI is not a tech project, it is a ledger: what a missed call is worth decides how much you should spend to answer it." },
    description: {
      zh: "在挑 ASR 厂商之前,先把生意看清楚。一家按摩、推拿、足疗或养生门店的客服工作,表面上是「接电话」,实际上是四件事叠在一起:接住预约(带来当天营收)、答清问题(价格、项目、时长、地址、停车、是否需要预约)、改约与提醒(决定爽约率)、以及事后召回(决定复购)。这四件事里,只有第一件有明确的时间窗口——顾客在路上、在午休、在下班前那十分钟打进来,前台正在给客人做理疗、在收银、在带客上钟,电话响了六声没人接,这一单就去了隔壁。第一章用一个排队模型把这笔漏接账算出来:日话量、高峰集中度、前台并发人数、平均通话时长,推出高峰时段的呼损率和每月流失的预约金额——这是本书后面所有技术投入的预算上限。第二章回答「哪些该交给 AI」:把十二类真实咨询按占比、复杂度和是否需要写操作分档,你会发现头部五类占了八成话量却几乎不需要判断力,而尾部那些最难的,恰恰是把 AI 硬推上去会赔掉口碑的。第三章把一通电话拆成轮次:问候、听意图、补槽位、查空档、确认、收尾,每一轮都有失败概率,连乘之后就是真实的自助成交率——这也解释了为什么「少问一轮」比「模型再准两个点」更值钱。",
      en: "Before picking an ASR vendor, look at the business. Customer service for a massage, tuina, foot-reflexology or wellness shop looks like answering the phone, but it is four jobs stacked: catching bookings (today's revenue), answering questions (price, service, duration, address, parking, whether booking is needed), rescheduling and reminding (which sets the no-show rate), and winning people back afterwards (which sets repeat business). Only the first has a hard time window — the customer calls from the road, at lunch, in the ten minutes before leaving work, while the front desk is mid-treatment, at the till, or walking a guest to a room. Six rings, nobody answers, and that booking went next door. Chapter one computes that missed-call bill with a queueing model: daily call volume, peak concentration, front-desk concurrency and average handle time give a peak-hour blocking rate and a monthly value of lost bookings — the budget ceiling for every technical decision later in this book. Chapter two answers which work should go to the AI: rank twelve real enquiry types by share, complexity and whether they write to a system, and you find the top five are eighty percent of volume with almost no judgement required, while the hardest tail is exactly where forcing the AI costs you your reputation. Chapter three takes one call apart into turns — greeting, intent, slot filling, availability, confirmation, close — each with a failure probability whose product is the real self-service completion rate, which is why removing one turn is worth more than two points of model accuracy.",
    },
  },
  {
    id: "m2", arch: "m2-arch", code: "AS", accent: "accent", level: 1,
    zh: "听清:语音识别 ASR", en: "Hearing: Automatic Speech Recognition",
    tagline: { zh: "电话把声音砍到 4 kHz 以下,你的模型要在被砍剩的那一半里听出一个手机号。", en: "The phone network cuts audio below 4 kHz, and your model must hear an eleven-digit number in what is left." },
    description: {
      zh: "语音客服的第一道关是把声音变成文字,而这道关比大多数人想的难。难点不在模型,在信道:固话和手机来电走的是 8 kHz 采样的窄带编码,奈奎斯特定理直接把 4 kHz 以上的能量全部砍掉,而汉语里区分「四」和「十」、「f」和「s」、「zhi」和「zi」的关键擦音能量,恰恰大量分布在被砍掉的那一段。所以同一个模型,在 16 kHz 的麦克风上能做到 3% 的字错率,在电话线上可能是 9%。本模块从音频的物理量开始:采样率、位深、编码、码率,为什么一分钟通话是几百 KB 而不是几 MB;然后讲模型:从 DNN-HMM 到 CTC、到今天做流式识别主力的 RNN-T、到非自回归的 Paraformer 和整句的 Whisper,它们各自为什么快、为什么准、为什么有的天生不能流式;接着讲一个被严重低估的组件——VAD 与端点检测:它决定了机器什么时候认为「你说完了」,阈值定短了就抢话,定长了就呆滞,这一个参数对体验的影响超过模型准确率;最后讲评测与优化:CER 怎么算、热词与语言模型偏置怎么把技师名字和项目名救回来、以及为什么 5% 的字错率意味着手机号只有一半能一次听对。",
      en: "The first gate in voice service is turning sound into text, and it is harder than most people assume. The difficulty is not the model, it is the channel: landline and mobile calls arrive as 8 kHz narrowband, and Nyquist removes every component above 4 kHz — precisely where the fricative energy that separates Mandarin si from shi, or f from s, mostly lives. The same model that reaches 3% character error on a 16 kHz microphone can sit at 9% on a phone line. This module starts with the physics: sample rate, bit depth, codec and bitrate, and why a minute of call audio is a few hundred kilobytes rather than a few megabytes. Then the models: DNN-HMM to CTC, to the RNN-T that carries most streaming recognition today, to the non-autoregressive Paraformer and the whole-utterance Whisper — why each is fast, accurate, or structurally unable to stream. Then a badly underrated component, voice activity detection and endpointing, which decides when the machine believes you finished: set the tail silence short and it interrupts, long and it feels slow, and that one parameter moves perceived quality more than model accuracy does. Finally evaluation and tuning: how character error rate is computed, how hotword and language-model biasing rescue therapist names and service names, and why 5% character error means only about half of phone numbers are heard correctly on the first try.",
    },
  },
  {
    id: "m3", arch: "m3-arch", code: "TS", accent: "primary", level: 2,
    zh: "说好:语音合成 TTS", en: "Speaking: Text to Speech",
    tagline: { zh: "顾客不会评价你的声学模型,他们只会说「一听就是机器人」——那句话里 80% 是韵律和读法。", en: "Nobody rates your acoustic model; they say it sounds like a robot — and 80% of that verdict is prosody and how numbers are read." },
    description: {
      zh: "合成侧的技术演进很清晰:早年的拼接合成靠一个巨大的录音库拼字,自然但僵硬;参数合成(HMM)灵活但闷;今天的主流是神经网络两段式——声学模型(Tacotron 2 / FastSpeech 2)把文本变成梅尔频谱,声码器(HiFi-GAN)把频谱变成波形——以及端到端的 VITS 与 CosyVoice 这类可零样本复刻音色的模型。MOS 从参数时代的 3.5 一路推到 4.4 以上,已经很接近真人。但门店场景里让人出戏的,几乎从来不是音质,而是三件小事:数字怎么读(「138 元」读成「一百三十八元」还是「幺三八」)、时间怎么读(「13:30」是「十三点三十」还是「下午一点半」)、多音字读错(「重(chóng)做」读成「重(zhòng)做」)。本模块先讲合成的技术路线与成本(RTF、显存、并发),再用一个真实的门店话术做文本规范化与 SSML 标注实验,然后处理一个越来越常见也越来越敏感的需求——用店长或金牌技师的声音做品牌音色:技术上几秒样本就能复刻,法律上必须有本人书面授权并按规定对合成内容做标识;最后讲流式合成:为什么要按标点切句边合成边播,以及缓冲欠载时那一下卡顿是怎么来的。",
      en: "The synthesis side has a clean history: concatenative systems stitched a huge recorded corpus — natural but rigid; parametric HMM systems were flexible but muffled; today's mainstream is the two-stage neural pipeline — an acoustic model (Tacotron 2 / FastSpeech 2) turning text into a mel spectrogram and a vocoder (HiFi-GAN) turning that into a waveform — alongside end-to-end VITS and CosyVoice-class models that clone a voice from seconds of audio. Mean opinion scores moved from about 3.5 in the parametric era to above 4.4, close to human. Yet what breaks the illusion in a shop is almost never audio quality; it is three small things: how numbers are read (138 yuan as one-hundred-thirty-eight or digit by digit), how times are read (13:30 as thirteen-thirty or half past one in the afternoon), and mispronounced heteronyms. This module covers the synthesis routes and their cost (real-time factor, VRAM, concurrency), then runs a real shop script through text normalisation and SSML markup, then handles an increasingly common and legally sensitive request — using the owner's or a star therapist's voice as the brand voice: seconds of audio suffice technically, while the law requires that person's written authorisation and labelling of synthetic output. It closes with streaming synthesis: why you chunk on punctuation and play while you synthesise, and where that stutter of buffer underrun comes from.",
    },
  },
  {
    id: "m4", arch: "m4-arch", code: "BR", accent: "accent", level: 2,
    zh: "会想:大模型对话大脑", en: "Thinking: The LLM Dialogue Brain",
    tagline: { zh: "大模型让「听懂」变便宜了,但也让「胡说」变便宜了——门店的价格表不能靠模型的记忆。", en: "LLMs made understanding cheap — and made making things up just as cheap. Your price list cannot live in the model's memory." },
    description: {
      zh: "听清了、也能说了,中间那个决定说什么的部分才是客服的价值所在。传统做法是意图分类加槽位填充:先判断这句话属于「问价格」还是「约时间」,再把日期、人数、项目、技师抽出来;这套东西在头部意图上又快又准又便宜,但每加一个新说法就要加一条规则或一批标注。大模型把这件事反过来了:零样本就能理解长尾表达,代价是它不知道你家的价格、不知道今天几点还有空、也不知道哪个技师休假,而且在不知道的时候会非常自信地编一个。所以这个模块的四章其实是一条链:第一章把意图识别的三种做法放在同一个覆盖率模型里比;第二章讲 RAG——把门店知识(价目、项目时长、营业时间、地址停车、优惠规则、禁忌与注意事项)切块、向量化、检索后塞进上下文,并讲清楚召回率和幻觉率的关系;第三章讲工具调用——查空档、占位、确认下单、改约退单,这些是有副作用的写操作,必须考虑超时、重试和幂等,否则一次网络抖动就是两个重复预约;第四章讲话术与护栏:人设、禁语、知识边界、以及按摩养生行业绕不开的两条红线——不许宣称疗效、不许接住任何越界的暗示,遇到就礼貌终止并转人工留痕。",
      en: "Once you can hear and speak, the part in between — deciding what to say — is where the service actually lives. The classical approach is intent classification plus slot filling: decide whether this utterance asks about price or books a time, then extract date, party size, service and therapist. It is fast, accurate and cheap on head intents, but every new phrasing costs a rule or a batch of labels. LLMs invert that: they understand the long tail zero-shot, at the price of not knowing your prices, today's open slots, or who is on leave — and of inventing all three with great confidence. So this module's four chapters form a chain. The first compares three approaches to intent under one coverage model. The second covers RAG: chunking the shop's knowledge (price list, durations, opening hours, address and parking, promotions, contraindications), embedding it, retrieving it into context, and making the link between recall and hallucination explicit. The third covers tool calling — check availability, hold a slot, confirm, reschedule, cancel — side-effecting writes that need timeouts, retries and idempotency, or one network hiccup becomes two duplicate bookings. The fourth covers the script and the guardrails: persona, banned phrasing, knowledge boundaries, and the two lines a massage and wellness business cannot cross — never claim medical efficacy, and never entertain an out-of-bounds suggestion; end politely, hand off to a human, and keep the record.",
    },
  },
  {
    id: "m5", arch: "m5-arch", code: "RT", accent: "primary", level: 3,
    zh: "接得住:实时链路与延迟", en: "Holding the Line: Realtime Pipelines & Latency",
    tagline: { zh: "人在电话里能忍受的沉默大约是 1 秒,而你的链路上有六段,每一段都想多要 200 毫秒。", en: "A human tolerates about one second of silence on a call; your pipeline has six stages, and each one wants another 200 ms." },
    description: {
      zh: "把 ASR、大模型和 TTS 接成一条线不难,难的是让它像人一样接得上话。一段真实的电话对话里,从顾客说完最后一个字到机器开口,中间要走完:尾点静音判定(等你确实说完了)、ASR 最终结果、大模型首字、TTS 首包、网络与播放缓冲。任何一段拖慢 300 毫秒,顾客的感受都是「这人是不是走神了」。本模块先把延迟预算像财务预算一样摊开,看每一段花多少、哪些能重叠、哪些必须串行,再讲三个真正能把总延迟砍下来的工程手段:用 ASR 的中间结果提前喂给大模型、大模型流式输出的第一句话就开始合成、TTS 边合成边播。然后是打断:人在真实通话里会插话,系统必须一边说一边听,这需要回声消除把自己播出去的声音从麦克风里减掉,否则机器会把自己的声音当成顾客在说话;打断阈值定得太敏感,门店背景里的电视声和另一位顾客的说话声都会让它突然闭嘴。最后一章比较两条路线:级联(ASR→LLM→TTS,可控、可审、可接知识库)与端到端语音大模型(延迟低、语气好,但文本层的审核与知识注入更难),并给出在门店场景下的取舍。",
      en: "Wiring ASR, an LLM and TTS into a line is easy; making it take its turn like a person is not. In a real call, between the customer's last syllable and the machine's first, the pipeline must finish: tail-silence endpointing (confirming you really stopped), the final ASR result, the LLM's first token, the TTS first packet, and network plus playback buffering. Let any stage slip 300 ms and the caller feels the other party has drifted off. This module lays out the latency budget the way a finance team lays out a budget — what each stage spends, what can overlap, what must stay serial — then covers the three engineering moves that genuinely cut the total: feeding partial ASR results to the LLM early, starting synthesis on the LLM's first streamed sentence, and playing TTS while it is still being generated. Then barge-in: people interrupt, so the system must listen while it speaks, which requires acoustic echo cancellation to subtract its own output from the microphone, or it hears itself as the customer; set the interrupt threshold too sensitively and the shop's television or a second customer talking nearby will silence it mid-sentence. The last chapter compares the two architectures: the cascade (ASR → LLM → TTS: controllable, auditable, easy to ground in a knowledge base) and end-to-end speech models (lower latency, better prosody, harder to moderate and ground), with a recommendation for the shop case.",
    },
  },
  {
    id: "m6", arch: "m6-arch", code: "PF", accent: "accent", level: 2,
    zh: "平台与选型:买、租、还是自建", en: "Platforms & Selection: Buy, Rent or Build",
    tagline: { zh: "同一段三分钟的通话,在三家平台上的账单可能差五倍,而体验差不到 5%。", en: "The same three-minute call can bill five times higher on one platform than another, for less than 5% difference in experience." },
    description: {
      zh: "这个模块是全书最容易过时、也最值钱的一块:把市面上的语音能力供给方摊开,讲清楚它们的差别到底在哪,以及一家门店或小连锁应该怎么选。国内云厂商(阿里云智能语音交互、腾讯云语音、科大讯飞、火山引擎、百度智能云)在中文电话场景、方言覆盖和合规落地上有天然优势;国际厂商(微软 Azure Speech、Deepgram、AssemblyAI、ElevenLabs、OpenAI 的实时与转写接口)在多语种、音色表现力和实时接口上各有强项,但要额外考虑网络时延与数据出境;开源自建这条路(FunASR / Paraformer / SenseVoice、Whisper、CosyVoice、GPT-SoVITS、Piper、Silero VAD)前期不花钱、数据不出门,代价是一张显卡、一个会运维的人和一条持续的升级路。本模块用三个可调模型帮你做决定:一个按你自己的权重给十余家平台打分的选型矩阵、一个算自建与云服务成本交叉点的 TCO 模型(GPU 折旧、电费、运维人力 vs 每分钟单价),以及一个把单通电话的成本拆成 ASR 分钟费、线路费、大模型 token 费和 TTS 字符费的瀑布图——最后那张图会告诉你,大多数门店的语音账单里最贵的往往不是 AI,是电话线。",
      en: "This is the module that dates fastest and pays best: the supply side of speech, what actually differs between vendors, and how a single shop or a small chain should choose. Chinese cloud vendors (Alibaba Cloud Intelligent Speech, Tencent Cloud, iFlytek, Volcano Engine, Baidu) have structural advantages on Mandarin telephony, dialect coverage and domestic compliance. International vendors (Microsoft Azure Speech, Deepgram, AssemblyAI, ElevenLabs, OpenAI's realtime and transcription APIs) bring multilingual coverage, expressive voices and strong realtime APIs, with network latency and cross-border data transfer as extra considerations. Self-hosting open source (FunASR / Paraformer / SenseVoice, Whisper, CosyVoice, GPT-SoVITS, Piper, Silero VAD) costs nothing per minute and keeps data in the building, at the price of a GPU, someone who can operate it, and a permanent upgrade treadmill. Three adjustable models drive the decision: a selection matrix scoring a dozen platforms against your own weights, a total-cost model finding the break-even concurrency between self-hosting and per-minute cloud pricing (GPU depreciation, power and staffing versus unit price), and a waterfall decomposing one call's cost into ASR minutes, telephony minutes, LLM tokens and TTS characters — the last of which usually reveals that the most expensive thing on a shop's voice bill is not the AI, it is the phone line.",
    },
  },
  {
    id: "m7", arch: "m7-arch", code: "CH", accent: "primary", level: 2,
    zh: "渠道与接入:电话、微信、平台", en: "Channels: Phone, WeChat and the Platforms",
    tagline: { zh: "顾客在哪儿问,你就得在哪儿答;而每条渠道的规则、延迟和红线都不一样。", en: "You must answer where the customer asks — and every channel has its own rules, latency and red lines." },
    description: {
      zh: "语音能力做好了,还得接到顾客真正在用的入口上。门店的入口有三类,性格完全不同。电话是唯一真正实时的渠道:它走 SIP 中继或云呼叫中心,有并发线路数的硬约束,接不住就是忙音;它也是合规压力最大的渠道,尤其是外呼——商业性语音呼叫必须事先取得同意,拒绝之后不得再拨,时段与频次都有约束,做错了不只是被投诉,可能直接被运营商停号。微信生态是复购的主场:公众号客服消息有 48 小时窗口、小程序客服、企业微信可以一对一长期留存关系,AI 在这里的价值不是接电话而是把沉睡会员唤醒——同时要小心触达频次,发多了换来的是拉黑,那是把长期资产换短期 GMV。第三类是美团、大众点评、抖音这样的平台 IM:响应速度直接影响你的流量权重,AI 的首要任务是秒回和准确报价,其次才是转化。最后一章讲人机协同:置信度多低该转人工、情绪识别到什么程度该插入坐席、转过去之后上下文怎么交接——做得好的系统,顾客甚至不知道刚才换了人。",
      en: "A working voice stack still has to meet customers where they already are, and a shop has three very different doorways. The phone is the only truly realtime one: it runs over SIP trunks or a cloud contact centre, has a hard concurrency limit, and returns a busy tone when you run out. It also carries the heaviest compliance load, especially outbound — commercial voice calls require prior consent, must stop permanently once refused, and are bounded by time-of-day and frequency rules; getting this wrong risks more than complaints, it risks losing the number. The WeChat ecosystem is where repeat business lives: official-account service messages have a 48-hour window, mini-programs have their own service channel, and WeCom keeps a durable one-to-one relationship. The AI's value here is not answering calls but waking dormant members — while watching contact frequency, because over-messaging trades a long-term asset for short-term GMV. The third doorway is platform IM on Meituan, Dianping and Douyin, where response speed feeds your ranking, so instant and accurate replies come before conversion. The final chapter covers human handoff: how low a confidence score should trigger it, what emotional signal should summon an agent, and how context transfers — done well, the customer never notices the switch.",
    },
  },
  {
    id: "m8", arch: "m8-arch", code: "OP", accent: "accent", level: 3,
    zh: "运营、合规与增长", en: "Operations, Compliance & Growth",
    tagline: { zh: "上线只是开始;真正决定成败的是你有没有一条「坏案例 → 修 → 验证」的闭环,以及有没有踩线。", en: "Go-live is the start; what decides the outcome is whether you have a bad-case loop — and whether you stayed inside the lines." },
    description: {
      zh: "AI 客服上线第一周的表现几乎总是让人失望:热词没配、知识库缺条目、尾点阈值太短老抢话、某个方言口音识别一塌糊涂。这很正常,问题在于你有没有一条把这些问题捞出来并修掉的闭环。本模块先讲质检:靠人耳抽检 5% 的时代已经过去了,今天可以把全部通话转写后交给模型逐条打分,但抽样统计仍然重要——因为你要知道 200 通样本里发现 3 个坏案例,意味着真实坏案例率的区间有多宽。第二章是全书风险最高的一章:合规。录音要告知、声纹属于敏感个人信息需要单独同意、合成音频要按《人工智能生成合成内容标识办法》做标识、用真人音色要有本人书面授权、外呼要遵守同意与退订规则、宣传不能触碰疗效与医疗暗示、遇到越界咨询必须拒绝并留痕。这些不是可选项,任何一条踩了都可能让整个项目下线。第三章把前面所有东西接成一个增长飞轮:通话数据变成标注、标注变成模型与知识库的改进、改进带来更高的自助率和复购,并给出一份分阶段的落地路线图和一次八维度的成熟度自评。",
      en: "The first week of a live AI service is almost always disappointing: hotwords unconfigured, knowledge base missing entries, the endpoint threshold too short so it interrupts, one regional accent recognised terribly. That is normal; what matters is whether you have a loop that catches and fixes these. This module starts with quality assurance: the era of listening to 5% of calls by ear is over — you can transcribe everything and have a model score each call — but sampling statistics still matter, because finding 3 bad cases in 200 samples implies a confidence interval you need to read honestly. The second chapter is the riskiest in the book: compliance. Recording requires notice; a voiceprint is sensitive personal information requiring separate consent; synthetic audio must be labelled under China's rules on labelling AI-generated content; using a real person's voice requires their written authorisation; outbound calling must honour consent and opt-out; marketing must not touch therapeutic or medical claims; and out-of-bounds enquiries must be refused and logged. None of this is optional, and any one of them can take the whole project offline. The third chapter connects everything into a growth flywheel — calls become labels, labels become model and knowledge improvements, improvements raise self-service rate and repeat business — and closes with a staged rollout roadmap and an eight-dimension maturity self-assessment.",
    },
  },
  {
    id: "m9", arch: "m9-arch", code: "CS", accent: "primary", level: 3, role: "case",
    zh: "案例研究", en: "Case Studies",
    tagline: { zh: "两个完整的复盘:一本三店连锁的账,和一通两分十七秒的电话。", en: "Two full post-mortems: a three-shop chain's ledger, and one call of two minutes seventeen seconds." },
    description: {
      zh: "最后一个模块不引入新概念,而是把整本书用两个完整案例走一遍。第一个案例是一本账:一家开了三家店的中医推拿连锁,日均来电 210 通、前台 4 人、客单价 268 元,他们上了一套 AI 语音客服——一次性集成投入、每月平台与线路费、省下的前台工时、挽回的漏接预约、被唤醒的沉睡会员,减掉被 AI 劝退的那部分顾客,最后算出月净收益、回收期和三年累计。模型里每个参数都可调,你可以把它换成自己店的数字。第二个案例是一通电话的全链路复盘:一位顾客晚上八点打进来想约明天下午的肩颈理疗并指定王师傅,机器从接起到确认用了 2 分 17 秒、11 个轮次,其中有一轮识别错了技师名、有一轮因为知识库缺少「会员卡能否抵用团购」的条目差点编造答案。逐步展开每一跳的耗时、文本、置信度和发生的事情,再注入三类典型故障,看整通电话怎么从成功滑向转人工——这是把前面二十七章的每个参数放回真实上下文里的一次检验。",
      en: "The last module introduces nothing new; it walks the whole book through two complete cases. The first is a ledger: a three-shop tuina chain taking 210 calls a day with four front-desk staff and an average ticket of 268 yuan deploys a voice AI service — one-off integration cost, monthly platform and telephony fees, front-desk hours saved, missed bookings recovered, dormant members woken, minus the customers the AI drives away — producing monthly net benefit, payback period and a three-year total. Every parameter is adjustable, so you can substitute your own shop's numbers. The second is the end-to-end post-mortem of a single call: a customer rings at eight in the evening wanting tomorrow afternoon's neck-and-shoulder treatment with a specific therapist; the machine takes 2 minutes 17 seconds and 11 turns from pickup to confirmation, mishears the therapist's name in one turn, and nearly invents an answer in another because the knowledge base lacks an entry on whether a membership card can offset a group-buy voucher. Every hop's timing, text, confidence and events unfold step by step, and three classic faults can be injected to watch the call slide from success into human handoff — a final test of every parameter from the previous twenty-seven chapters, back in real context.",
    },
  },
];

const CHAPTERS = [
  /* ============ M1 · BZ 生意场景与客服账 ============ */
  {
    id: "v1", code: "BZ1", moduleId: "m1", difficulty: 1, hours: 4, prereq: [], viz: "shopLab",
    props: ["漏接率", "Erlang 呼损", "高峰集中度", "预约价值", "投入上限"],
    title: { zh: "一家按摩店每个月漏掉多少钱", en: "How Much a Massage Shop Loses to Missed Calls" },
    summary: {
      zh: "这一章不讲技术,只算一笔账,而这笔账决定了后面二十八章值不值得看。门店的电话有一个残酷的特性:它不排队。顾客打进来,忙音或者六声无人接,他不会等,他会挂掉再打下一家——你甚至不知道这通电话存在过。本章用电话业务里最经典的 Erlang-B 呼损模型把这件事量化:给定日来电量、高峰时段的集中度(通常 40% 的话量挤在 3 个小时里)、前台能同时处理的通话数、以及平均每通时长,算出高峰小时的呼损率,再乘上预约转化率、客单价和复购倍数,得到每月流失的营业额。声控台让你把这四个参数拖起来:你会看到前台从 1 个加到 2 个,呼损率能从三成掉到几个百分点——但人工加不动的时候,AI 的价值恰恰是把并发数变成一个几乎无限的数字。算完这笔账你会得到一个具体的数字:每月漏掉多少钱。这个数字就是你这套 AI 客服系统的投入上限,也是全书所有技术选型的预算基准。",
      en: "This chapter contains no technology, only arithmetic — arithmetic that decides whether the other twenty-eight chapters are worth your time. A shop's phone line has a brutal property: it does not queue. The customer hears a busy tone or six unanswered rings, does not wait, hangs up and calls the next shop, and you never learn the call existed. We quantify this with telephony's classic Erlang-B blocking model: given daily call volume, peak concentration (typically 40% of calls inside three hours), how many calls the front desk can handle at once, and average handle time, compute peak-hour blocking, then multiply by booking conversion, average ticket and a repeat multiplier to get monthly revenue lost. The bench lets you drag all four: going from one front-desk person to two can drop blocking from thirty percent to a few percent — and when you cannot add people, the AI's real contribution is making concurrency effectively unlimited. You leave with one concrete number: money lost per month. That number is the ceiling on what this system is worth, and the budget baseline for every technical choice in the book.",
    },
    objectives: [
      { zh: "用 Erlang-B 估算门店高峰时段的电话呼损率", en: "Estimate peak-hour call blocking with Erlang-B" },
      { zh: "把漏接电话换算成可比较的月度营收损失", en: "Convert missed calls into comparable monthly revenue loss" },
      { zh: "解释为什么电话渠道不排队、不可补救", en: "Explain why the phone channel neither queues nor forgives" },
      { zh: "为自己的门店定出 AI 客服的投入上限", en: "Set an investment ceiling for your own shop" },
    ],
    outline: [
      { zh: "门店客服的四件事,只有一件有时间窗口", en: "Four jobs, only one with a time window" },
      { zh: "Erlang-B:并发线路、话务强度与呼损", en: "Erlang-B: lines, traffic intensity and blocking" },
      { zh: "从漏接通数到流失营业额", en: "From missed calls to lost revenue" },
      { zh: "这笔账怎么变成你的技术预算", en: "Turning the bill into a technology budget" },
    ],
  },
  {
    id: "v2", code: "BZ2", moduleId: "m1", difficulty: 1, hours: 4, prereq: ["v1"], viz: "autoLab",
    props: ["任务分级", "自助解决率", "转人工率", "过度自动化", "工时节省"],
    title: { zh: "十二类咨询,哪些该交给 AI", en: "Twelve Enquiry Types, and Which Ones the AI Should Take" },
    summary: {
      zh: "把门店一个月的通话记录拉出来分类,结果高度一致:问价格和项目、问营业时间和地址、问今天还有没有空位、预约与改约、问技师在不在,这五类就占了八成。它们的共同点是答案确定、不需要判断力、也不需要同情心——这正是 AI 最擅长的区间。剩下两成里藏着完全不同的东西:投诉与不满、疼痛与身体状况的咨询、团购券与会员卡的复杂核销、以及极少数越界的试探。这些要么需要共情,要么需要授权,要么需要一个人来承担责任。本章的声控台把十二类咨询按占比、复杂度、是否需要写操作排开,你可以逐类点选「交给 AI」或「留给人工」,实时看到自助解决率、转人工率、节省的前台工时和顾客满意度。有意思的是它会让你撞上「过度自动化」的墙:当你把投诉类也勾给 AI,自助率的数字确实上升了,但满意度和转人工后的挽回成本一起恶化——因为顾客不是被解决了,是被劝退了。",
      en: "Pull a month of call logs and the distribution is remarkably consistent: price and services, hours and address, availability today, booking and rescheduling, and whether a particular therapist is in — five categories are eighty percent of volume. They share three traits: the answer is definite, no judgement is required, and no sympathy is required. That is exactly the AI's zone. The remaining fifth hides something else entirely: complaints, questions about pain and physical conditions, complex redemption of vouchers and membership cards, and a rare out-of-bounds probe. Those need empathy, authority, or a person willing to own the outcome. The bench arranges twelve enquiry types by share, complexity and whether they write to a system; you click each one to the AI or the humans and watch self-service rate, handoff rate, front-desk hours saved and satisfaction respond live. The instructive part is hitting the over-automation wall: assign complaints to the AI and the self-service number does rise, while satisfaction and post-handoff recovery cost both deteriorate — because the customer was not served, they were deflected.",
    },
    objectives: [
      { zh: "按占比、复杂度与副作用给客服任务分级", en: "Rank service tasks by share, complexity and side effects" },
      { zh: "区分「AI 能答」与「AI 该答」", en: "Separate what the AI can answer from what it should answer" },
      { zh: "估算自动化带来的工时节省与残留人工", en: "Estimate hours saved and the human work that remains" },
      { zh: "识别过度自动化的反噬信号", en: "Recognise the backlash signals of over-automation" },
    ],
    outline: [
      { zh: "一个月通话记录的真实分布", en: "The real distribution in a month of calls" },
      { zh: "三个分级维度:占比、复杂度、写操作", en: "Three axes: share, complexity, write operations" },
      { zh: "自助解决率与转人工率的此消彼长", en: "Self-service rate against handoff rate" },
      { zh: "过度自动化:被解决还是被劝退", en: "Over-automation: served, or deflected" },
    ],
  },
  {
    id: "v3", code: "BZ3", moduleId: "m1", difficulty: 1, hours: 5, prereq: ["v2"], viz: "callLab",
    props: ["对话轮次", "槽位澄清", "连乘成功率", "通话时长", "少问一轮"],
    title: { zh: "一通预约电话的解剖:轮次、槽位与成交率", en: "Anatomy of a Booking Call: Turns, Slots and Completion" },
    summary: {
      zh: "一通成功的预约电话有固定的骨架:问候、听出意图、把缺的槽位补齐(日期、时间、项目、人数、技师、姓名电话)、查空档、确认、收尾。每一轮都有可能失败——顾客说话被识别错、机器问得不清楚顾客答非所问、查空档超时、顾客中途失去耐心。关键在于这些概率是连乘的:每轮 95% 的成功率听起来很高,但八轮连乘只剩 66%。本章的声控台把这条链摆出来,让你调每轮的成功率、需要补的槽位数量、以及是否启用「一次多槽抽取」(顾客一句「明天下午三点两个人做肩颈」直接填满四个槽)和「默认值兜底」(不指定技师就派单)。你会立刻看到一个反直觉的结论:把每轮成功率从 95% 提到 97%(需要换更贵的模型)带来的收益,远小于把轮次从 8 轮压到 5 轮(只需要改提示词和交互设计)。这是整本书最重要的工程直觉之一——在对话系统里,减少交互次数几乎总是比提升单次准确率更划算。",
      en: "A successful booking call has a fixed skeleton: greet, catch the intent, fill the missing slots (date, time, service, party size, therapist, name and number), check availability, confirm, close. Every turn can fail — the customer is misrecognised, the machine asks unclearly and gets an unrelated answer, the availability lookup times out, the customer loses patience. The crucial part is that these probabilities multiply: 95% per turn sounds excellent, yet eight turns leave 66%. The bench lays the chain out so you can move per-turn success, the number of slots still to fill, and whether to enable multi-slot extraction (one sentence — tomorrow at three, two people, neck and shoulders — filling four slots at once) and default fallbacks (assign any available therapist when none is named). A counter-intuitive result appears immediately: lifting per-turn success from 95% to 97% (which costs a more expensive model) gains far less than cutting turns from eight to five (which costs a prompt rewrite). That is one of the book's most important engineering intuitions — in dialogue systems, removing an interaction almost always beats improving a single one.",
    },
    objectives: [
      { zh: "画出一通预约电话的轮次骨架与槽位清单", en: "Map a booking call's turn skeleton and slot list" },
      { zh: "用连乘模型解释多轮对话为什么脆弱", en: "Explain multi-turn fragility with the product model" },
      { zh: "比较「提升单轮准确率」与「减少轮次」的收益", en: "Compare raising per-turn accuracy against removing turns" },
      { zh: "设计一次多槽抽取与默认值兜底", en: "Design multi-slot extraction and default fallbacks" },
    ],
    outline: [
      { zh: "预约电话的六段骨架", en: "The six-stage skeleton of a booking call" },
      { zh: "槽位:哪些必须问,哪些可以猜", en: "Slots: which must be asked, which can be guessed" },
      { zh: "连乘成功率与轮次的关系", en: "Chained success and the cost of a turn" },
      { zh: "少问一轮胜过多准两点", en: "One fewer question beats two more points" },
    ],
  },

  /* ============ M2 · AS 听清:语音识别 ============ */
  {
    id: "v4", code: "AS1", moduleId: "m2", difficulty: 1, hours: 5, prereq: [], viz: "audioLab",
    props: ["采样率", "奈奎斯特", "窄带电话", "编码与码率", "擦音丢失"],
    title: { zh: "声音怎么变成数字:采样率、编码与电话窄带", en: "Sound into Numbers: Sample Rate, Codecs and the Narrowband Phone" },
    summary: {
      zh: "所有语音问题的根都在这一章。麦克风把空气压力的连续变化变成电压,采样把它按固定间隔切成一串数字,位深决定每个数字有多精细。奈奎斯特定理说得很硬:采样率的一半就是你能记录的最高频率——16 kHz 采样能记到 8 kHz,而电话系统的 8 kHz 采样只能记到 4 kHz。这不是工程妥协可以绕过的,是物理上限。问题在于汉语的擦音和塞擦音(s、sh、f、x、c、ch)的辨别能量大量落在 4 kHz 以上,所以电话里「四」和「十」、「十四」和「四十」特别容易听混,而这两个词恰恰天天出现在预约电话里。声控台让你切换 8 kHz G.711、8 kHz Opus、16 kHz PCM、16 kHz Opus 等组合,实时算出码率、每分钟字节数、月存储量和可记录的频率上限,并用一张频谱示意图显示被砍掉的部分,同时给出该配置下数字与人名识别的相对难度。这一章也解释了一个常见的困惑:为什么同一家厂商的模型,你在网页上试很准,一接到电话上就明显变差。",
      en: "Every speech problem is rooted here. A microphone turns continuous air pressure into voltage; sampling slices it at fixed intervals into numbers; bit depth sets how finely each number is resolved. Nyquist is unforgiving: half the sample rate is the highest frequency you can record — 16 kHz captures up to 8 kHz, while telephony's 8 kHz captures only up to 4 kHz. This is a physical ceiling, not an engineering compromise. The trouble is that the discriminating energy of Mandarin fricatives and affricates (s, sh, f, x, c, ch) sits largely above 4 kHz, which is why four and ten, fourteen and forty are so easily confused on a phone — words that appear in every single booking call. The bench switches between 8 kHz G.711, 8 kHz Opus, 16 kHz PCM and 16 kHz Opus, computing bitrate, bytes per minute, monthly storage and the recordable frequency ceiling, drawing the spectrum that was cut away, and rating how hard digits and names become under that configuration. It also explains a common puzzle: why the same vendor's model is impressively accurate in the browser demo and visibly worse the moment it is on a phone line.",
    },
    objectives: [
      { zh: "用奈奎斯特定理解释窄带电话的频率上限", en: "Explain the narrowband ceiling with the Nyquist theorem" },
      { zh: "计算不同采样率与编码下的码率和存储量", en: "Compute bitrate and storage across sample rates and codecs" },
      { zh: "说明为什么电话里数字与人名最容易错", en: "Say why digits and names fail most on phone lines" },
      { zh: "为门店场景选择合适的音频参数", en: "Choose audio parameters for the shop scenario" },
    ],
    outline: [
      { zh: "采样、量化、位深:三个基本量", en: "Sampling, quantisation, bit depth" },
      { zh: "奈奎斯特与 8 kHz 电话的 4 kHz 天花板", en: "Nyquist and the 4 kHz ceiling of 8 kHz telephony" },
      { zh: "G.711 / G.729 / Opus:压缩的取舍", en: "G.711 / G.729 / Opus: the compression trade" },
      { zh: "被砍掉的擦音,和被听错的数字", en: "The lost fricatives and the misheard digits" },
    ],
  },
  {
    id: "v5", code: "AS2", moduleId: "m2", difficulty: 2, hours: 6, prereq: ["v4"], viz: "asrLab",
    props: ["CTC", "RNN-T", "Paraformer", "Whisper", "流式与 chunk"],
    title: { zh: "ASR 模型:CTC、RNN-T、Paraformer 与 Whisper", en: "ASR Models: CTC, RNN-T, Paraformer and Whisper" },
    summary: {
      zh: "识别模型这些年的主线,是一场「怎么把声音帧对齐到文字」的接力。早期的 DNN-HMM 需要强制对齐和发音词典,工程繁重;CTC 用一个空白符号让模型自己学会对齐,简单高效,但每帧独立解码、不建模输出之间的依赖,容易出现「听起来对、读起来不通」的结果;RNN-T 在 CTC 上加了一个预测网络,把已输出的文字也纳入条件,既能流式又有语言建模能力,是今天绝大多数实时语音产品的主力;Paraformer 走另一条路——非自回归一次性并行输出,推理特别快,适合大批量转写;而 Whisper 这类基于注意力的编解码器模型,依赖整段音频的全局注意力,准确率和鲁棒性都很好,但结构上天生不适合低延迟流式(常见做法是切片近似,代价是边界错误)。本章的声控台把这四种放在同一张「延迟—准确率—成本」的坐标里,并让你调 chunk 大小和右侧前瞻帧数,看首字延迟与字错率此消彼长:chunk 越小越跟手,但模型看到的上下文越少,错得越多。",
      en: "The main line of recognition research is a relay race about aligning audio frames to text. Early DNN-HMM systems needed forced alignment and a pronunciation lexicon — heavy engineering. CTC introduced a blank symbol so the model learns alignment itself: simple and efficient, but it decodes each frame independently without modelling dependencies between outputs, which produces results that sound right and read wrong. RNN-T adds a prediction network conditioning on what has already been emitted, giving both streaming and language modelling, and carries most realtime speech products today. Paraformer takes another route — non-autoregressive, emitting the whole utterance in parallel, very fast, ideal for bulk transcription. Attention encoder-decoder models like Whisper rely on global attention over a whole segment, giving excellent accuracy and robustness but structurally resisting low-latency streaming (the common workaround, chunked approximation, pays in boundary errors). The bench places all four on one latency-accuracy-cost plane and lets you move chunk size and right-context lookahead to watch first-token latency trade against character error: smaller chunks feel more responsive and see less context, so they make more mistakes.",
    },
    objectives: [
      { zh: "解释 CTC 的对齐机制与它的独立性假设", en: "Explain CTC alignment and its independence assumption" },
      { zh: "说明 RNN-T 为什么能同时流式与建模语言", en: "Say why RNN-T streams and models language at once" },
      { zh: "比较自回归与非自回归解码的速度差异", en: "Compare autoregressive and non-autoregressive decoding" },
      { zh: "用 chunk 与前瞻解释流式的延迟—准确率权衡", en: "Use chunk and lookahead to explain the streaming trade-off" },
    ],
    outline: [
      { zh: "对齐问题:从 HMM 到 CTC", en: "The alignment problem: HMM to CTC" },
      { zh: "RNN-T:流式识别的今天", en: "RNN-T: today's streaming workhorse" },
      { zh: "Paraformer 的并行解码与 Whisper 的全局注意力", en: "Paraformer's parallel decoding, Whisper's global attention" },
      { zh: "chunk、前瞻与首字延迟", en: "Chunk, lookahead and first-token latency" },
    ],
  },
  {
    id: "v6", code: "AS3", moduleId: "m2", difficulty: 2, hours: 5, prereq: ["v5"], viz: "vadLab",
    props: ["VAD", "尾点静音", "误截断", "语义端点", "抢话与呆滞"],
    title: { zh: "VAD 与端点检测:机器凭什么认为你说完了", en: "VAD and Endpointing: How the Machine Decides You Finished" },
    summary: {
      zh: "这一章讲的是整套系统里最不起眼、却最影响体验的一个参数。机器必须判断顾客「说完了没有」,而它唯一可靠的信号是停顿。做法是设一个尾点静音阈值:检测到连续 N 毫秒没有语音,就认为一轮结束,把音频交给识别与大模型。阈值定短了(比如 300 毫秒),顾客说「我想约明天下午……嗯……三点」这种自然停顿就会被切断,机器抢话,顾客体验极差;定长了(比如 1200 毫秒),每一轮都要多等一秒,对话变得像在跟一个反应迟钝的人说话。真实的人类思考停顿分布是有长尾的,所以这是一个概率问题,不是一个可以拍死的常数。本章的声控台用蒙特卡洛模拟真实的停顿分布,让你拖动阈值,同时看到误截断率(顾客被打断的比例)和平均等待延迟两条曲线交叉,找到那个通常落在 600–800 毫秒的最优区间;再打开「语义端点检测」——让模型判断这句话在语法和意图上是否完整——你会看到两条曲线同时往下走,这就是它值得多花那点算力的原因。",
      en: "This chapter covers the least glamorous parameter in the stack and the one that most shapes how the system feels. The machine must decide whether the customer has finished, and its only reliable signal is silence. The standard approach is a tail-silence threshold: after N milliseconds without speech, end the turn and hand the audio onward. Set it short — say 300 ms — and a natural hesitation in I would like to book tomorrow afternoon… um… three o'clock gets cut off, the machine talks over the customer, and the experience collapses. Set it long — 1200 ms — and every turn waits an extra second, making the conversation feel like talking to someone slow. Real human thinking pauses have a long tail, so this is a probability problem rather than a constant you can pick. The bench runs a Monte-Carlo over a realistic pause distribution while you drag the threshold, showing the false-cut rate and the average added delay crossing somewhere around 600–800 ms. Then switch on semantic endpointing — letting a model judge whether the utterance is grammatically and intentionally complete — and both curves fall together, which is exactly why it is worth the extra compute.",
    },
    objectives: [
      { zh: "说明 VAD 与端点检测的区别与各自职责", en: "Distinguish VAD from endpointing and their duties" },
      { zh: "用停顿分布解释误截断与等待延迟的权衡", en: "Use pause distributions to explain the cut-versus-wait trade" },
      { zh: "为门店电话场景选一个合适的尾点阈值", en: "Pick a tail-silence threshold for shop telephony" },
      { zh: "说明语义端点检测为什么能同时改善两端", en: "Say why semantic endpointing improves both sides at once" },
    ],
    outline: [
      { zh: "能量 VAD、WebRTC VAD 与 Silero 神经 VAD", en: "Energy VAD, WebRTC VAD and neural Silero VAD" },
      { zh: "尾点静音阈值:抢话与呆滞的两端", en: "Tail silence: interrupting at one end, dawdling at the other" },
      { zh: "真实停顿分布的长尾", en: "The long tail of real pauses" },
      { zh: "语义端点检测:让模型判断说完没有", en: "Semantic endpointing: let a model judge completeness" },
    ],
  },
  {
    id: "v7", code: "AS4", moduleId: "m2", difficulty: 2, hours: 6, prereq: ["v5"], viz: "werLab",
    props: ["WER 与 CER", "编辑距离", "热词偏置", "槽位准确率", "ITN 逆文本规范化"],
    title: { zh: "评测与调优:CER、热词,和那个总是听错的手机号", en: "Evaluation and Tuning: CER, Hotwords, and the Phone Number It Keeps Getting Wrong" },
    summary: {
      zh: "厂商宣传页上的准确率不能直接用,原因有三:测试集不是你的场景、指标不是你关心的那个、而且他们通常报的是安静环境的宽带音频。本章先把评测做扎实:词错率 WER 和中文更常用的字错率 CER,都是用编辑距离算出来的——替换、删除、插入三类错误的总数除以参考文本长度,声控台里跑的是真实的 Levenshtein 动态规划,你可以改识别结果看数字怎么动。然后是更重要的一步:业务指标。对门店来说,「整段转写的 CER 是 5%」几乎没有意义,真正要命的是关键槽位——手机号、日期时间、金额、技师姓名——是否完全正确。而这里有个可怕的数学:一个 11 位手机号,每个字符独立错误率 5%,全对的概率只有 0.95 的 11 次方,大约 57%。也就是说近一半的顾客要重复一遍号码。补救手段有三层:热词与语言模型偏置(把技师名和项目名的先验概率抬上去)、逆文本规范化(把「一三八」正确还原为数字串)、以及在关键槽位上做复述确认。",
      en: "Vendor accuracy claims are unusable as they stand, for three reasons: the test set is not your scenario, the metric is not the one you care about, and the numbers usually come from clean wideband audio. This chapter first makes evaluation solid: word error rate, and the character error rate more commonly used for Chinese, are both edit distances — substitutions, deletions and insertions summed and divided by reference length — and the bench runs a real Levenshtein dynamic program you can feed your own hypothesis to. Then the step that matters more: business metrics. A 5% CER over a full transcript means almost nothing to a shop; what matters is whether the critical slots — phone number, date and time, amount, therapist name — are exactly right. And here the arithmetic is alarming: for an eleven-digit number with 5% independent per-character error, the probability that all eleven are right is 0.95 to the eleventh, about 57%. Nearly half your customers have to repeat their number. The remedies come in three layers: hotword and language-model biasing to raise the prior on therapist and service names, inverse text normalisation to recover a proper digit string, and read-back confirmation on the critical slots.",
    },
    objectives: [
      { zh: "用编辑距离手工计算一段转写的 CER", en: "Compute a transcript's CER by edit distance" },
      { zh: "区分整体字错率与关键槽位准确率", en: "Separate overall CER from critical-slot accuracy" },
      { zh: "用热词与偏置提升专有名词识别", en: "Raise proper-noun accuracy with hotwords and biasing" },
      { zh: "设计关键信息的复述确认策略", en: "Design read-back confirmation for critical fields" },
    ],
    outline: [
      { zh: "WER / CER:三类错误与编辑距离", en: "WER / CER: three error types and edit distance" },
      { zh: "为什么厂商的准确率不能直接用", en: "Why vendor accuracy numbers do not transfer" },
      { zh: "槽位准确率:0.95 的 11 次方", en: "Slot accuracy: 0.95 to the eleventh" },
      { zh: "热词、ITN 与复述确认", en: "Hotwords, ITN and read-back confirmation" },
    ],
  },

  /* ============ M3 · TS 说好:语音合成 ============ */
  {
    id: "v8", code: "TS1", moduleId: "m3", difficulty: 2, hours: 5, prereq: [], viz: "ttsLab",
    props: ["声学模型", "声码器", "VITS", "MOS", "实时率 RTF"],
    title: { zh: "TTS 技术路线:从拼接到神经声码器", en: "TTS Routes: From Concatenation to Neural Vocoders" },
    summary: {
      zh: "语音合成有四条历史路线,今天仍能在不同的产品里见到它们的影子。拼接合成把真人录音切成音素片段再拼起来,自然度高但只能说录过的话,换一句话就得重录;参数合成用 HMM 建模声学参数再用声码器还原,灵活、体积小,但那种闷闷的电音感很明显;神经两段式是当前主流——声学模型(Tacotron 2 的自回归注意力、FastSpeech 2 的非自回归并行)把文本变成梅尔频谱,神经声码器(WaveNet 太慢,HiFi-GAN 是今天的实用解)把频谱还原成波形;端到端模型(VITS、CosyVoice 系列)则把两段合一,还顺带解决了零样本音色复刻。评价上,MOS 平均意见分从参数时代的 3.5 走到今天的 4.4 以上,人类真声大约 4.7。但对工程更重要的指标是 RTF 实时率——合成 1 秒音频要花多少秒计算:GPU 上好的模型能到 0.05,也就是 20 倍实时;CPU 上可能只有 0.5,并发一上来就排队。声控台让你在质量、延迟、显存和并发之间挪动,看一台服务器到底能扛多少路同时通话。",
      en: "Speech synthesis has four historical routes, and you can still see all of them in shipping products. Concatenative systems cut real recordings into units and stitch them: highly natural, but limited to what was recorded, so a new sentence means a new session. Parametric HMM systems model acoustic parameters and reconstruct with a vocoder: flexible and tiny, with an unmistakable muffled buzz. The neural two-stage pipeline is today's mainstream — an acoustic model (Tacotron 2's autoregressive attention, FastSpeech 2's non-autoregressive parallelism) maps text to a mel spectrogram, and a neural vocoder (WaveNet was too slow; HiFi-GAN is the practical answer) maps that to a waveform. End-to-end models (VITS, the CosyVoice family) fuse both stages and throw in zero-shot voice cloning. On quality, mean opinion scores moved from about 3.5 in the parametric era to above 4.4 today, against roughly 4.7 for real human speech. But the number that matters to engineering is the real-time factor: seconds of compute per second of audio. A good model on a GPU reaches 0.05 — twenty times realtime; on CPU it may be 0.5, and concurrency immediately queues. The bench moves quality, latency, VRAM and concurrency together so you can see how many simultaneous calls one server actually carries.",
    },
    objectives: [
      { zh: "说清声学模型与声码器各自负责什么", en: "State what the acoustic model and the vocoder each do" },
      { zh: "比较自回归与非自回归合成的速度与稳定性", en: "Compare autoregressive and non-autoregressive synthesis" },
      { zh: "用 RTF 估算一台机器能承载的并发路数", en: "Estimate concurrent calls per machine from RTF" },
      { zh: "在 MOS、延迟与成本之间做出选择", en: "Choose between MOS, latency and cost" },
    ],
    outline: [
      { zh: "四条路线:拼接、参数、神经两段、端到端", en: "Four routes: concatenative, parametric, two-stage, end-to-end" },
      { zh: "声码器的进化:WaveNet 到 HiFi-GAN", en: "Vocoder evolution: WaveNet to HiFi-GAN" },
      { zh: "MOS 与主观评测的组织方法", en: "MOS and how to run a subjective evaluation" },
      { zh: "RTF、显存与并发容量", en: "RTF, VRAM and concurrency capacity" },
    ],
  },
  {
    id: "v9", code: "TS2", moduleId: "m3", difficulty: 2, hours: 5, prereq: ["v8"], viz: "prosodyLab",
    props: ["文本规范化", "SSML", "多音字", "数字读法", "停顿与语速"],
    title: { zh: "读对比读好更重要:文本规范化与 SSML", en: "Reading Correctly Beats Reading Beautifully: Normalisation and SSML" },
    summary: {
      zh: "顾客说「一听就是机器人」,八成不是音质问题,是读法问题。门店话术里全是雷区:「¥138」要读成「一百三十八元」;手机号「13812345678」要读成一串数字并且按 3-4-4 分组停顿,读成「一百三十八亿……」就彻底失败;「13:30」在口语里是「下午一点半」而不是「十三点三十分」;「B 座 2F」要读成「B 座二楼」;多音字更是天天踩——「重(chóng)新」「行(háng)业」「的(dí)确」「长(zhǎng)按」。解决方案分两层:第一层是文本规范化(TN),在文本进入合成之前把数字、时间、金额、单位、符号按读法展开,这一步是规则加词典的活儿,不该指望模型自己猜;第二层是 SSML,用标记语言显式控制停顿、语速、音高、以及「按电话号码读」这类朗读方式。本章的声控台内置了一个真实的中文规范化小引擎:输入一段门店话术,切换「裸文本 / +TN / +TN+SSML」三档,看到实际送进合成器的文本怎么变,并给出可懂度评分与顾客复述错误率的估计。",
      en: "When a customer says it sounds like a robot, four times out of five the problem is not audio quality but reading. Shop scripts are full of mines: 138 yuan must be read as one hundred thirty-eight yuan; a mobile number must be read digit by digit with 3-4-4 grouping pauses, never as a huge cardinal number; 13:30 in speech is half past one in the afternoon, not thirteen thirty; Block B 2F is Block B, second floor. Chinese heteronyms are a daily hazard. The fix comes in two layers. First, text normalisation: before synthesis, expand numbers, times, amounts, units and symbols into how they are actually said — a job for rules and dictionaries, not something to leave the model to guess. Second, SSML: a markup language that explicitly controls pauses, rate, pitch, and reading modes such as read this as a telephone number. The bench ships a real miniature Chinese normalisation engine — paste a shop script, switch between raw text, normalised, and normalised plus SSML, watch exactly what text reaches the synthesiser, and read off an intelligibility score and an estimated rate of customers asking you to repeat.",
    },
    objectives: [
      { zh: "列出中文 TTS 的主要读法雷区", en: "List the main Chinese TTS reading hazards" },
      { zh: "写出金额、时间、电话号码的规范化规则", en: "Write normalisation rules for amounts, times and numbers" },
      { zh: "用 SSML 控制停顿、语速与朗读方式", en: "Control pauses, rate and speak-as with SSML" },
      { zh: "为门店常用话术建立一份读法词典", en: "Build a pronunciation dictionary for shop scripts" },
    ],
    outline: [
      { zh: "TN 文本规范化:数字、时间、金额、单位", en: "Text normalisation: numbers, times, amounts, units" },
      { zh: "多音字与专有名词的读法词典", en: "Heteronyms and a proper-noun dictionary" },
      { zh: "SSML:break、prosody、say-as", en: "SSML: break, prosody, say-as" },
      { zh: "可懂度:让顾客不用问第二遍", en: "Intelligibility: never make them ask twice" },
    ],
  },
  {
    id: "v10", code: "TS3", moduleId: "m3", difficulty: 2, hours: 5, prereq: ["v8"], viz: "cloneLab",
    props: ["零样本复刻", "微调音色", "声音权", "深度合成标识", "书面授权"],
    title: { zh: "品牌音色与音色克隆:技术很容易,合规很硬", en: "Brand Voice and Cloning: Easy in Code, Hard in Law" },
    summary: {
      zh: "很多老板的第一反应是:能不能用我自己的声音,或者店里那位最受欢迎的技师的声音?技术上这件事已经非常容易——CosyVoice、GPT-SoVITS 这类模型用三到十秒的参考音频就能做零样本复刻,相似度大概能到七成;给上半小时干净录音做微调,相似度能到九成以上,连口头禅和停顿习惯都学得像。正因为太容易,风险也被放大了。《民法典》第 1023 条把自然人的声音参照肖像权保护,未经本人同意不得使用;针对深度合成和 AI 生成内容的规定要求对合成音频进行标识;而实际操作里最容易出事的是离职——一位技师离职后,他的声音还在给你的门店接电话,这在法律上和情理上都站不住。本章的声控台有两半:上半是技术模型,样本时长对相似度的对数增长曲线、微调成本与推理成本;下半是一份合规闸门清单——本人书面授权、用途与期限限定、可撤回条款、合成内容标识、离职后停用流程——任何一项没勾上,系统就判定「不可上线」并说明理由。",
      en: "Many owners' first instinct is to use their own voice, or the voice of the shop's most requested therapist. Technically this is now trivial: CosyVoice, GPT-SoVITS and similar models clone zero-shot from three to ten seconds of reference audio at roughly seventy percent similarity, and half an hour of clean recording fine-tuned pushes past ninety percent, catching verbal tics and pause habits. Because it is so easy, the risk is magnified. China's Civil Code article 1023 protects a natural person's voice by reference to portrait rights, so it cannot be used without consent; rules on deep synthesis and AI-generated content require synthetic audio to be labelled. In practice the most common accident is departure — a therapist leaves, and their voice is still answering your phone, which stands up neither legally nor morally. The bench has two halves. The upper half is the technical model: a logarithmic similarity curve against sample duration, plus fine-tuning and inference cost. The lower half is a compliance gate — written authorisation, scope and term limits, a revocation clause, synthetic-content labelling, and a shutdown procedure on departure — and any unchecked item marks the system not ready to ship, with the reason spelled out.",
    },
    objectives: [
      { zh: "比较零样本复刻与微调的相似度与成本", en: "Compare zero-shot cloning with fine-tuning on similarity and cost" },
      { zh: "说明使用真人声音必须具备的授权要件", en: "State the authorisation required to use a real voice" },
      { zh: "理解合成内容标识的要求与做法", en: "Understand synthetic-content labelling requirements" },
      { zh: "设计一套音色的启用、限期与停用流程", en: "Design an enable, expire and retire flow for a voice" },
    ],
    outline: [
      { zh: "零样本复刻怎么做到的:说话人嵌入与提示音频", en: "How zero-shot cloning works: speaker embeddings and prompt audio" },
      { zh: "样本时长与相似度的关系", en: "Sample duration against similarity" },
      { zh: "声音权、书面授权与用途限定", en: "Voice rights, written authorisation, scope limits" },
      { zh: "标识、留痕与离职停用", en: "Labelling, records and retiring a departed voice" },
    ],
  },
  {
    id: "v11", code: "TS4", moduleId: "m3", difficulty: 3, hours: 5, prereq: ["v8"], viz: "streamLab",
    props: ["流式合成", "首包延迟", "分句切块", "缓冲欠载", "播放时钟"],
    title: { zh: "流式合成:首包延迟与那一下卡顿", en: "Streaming Synthesis: First-Packet Latency and That Stutter" },
    summary: {
      zh: "假设机器要说一句 40 个字的话。整句合成的做法是等全部音频生成完再开始播,顾客要等的时间等于整句的合成时间;流式合成则按标点把文本切成几块,第一块一合成完就开始播,后面几块在播放的同时继续生成——顾客等待的时间只等于第一块的合成时间,通常能从八百毫秒降到两百毫秒以内。这是实时语音里性价比最高的一个优化。但它引入了一个新的失败模式:缓冲欠载。如果合成速度跟不上播放速度(RTF 太高、GPU 被别的请求抢了、某一块文本特别长),播放指针会追上生成指针,顾客听到的就是话说到一半突然卡住,然后接上——比慢一点开口更难受。本章的声控台是一个真实的双指针排队模拟:你调文本长度、切块策略(按标点 / 按固定字数 / 首块特意切短)、RTF 和网络抖动,时间轴上会画出每一块的生成区间和播放区间,并标出发生欠载的位置。你会发现一个实用技巧:把第一块特意切得很短(比如只要「好的,」两个字),首包延迟立刻减半,而后面的块有充足时间追上。",
      en: "Suppose the machine must say a forty-character sentence. Whole-utterance synthesis waits for all the audio before playing, so the customer waits the full synthesis time. Streaming synthesis splits the text on punctuation, plays the first chunk as soon as it is ready, and keeps generating the rest during playback — so the wait equals only the first chunk's synthesis time, typically falling from eight hundred milliseconds to under two hundred. It is the highest-return optimisation in realtime voice. It also introduces a new failure mode: buffer underrun. If generation cannot keep up with playback (RTF too high, the GPU stolen by another request, one chunk unusually long), the playback pointer catches the generation pointer and the customer hears the sentence freeze mid-word and resume — worse than simply starting later. The bench is a real two-pointer queue simulation: set text length, chunking strategy (punctuation, fixed length, or a deliberately short first chunk), RTF and network jitter, and the timeline draws each chunk's generation and playback spans with underruns marked. One practical trick becomes obvious: make the first chunk tiny — just a two-character acknowledgement — and first-packet latency halves while later chunks get ample time to catch up.",
    },
    objectives: [
      { zh: "区分整句合成与流式合成的等待时间", en: "Distinguish whole-utterance and streaming wait times" },
      { zh: "用双指针模型解释缓冲欠载的成因", en: "Explain underrun with the two-pointer model" },
      { zh: "设计一个首块更短的分句切块策略", en: "Design a chunking strategy with a shorter first chunk" },
      { zh: "在 RTF 波动下估算安全的缓冲水位", en: "Estimate a safe buffer level under RTF jitter" },
    ],
    outline: [
      { zh: "首包延迟:顾客真正等的是什么", en: "First-packet latency: what the customer actually waits for" },
      { zh: "切块策略:标点、字数与首块特例", en: "Chunking: punctuation, length and the first-chunk exception" },
      { zh: "生成指针追不上播放指针", en: "When generation cannot outrun playback" },
      { zh: "抖动、预取与缓冲水位", en: "Jitter, prefetch and buffer level" },
    ],
  },

  /* ============ M4 · BR 会想:大模型对话大脑 ============ */
  {
    id: "v12", code: "BR1", moduleId: "m4", difficulty: 2, hours: 5, prereq: ["v3"], viz: "intentLab",
    props: ["意图分类", "槽位填充", "长尾覆盖", "Zipf 分布", "边际成本"],
    title: { zh: "意图与槽位:规则、分类器,还是直接上大模型", en: "Intents and Slots: Rules, a Classifier, or Just an LLM" },
    summary: {
      zh: "理解顾客在说什么,有三条路。规则匹配最直接:关键词命中「多少钱」就归到问价格,写一条规则五分钟搞定,头部意图上准确率极高、延迟几乎为零、成本为零;但顾客的说法是长尾的——「你们那个捏背的多少米」「肩颈的套餐现在啥价」——规则覆盖到八成之后,每多覆盖一个百分点都要新增大量规则,而且相互冲突。训练一个意图分类器(BERT 之类)能泛化得好一些,代价是要标注数据,每加一个新意图就要重新标注和训练。大模型把这件事变成了零样本:把意图列表和槽位定义写进提示词,长尾说法直接就懂,新增一个意图只要加一行描述;代价是每轮几百毫秒延迟和按 token 计费的成本,以及偶尔的不稳定。本章的声控台用 Zipf 分布模拟真实的意图长尾(头部五个意图占八成),让你调意图总数、长尾比例和三种方案的配比,实时算出覆盖率、准确率、每轮延迟与成本,还有最有说服力的那个指标:新增一个意图的边际成本。多数门店的最优解是混合——头部走规则秒回,长尾兜底交给大模型。",
      en: "There are three roads to understanding what a customer said. Rules are the most direct: match a keyword for price and you have an intent in five minutes, with near-perfect accuracy on head intents, no latency and no cost. But phrasing has a long tail, and past about eighty percent coverage every additional point costs a pile of new rules that start contradicting each other. Training an intent classifier generalises better at the cost of labelled data, with relabelling and retraining for every new intent. LLMs turn the job zero-shot: put the intent list and slot definitions in the prompt and long-tail phrasing is understood immediately, with a new intent costing one line of description — paid for in a few hundred milliseconds per turn, per-token billing, and occasional instability. The bench models the real long tail with a Zipf distribution (the top five intents covering eighty percent of traffic) and lets you set intent count, tail weight and the mix of the three approaches, computing coverage, accuracy, per-turn latency and cost live — plus the most persuasive metric of all, the marginal cost of adding one intent. For most shops the optimum is hybrid: rules answer the head instantly, the LLM catches the tail.",
    },
    objectives: [
      { zh: "用 Zipf 分布描述意图的长尾结构", en: "Describe intent long tails with a Zipf distribution" },
      { zh: "比较规则、分类器与大模型的覆盖与成本", en: "Compare rules, classifiers and LLMs on coverage and cost" },
      { zh: "估算新增一个意图的边际成本", en: "Estimate the marginal cost of one more intent" },
      { zh: "设计一套头部走规则、长尾走模型的混合方案", en: "Design a hybrid: rules for the head, model for the tail" },
    ],
    outline: [
      { zh: "意图与槽位:传统对话系统的两根支柱", en: "Intents and slots: the two pillars of classic dialogue" },
      { zh: "长尾说法与规则的边际递减", en: "Long-tail phrasing and diminishing rule returns" },
      { zh: "大模型零样本理解的代价", en: "What zero-shot understanding costs" },
      { zh: "混合方案与路由策略", en: "Hybrid design and routing policy" },
    ],
  },
  {
    id: "v13", code: "BR2", moduleId: "m4", difficulty: 2, hours: 6, prereq: ["v12"], viz: "ragLab",
    props: ["知识库切块", "向量检索", "召回率", "幻觉率", "不知道就转人工"],
    title: { zh: "门店知识库与 RAG:让模型只说它查得到的话", en: "The Shop Knowledge Base and RAG: Only Say What You Can Look Up" },
    summary: {
      zh: "大模型不知道你家 68 分钟的肩颈理疗卖多少钱,也不知道周三下午王师傅休假。让它知道有两条路:把知识写进提示词(简单,但知识一多就撑爆上下文也拉高成本),或者做检索增强——把门店知识切成小块、向量化存起来,每次对话先检索出最相关的几块再塞进上下文。这一章把 RAG 在门店场景里的每个参数讲透:切块大小(太小丢上下文,太大稀释相关性)、top-k(太小召回不足,太大引入噪声并挤占上下文)、以及最关键的——检索失败时会发生什么。这是幻觉的主要来源:模型在上下文里找不到价格,又被训练成一个乐于助人的助手,于是它会编一个听起来很合理的数字,顾客到店才发现对不上,这一单不只是黄了,还赔了口碑。声控台把知识条目数、切块大小、top-k、embedding 质量摆在一起,算出召回率、上下文 token 数、每轮成本和幻觉发生率,并让你打开那个价值最高的开关:「检索不到就明确说不知道并转人工」——它会让幻觉率直接塌到接近零,代价是转人工率上升几个点,这笔交易在门店场景里几乎永远划算。",
      en: "The model does not know what your 68-minute neck-and-shoulder treatment costs, nor that a particular therapist is off on Wednesday afternoons. There are two ways to tell it: put the knowledge in the prompt (simple, but it blows the context and the bill as knowledge grows), or retrieve — chunk the shop's knowledge, embed it, and fetch the most relevant pieces into context each turn. This chapter takes every RAG parameter apart in the shop setting: chunk size (too small loses context, too large dilutes relevance), top-k (too small under-recalls, too large adds noise and eats context), and most importantly what happens when retrieval fails. That is the main source of hallucination: the model cannot find a price in context, has been trained to be helpful, and invents a plausible number — which the customer discovers at the door, losing both the booking and your reputation. The bench puts entry count, chunk size, top-k and embedding quality together, computing recall, context tokens, per-turn cost and hallucination rate, and lets you flip the highest-value switch of all: when retrieval fails, say so plainly and hand off. Hallucination collapses to near zero for a few points of extra handoff — a trade that is almost always worth taking in a shop.",
    },
    objectives: [
      { zh: "为门店知识设计合适的切块与元数据", en: "Design chunking and metadata for shop knowledge" },
      { zh: "解释召回率与幻觉率之间的因果关系", en: "Explain the causal link between recall and hallucination" },
      { zh: "在上下文成本与召回之间选 top-k", en: "Pick top-k between context cost and recall" },
      { zh: "写出一条「查不到就转人工」的兜底策略", en: "Write a fallback: when nothing is found, hand off" },
    ],
    outline: [
      { zh: "门店知识有哪几类,更新频率各不同", en: "Kinds of shop knowledge and their update rates" },
      { zh: "切块、向量化与检索", en: "Chunking, embedding and retrieval" },
      { zh: "幻觉从哪里来:找不到却还要回答", en: "Where hallucination comes from: answering without evidence" },
      { zh: "兜底策略与知识库的维护责任人", en: "Fallbacks and who owns the knowledge base" },
    ],
  },
  {
    id: "v14", code: "BR3", moduleId: "m4", difficulty: 3, hours: 6, prereq: ["v13"], viz: "toolLab",
    props: ["工具调用", "查空档", "占位锁", "幂等键", "补偿与回滚"],
    title: { zh: "工具调用:查空档、占位、下单、改约", en: "Tool Calling: Availability, Holds, Bookings and Changes" },
    summary: {
      zh: "答问是只读的,预约是写操作,这是两个世界。一次真实的预约至少要三步:查某天某项目的空档、把选中的时段占住(否则两个顾客会同时约到同一位技师同一个时段)、以及在顾客确认后落单。每一步都是网络调用,都可能超时、失败、或者更麻烦的——成功了但响应丢了。最后这种情况是所有事故的源头:系统没收到响应就重试,于是一个顾客产生了两条预约,前台第二天面对两个 15:00 的王师傅。解决办法是幂等:每次业务操作带一个由会话和意图派生的唯一键,服务端见过这个键就直接返回上次的结果而不是再执行一次。本章的声控台把三步链路摆出来,让你调每步的成功率、超时时间、重试次数,并打开或关闭幂等键与占位超时释放,实时算出端到端预约成功率、重复预约事故率、平均耗时,以及占位没释放造成的「幽灵占用」——那是另一个真实的坑:顾客问完没确认就挂了,那个时段被锁住一小时,别人约不了。",
      en: "Answering questions is read-only; booking is a write, and those are different worlds. A real booking takes at least three steps: look up availability for a service on a date, hold the chosen slot (or two customers book the same therapist at the same time), and commit once the customer confirms. Each step is a network call that can time out, fail, or — worse — succeed with a lost response. That last case is the source of most incidents: the caller sees no response, retries, and one customer becomes two bookings, leaving the front desk with two 3 p.m. appointments for the same therapist. The remedy is idempotency: every business operation carries a unique key derived from the session and the intent, and a server that has seen the key returns the previous result instead of executing again. The bench lays the three-step chain out with per-step success, timeout and retry counts, plus switches for idempotency keys and hold expiry, computing end-to-end booking success, duplicate-booking incident rate, average duration, and the ghost holds left when a slot is locked but never confirmed — another real trap: the customer hangs up after asking, and that slot is unbookable for an hour.",
    },
    objectives: [
      { zh: "把一次预约拆成只读与写操作两类步骤", en: "Split a booking into read and write steps" },
      { zh: "用幂等键消除重试造成的重复预约", en: "Kill retry-induced duplicates with idempotency keys" },
      { zh: "为占位设计超时释放与补偿逻辑", en: "Design hold expiry and compensation" },
      { zh: "估算端到端预约成功率与事故率", en: "Estimate end-to-end booking success and incident rate" },
    ],
    outline: [
      { zh: "三步链路:查、占、落单", en: "The three-step chain: check, hold, commit" },
      { zh: "超时与重试:成功了但响应丢了", en: "Timeouts and retries: succeeded, response lost" },
      { zh: "幂等键怎么设计", en: "Designing the idempotency key" },
      { zh: "幽灵占用与补偿释放", en: "Ghost holds and compensating releases" },
    ],
  },
  {
    id: "v15", code: "BR4", moduleId: "m4", difficulty: 2, hours: 5, prereq: ["v13"], viz: "promptLab",
    props: ["人设与话术", "禁语清单", "知识边界", "疗效红线", "越界拒绝"],
    title: { zh: "话术、人设与护栏:按摩行业绕不开的两条红线", en: "Script, Persona and Guardrails: Two Lines This Industry Cannot Cross" },
    summary: {
      zh: "提示词不是写作文,它是这套系统的行为规范书。对按摩养生门店来说,它至少要写清五件事:人设(你是某某店的前台,说话简短、热情但不腻)、能力边界(只回答知识库里有的,价格与空档必须查询工具而不能凭记忆)、禁语(不得使用治疗、根治、疗效、药用等医疗性表述——这是《广告法》和医疗广告管理的硬红线,养生服务不是医疗服务)、身体状况咨询的处理(顾客说腰疼、孕期、术后,机器不能诊断也不能建议疗程,应当提示到店由技师评估或建议就医)、以及最重要的一条:任何带有越界暗示的试探,都要用中性、不带评判的措辞明确拒绝、结束该话题并留痕转人工——这是正规门店与灰色场所的分界线,也是你的平台账号和营业执照的护城河。本章的声控台是一个可运行的测试集:八条真实的试探性对话逐条跑过五道护栏(角色约束、知识边界、禁语过滤、敏感场景规则、转人工触发),逐条显示通过或击穿,并给出综合风险分与上线判定。",
      en: "A prompt is not an essay; it is this system's code of conduct. For a massage and wellness shop it must state at least five things. The persona (you are the front desk of this shop: brief, warm, not saccharine). The capability boundary (answer only from the knowledge base; prices and availability must come from a tool call, never from memory). Banned phrasing (no treat, cure, therapeutic effect or medicinal claims — a hard line under advertising and medical-advertising rules, because wellness service is not medical service). How to handle questions about physical conditions (a customer mentions back pain, pregnancy or recent surgery: the machine must not diagnose or prescribe a course, but invite an in-person assessment or suggest seeing a doctor). And most important, that any out-of-bounds probe is refused in neutral, non-judgemental wording, the topic ended, the exchange logged and handed to a human — the line between a legitimate shop and a grey one, and the moat around your platform accounts and your licence. The bench is a runnable test set: eight real probing dialogues pass through five guardrails (role constraint, knowledge boundary, banned phrasing, sensitive-scenario rules, handoff trigger), each shown as held or breached, with a composite risk score and a ship or hold verdict.",
    },
    objectives: [
      { zh: "写出一份包含人设、边界与禁语的系统提示词", en: "Write a system prompt with persona, boundary and banned phrasing" },
      { zh: "说明养生服务为什么不能作疗效表述", en: "Say why wellness services cannot make efficacy claims" },
      { zh: "设计身体状况咨询的安全应答模板", en: "Design safe replies for health-condition questions" },
      { zh: "为越界试探建立拒绝、结束与留痕流程", en: "Build a refuse, end and log flow for out-of-bounds probes" },
    ],
    outline: [
      { zh: "人设:一句话讲清你是谁、怎么说话", en: "Persona: who you are and how you speak" },
      { zh: "能力边界:价格和空档必须查工具", en: "Boundary: prices and slots come from tools" },
      { zh: "禁语与疗效红线", en: "Banned phrasing and the efficacy line" },
      { zh: "越界试探:拒绝、结束、留痕、转人工", en: "Out-of-bounds probes: refuse, end, log, hand off" },
    ],
  },

  {
    id: "v31", code: "BR5", moduleId: "m4", difficulty: 3, hours: 6, prereq: ["v12", "v13", "v15"], viz: "tierLab",
    props: ["FAQ 精确层", "RAG 检索层", "兜底出口", "配置继承", "多店命名空间"],
    title: { zh: "三层应答与多店配置:把零件接成一条链路", en: "Three Tiers and Many Shops: Wiring the Parts Into One Path" },
    summary: {
      zh: "前面四章各讲了一个零件:意图识别、知识检索、工具调用、护栏。这一章把它们接起来,回答两个工程问题——一次提问在系统里到底走哪条路,以及同一套部署怎么给三十家店发三十份不同的配置。答案是三层降级加一次配置继承。三层是:FAQ 精确层接住高频问题,人写的答案、三毫秒、零 token、每一条都可追责;未命中才降级到 RAG 检索层,检索门店知识库、分数达标才让模型带证据作答;分数不足再降级到兜底层,明确说不知道、转人工、并且把这次缺口记下来。顺序不能反——把高频问题送去检索是纯浪费:多三百毫秒延迟、多一份 token 成本、还多一次检索错块的机会,而答案本来就是确定的。多店那一半靠继承:全局层放引擎与护栏,品牌层放连锁统一的价目与音色,门店层只放这家店真正不同的那几行,覆盖优先级是门店大于品牌大于全局。这样加一家店等于加一行数据,而不是复制一份话术表。反方向同样重要:护栏、禁语、合规告知只放在全局层,任何门店都不能覆盖。",
      en: "The previous four chapters each covered one part: intent recognition, retrieval, tool calling, guardrails. This chapter wires them together and answers two engineering questions — which path a question actually takes through the system, and how one deployment gives thirty shops thirty different configurations. The answer is three tiers of degradation plus one inheritance chain. The tiers: an exact FAQ layer catches high-frequency questions with human-written answers in three milliseconds at zero tokens, every one of them attributable; only a miss degrades to the retrieval layer, which searches the shop's knowledge base and lets the model answer with evidence when the score clears a threshold; a score below that degrades again to the fallback layer, which says so plainly, hands off, and records the gap. The order cannot be reversed — sending a high-frequency question through retrieval is pure waste: three hundred milliseconds of latency, a token bill, and one more chance to retrieve the wrong chunk, for an answer that was already determinate. The multi-shop half rests on inheritance: a global layer holds the engines and the guardrails, a brand layer holds the chain's shared prices and voice, and a shop layer holds only the handful of lines genuinely different here, with shop overriding brand overriding global. Adding a shop becomes adding a row rather than copying a script table. The other direction matters equally: guardrails, banned phrasing and the compliance notice live only in the global layer, where no shop can override them.",
    },
    objectives: [
      { zh: "画出一次提问在三层之间的降级路径", en: "Draw a question's degradation path across the three tiers" },
      { zh: "说清为什么 FAQ 必须排在 RAG 之前", en: "Say why the FAQ layer must sit in front of retrieval" },
      { zh: "解释阈值与兜底为什么是同一件事", en: "Explain why the threshold and the fallback are one thing" },
      { zh: "用三层继承把门店差异从代码降级成数据", en: "Demote shop differences from code to data with three-layer inheritance" },
    ],
    outline: [
      { zh: "两个基线拓扑:固定话术,以及话术加模型直答", en: "Two baseline topologies: fixed scripts, and scripts plus direct generation" },
      { zh: "三层降级:精确、有据、兜底", en: "Three tiers: exact, evidenced, fallback" },
      { zh: "顺序为什么不能反", en: "Why the order cannot be reversed" },
      { zh: "一套部署,多店配置:全局 / 品牌 / 门店", en: "One deployment, many shops: global, brand, shop" },
    ],
  },
  /* ============ M5 · RT 接得住:实时链路 ============ */
  {
    id: "v16", code: "RT1", moduleId: "m5", difficulty: 3, hours: 6, prereq: ["v6", "v11"], viz: "latencyLab",
    props: ["延迟预算", "首字延迟", "流式重叠", "P95", "自然对话阈值"],
    title: { zh: "端到端延迟预算:1 秒钟要分给六个人花", en: "The Latency Budget: One Second Split Six Ways" },
    summary: {
      zh: "人在电话里能忍受的沉默大约是一秒,超过一秒半就会「喂?」一声。而从顾客说完到机器出声,这条链路上有六段各自要花钱:网络上行、尾点静音判定、ASR 出最终结果、大模型出第一个字、TTS 出第一个音频包、播放缓冲。本章把它当成一份财务预算来做:先把每段的典型值摆出来(尾点 600 毫秒通常是最大的一笔),再问三个问题——哪些能砍、哪些能重叠、哪些必须串行。三个最有效的手段都是重叠而不是加速:把 ASR 的中间结果提前送给大模型预热、大模型流式输出的第一句话就立刻送去合成、TTS 边合成边播。声控台把六段做成可调的瀑布图,并加上抖动算出 P50 与 P95——因为顾客记住的是最慢的那几次,不是平均值。你会看到一个典型的 1.4 秒链路在开启三项重叠优化后落到 0.75 秒左右,以及一个残酷的事实:如果尾点阈值不动,无论你换多贵的模型,都跨不过那条一秒线。",
      en: "A person tolerates about a second of silence on a call and says hello again after about one and a half. Between the customer's last syllable and the machine's first sound, six stages each spend from that budget: network uplink, tail-silence endpointing, the final ASR result, the LLM's first token, the TTS first packet, and playback buffering. This chapter treats it like a finance budget: list each stage's typical cost (the 600 ms endpoint is usually the largest single line), then ask three questions — what can be cut, what can overlap, what must stay serial. The three most effective moves are all overlap rather than speed: send partial ASR results to warm the LLM, synthesise the LLM's first streamed sentence immediately, and play TTS while it is still generating. The bench renders the six stages as an adjustable waterfall and adds jitter to compute P50 and P95, because customers remember the slowest few turns, not the average. A typical 1.4-second pipeline falls to around 0.75 seconds with the three overlaps enabled — alongside a hard truth: leave the endpoint threshold alone and no amount of money spent on models will get you under one second.",
    },
    objectives: [
      { zh: "拆出端到端语音链路的六段延迟", en: "Decompose the six stages of end-to-end voice latency" },
      { zh: "区分能重叠与必须串行的阶段", en: "Separate overlappable stages from serial ones" },
      { zh: "用 P95 而不是均值来评价对话体验", en: "Judge conversational feel by P95, not the mean" },
      { zh: "给出一条低于 1 秒的可行优化路径", en: "Lay out a workable path to under one second" },
    ],
    outline: [
      { zh: "六段预算与它们的典型值", en: "Six budget lines and their typical values" },
      { zh: "三个重叠优化:预热、首句合成、边合成边播", en: "Three overlaps: warm-up, first-sentence synthesis, play-while-generating" },
      { zh: "抖动、P50 与 P95", en: "Jitter, P50 and P95" },
      { zh: "填充语与等待音:心理延迟的处理", en: "Fillers and hold tones: managing perceived latency" },
    ],
  },
  {
    id: "v17", code: "RT2", moduleId: "m5", difficulty: 3, hours: 5, prereq: ["v16"], viz: "bargeLab",
    props: ["barge-in 打断", "回声消除 AEC", "半双工与全双工", "误打断", "背景噪声"],
    title: { zh: "打断:一边说一边听,还不能把自己当成顾客", en: "Barge-in: Listening While Speaking, Without Hearing Yourself" },
    summary: {
      zh: "真人对话里,你说到一半对方插一句「不用了我就想问个价」,你会立刻停下来。机器要做到这件事,必须同时开着播放和录音,而这带来一个物理问题:麦克风会收到自己刚播出去的声音。没有回声消除,系统会把自己的语音识别成顾客在说话,于是自己把自己打断,陷入循环。AEC 的原理是用已知的播放信号作为参考,从麦克风信号里自适应地减掉它;电话链路上运营商侧通常也有一层,但延迟和非线性失真会让消除不彻底,残余回声就成了误触发的来源。另一个误触发来源是门店环境本身:前台旁边的电视、背景音乐、另一位顾客的说话声。所以打断检测不能只看「有没有声音」,还要看能量持续时长、是否像人声、以及(更聪明的做法)识别出的文字是不是有意义的内容。声控台让你调回声消除强度、打断触发阈值和持续时长门限、以及门店背景噪声水平,同时看到两个指标:真实打断的响应延迟,和每分钟误打断次数——两者朝相反方向移动,你要找的是那个让对话既跟手又不神经质的平衡点。",
      en: "In human conversation, when someone cuts in with never mind, I just want the price, you stop immediately. For a machine to do that, playback and recording must run at once — which creates a physical problem: the microphone picks up what the speaker just played. Without echo cancellation the system recognises its own voice as the customer, interrupts itself, and loops. AEC uses the known playback signal as a reference and adaptively subtracts it from the microphone signal; carriers usually apply a layer of their own on phone links, but delay and non-linear distortion leave residual echo, and residual echo is a false-trigger source. The other source is the shop itself: the television by the front desk, background music, another customer talking. So interrupt detection cannot just ask whether there is sound — it must weigh how long the energy persists, whether it looks like speech, and, more cleverly, whether the recognised words carry meaning. The bench moves echo-cancellation strength, the trigger threshold and duration gate, and ambient noise level, showing two numbers at once: response delay on genuine interruptions and false interruptions per minute. They move in opposite directions, and what you are hunting is the point where the conversation is responsive without being jumpy.",
    },
    objectives: [
      { zh: "说明为什么全双工打断必须依赖回声消除", en: "Say why full-duplex barge-in requires echo cancellation" },
      { zh: "区分真实打断与环境噪声的误触发", en: "Separate genuine interrupts from ambient false triggers" },
      { zh: "用持续时长与语义内容做二次确认", en: "Use duration and semantic content as a second gate" },
      { zh: "为嘈杂门店环境调一组打断参数", en: "Tune interrupt parameters for a noisy shop" },
    ],
    outline: [
      { zh: "半双工的笨办法与全双工的代价", en: "The half-duplex shortcut and the full-duplex price" },
      { zh: "AEC:用参考信号减掉自己的声音", en: "AEC: subtracting yourself with a reference signal" },
      { zh: "误打断的三个来源", en: "Three sources of false interruption" },
      { zh: "打断后的状态恢复:说到哪了", en: "Recovering state after an interrupt: where were we" },
    ],
  },
  {
    id: "v18", code: "RT3", moduleId: "m5", difficulty: 3, hours: 6, prereq: ["v16"], viz: "e2eLab",
    props: ["级联架构", "端到端语音模型", "可控性", "可审计", "选型决策"],
    title: { zh: "级联 vs 端到端语音模型:门店该选哪条路", en: "Cascade vs End-to-End Speech Models: Which Road for a Shop" },
    summary: {
      zh: "2024 年之后出现了第二条技术路线:端到端的语音大模型直接吃音频、吐音频,中间不经过文本。它的优势非常真实——延迟能压到几百毫秒、语气和情绪自然得多、还能听出顾客的犹豫和不耐烦。但它也带来了三个门店场景里很难接受的代价。第一是可控性:级联架构里,大模型输出的是文本,你可以在文本层做敏感词过滤、价格校验、禁语拦截,再决定要不要合成;端到端模型直接出音频,审核点变得模糊。第二是可审计:出了纠纷,级联链路每一跳都有文本记录可查,端到端只有音频。第三是知识注入与工具调用的成熟度,以及私有化部署的可得性和成本。本章的声控台是一个决策器:你给八个维度(延迟、自然度、成本、可控性、可审计、知识库接入、工具调用、私有化)设权重,它算出两条路线的匹配度并给出推荐与关键决定因素。结论在今天多数门店场景里是明确的——预约和报价走级联,因为要能审;而闲聊、安抚、引导这类不涉及承诺的环节,端到端的体验优势值得用起来。",
      en: "Since 2024 a second architecture exists: end-to-end speech models that consume audio and emit audio without passing through text. Its advantages are real — latency down to a few hundred milliseconds, far more natural prosody, and the ability to hear hesitation or impatience. It also brings three costs that are hard to accept in a shop. First, controllability: in a cascade the model emits text, so you can filter sensitive terms, validate prices and block banned phrasing before deciding to synthesise; an end-to-end model emits audio and the checkpoint blurs. Second, auditability: in a dispute, a cascade has text at every hop, while end-to-end has only audio. Third, the maturity of knowledge grounding and tool calling, plus the availability and cost of on-premise deployment. The bench is a decision tool: weight eight dimensions (latency, naturalness, cost, controllability, auditability, knowledge grounding, tool calling, on-premise) and it scores both architectures, recommends one and names the deciding factor. For most shops today the answer is clear — bookings and quotes go through the cascade because they must be auditable, while small talk, reassurance and guidance, which promise nothing, are where the end-to-end experience advantage is worth taking.",
    },
    objectives: [
      { zh: "画出两条架构的数据流与审核点", en: "Draw both architectures' data flow and checkpoints" },
      { zh: "比较延迟、可控性与可审计性的取舍", en: "Trade latency against controllability and auditability" },
      { zh: "判断哪些环节适合端到端、哪些必须级联", en: "Judge which stages suit end-to-end and which need the cascade" },
      { zh: "用加权打分法做一次架构选型", en: "Run a weighted architecture selection" },
    ],
    outline: [
      { zh: "级联:三个模型一条线", en: "The cascade: three models in a line" },
      { zh: "端到端语音模型:音频进、音频出", en: "End-to-end speech: audio in, audio out" },
      { zh: "文本层是唯一能审的地方", en: "The text layer is the only place you can audit" },
      { zh: "混合方案:按环节分流", en: "Hybrid: routing by stage" },
    ],
  },

  /* ============ M6 · PF 平台与选型 ============ */
  {
    id: "v19", code: "PF1", moduleId: "m6", difficulty: 2, hours: 6, prereq: ["v7", "v8"], viz: "platformLab",
    props: ["云厂商对比", "方言覆盖", "音色库", "数据合规", "加权选型"],
    title: { zh: "平台地图:阿里、腾讯、讯飞、火山、Azure、Deepgram 与开源", en: "The Platform Map: Alibaba, Tencent, iFlytek, Volcano, Azure, Deepgram and Open Source" },
    summary: {
      zh: "这一章把今天能买到、能租到、能自己搭的语音能力供给方摊在一张表上,并说清它们的真实差别。国内云厂商里,阿里云的智能语音交互把 Paraformer 与 CosyVoice 系列产品化,电话八千赫场景调优充分;腾讯云在音视频与 IM 生态里衔接顺;科大讯飞的方言和口音覆盖是老本行,粤语、川渝、东北、河南等场景通常明显领先;火山引擎在音色复刻与表现力上激进;百度智能云在成本上常有优势。国际厂商里,微软 Azure Speech 的中文与多语种都扎实且企业合规完备,Deepgram 与 AssemblyAI 以低延迟和高性价比的英文转写见长,ElevenLabs 的音色表现力是标杆,OpenAI 的实时接口把端到端语音带进了产品。开源侧,FunASR(Paraformer / SenseVoice)、Whisper、CosyVoice、GPT-SoVITS、Piper、Silero VAD 组成了一套完整可自建的链路。声控台是一个加权选型矩阵:你给八项需求(中文电话、方言、热词、流式、音色复刻、私有化、单价、合规)打权重,它给十二个候选算出匹配度、给出前三推荐并指出决定性因素。价格与能力以官网为准,这张表是帮你问对问题,不是替你签合同。",
      en: "This chapter puts every speech supplier you can buy, rent or self-host on one table and says what actually differs. Among Chinese clouds, Alibaba Cloud productises the Paraformer and CosyVoice families with heavy tuning for 8 kHz telephony; Tencent Cloud integrates smoothly with its media and IM ecosystem; iFlytek's dialect and accent coverage is its home turf and usually leads clearly on Cantonese, Sichuan, Northeastern and Henan speech; Volcano Engine is aggressive on voice cloning and expressiveness; Baidu often competes on price. Among international vendors, Microsoft Azure Speech is solid across Chinese and multilingual with mature enterprise compliance; Deepgram and AssemblyAI lead on low-latency, cost-effective English transcription; ElevenLabs sets the bar for expressive voices; OpenAI's realtime API brought end-to-end speech into products. On the open-source side, FunASR (Paraformer / SenseVoice), Whisper, CosyVoice, GPT-SoVITS, Piper and Silero VAD form a complete self-hostable stack. The bench is a weighted selection matrix: weight eight requirements (Mandarin telephony, dialects, hotwords, streaming, voice cloning, on-premise, unit price, compliance) and it scores twelve candidates, names a top three and identifies the deciding factor. Prices and capabilities change — treat the table as a way to ask the right questions, not as a contract.",
    },
    objectives: [
      { zh: "说出主要厂商各自的结构性优势", en: "Name each major vendor's structural advantage" },
      { zh: "把模糊的偏好变成可加权的选型指标", en: "Turn vague preferences into weighted criteria" },
      { zh: "识别选型中真正的决定性因素", en: "Identify the genuinely deciding factor" },
      { zh: "设计一次可复现的厂商对比测试", en: "Design a reproducible vendor bake-off" },
    ],
    outline: [
      { zh: "国内云:阿里、腾讯、讯飞、火山、百度", en: "Chinese clouds: Alibaba, Tencent, iFlytek, Volcano, Baidu" },
      { zh: "国际厂商:Azure、Deepgram、ElevenLabs、OpenAI", en: "International: Azure, Deepgram, ElevenLabs, OpenAI" },
      { zh: "开源栈:FunASR、Whisper、CosyVoice、GPT-SoVITS", en: "Open source: FunASR, Whisper, CosyVoice, GPT-SoVITS" },
      { zh: "加权打分与自测集的重要性", en: "Weighted scoring and why you need your own test set" },
    ],
  },
  {
    id: "v20", code: "PF2", moduleId: "m6", difficulty: 3, hours: 6, prereq: ["v19"], viz: "buildLab",
    props: ["自建 vs 云", "GPU 并发", "TCO 交叉点", "运维人力", "数据不出门"],
    title: { zh: "自建还是买:GPU、并发与那个成本交叉点", en: "Build or Buy: GPUs, Concurrency and the Break-Even Point" },
    summary: {
      zh: "自建的诱惑很直接:开源模型免费、数据不出门、没有按分钟计费的焦虑。但自建的成本从来不是显卡钱。把账算全要包括:显卡采购或租用(一张 24 GB 的卡大约能扛十几到三十路并发的 ASR 加 TTS,取决于模型和量化)、服务器与电费、以及最大的一笔——一个懂模型部署、会调优、能在半夜服务挂了时爬起来的人的时间。云服务的账则简单得多:按分钟或按字符,用多少付多少,没有闲置成本,但用量一大,单价的乘法会很吓人。本章的声控台把两条成本曲线画在一起:云是一条过原点的直线(斜率 = 单价),自建是一条阶梯函数(每超过一张卡的并发容量就跳一级)加上固定的运维成本。交叉点就是你的决策点,它通常落在每月几万到十几万分钟的量级——这意味着单店几乎一定应该买,而一个几十家店的连锁到某个规模后自建开始划算。声控台还让你把「数据必须留在本地」作为硬约束打开,这时问题就不再是成本,而是自建的最低可行配置是什么。",
      en: "Self-hosting is tempting: open models are free, data stays in the building, and there is no per-minute meter running. But the cost of self-hosting was never the graphics card. A complete account includes the card (a 24 GB GPU carries roughly ten to thirty concurrent ASR-plus-TTS sessions depending on model and quantisation), the server and its power, and the largest line of all — the time of someone who can deploy models, tune them, and get out of bed when the service dies at 2 a.m. The cloud bill is far simpler: per minute or per character, pay for what you use, no idle cost, but the multiplication gets frightening at volume. The bench draws both curves together: the cloud is a straight line through the origin (slope = unit price), self-hosting is a step function (another step each time concurrency exceeds one card) plus a fixed operations cost. The crossing is your decision point, and it usually lands somewhere in the tens of thousands to low hundreds of thousands of minutes per month — meaning a single shop should almost certainly buy, while a chain of dozens of shops eventually finds self-hosting cheaper. You can also switch on data must stay on-premise as a hard constraint, at which point the question stops being cost and becomes the minimum viable build.",
    },
    objectives: [
      { zh: "把自建成本算全:硬件、电力、人力、折旧", en: "Cost a build fully: hardware, power, staffing, depreciation" },
      { zh: "估算单张显卡能承载的并发路数", en: "Estimate concurrent sessions per GPU" },
      { zh: "找出自建与云服务的成本交叉点", en: "Find the break-even between build and cloud" },
      { zh: "在数据不出门的硬约束下做最小可行方案", en: "Design a minimum viable build under a data-residency constraint" },
    ],
    outline: [
      { zh: "云的线性账单与自建的阶梯账单", en: "Linear cloud billing, stepped self-host billing" },
      { zh: "一张卡能跑多少路:模型、量化与批处理", en: "Sessions per card: model, quantisation, batching" },
      { zh: "被低估的运维人力成本", en: "The underestimated cost of operations" },
      { zh: "混合部署:高峰溢出到云", en: "Hybrid: overflow the peak to the cloud" },
    ],
  },
  {
    id: "v21", code: "PF3", moduleId: "m6", difficulty: 2, hours: 5, prereq: ["v20"], viz: "costLab",
    props: ["单通成本", "成本瀑布", "线路费", "token 成本", "人工对比"],
    title: { zh: "一通电话到底花多少钱:成本瀑布与人工对比", en: "What One Call Really Costs: The Waterfall, and the Human Comparison" },
    summary: {
      zh: "把一通三分钟的 AI 客服电话拆开,账单有四段:语音识别按分钟计(通常是每分钟几分钱)、电话线路按分钟计(呼入呼出、固话手机、不同运营商价格不同)、大模型按 token 计(输入包含系统提示词、知识库检索结果和对话历史,所以输入 token 通常远多于输出)、语音合成按字符计。把这四段画成瀑布图,你会看到一个反直觉的结果:在多数门店配置下,最贵的一段往往不是 AI,而是电话线路;第二贵的是大模型的输入 token,而这一项恰恰是最可优化的——把知识库检索从 top-8 降到 top-3、把对话历史做摘要压缩,成本能直接腰斩。声控台让你调通话时长、轮次数、检索条数、提示词长度和各项单价,实时算出单通成本与月度总成本,并和人工客服的单通成本对比(月薪、社保、工位、有效通话时长换算),同时给出「每通电话带来的期望收益」——因为成本只有和收益放在一起才有意义,一通把顾客约到店的电话花两块钱和花两毛钱,在客单价两百多的生意里差别没有想象中大。",
      en: "Break a three-minute AI call apart and the bill has four lines: recognition billed per minute (typically a few cents), telephony billed per minute (inbound or outbound, landline or mobile, varying by carrier), the LLM billed per token (input carries the system prompt, retrieved knowledge and dialogue history, so input usually dwarfs output), and synthesis billed per character. Draw those as a waterfall and a counter-intuitive result appears: under most shop configurations the most expensive line is not the AI but the phone line, and the second is LLM input tokens — which happens to be the most optimisable of the four, since dropping retrieval from top-8 to top-3 and summarising dialogue history can halve it. The bench moves call duration, turn count, retrieved chunks, prompt length and every unit price, computing per-call and monthly cost live, comparing against a human agent's per-call cost (salary, contributions, workspace, converted through effective talk time), and showing expected revenue per call alongside — because cost only means something next to revenue, and in a business with a 200-plus yuan ticket, the difference between a two-yuan call and a twenty-cent call matters less than people assume.",
    },
    objectives: [
      { zh: "把一通电话的成本拆成四段并定位最贵的一段", en: "Split a call's cost four ways and find the biggest line" },
      { zh: "解释为什么输入 token 通常远多于输出", en: "Explain why input tokens usually dwarf output" },
      { zh: "算出人工客服的真实单通成本", en: "Compute a human agent's true per-call cost" },
      { zh: "用收益而不是成本来判断该不该接这通电话", en: "Judge by revenue, not cost, whether to take the call" },
    ],
    outline: [
      { zh: "四段账单:识别、线路、token、合成", en: "Four lines: recognition, telephony, tokens, synthesis" },
      { zh: "成本瀑布与最贵的那一段", en: "The waterfall and the most expensive line" },
      { zh: "压缩上下文:最容易拿到的优化", en: "Compressing context: the easiest win" },
      { zh: "与人工对比:成本、容量与边界", en: "Against a human: cost, capacity and boundary" },
    ],
  },

  /* ============ M7 · CH 渠道与接入 ============ */
  {
    id: "v22", code: "CH1", moduleId: "m7", difficulty: 3, hours: 6, prereq: ["v1"], viz: "telLab",
    props: ["SIP 中继", "并发线路", "呼入与外呼", "外呼合规", "投诉率"],
    title: { zh: "电话接入:SIP 中继、并发线路与外呼的红线", en: "Telephony: SIP Trunks, Concurrency and the Outbound Red Line" },
    summary: {
      zh: "电话是最古老也最严肃的渠道。技术上,今天的接法是 SIP 中继或云呼叫中心:运营商把呼叫通过 SIP 协议送到你的服务器(自建常用 FreeSWITCH 或 Asterisk),或者你直接用云厂商的呼叫中心,音频以 RTP 流的形式进出。这里有两个硬约束:一是并发线路数,买了多少路就只能同时通多少路,超了就是忙音,这直接关联第一章的呼损模型;二是编码,线路上通常是 8 kHz 的 G.711 或 G.729,回到了第四章讲的窄带问题。比技术更硬的是合规,尤其是外呼。商业性语音呼叫必须事先取得接收方同意,用户明确拒绝后不得再次拨打,时段与频次都有约束,且必须提供便捷的拒收方式;做得过火不仅招投诉,还可能被运营商限制或停用号码,而一家门店的号码是资产。本章的声控台有两部分:一个是并发线路与呼损的工程模型,另一个是外呼骚扰模型——调拨打频次和名单质量,看接通率、转化率和投诉率怎么变化,你会看到投诉率随频次超线性上升,而转化率早就见顶了。",
      en: "The phone is the oldest and most serious channel. Technically, today's integration is a SIP trunk or a cloud contact centre: the carrier delivers calls over SIP to your server (self-hosted setups usually run FreeSWITCH or Asterisk), or you use a cloud contact centre directly, with audio flowing as RTP. Two hard constraints apply. First, concurrent channels: you can only carry as many simultaneous calls as you bought, and beyond that callers hear busy — directly the blocking model from chapter one. Second, the codec: trunks usually carry 8 kHz G.711 or G.729, returning you to the narrowband problem from chapter four. Harder than the technology is compliance, especially outbound. Commercial voice calls require prior consent from the recipient, must never be repeated after an explicit refusal, are bounded in time of day and frequency, and must offer an easy way to opt out. Overdoing it invites complaints and can get the number restricted or cut off by the carrier — and a shop's number is an asset. The bench has two halves: an engineering model of concurrency and blocking, and an outbound nuisance model where dialling frequency and list quality move connect rate, conversion and complaint rate, showing complaints rising super-linearly long after conversion has plateaued.",
    },
    objectives: [
      { zh: "说清 SIP 中继与云呼叫中心的接入方式", en: "Describe SIP trunk and cloud contact-centre integration" },
      { zh: "按呼损目标反推需要的并发线路数", en: "Size concurrent channels from a blocking target" },
      { zh: "列出外呼的同意、时段与退订要求", en: "List consent, timing and opt-out duties for outbound" },
      { zh: "用投诉率约束外呼频次", en: "Bound outbound frequency with complaint rate" },
    ],
    outline: [
      { zh: "SIP、RTP 与 FreeSWITCH / 云呼叫中心", en: "SIP, RTP and FreeSWITCH or a cloud contact centre" },
      { zh: "并发线路、呼损与排队", en: "Channels, blocking and queueing" },
      { zh: "外呼合规:同意、时段、频次、退订", en: "Outbound compliance: consent, timing, frequency, opt-out" },
      { zh: "号码是资产:别把它打废了", en: "The number is an asset: do not burn it" },
    ],
  },
  {
    id: "v23", code: "CH2", moduleId: "m7", difficulty: 2, hours: 5, prereq: ["v2"], viz: "wechatLab",
    props: ["私域复购", "企业微信", "48 小时窗口", "触达频次", "拉黑率"],
    title: { zh: "微信与平台 IM:复购的主场与响应速度的权重", en: "WeChat and Platform IM: Where Repeat Business Lives" },
    summary: {
      zh: "电话接住的是新客,微信留住的是熟客,而按摩养生这门生意的利润几乎全在熟客身上。微信生态里有几个能力边界要记清楚:公众号的客服消息只能在用户互动后的 48 小时窗口内主动发送,超出窗口就只能用模板/订阅消息且受严格限制;小程序有自己的客服会话;企业微信可以把顾客加到员工的企业身份下,关系长期留存、会话可存档——这是门店最该经营的资产。AI 在这里的角色和电话完全不同:不是接住一通来电,而是持续、恰当地维持关系——到期提醒(上次做完肩颈是 23 天前)、空档促销(今天下午三点有空位)、节气与季节话术、会员权益到期提醒。关键变量不是文案质量,是频次。声控台用一个真实的衰减模型:会员池、触达频次、每次打开率、转化率算出月度召回订单和 GMV,同时算拉黑与退关的累积——频次从每月一次提到每周一次,短期 GMV 上涨,但半年后可触达的会员池会明显缩水,那是把长期资产换了短期数字。另外半个话题是美团、大众点评、抖音的平台 IM:那里的首响时间直接影响店铺权重,AI 的第一价值是秒回。",
      en: "The phone catches new customers; WeChat keeps the regulars — and in massage and wellness nearly all the profit sits with regulars. A few capability boundaries are worth memorising. An official account can only push service messages inside a 48-hour window after the user interacts; outside it you are limited to template or subscription messages under strict rules. Mini-programs have their own service session. WeCom attaches the customer to a staff member's corporate identity, so the relationship persists and conversations can be archived — the asset a shop should be building. The AI's role here differs completely from telephony: not catching one call, but maintaining a relationship appropriately over time — a due reminder (your last neck-and-shoulder session was 23 days ago), filling an empty slot this afternoon, seasonal scripts, membership expiry notices. The decisive variable is not copywriting, it is frequency. The bench runs a real decay model: member pool, contact frequency, open rate and conversion give monthly recovered orders and GMV, while blocks and unfollows accumulate — raising frequency from monthly to weekly lifts GMV now and visibly shrinks the reachable pool six months later, trading a long-term asset for a short-term number. The other half of the chapter is platform IM on Meituan, Dianping and Douyin, where first-response time feeds your ranking and the AI's first job is simply to reply instantly.",
    },
    objectives: [
      { zh: "说清公众号、小程序与企业微信的触达边界", en: "State the reach limits of official accounts, mini-programs and WeCom" },
      { zh: "用衰减模型评估触达频次的长期代价", en: "Assess frequency's long-term cost with a decay model" },
      { zh: "设计一套基于到店周期的召回话术", en: "Design win-back scripts based on visit cycles" },
      { zh: "解释平台 IM 首响时间为什么影响流量", en: "Explain why platform first-response time drives traffic" },
    ],
    outline: [
      { zh: "三个微信入口与各自的规则", en: "Three WeChat doorways and their rules" },
      { zh: "私域复购:到期提醒与空档促销", en: "Private-domain repeat: due reminders and empty-slot offers" },
      { zh: "频次、打开率与拉黑的累积", en: "Frequency, open rate and accumulating blocks" },
      { zh: "美团、点评、抖音:秒回的权重", en: "Meituan, Dianping, Douyin: the weight of an instant reply" },
    ],
  },
  {
    id: "v24", code: "CH3", moduleId: "m7", difficulty: 2, hours: 5, prereq: ["v15"], viz: "handoffLab",
    props: ["置信度阈值", "情绪识别", "转人工", "上下文交接", "坐席排队"],
    title: { zh: "人机协同:什么时候必须把电话递给人", en: "Human Handoff: When the Machine Must Pass the Phone" },
    summary: {
      zh: "一套成熟的 AI 客服,衡量它的指标不是「多少不用转人工」,而是「该转的时候转得多干脆」。触发转人工的信号有四类:识别置信度低(连续两轮听不清)、意图落在人工专属清单里(投诉、退款、身体不适、越界试探)、情绪信号(语速陡增、音量上升、出现负面词)、以及顾客直接说「转人工」——最后这一条必须无条件立刻生效,任何形式的挽留都会让人更恼火。技术上,置信度阈值是一个典型的分类权衡:阈值定高,转人工率上升、人工成本上去,但错误放行少;阈值定低,AI 多扛一些,但会把本该转的对话砸在自己手里。声控台把这条权衡画成 ROC 式的曲线,再叠一层排队模型:坐席人数有限,转过去的对话要排队,等待时间超过三十秒的话,顾客的满意度损失可能比 AI 答错还大。最后一个常被忽略的细节是上下文交接:接手的人必须立刻看到之前说了什么、顾客要约什么、AI 卡在哪一步,否则顾客要从头讲一遍,那一刻之前所有的效率提升都被抵消掉了。",
      en: "A mature AI service is not measured by how rarely it hands off, but by how cleanly it hands off when it should. Four signals trigger a transfer: low recognition confidence (two consecutive unclear turns), an intent on the humans-only list (complaints, refunds, physical discomfort, out-of-bounds probes), emotional signals (speech rate jumping, volume rising, negative words appearing), and the customer simply saying get me a person — which must take effect immediately and unconditionally, because any attempt to retain them makes it worse. Technically the confidence threshold is a classic classification trade-off: set it high and handoff rate and labour cost rise while fewer errors slip through; set it low and the AI carries more while ruining conversations it should have passed on. The bench draws that trade as an ROC-style curve and layers a queueing model on top: agents are finite, transfers wait, and beyond about thirty seconds the satisfaction damage can exceed what a wrong AI answer would have caused. The most commonly neglected detail is context transfer: whoever picks up must immediately see what was said, what the customer wants to book and where the AI got stuck — otherwise the customer repeats everything, and every efficiency gain up to that moment is cancelled.",
    },
    objectives: [
      { zh: "列出四类必须转人工的触发信号", en: "List the four signals that must trigger handoff" },
      { zh: "在置信度阈值上做转人工率与错误率的权衡", en: "Trade handoff rate against error rate on the threshold" },
      { zh: "用排队模型估算坐席人数与等待时长", en: "Size agents and wait time with a queueing model" },
      { zh: "设计一份让坐席秒懂的上下文交接卡", en: "Design a context card an agent reads in seconds" },
    ],
    outline: [
      { zh: "四类触发信号与优先级", en: "Four trigger signals and their priority" },
      { zh: "阈值权衡:多转与少转的代价", en: "Threshold trade: the cost of transferring too much or too little" },
      { zh: "坐席排队与等待时间的满意度代价", en: "Agent queueing and the satisfaction cost of waiting" },
      { zh: "上下文交接:别让顾客说第二遍", en: "Context transfer: never make them repeat themselves" },
    ],
  },

  {
    id: "v30", code: "CH4", moduleId: "m7", difficulty: 3, hours: 6, prereq: ["v22", "v16"], viz: "twilioLab",
    props: ["Programmable Voice", "TwiML", "Media Streams", "ConversationRelay", "号码与落地合规"],
    title: { zh: "Twilio:把电话接进代码的那条国际路径", en: "Twilio: The International Path From a Phone Line Into Code" },
    summary: {
      zh: "第二十二章讲的是自建 SIP 中继和云呼叫中心,这一章讲那条在中国大陆之外几乎是默认答案的路:Twilio。它的价值不在于技术多先进,而在于它把「买号码、接呼叫、把音频弄进你的程序」这三件原本要跟运营商打交道几周的事,压缩成了一个 webhook 和一段 WebSocket。接入方式有三档,取舍很清晰:最省事的是 TwiML,用几行 XML 就能让电话说话、收集按键甚至做语音识别,两天上线,但音色和识别都用 Twilio 的,中文效果一般、可控性最低;最彻底的是 Media Streams,用一段 XML 把通话音频以 8 kHz μ-law、每 20 毫秒一帧的 base64 分叉到你自己的 WebSocket,你的 ASR、大模型、TTS 全都自己接——本书前五个模块讲的一切在这条路上都能用上;中间那档是 ConversationRelay,Twilio 负责识别与合成、只把文本推给你,你只写「大脑」,上手快但文本层之外的控制权交了出去。这一章还要讲清三件容易踩的事:号码的监管绑定(很多国家买号要提交地址与证件)、区域 edge 选择对延迟预算的真实影响(从亚洲打到美东 edge 会吃掉你在第十六章省下的全部时间)、以及中国大陆场景下的落地限制——对开在大陆的门店,Twilio 通常不是正确答案,而对港澳台、东南亚、北美的门店或需要接待海外顾客的连锁,它是从零到一条能跑的 AI 电话线最快的路。",
      en: "Chapter CH1 covered self-hosted SIP trunks and cloud contact centres; this chapter covers the road that is close to the default answer outside mainland China: Twilio. Its value is not technical novelty but compression — buying a number, receiving calls, and getting the audio into your program, three things that otherwise mean weeks of dealing with a carrier, become one webhook and one WebSocket. There are three integration tiers with a clear trade. The simplest is TwiML: a few lines of XML make the phone speak, gather digits, even recognise speech; live in two days, but the voice and the recognition are Twilio's, Chinese quality is mediocre, and control is minimal. The most thorough is Media Streams: one XML element forks the call audio to your own WebSocket as base64 8 kHz mu-law in 20-millisecond frames, and your ASR, your model and your TTS all plug in — everything from this book's first five modules applies on this road. In between sits ConversationRelay, where Twilio does recognition and synthesis and pushes you only text, so you write the brain alone: fast to stand up, at the cost of control below the text layer. The chapter also covers three things that catch people out: regulatory bundles on numbers (many countries require an address and identity documents to buy one), how much the choice of region edge really costs your latency budget (reaching a US edge from Asia eats every millisecond chapter RT1 saved), and the constraints in mainland China — for a shop trading on the mainland Twilio is usually not the right answer, while for shops in Hong Kong, Taiwan, Southeast Asia or North America, or chains serving overseas customers, it is the fastest path from nothing to a working AI phone line.",
    },
    objectives: [
      { zh: "说清 TwiML、Media Streams 与 ConversationRelay 三档的边界", en: "State the boundary between TwiML, Media Streams and ConversationRelay" },
      { zh: "读懂 Media Streams 的音频帧格式与双向回送", en: "Read the Media Streams frame format and send audio back" },
      { zh: "估算 edge 选择对端到端延迟预算的影响", en: "Estimate what the edge choice costs the latency budget" },
      { zh: "判断 Twilio 在自己所在市场是不是正确答案", en: "Judge whether Twilio is the right answer in your own market" },
    ],
    outline: [
      { zh: "一个 webhook 和一段 TwiML:电话如何进入代码", en: "One webhook and some TwiML: how a call enters code" },
      { zh: "Media Streams:8 kHz μ-law、20 毫秒一帧的双向音频", en: "Media Streams: bidirectional 8 kHz mu-law in 20 ms frames" },
      { zh: "ConversationRelay:把识别与合成交出去,只留大脑", en: "ConversationRelay: hand over ASR and TTS, keep the brain" },
      { zh: "号码、edge 与落地:什么时候不该选 Twilio", en: "Numbers, edges and jurisdiction: when not to choose Twilio" },
    ],
  },
  /* ============ M8 · OP 运营、合规与增长 ============ */
  {
    id: "v25", code: "OP1", moduleId: "m8", difficulty: 2, hours: 5, prereq: ["v7"], viz: "qaLab",
    props: ["全量质检", "抽样置信区间", "坏案例闭环", "指标体系", "迭代周期"],
    title: { zh: "质检与评测闭环:上线第一周一定很难看", en: "QA and the Feedback Loop: Week One Always Looks Bad" },
    summary: {
      zh: "上线第一周的数据一定难看,这不是失败,是信息。关键是你有没有能力把难看的地方精确定位出来。传统质检靠人耳抽检百分之几的通话,今天可以把全部通话转写后交给模型逐条打分——成本大约是每通几分钱,覆盖率从 5% 变成 100%,这是 AI 客服反过来改造客服管理的地方。但抽样统计仍然要懂:如果你只抽了 200 通、发现 3 个严重坏案例,真实坏案例率的 95% 置信区间大概是 0.3% 到 4.3%,这个区间宽得没法做决策,而把样本量提到 1000 通,区间会收窄到能用的程度。本章先建指标体系:接通率、自助完成率、转人工率、关键槽位准确率、平均处理时长、首响时间、预约转化率、顾客满意度,并说明哪些是结果指标、哪些是可操作的过程指标。然后是闭环:坏案例分类(识别错、知识缺、话术差、工具失败)→ 归因到具体组件 → 修(加热词、补知识条目、改提示词、加重试)→ 灰度验证 → 回归测试集。声控台模拟这条闭环:调抽样比例、迭代周期和修复率,看自助率随周次爬升的曲线,以及不做闭环时它怎么在原地打转。",
      en: "Week one's numbers always look bad, and that is information, not failure — provided you can locate exactly where the badness lives. Traditional QA had a human listen to a few percent of calls; today you can transcribe everything and have a model score each call at a few cents apiece, taking coverage from 5% to 100%. That is where AI service quietly reforms service management. But sampling statistics still matter: sample 200 calls, find 3 serious bad cases, and the 95% confidence interval on the true rate runs roughly 0.3% to 4.3% — far too wide to act on, while a thousand calls narrows it to something usable. The chapter first builds the metric set: connect rate, self-service completion, handoff rate, critical-slot accuracy, average handle time, first response time, booking conversion and satisfaction, distinguishing outcome metrics from actionable process metrics. Then the loop: classify bad cases (misrecognition, missing knowledge, weak script, tool failure), attribute each to a component, fix it (add hotwords, add a knowledge entry, rewrite the prompt, add a retry), validate on a canary, and freeze it into a regression set. The bench simulates that loop — sampling ratio, iteration cadence and fix rate drive a self-service curve climbing week by week, next to the flat line you get without a loop.",
    },
    objectives: [
      { zh: "建立一套结果指标与过程指标分开的体系", en: "Build a metric set separating outcomes from process" },
      { zh: "用置信区间判断抽样结论可不可信", en: "Judge sampling conclusions with confidence intervals" },
      { zh: "把坏案例归因到具体组件", en: "Attribute bad cases to specific components" },
      { zh: "设计回归测试集防止改一处坏一处", en: "Build a regression set so fixes stop breaking other things" },
    ],
    outline: [
      { zh: "从人耳抽检到全量模型质检", en: "From listening by ear to model-scored full coverage" },
      { zh: "抽样量、坏案例数与置信区间", en: "Sample size, bad-case count and confidence intervals" },
      { zh: "坏案例的四类归因", en: "Four attributions for a bad case" },
      { zh: "灰度、回归与迭代节奏", en: "Canary, regression and iteration cadence" },
    ],
  },
  {
    id: "v26", code: "OP2", moduleId: "m8", difficulty: 3, hours: 6, prereq: ["v10", "v15", "v22"], viz: "complyLab",
    props: ["录音告知", "敏感个人信息", "合成内容标识", "疗效宣称", "留痕与拒绝"],
    title: { zh: "合规清单:任何一条踩了都可能让项目下线", en: "The Compliance Checklist: Any One Miss Can End the Project" },
    summary: {
      zh: "这是全书风险最高的一章,请当成必读。把门店语音 AI 会碰到的合规义务摊开,大致有四组。第一组是个人信息:通话录音属于个人信息处理,必须在通话开始时明确告知并取得同意;声纹属于生物识别信息,是敏感个人信息,需要单独同意和更强的保护;收集要最小必要(别因为想训练模型就把所有录音永久留着),存储期限要明示,并提供查询与删除通道。第二组是 AI 生成内容:使用合成语音服务顾客,应按《人工智能生成合成内容标识办法》的要求做标识;使用真人音色必须有本人书面授权,《民法典》第 1023 条把声音参照肖像权保护。第三组是营销与外呼:商业性语音呼叫需事先同意、拒绝后不得再拨、提供便捷退订。第四组是行业特有的表述红线:养生按摩不是医疗,不得宣称治疗、疗效、根治;不得对症状作诊断性回应;遇到越界试探必须拒绝、结束、留痕。声控台是一份可勾选的闸门清单:十四项义务,每项标注依据、后果与自查方法,未勾满就给出「不可上线」的判定和优先整改顺序。本章内容为工程与管理提示,不构成法律意见,正式上线前请咨询专业律师。",
      en: "This is the highest-risk chapter in the book; treat it as required reading. The compliance duties a shop's voice AI touches fall into four groups. First, personal information: recording a call is personal-information processing and requires clear notice and consent at the start of the call; a voiceprint is biometric and therefore sensitive personal information, requiring separate consent and stronger protection; collection must be minimal and necessary (do not keep every recording forever because you might train on it), retention periods must be disclosed, and access and deletion must be possible. Second, AI-generated content: serving customers with synthetic speech should be labelled under China's rules on labelling AI-generated content, and using a real person's voice requires their written authorisation, since Civil Code article 1023 protects voice by reference to portrait rights. Third, marketing and outbound: commercial voice calls need prior consent, must cease permanently on refusal, and must offer easy opt-out. Fourth, the industry's own language red lines: wellness massage is not medical treatment, so no claims of treating, curing or therapeutic effect, no diagnostic responses to symptoms, and any out-of-bounds probe must be refused, ended and logged. The bench is a checkable gate of fourteen duties, each with its basis, consequence and self-check method; leave any unchecked and it returns a not ready to ship verdict with a remediation order. This chapter is engineering and management guidance, not legal advice — consult a qualified lawyer before going live.",
    },
    objectives: [
      { zh: "列出录音、声纹与存储的告知与同意要求", en: "List notice and consent duties for recording, voiceprints and storage" },
      { zh: "说明合成语音的标识义务与真人音色授权", en: "State labelling duties and voice authorisation requirements" },
      { zh: "写出养生行业的禁语与安全应答模板", en: "Write the industry's banned phrasing and safe replies" },
      { zh: "用闸门清单判断一个系统能不能上线", en: "Use the gate checklist to judge readiness to ship" },
    ],
    outline: [
      { zh: "个人信息:告知、同意、最小必要、期限", en: "Personal information: notice, consent, minimisation, retention" },
      { zh: "生成内容标识与声音授权", en: "Content labelling and voice authorisation" },
      { zh: "外呼合规与退订", en: "Outbound compliance and opt-out" },
      { zh: "行业红线:疗效表述与越界请求", en: "Industry red lines: efficacy claims and out-of-bounds requests" },
    ],
  },
  {
    id: "v27", code: "OP3", moduleId: "m8", difficulty: 3, hours: 6, prereq: ["v25", "v26"], viz: "maturityLab",
    props: ["数据飞轮", "分阶段落地", "成熟度自评", "八维雷达", "下一步"],
    title: { zh: "数据飞轮与落地路线图:从第一个月到第十二个月", en: "The Data Flywheel and a Rollout Roadmap: Month One to Month Twelve" },
    summary: {
      zh: "最后一章把前面所有东西接起来,并回答一个实际问题:第一个月该做什么,第三个月该做什么,第十二个月应该长成什么样。推荐的路径是保守的,原因也很实际——门店没有试错的预算。第一阶段(1–2 个月)只做文字渠道的问答:微信和平台 IM,知识库上线,不碰写操作,风险最低而覆盖了最高频的咨询。第二阶段(3–4 个月)加预约写操作,先在 IM 上跑,把工具调用、幂等和占位释放跑稳。第三阶段(5–8 个月)才上电话,因为电话是实时的、不可撤回的、也是合规压力最大的。第四阶段(9–12 个月)做外呼与主动召回,并把全量质检与迭代闭环制度化。贯穿其中的是数据飞轮:每一通电话产生转写,转写产生标注,标注改进热词、知识库和提示词,改进带来更高的自助率,更高的自助率带来更多的通话——飞轮转起来之后,你的系统会比任何一家通用厂商更懂你这家店。本章的声控台是一份八维度、二十四题的成熟度自评,答完给出雷达图、当前阶段判定和下一步最该补的三件事,结果保存在你自己的浏览器里。",
      en: "The final chapter connects everything and answers a practical question: what to do in month one, month three, and what this should look like by month twelve. The recommended path is conservative for a practical reason — a shop has no budget for trial and error. Phase one (months 1–2) does text channels only: WeChat and platform IM, knowledge base live, no write operations — the lowest risk while covering the most frequent enquiries. Phase two (months 3–4) adds booking writes, still on IM, until tool calls, idempotency and hold release are stable. Phase three (months 5–8) finally takes the phone, because the phone is realtime, irreversible and carries the heaviest compliance load. Phase four (months 9–12) adds outbound and proactive win-back, and institutionalises full-coverage QA and the iteration loop. Running through all of it is the data flywheel: every call produces a transcript, transcripts produce labels, labels improve hotwords, knowledge and prompts, improvements raise self-service rate, and higher self-service brings more calls into the loop — once it spins, your system understands your shop better than any general vendor can. The bench is an eight-dimension, twenty-four-question maturity self-assessment producing a radar chart, a phase verdict and the three things most worth fixing next, saved in your own browser.",
    },
    objectives: [
      { zh: "说明数据飞轮的四个环节如何互相驱动", en: "Explain how the flywheel's four stages drive each other" },
      { zh: "把落地拆成四个阶段并说明排序理由", en: "Split rollout into four phases and justify the order" },
      { zh: "用八个维度给自己的系统打分", en: "Score your own system on eight dimensions" },
      { zh: "选出下一步最该补的三件事", en: "Pick the three things most worth fixing next" },
    ],
    outline: [
      { zh: "数据飞轮:通话、标注、改进、自助率", en: "The flywheel: calls, labels, improvements, self-service" },
      { zh: "四个阶段:先文字后语音、先读后写", en: "Four phases: text before voice, read before write" },
      { zh: "八个维度的成熟度自评", en: "The eight-dimension maturity assessment" },
      { zh: "下一步:三件最该做的事", en: "Next: the three things that matter most" },
    ],
  },

  /* ============ M9 · CS 案例研究 ============ */
  {
    id: "v28", code: "CS1", moduleId: "m9", difficulty: 3, hours: 7, prereq: ["v1", "v21"], viz: "chainLab",
    props: ["三店连锁", "回收期", "月净收益", "沉睡会员唤醒", "劝退损失"],
    title: { zh: "案例一:一家三店连锁的 AI 客服账本", en: "Case One: A Three-Shop Chain's AI Service Ledger" },
    summary: {
      zh: "把前面的所有模型合成一本真实的账。案例门店是一家三店的中医推拿连锁:日均来电 210 通、前台 4 人、客单价 268 元、会员 6800 人、其中三个月未到店的沉睡会员约 2400 人。他们上了一套 AI 语音客服,支出侧包括一次性集成与调试、每月的平台费(识别、合成、大模型)、线路费和一个兼职运维的人力分摊;收入侧包括挽回的漏接预约、释放的前台工时(转去做到店服务与加钟推荐)、被唤醒的沉睡会员,以及一项必须诚实计入的负项——被 AI 体验劝退的顾客。声控台把这些参数全部开放:你可以把门店数改成 1 或 20,把 AI 覆盖的咨询比例从三成拉到九成,看月净收益、回收期和三年累计怎么变。三个反直觉的结论会自己浮出来:第一,收益的最大头往往不是省人力,而是挽回漏接;第二,覆盖率拉到九成时净收益反而可能下降,因为劝退损失是超线性的;第三,门店越多,一次性集成成本被摊得越薄,连锁天然比单店更适合上这套东西。",
      en: "This case assembles every earlier model into one real ledger. The subject is a three-shop tuina chain: 210 calls a day, four front-desk staff, an average ticket of 268 yuan, 6,800 members of whom about 2,400 have not visited in three months. They deploy a voice AI service. The cost side covers one-off integration and tuning, monthly platform fees (recognition, synthesis, LLM), telephony, and an allocation of part-time operations staffing. The benefit side covers recovered missed bookings, freed front-desk hours (redeployed to in-shop service and upsell), reawakened dormant members, and one negative line that must be counted honestly — customers driven away by the AI experience. Every parameter is exposed: set shop count to 1 or 20, push AI coverage from thirty percent to ninety, and watch monthly net benefit, payback period and the three-year total respond. Three counter-intuitive conclusions surface on their own. First, the largest benefit is usually not saved labour but recovered missed calls. Second, pushing coverage to ninety percent can reduce net benefit, because deflection loss grows super-linearly. Third, the more shops there are, the thinner the one-off integration spreads — a chain is structurally a better fit for this than a single shop.",
    },
    objectives: [
      { zh: "把一套 AI 客服的投入产出做成可调模型", en: "Turn the investment case into an adjustable model" },
      { zh: "识别收益中占比最大的那一项", en: "Identify the largest single benefit line" },
      { zh: "把「被劝退的顾客」作为负项诚实计入", en: "Count deflected customers honestly as a negative" },
      { zh: "解释为什么连锁比单店更适合上 AI 客服", en: "Explain why chains fit this better than single shops" },
    ],
    outline: [
      { zh: "案例门店的基础数据", en: "The subject shop's baseline numbers" },
      { zh: "支出侧:一次性、月度与人力", en: "Costs: one-off, monthly and staffing" },
      { zh: "收益侧:挽回、省时、唤醒,减去劝退", en: "Benefits: recovery, hours, reactivation, minus deflection" },
      { zh: "回收期、敏感性与规模效应", en: "Payback, sensitivity and scale effects" },
    ],
  },
  {
    id: "v29", code: "CS2", moduleId: "m9", difficulty: 3, hours: 7, prereq: ["v16", "v25"], viz: "traceLab",
    props: ["全链路复盘", "逐跳时延", "故障注入", "技师名识别错", "知识缺失"],
    title: { zh: "案例二:一通 2 分 17 秒的电话,逐跳复盘", en: "Case Two: One Call of 2m17s, Hop by Hop" },
    summary: {
      zh: "最后一章把整本书放回一通真实的电话里。晚上八点零三分,一位老顾客打进来:想约明天下午的肩颈理疗,指定王师傅,还想问上次买的团购券能不能和会员卡一起用。机器从接起到确认用了 2 分 17 秒、11 个轮次。声控台把这 11 轮逐条展开:每一轮的用户语音时长、尾点等待、ASR 结果与置信度、检索命中的知识块、大模型输出、TTS 首包与播放时长,以及那一轮实际发生了什么。你会看到两处真实的坑:第 5 轮把「王师傅」识别成了「黄师傅」(热词里没有这个名字),靠第 6 轮的复述确认救了回来;第 8 轮问团购券叠加规则时知识库没有对应条目,幸好开了「查不到就说不知道」的兜底,没有编造。然后你可以注入三类故障——删掉热词、把尾点阈值调到 300 毫秒、抽掉知识库条目——看这通电话怎么从「成功预约」滑向「转人工」甚至「顾客挂断」。这是对前面二十八章每一个参数的一次实战检验。",
      en: "The last chapter puts the whole book back inside one real call. At 8:03 p.m. a returning customer rings: she wants tomorrow afternoon's neck-and-shoulder treatment with a specific therapist, and asks whether the group-buy voucher she bought can be combined with her membership card. From pickup to confirmation the machine takes 2 minutes 17 seconds across 11 turns. The bench unfolds all eleven: each turn's speech duration, endpoint wait, ASR result and confidence, the knowledge chunks retrieved, the LLM output, TTS first packet and playback duration, and what actually happened. Two genuine traps appear. In turn 5 the therapist's surname is misrecognised because the name was never added to the hotword list, and the read-back confirmation in turn 6 rescues it. In turn 8, asked whether a voucher stacks with a membership card, the knowledge base has no entry — and only the say-so-when-you-cannot-find-it fallback prevents an invention. You can then inject three faults — remove the hotwords, drop the endpoint threshold to 300 ms, delete the knowledge entry — and watch the call slide from confirmed booking to human handoff and finally to the customer hanging up. It is a live test of every parameter from the previous twenty-eight chapters.",
    },
    objectives: [
      { zh: "读懂一通电话的逐跳时延与状态", en: "Read a call's per-hop latency and state" },
      { zh: "定位一次识别错误的根因与挽救手段", en: "Trace a misrecognition to its cause and its rescue" },
      { zh: "观察知识缺失如何变成幻觉或兜底", en: "Watch missing knowledge become invention or fallback" },
      { zh: "通过故障注入验证系统的脆弱点", en: "Find the fragile points through fault injection" },
    ],
    outline: [
      { zh: "11 个轮次的完整时间轴", en: "The full timeline of eleven turns" },
      { zh: "第 5 轮:热词缺失与复述确认", en: "Turn 5: a missing hotword and the read-back rescue" },
      { zh: "第 8 轮:知识缺失与兜底策略", en: "Turn 8: missing knowledge and the fallback" },
      { zh: "注入三类故障,看它怎么崩", en: "Inject three faults and watch it fail" },
    ],
  },
];

// Derived totals used by the home page hero.
const DEMO_COUNT = CHAPTERS.filter((c) => c.viz).length;      // interactive benches
const TOTAL_HOURS = CHAPTERS.reduce((s, c) => s + (c.hours || 0), 0);

window.MODULES = MODULES;
window.CHAPTERS = CHAPTERS;
window.DEMO_COUNT = DEMO_COUNT;
window.TOTAL_HOURS = TOTAL_HOURS;
