Files
zyt/docs/plans/followup-audio-deployment.md
T

172 lines
21 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 回访录音提炼:实现与发布门禁
## 当前交付边界
源码在隔离工作副本 `/Users/long/.codex/worktrees/followup-audio/zyt`。原目录 `/Users/long/Work/zyt` 的已有源码保持不变;功能位于 `codex/followup-audio` 分支,初始提交 `70be2fc70`。未推送、未迁移生产、未部署。入口默认关闭。
已完成网页患者编辑入口、诊单绑定私有分片上传、独立持久异步任务、Dify原始音频传递、可编辑审阅草稿、显式人工确认、多表原子写入、同日真实记录明细、历史跟踪备注、来源审核轨迹及90天清理。
**真实模型门禁仍未通过**:9月29日本地 Dify 探针返回 `CONFIG_MISSING`;10月7日已按用户指定兼容接口发起合成音频检查,返回 `AUDIO_NOT_PROCESSED`,详见文末。不能把 HTTP 200、上传成功或文本能力当成音频识别成功。媒体测试中的完整一小时WAV→MP3、HTTP回环模型和浏览器合成数据不是实际Dify识别验收;混合口音需代表性脱敏样例。
## 2026-10-08 双声道修订(优先于下方早先混音验收)
- `asr_then_llm` 对双声道默认按15秒原始时间窗分别转写左右声道,不再使用混音。`ASR_STEREO_CHUNK_SECONDS=15`(1..30)独立于mono的120秒设置;两声道总段数仍上限1000,>2声道明确拒绝。
- 新checkpoint v2和提供方指纹绑定声道数、`separate-fixed-windows-v1`策略、窗口长度与exact-zero策略。证据增加不可修改的`channel`、原始区间;mono/旧结果兼容读取,旧未知请求不能改绑/重放。
- 只有转换后的PCM16k/s16le逐字节全零,才以`local_silence`本地完成并保留区间,无上游请求ID。任何非零低音量仍送ASR;这不是VAD或按音量丢段。
- 页面增加左右声道片段、原音/左/右回听和人工角色映射:`unconfirmed`、`left_service`、`right_service`。患者侧标签是“患者/家属端”,不能只按声道推定本人。角色标注不自动重提炼;保存/切换角色取消全部选中,须重新逐项核对。新stereo未保存角色禁止apply,角色及审核人进入加密审计。
- 4份获准录音在私有一次性MySQL均通过完整Upload→queued→独立Worker→左右ASR→Qwen→review;338个声道时间窗全部覆盖,314次真实ASR、24个本地数字静音、4次提炼。原始文件不变,全部11个候选初始未选中且须复核。
- 按转写上下文核对,仍有约数变精确值、含混姓名/电话号码、未提及病史补阴性等错误,某样本还重复生成大量待核实文字。**分声道实现通过,不等于内容准确度通过;4份语义门槛仍FAIL**,也未取得逐字人工回听金标准。
- 隔离诊单实际测试13项通过:未确认角色不能写入、确认角色清选择、旧版本拒绝、伪造channel拒绝、切换角色重审、明确采纳1个有依据字段、审计带角色、重复新增0行;没有真实患者业务写入。
- 本轮后端26个脚本、前端64项、质量41项及独立复核通过;全量类型检查仍60项基线诊断,无本功能新增。完整前端构建通过,无发布脚本。原生浏览器WebAudio/实机听感验收仍缺,严格mock和源码审核不能替代实际试听。
- 所有生产开关继续关闭。本轮没有部署应用或改变模型服务/GPU;不会因为用户猜测一边客服一边患者,就预填已确认角色。下方早先混音、缓存导入与失败记录保留为历史证据。
## 2026-10-08 最新结论:两阶段链路可用,语义准确度门禁未通过
已增加 `asr_then_llm`:真实音频分片 → ASR 转写 → 可配置文本模型提炼 → 人工审阅。原件、转写、提炼响应分别留存,不把纯文本输入冒充模型听过音频;前端显示转写进度与失败后保留的原文。该结论优先于本文下方历史记录,**不等于功能已获准上线**。
- 用户授权的 4 段录音,独立转写共 23/23 片段成功,约 41.8 分钟。没有逐字人工听写金标准,不能据此报告 ASR 准确率,也不代表所有方言/一小时真实模型通过。
- 使用这些真实 ASR 缓存,当前 Qwen 的 4 份 V2 结果通过结构校验,但逐项对照原文发现提问当症状、约数精确化、未提及病史补阴性、说话人/机构归属错误。**4 份均未通过严格语义验收**;所有 13 个候选均需复核、未自动选中。身份可绑定任意本地测试诊单,不意味着可以忽略录音内的患者/家属区别。
- 完整原生 Upload → 持久队列 → 独立 Worker → 实际 ASR → 实际 Qwen → review 已用 1 段录音在隔离 MySQL 跑通;其他准确度评估复用缓存,不冒充重复执行完整链路。
- 只在一次性测试诊单明确审阅后采纳有支持的字段;含糊血糖、未经确认病史等不写入。另将已校验的真实缓存结果显式导入本地验收任务测试日常记录,不冒充新的 ASR 请求。冲突刷新会取消选择,必须重新审阅;重复确认不增行。
- 原生首轮有一个 ASR 片段超时终态不明,保留 `needs_reconciliation`,没有清除或盲目重发。独立缓存的成功不改变这个旧请求的状态。
- vLLM 对 `uniqueItems` 返回 HTTP400;生成 schema 已适配,服务端仍拒绝重复引用与重复枚举。开启 thinking 的 4 次请求耗尽 8192 输出预算而不完整;显式关闭 thinking 后结构输出完成,**但语义质量仍失败**,不可拿结构成功替代准确度。
- 所有生产开关保持关闭;未部署本功能、未执行生产迁移、未改真实患者记录。本轮本地 MySQL 使用专用随机 `fa_pipeline_*` 库和真实 Access/Store/Worker/Apply 服务,不是生产登录态浏览器验收。
### 两阶段配置与恢复约束
使用 `server/.env.followup-audio.example` 中 `QWEN_DRIVER=asr_then_llm` 及独立 `QWEN_ASR_*`、`QWEN_EXTRACTION_*` 配置;`openai` 槽位也支持同样结构。名称只是逻辑槽位,不自动更换供应商。模型地址、密钥、模型名、chunk策略、输出格式/schema/citation版本及 thinking 选项均绑定验证指纹。
- ASR 默认 120 秒 PCM16k 单声道分片,整段覆盖;未做说话人分离,片段边界不是逐字时间戳。小分片配置产生超过 1000 段时在首次网络请求前拒绝。
- `QWEN_EXTRACTION_RESPONSE_FORMAT=json_schema`、`QWEN_EXTRACTION_MAX_TOKENS=8192`;当前本地验收显式 `QWEN_EXTRACTION_ENABLE_THINKING=false`。这只是已测配置,不是质量通过凭证,不提供协议/模型自动回退。
- 服务端证据窗口由真实转写生成,模型只返回引用 ID。宽窗口不能证明每个事实;程序保留模型日期弃权,不把别句日期补给该事件,矛盾日期留空并强制审阅。
- 阶段检查点存入加密的 `pipeline_cipher`;先持久化 intent,再发请求。已完成 ASR 可复用;超时/未知结果锁定核验;已完成但不合格的提炼保留,不重新无限调用。
- 已部署旧表时补执行 `server/sql/2026_10_08_followup_audio_pipeline.sql`,**仍须正式发布授权和备份**。首次安装用更新后的主迁移,两个迁移均幂等。90 天清理覆盖新检查点全文,必要采纳证据仍按审核规则保留。
- HTTPS 为默认;仅经管理员明确建立的 SSH 隧道可启用 `QWEN_ALLOW_LOOPBACK_TUNNEL=true`,只接受字面 `127.0.0.1`/`[::1]`。不允许外网 HTTP、localhost 别名或跨地址重定向。不要把本地临时隧道地址直接当生产配置。
- 这次连接的是本单位 ASR 与 Qwen 服务。Dify 侧尚未配置/验收 speech2text 应用,不宣称已通过 Dify 音频链路。
下一质量门槛:取得人工回听/标注金标准,先解决主体、问答肯否、模糊数量和未提及字段问题,再测短/中/一小时及混合口音;门槛通过前不得打开真实客服入口。部署回退脚本恢复源码,不撤销任何已写业务记录。
## 使用与数据规则
- 入口:网页问诊列表→编辑患者→回访录音。PC/H5共用现有编辑组件;桌面客户端不增加入口。
- 每次一个文件,MP3/M4A/WAV/AMR,最多500MiB/3600秒,按服务端实际元数据检查,2MiB分片。
- 原件只在 `server/runtime/private/followup_audio/`;播放需登录令牌请求头及患者权限。没有公开录音URL,没有伪造医生通话记录。
- AMR或超出上游预算的录音生成私有临时32kbps/16kHz单声道MP3,校验完整时长,不截断、不自动分段改语义;默认上游预算20MiB。原件保留不改,处理副本在成功或失败后清理。
- 通话时间采用北京时间,模型解释今天/昨天等;模糊范围需客服确认,不能自动随便取一天。时段缺钟点时早上08:00、中午12:00、下午15:00、晚上20:00、睡前22:00,明确标记估算并要求审阅。
- 同值不同测量保留不同记录;重复提及同一事件才合并。默认钟点相同不自动去重。相同诊单+音频SHA256+模型复用原任务,换文件名、上传ID或通话时间不绕过未知请求防重发;日期错误在审阅中修正,已采纳任务不重新写入。
- 字段、字典、身份锁、phonePlain和dailyRecord权限均在服务端校验。未提及不等于正常/无,不自动生成诊断或处方。病历当前值与历史记录分开选择。
- 草稿有版本和不可变来源证据。冲突刷新最新快照并取消选择,保留用户编辑供重新确认;不静默覆盖。原病历表单有未保存修改时不允许被录音结果覆盖。
- 确认写入使用一个事务(单次READ COMMITTED,不更改连接默认),锁定任务与真实记录,审计同事务,重复确认幂等。存在不明确的值可不采纳,不要求全部接受。
## 配置与运行前检查
1. 核对实际数据库字段/表前缀,先在线备份;经发布授权后应用 `server/sql/2026_09_29_followup_audio.sql`。SQL使用现有 `zyt_` 前缀,可重复执行;新增四表及血糖时间来源字段,不改旧数据。不得把测试库模拟schema当作生产已核验。
2. 使用 `server/.env.followup-audio.example` 配置明确的驱动、模型和凭据。`dify` 可复用现有 `[prescription_ai]` base URL/profile凭据;`openai_audio` 必须显式配置自己的地址、密钥和模型。不会自动切换协议/供应商或回退为纯文本。不要把生产数据库凭据复制到本地测试环境,不要把API密钥放前端或提交Git。
3. PHP8.2已做本地验证;需要curl/fileinfo/openssl、可执行FFmpeg/FFprobe。Web/worker用户都必须可读写同一个私有录音目录,目录不能放在public或公开存储域名下。部署多节点需共享私有存储和稳定加密密钥(至少32字符,离线备份),不要依赖各节点自动生成不同runtime key。
4. 反向代理/PHP单请求上限至少容纳2MiB分片及表单开销;不要将500MiB整个请求交给PHP上传。短期处理副本也要计入磁盘容量。
5. Dify存储副本/缓存的保留与删除策略另行核验;本系统90天删除不代表Dify侧已删除。现实现没有通用Dify远端删除凭证或删除API,不能作此承诺。
## 合成音频能力门禁
本地生成器仅接收输出目录,不接受患者录音;使用macOS `say`(默认Tingting)和FFmpeg/FFprobe。Linux消费者不需要say;可安全转移已生成合成夹具及清单。不要重新生成已有manifest或重发状态未确认的探针。
现成合成夹具:`/private/tmp/zyt-followup-audio-synthetic-gate/manifest.json`(60/900/3598秒,独立首尾口令和事实;预期答案只在本地manifest,不能放进提示词)。需要重建时使用新的输出目录:
```sh
cd /Users/long/.codex/worktrees/followup-audio/zyt
python3 scripts/generate_followup_audio_fixtures.py --output /private/tmp/followup-audio-new-synthetic-gate
cd server
php think followup-audio:probe --synthetic --manifest /private/tmp/zyt-followup-audio-synthetic-gate/manifest.json --profile qwen --case all
# 仅当另一个profile也准备供客服使用时,再单独验证openai。
```
必须核实真实音频被读取、首尾事实和数值/否定/时间关系、未截断长录音,再用获准的代表性脱敏口音样例人工回听评估。上传成功、HTTP200或纯文本回复都不是通过。探针有状态文件:已通过的case复用;结果未知先核对原Dify任务/账单,禁止自动重发。不要删除未知状态来强制重试。
只有验证通过的profile可加入 `VERIFIED_PROFILES`,且必须将本次报告的 `application_fingerprint` 写入对应 `QWEN_VERIFIED_FINGERPRINT` / `OPENAI_VERIFIED_FINGERPRINT`;人工核验通过后才设置 `AUDIO_VERIFIED=true`,经授权发布后再设 `ENABLED=true`。默认 `timestamp_verified=false`,不把LLM编出的毫秒位置当成ASR对齐。
## 独立消费者、清理与故障处理
```sh
# cwd为部署目录的server;用Supervisor/systemd管理,不在网页请求内跑模型。
php think followup-audio:work
php think followup-audio:work --once
php think followup-audio:cleanup
```
- 初始全局并发1,可通过CONCURRENCY配置1–8;独立数据库租约/互斥,不占用原处方队列。
- 600秒租约高于240秒请求预算,处理过程中续租。权限撤销、患者重绑、旧worker令牌均不得继续上传/回写。
- `needs_reconciliation` 表示请求可能已经被接收,不能自动/手动再次调用;请核对已保存上游IDs、任务和账单。现版本不提供绕过此状态的“强制重试”按钮。
- 经确认且允许重试的本地失败保留已有成果。模型不支持音频、结构校验失败或能力未验证,不得成功落诊单。
- 将cleanup纳入目标环境已批准的定时任务(本轮未安装定时任务)。90天起点为上传创建时间;清除原件、完整转写及可还原全文的分析副本,保留正式记录和必要采纳审计;活动租约不被清理,清理部分失败退出非零,便于监控。
- 关注队列等待时间、needs_reconciliation、失败码、租约恢复、磁盘和清理errors;日志不得包含录音内容、完整病历或凭据。
## 本地检查与回退
已有验证:真正隔离MySQL并发claim/create/apply、权限撤回/转派、单事务故障回滚;真实HTTP multipart和媒体处理;实际Vue组件测试;浏览器合成数据完成任务详情→草稿→确认采纳→刷新。完整网页构建通过,未执行发布脚本。全量vue-tsc在同一生成声明环境下基线和修改后均60条诊断,本功能文件0条,新诊断签名0。
```sh
cd /Users/long/.codex/worktrees/followup-audio/zyt
FOLLOWUP_AUDIO_TEST_ALLOW_DISPOSABLE=1 FOLLOWUP_AUDIO_TEST_MYSQL_PORT=23316 FOLLOWUP_AUDIO_TEST_MYSQL_PASSWORD="$TEST_MYSQL_PASSWORD" php server/tests/FollowupAudioCoreTest.php
node --test admin/tests/followup-audio.test.cjs
node admin/scripts/verify-daily-records.cjs
```
数据库命令只允许一次性本地测试容器,禁止换为线上端口。测试容器密码通过专用环境变量注入,不读取业务 .env。
发布回退先关闭入口并停止本功能消费者,保留旧静态资源和数据库备份。新增表/列应保留以保护审计;源码回退脚本只恢复源码,**不撤销已写入病历**。正式记录若需恢复,必须按审核轨迹和当前版本另做受控补偿,不能整库回滚覆盖其他人的新数据。
主交付四角色及源码回退验证位于 `/Users/long/Work/zyt/artifacts/followup-audio-20260929/`;补充研究、局部检查与未通过的真实模型门禁保留在工作副本 `artifacts/followup-audio-implementation/`。
## 2026-09-29 页面重整与提炼补测
- 页面改为“紧凑录音列表 → 摘要和分类结果表 → 单项核对弹窗”。上传和原文默认收起,已写入记录用只读文本;窄屏固定核对操作列,日期/估算标记仍可见。
- 在真实诊单抽屉组件、1440×1000视口、相同4项合成数据下,常驻输入控件从25个减至4个复选框,内容滚动高度从2802px减至852px;上传、取消单项编辑、分类不丢选择、草稿与确认写入均做了浏览器合成接口验证。它不是生产/真实模型识别证据。
- 独立补测发现并修复:临床语义候选不再因空字段而默认采纳;对家属/提问/否定/纠正等上下文强制人工复核。该规则是保守防守,不证明语言语义识别准确。
- 饮食早餐/午餐/晚餐提炼别名映射到实际`*_foods`字段,审阅读回、审计、现有日常表单编辑和提交保持一致,未选餐食及图片不清空;CSV合法值`0`不再被`empty()`抹去;医院诊断选项使用现有页面的三个静态枚举。
- 补测通过30条规则、16条防守和5条跨层检查,以及真实隔离MySQL113项检查。9个模型语义案例仍标注NOT_VERIFIED;真实Dify最新探针仍CONFIG_MISSING。不得把这些本地测试描述为真实ASR、方言或一小时录音识别已通过。
- 本次完整补测账本见工作副本`artifacts/followup-audio-implementation/extraction-acceptance-20260929.md`和`.json`,保留修复前结果、修复后结果及独立回滚证据。主四角色路径保持不变,本次记录追加到原验证记录。
## 2026-10-07:真实样本、本地验收与可配置模型
### 本轮实际结果与边界
- 用户提供的4段MP3均可读取,长度分别约10分52秒、12分25秒、6分54秒、11分39秒。真实 Upload 服务完成分片、重复分片幂等、私有重组、全量 FFmpeg 解码;原始 SHA256 全部不变。验收用本地一次性 SQLite 和合成授权,不代表生产 HTTP/RBAC 验收。
- 新的 opt-in `server/tests/FollowupAudioSampleAcceptanceTest.php` 读取 Git 外0600私有manifest;无参数安全跳过。4段录音与44个独立合成日期候选合计287项检查通过。模拟通话日期为2026-10-07、2026-03-01、2026-01-01、2024-03-01,覆盖普通日、跨月、跨年、闰年和模糊日期;这些候选不是从患者录音识别出来的内容。
- 用户指定的兼容接口 `/models` 可访问。合成短音频按 `input_audio` 发给 `gpt-6.1-sol` 得到 HTTP200,但明确回复未收到可读取音频;`/audio/transcriptions` 请求返回404。新实现的原生能力探针再次以结构化输出契约执行,退出2、`AUDIO_NOT_PROCESSED`。未继续发送15分钟/一小时样本,没有发送4段真实患者录音,没有写入任何正式诊单。
- HTTPS 实测证书与域名不匹配。没有关闭证书校验。HTTP只用于明确获准的合成检查,正式患者处理强制HTTPS,不能靠开启布尔配置绕过。
- 上述结果说明目前所测入口/模型不能完成本功能的音频识别,不等于该服务所有未测模型均不支持。纯文本提炼不能替代语音转写。
- 音频请求格式参考[OpenAI官方音频输入说明](https://developers.openai.com/api/docs/guides/audio-chat-completions),实际服务能力以本轮请求证据为准。
### 模型配置(服务端环境变量,不在客服页面暴露凭据)
当前沿用 `qwen` / `openai` 两个稳定逻辑槽位;槽位不再决定固定品牌/模型,前端显示服务端配置的标签。示例:
```ini
[followup_audio]
ENABLED = false
AUDIO_VERIFIED = false
VERIFIED_PROFILES =
PROFILE = openai
OPENAI_DRIVER = openai_audio
OPENAI_BASE_URL = https://HOST/v1
OPENAI_API_KEY =
OPENAI_MODEL = MODEL
OPENAI_LABEL = 回访分析模型
OPENAI_VERIFIED_FINGERPRINT =
ALLOW_INSECURE_SYNTHETIC = false
```
- API_KEY只通过部署环境或Git忽略的本地`.env`注入;本轮本地`.env`权限0600,未打包/提交。正式配置不要使用文档占位值。
- `DRIVER=dify`选择Dify应用,内部使用哪个模型需在Dify端配置;本地MODEL字段不能改变Dify应用模型。`DRIVER=openai_audio`会将MODEL实际发送到Chat Completions,必须是该服务真正支持音频输入的模型。
- 地址、密钥、driver、model任一变化都会使验证指纹失效;修改配置后重启消费者并重新验证。标签变化不改变提供方身份。不要保留旧指纹作为新模型的验收凭证。
- 新任务将选择时的提供方指纹保存到现有`upstream_ids_json`,不增加数据库schema。消费者、心跳、checkpoint和完成均校验;旧任务缺少指纹或配置漂移不会静默改走新服务。已有结果不自动重分析,相同内容防重规则保持。
- 探针状态保存为 `probe-<profile>-<fingerprint>.json`,旧身份报告保留。未知/已开始的同身份请求不自动重发;失败先核对已有请求ID及响应。不能删除报告来强制重试。
- 需要先修复HTTPS并提供支持音频的模型/转写能力,然后完成短/中/长门禁,再对代表性真实录音人工回听、核对主体/否定/数值及时间。此步骤尚未完成,入口继续关闭。
### 本轮验证记录
ProviderConfig24、OpenAI音频传输47、Dify106、Media41、Probe14、隔离MySQL Core132、Worker20、Endpoint21、Access11、ProviderIntegration20均通过。音频传输单测采用受控响应/回环服务,不是外部ASR准确率;4个真实样本的287项检查只证明本地媒体与独立日期规则。网页未修改,38项录音组件测试回归通过。
完整证据在 `/Users/long/Work/zyt/artifacts/followup-audio-20260929/samples-20261007/`,主四角色路径沿用原交付;本地配置、真实音频和私有manifest都不纳入源码包。