Prechádzať zdrojové kódy

docs(readme): 把 2026-09-24 的改动单独成节并补全(用户要求「先将改动同步到 readme」)

今天(09-24)的改动原先混在 20260923 那节里,且有几条根本没写进 README。整理如下:
- 新增 `## 20260924` 节,按提交顺序收录今天的 7 项:
  ① NLS 的 token 改由后端网关签发(getALYToken,前端不再需要 AK/SK)
  ② 修「出字延迟高」(排空策略 1× → 4× 追赶;A/B 实测 1377~1434ms → 145~183ms)
  ③ 全量接口清点 → 死代码大清理(净删 2257 行,含两个孤儿 npm 依赖)
  ④ 环境只保留开发与生产(删 qingpu / test;合并 VITE_REDIRECT_MAP 等两项,顺带修掉
     以前生产包里 redirect.html 的语法错误)
  ⑤ 删掉走不到的卡片引导链 + asr 移出 three-libs
  ⑥ 修复「界面卡在正在初始化」(回归)+ 新增内联脚本语法检查
  ⑦ 接口清单按当前代码重核(13 个在用接口 + 5 条代理)
- 顺带修正 09-23 节里已过期的表述:asr 路径(three-libs → src/asr)、验证数(39/39 → 44/44)、
  `/nls-token/` 生产方案(已被网关端点取代)、旧 ASR 文档指向的文件已删除、
  技术债 #9 里已不存在的 `.env.qingpu` / `.env.test`

无代码改动;validate-harness 通过。

Co-Authored-By: Claude Code <noreply@anthropic.com>
gongtianxiao 1 mesiac pred
rodič
commit
770e4541f6
1 zmenil súbory, kde vykonal 65 pridanie a 44 odobranie
  1. 65 44
      README.md

+ 65 - 44
README.md

@@ -422,65 +422,86 @@
     `harness/tools/get-nls-token.mjs` 与 dev 端点共用;带 5 分钟提前刷新 + 并发去重
   - 验证:端点 200;**拿端点发的 token 真调通一次识别**(`status:20000000 SUCCESS`);
     `npm run build` 后**产物里搜三个凭据值 0 命中**
-- **修:界面卡在「正在初始化,请稍候...」**(2026-09-24,我自己上一轮删出来的回归):
-  删 `initToken` 时在 index.html 的内联脚本里留了个不配对的 `}` → 脚本语法错误 →
-  `hasSequence` 没置位、`sequenceLoaded` 没派发 → 加载遮罩永不隐藏。
-  **构建没抓到**,因为 Vite **不解析** index.html 里的普通 `<script>`(原样拷贝);
-  已修,并新增 `harness/tools/verify-inline-scripts.mjs`(编译 `dist/*.html` 的内联脚本 +
-  报出未替换的 `%VITE_*%` 占位符,变异测试确认会红),同时写进 `sops/verification.md`:
-  改了 index.html / redirect.html 必须跑它
-- **删掉走不到的卡片引导链 + asr 搬家**(用户 2026-09-24):
-  - `index.html` 的 `initToken()` / `initCardInfo()` / `else` 分支:**两个环境都走不到**
-    (`isKnowledgeApi` 恒 true),且它们的产物(`globalThis.token` / `cardInfo`)**全仓库无读者**;
-    那段本身还是坏的(调用从未挂到 globalThis 的 `changeLoadingText`/`showReloadView`,走到会抛)。
-    一并删掉随之孤儿的 `randomString` / `changeLoadingText` / `showReloadView` 与
-    `VITE_APP_KEY`(产物核对:`app_key` / `openid_to_token` 0 命中)
-  - `src/three-libs/asr/` → **`src/asr/`**,`three-libs/` 目录删除(名字有误导性,且只剩 asr 一个子目录);
-    import 同步为 `@/asr/nls`,验证脚本新增断言钉住新位置
-- **环境只保留开发与生产**(用户 2026-09-24):删掉 qingpu / test 两个构建模式 ——
-  `.env.qingpu` / `.env.test` 删除前,先把它俩**独有的配置合并进 `.env.production`**:
-  `VITE_DOWNLOAD_URL="/api/download/"` 与 **`VITE_REDIRECT_MAP`**(后者 production 原本没有,
-  直接删会让 `redirect.html` 的产物变成 JS 语法错误 —— **顺带修掉了**:以前 `npm run build`
-  打出来的生产包里那个页面就是坏的)
-  - 脚本:删 `build:qingpu` / `build:test`,保留 `zip`,新增 `npm run deploy:qingpu`(= build + zip)
-  - `index.html` 里两处**本来就是死的** qingpu 分支一并删掉(`isQingpuEnv` 写的是字面量 `"MODE"`、
-    永远 false 且无人读;`isLanMode` 赋值后从未使用)
-  - 另记一个**待清理项**(未动):`initToken()`(`openid_to_token` + `VITE_APP_KEY`)现在两个
-    环境都走不到(唯一为 false 的 test 环境已删),删它前要先理清与 `initCardInfo()` 的关系
-- **死代码清理**(用户 2026-09-24「B 全部删掉」,清单见
-  [`harness/docs/reference/current-api-call-sites.md`](harness/docs/reference/current-api-call-sites.md)):
-  - 删掉:厂商 OSS 上传的停用实现(含 `VITE_UPLOAD_SIGN_URL`)、旧厂商 ASR 全套
-    (`three-libs/asr/index.ts` + `VITE_ASR` / `VITE_XF_ASR` + vite `/asr/` 代理 +
-    `index.html` 的 `globalThis.isXF`)、`stream-message-coordinator.ts` 整个类(~1100 行)
-    + 随之孤儿的 `utils/scope-stream-boundary.ts`、`updateSessionInfo`、
-    `getModelServerUrl` / `isKnowledgeApi`、三个空目录、两处停用注释块,
-    以及因此变成孤儿的 npm 依赖 `qs` / `@microsoft/fetch-event-source`
-  - `MessageState` 枚举**是活的**,已从被删的协调器搬到 `src/types/record.ts`(取值未动)
-  - 教训记档:删文件要按**导出的符号名**反查引用(只查 import 路径会漏 ——
-    `components/utils.ts` 因此被误删,构建报错才发现,已恢复)
 - **实时语音识别已切到阿里云 NLS**(2026-09-23,接管旧厂商 SDK):
   - 协议层 `src/asr/nls-protocol.ts`(纯函数:帧构建 / 事件折叠 / 16k PCM 编码)
-    + 类 `src/three-libs/asr/nls.ts`,**对外契约与旧 SDK 一致** → 两个壳组件只改了 import
-    与一处构造参数(去掉硬编码 appKey),其余一行没动
+    + 类 `src/asr/nls.ts`(2026-09-24 前在 `src/three-libs/asr/`),**对外契约与旧 SDK 一致**
+    → 两个壳组件只改了 import 与一处构造参数(去掉硬编码 appKey),其余一行没动
   - **协议是实测出来的**:`StartTranscription`→`TranscriptionStarted`→二进制音频帧→
     `TranscriptionResultChanged`(中间结果,会被覆盖)→`SentenceEnd`(整句定稿)→`TranscriptionCompleted`;
     两条硬约束——**每条指令都要带 appkey**、**task_id 不能是空串**(各踩过一次,已写成断言)
   - 拼接全文只用 `SentenceEnd`;中间结果只用于实时显示
-  - 验证:`harness/tools/verify-nls-realtime.mjs` **39/39**,含**真实识别**(Windows TTS 造语音 →
-    推流给真实网关 → 识别结果与所说话一致)与切线的源码级断言
+  - 验证:`harness/tools/verify-nls-realtime.mjs`(**39/39**,2026-09-24 已长到 44/44),
+    含**真实识别**(Windows TTS 造语音 → 推流给真实网关 → 识别结果与所说话一致)与切线的源码级断言
   - 重采样按实际 `audioContext.sampleRate` 算(旧实现写死 44100 的隐患不再沿用)
   - 文档:`harness/docs/reference/asr-nls.md`(旧的 `asr-realtime.md` 标注为"换之前那套")
-  - ⚠️ 生产要由后端/网关做同样形态的 `/nls-token/` 端点(已登记技术债 #10);
+  - ⚠️ 当时生产要由后端/网关做同样形态的 `/nls-token/` 端点 —— **2026-09-24 起改为直接调
+    后端网关的 `getALYToken`**(见本页 `## 20260924` 第一条),技术债 #10 已大半还清;
     **浏览器实测由用户确认**(harness 不能替真人说话)
 - **实时语音转文字补了一份接口文档**(用户 2026-09-23 要求):
   [`harness/docs/reference/asr-realtime.md`](harness/docs/reference/asr-realtime.md) ——
-  SDK 在仓库里(`src/three-libs/asr/index.ts`),文档逐条对源码核对:构造参数与方法、
+  当时 SDK 就在仓库里(`src/three-libs/asr/index.ts`,**该文件已于 2026-09-24 删除**,
+  文档现在是历史记录),文档逐条对源码核对:构造参数与方法、
   `transform`/`error` 事件契约、**底层 WebSocket 协议**(`/common/asr_hub?project=qingpu&engine=aliyun_dashscope…`、
   首帧、16k/16bit 单声道 PCM 的二进制帧、结束帧、服务端 `start`/`stop` → mid/fin/breath 的映射)、
   两端的生命周期与调用点、以及**6 条已知坑**(采样率换算写死 44100、"权限被拒绝"文案不可达、
   `openMicroPhone` 只会 throw 或返回 true、非讯飞分支不主动关 socket、硬编码的十年期 JWT、
   `clear()` 会清空所有监听)。**只是文档,没改任何代码。**
-- **🔴 待处理(登记进技术债 #9)**:`.env.production` / `.env.qingpu` / `.env.test`
-  **都没有配 `VITE_DMS_API`** —— 这些环境打出来的包里附件**传不上去**(直接抛「未配置 DMS 接口地址」)、
+- **🔴 待处理(登记进技术债 #9)**:`.env.production` **没有配 `VITE_DMS_API`**
+  —— 该环境打出来的包里附件**传不上去**(直接抛「未配置 DMS 接口地址」)、
   DMS 会话落库也**静默跳过**。接生产要先定:DMS 从部署环境是否可达、**token 由谁注入**
   (开发环境是 vite 代理加的,生产得 nginx 做同样的事)
+
+## 20260924
+
+> 今天两条主线:**语音识别换阿里云 NLS 收尾**(token 改由后端网关签发)+ **一次大清理**
+> (死代码、多余环境、走不到的引导链)。清理过程中出过一次回归,已修并补上守护脚本。
+
+- **NLS 的 token 改由后端网关签发**(用户指定 `http://121.43.55.7:10011/proxy/verify/getALYToken`):
+  - 先验证再动前端:网关返回 `{code:200, content:"<token>"}`,**拿它 + 新 appkey 真调通一次识别**才改代码
+  - 前端改为调该端点(同源 `/aly-api/…`;端点只有 HTTP、页面是 HTTPS,必须走同源路径,
+    与 `/ip-api/` 同一个坑),**前端侧不再需要 AccessKeySecret** → vite 里那段"自己签 token"的
+    `/nls-token/` 中间件连同 AK/SK 通路一起删除
+  - 配置:`VITE_NLS_TOKEN_API` 进 `.env.development`;**appkey 放 `.env.development.local`**(gitignored)
+  - harness 工具统一成「显式 token → 网关 → AK/SK 自签」三级,**自动化验证走的就是生产同一条路**
+  - ⚠️ 生产仍需:nginx 转发 `/aly-api/` + 注入 `VITE_NLS_APPKEY`(技术债 #10)
+- **修「出字延迟高」**(用户反馈):实测定位到是**我自己的排空策略**——握手期间麦克风已在采集,
+  按 1× 实时排空那段积压**永远排不掉**(旧厂商 SDK 是 2×,所以它能追上)。
+  改成「平时实时、积压时 4× 追赶」+ 停止时按 ~5× 发 + 断句阈值 800→500ms。
+  A/B 实测(模拟 1s 积压):感知延迟 **1377~1434ms → 145~183ms**,定稿时 2087ms → 683ms。
+  另外实测确认:网关中间结果节奏恒 ~480ms(引擎固有,前端改不了)
+- **全量接口清点 → 死代码大清理**(用户:「列出来我来判断哪些可以删除」→「B 全部删掉」):
+  - 清点:grep 网络调用点 + 逐个 env 变量数引用,结果落进
+    [`harness/docs/reference/current-api-call-sites.md`](harness/docs/reference/current-api-call-sites.md) §0
+  - 删除:厂商 OSS 上传的停用实现(含 `VITE_UPLOAD_SIGN_URL`)、旧厂商 ASR 全套
+    (`three-libs/asr/index.ts` + `VITE_ASR` / `VITE_XF_ASR` + vite `/asr/` 代理 +
+    `index.html` 的 `globalThis.isXF`)、`stream-message-coordinator.ts` 整个类(~1100 行)
+    + 随之孤儿的 `utils/scope-stream-boundary.ts`、`updateSessionInfo`、
+    `getModelServerUrl` / `isKnowledgeApi`、三个空目录、两处停用注释块,
+    以及因此变成孤儿的 npm 依赖 `qs` / `@microsoft/fetch-event-source`(**净删 2257 行**)
+  - `MessageState` 枚举**是活的**,已从被删的协调器搬到 `src/types/record.ts`(取值未动)
+  - 教训记档:删文件要按**导出的符号名**反查引用(只查 import 路径会漏 ——
+    `components/utils.ts` 因此被误删,构建报错才发现,已恢复)
+- **环境只保留开发与生产**(用户要求):删除 qingpu / test 两个构建模式 ——
+  `.env.qingpu` / `.env.test` 删除前,先把它俩**独有的配置合并进 `.env.production`**:
+  `VITE_DOWNLOAD_URL="/api/download/"` 与 **`VITE_REDIRECT_MAP`**(后者 production 原本没有,
+  直接删会让 `redirect.html` 的产物变成 JS 语法错误 —— **顺带修掉了**:以前 `npm run build`
+  打出来的生产包里那个页面就是坏的)
+  - 脚本:删 `build:qingpu` / `build:test`,保留 `zip`,新增 `npm run deploy:qingpu`(= build + zip)
+  - `index.html` 里两处**本来就是死的** qingpu 分支一并删掉(`isQingpuEnv` 写的是字面量 `"MODE"`、
+    永远 false 且无人读;`isLanMode` 赋值后从未使用)
+- **删掉走不到的卡片引导链 + asr 搬家**(用户要求):先做影响评估再删 ——
+  `index.html` 的 `initToken()` / `initCardInfo()` / `else` 分支**两个环境都走不到**
+  (`isKnowledgeApi` 恒 true),产物(`globalThis.token` / `cardInfo`)**全仓库无读者**,
+  且那段本身是坏的(调用从未挂到 globalThis 的 `changeLoadingText`/`showReloadView`,走到会抛)。
+  一并删掉随之孤儿的 `randomString` / `changeLoadingText` / `showReloadView` 与 `VITE_APP_KEY`。
+  另:`src/three-libs/asr/` → **`src/asr/`**,`three-libs/` 目录删除(名字有误导性,且只剩 asr 一个子目录)
+- **修复:界面卡在「正在初始化,请稍候...」**(上面那次删除引入的回归):
+  删 `initToken` 时在 index.html 的内联脚本里留了个不配对的 `}` → 脚本语法错误 →
+  `hasSequence` 没置位、`sequenceLoaded` 没派发 → 加载遮罩永不隐藏。
+  **构建没抓到**,因为 Vite **不解析** index.html 里的普通 `<script>`(原样拷贝);
+  已修,并新增 `harness/tools/verify-inline-scripts.mjs`(编译 `dist/*.html` 的内联脚本 +
+  报出未替换的 `%VITE_*%` 占位符,变异测试确认会红),同时写进 `sops/verification.md`:
+  改了 index.html / redirect.html 必须跑它
+- **接口清单按当前代码重核**(用户:「再列出现在使用的接口」):13 个在用接口 + 5 条 vite 代理,
+  见 [`current-api-call-sites.md`](harness/docs/reference/current-api-call-sites.md) §0 A 表;
+  相比清点时新增一条漏记的(正文里 `<policy-list-updated>` 给的动态 link)