134 lines
14 KiB
Markdown
134 lines
14 KiB
Markdown
# 回访录音提炼:实现与发布门禁
|
||
|
||
## 当前交付边界
|
||
|
||
源码在隔离工作副本 `/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识别验收;混合口音需代表性脱敏样例。
|
||
|
||
## 使用与数据规则
|
||
|
||
- 入口:网页问诊列表→编辑患者→回访录音。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=followup_audio_isolated_test php server/tests/FollowupAudioCoreTest.php
|
||
node --test admin/tests/followup-audio.test.cjs
|
||
node admin/scripts/verify-daily-records.cjs
|
||
```
|
||
|
||
数据库命令只允许一次性本地测试容器,禁止换为线上端口。测试容器连接信息是固定合成测试凭据,不是业务凭据。
|
||
|
||
发布回退先关闭入口并停止本功能消费者,保留旧静态资源和数据库备份。新增表/列应保留以保护审计;源码回退脚本只恢复源码,**不撤销已写入病历**。正式记录若需恢复,必须按审核轨迹和当前版本另做受控补偿,不能整库回滚覆盖其他人的新数据。
|
||
|
||
主交付四角色及源码回退验证位于 `/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都不纳入源码包。
|