feat: 语音确认、联调与运维增强

- 语音:序数解析(第一个/第二个等)、解析失败计数与 API detail.retry_remaining;
  百度 ASR 固定 dev_pid 为普通话;SurgeryPipelineError 支持 extra 并入 HTTP detail。
- Demo:demo 路由与假 RTSP、客户端 index 与 README;BackendResolver 与配置调整。
- 可观测:消耗 TSV 日志、语音文件日志、终端 Markdown 辅助;相关测试与依赖更新。
- 注意:.env 仍被 gitignore,本地密钥不会进入本提交。

Made-with: Cursor
This commit is contained in:
Kevin
2026-04-23 14:24:20 +08:00
parent 42720f81cf
commit 0c05463617
39 changed files with 3030 additions and 143 deletions

View File

@@ -63,13 +63,16 @@ CONSUMABLE_CLASSIFIER_TOPK=5
# VIDEO_INFERENCE_INTERVAL_SEC=2
# VIDEO_INFERENCE_CONFIDENCE_THRESHOLD=0.35
# 置信度 >= 此值且命中候选清单时自动 vision 记账。提高到 0.9 可减少自动记账、更多走待确认。
# VIDEO_AUTO_CONFIRM_CONFIDENCE=0.55
# 置信度处于 [VIDEO_VOICE_CONFIRM_MIN_CONFIDENCE, VIDEO_AUTO_CONFIRM_CONFIDENCE) 时入队待确认(客户端拉取 pending-confirmation
# 默认 0.9Top1 置信度不足该值时入队待确认;达到且标签在 candidate_consumables 内则直接记 vision。
# VIDEO_AUTO_CONFIRM_CONFIDENCE=0.9
# 与 VIDEO_VOICE_CONFIRM_MIN_CONFIDENCE 共同决定何时自动 / 待确认(见 app/config 注释)。
# VIDEO_VOICE_CONFIRM_MIN_CONFIDENCE=0.35
# 待确认话术由服务端生成prompt_textTTS 一般在客户端播放;医生 WAV 上传后服务端 ASR 解析。
# 解析顺序:① pending 里展示的 topk序号/名称);② 仍不匹配时,对「开始手术」请求体中的 candidate_consumables 全文做名称子串匹配——医生报清单内其它耗材也以医生为准入账。
# 是否启用低置信度人工确认(客户端播报 + resolve 回传;服务端无麦克风/扬声器要求)。
# VOICE_CONFIRMATION_ENABLED=true
# 同一条待确认在语音/文本「解析失败」时累计的允许失败轮次(默认 2首败后再给 1 次重试提示;见 422 的 detail.retry_remaining
# VOICE_CONFIRM_MAX_FAILED_PARSE_ROUNDS=2
# VIDEO_VOICE_CONFIRM_DOCTOR_ID=voice
# (已弃用)服务端本机录音 / ffmpeg 音频输入;当前闭环不依赖。
# VOICE_RECORD_SECONDS=5
@@ -79,6 +82,21 @@ CONSUMABLE_CLASSIFIER_TOPK=5
# VIDEO_DETAIL_COOLDOWN_SEC=15
# VIDEO_JPEG_QUALITY=85
# VIDEO_RESULT_DOCTOR_ID=vision
# 每次单帧分类得到 top13 时打一条 INFO联调开生产建议 false
# VIDEO_LOG_INFERENCE_RESULTS=true
# 时间窗级消耗文本日志(制表符列;每例手术 start 时截断+表头,窗内结果追加;终端 Rich 为可读时间戳,文件内为 ISO 时间戳列)
# CONSUMPTION_TSV_LOG_ENABLED=true
# 须含 {surgery_id},如 logs/consumption_{surgery_id}.txt
# CONSUMPTION_TSV_LOG_PATH=logs/consumption_{surgery_id}.txt
# 同一时间窗结果在终端以 Markdown 表格打印Top13 分列 id / 名称 / 置信度)
# CONSUMPTION_LOG_MARKDOWN_TERMINAL=true
# 消耗日志时间戳列的时区IANA如 Asia/Shanghai不设置则用运行环境的系统时区
# CONSUMPTION_LOG_TIMEZONE=Asia/Shanghai
#
# 语音确认stderr 中可 grep 的 `VoiceConfirm ...` 行 + 每例手术 TSV与 `start_surgery` 同次截断写表头;成功/ASR/解析失败均追加一行)
# VOICE_FILE_LOG_ENABLED=true
# 须含 {surgery_id},如 logs/voice_{surgery_id}.txt
# VOICE_FILE_LOG_PATH=logs/voice_{surgery_id}.txt
# --- Hikvision: mount vendor Linux x86_64 .so at runtime (do not commit proprietary binaries) ---
# HIKVISION_LIB_DIR=/opt/hikvision/lib
@@ -102,3 +120,28 @@ CONSUMABLE_CLASSIFIER_TOPK=5
# BAIDU_SPEECH_SECRET_KEY=
# BAIDU_SPEECH_CONNECTION_TIMEOUT_MS=
# BAIDU_SPEECH_SOCKET_TIMEOUT_MS=
# 短语音识别模型:固定普通话(默认 1537勿用 1737 英语等)。代码会始终带上此 dev_pid。
# BAIDU_SPEECH_ASR_DEV_PID=1537
# --- MinIO语音 WAV 存桶;`docker-compose.dev.yml` 内已含 `minio` 服务;本机只跑 API 时填 127.0.0.1:9000---
# docker compose -f docker-compose.dev.yml up -d minio
# MINIO_ENDPOINT=127.0.0.1:9000
# MINIO_ACCESS_KEY=minioadmin
# MINIO_SECRET_KEY=minioadmin
# MINIO_BUCKET=operation-room-voice
# MINIO_SECURE=false
# optional: MINIO_REGION=
# --- Demo 浏览器客户端 / 一键联调假 RTSP仅开发生产关---
# demo_client/index.html 跨源访问本服务 / 一键开录
# DEMO_CORS_ENABLED=true
# DEMO_CORS_ORIGINS=*
# 为 true 时提供 POST /internal/demo/orchestrate-and-start需能执行 docker+ffmpeg 的**同一进程**内起 MediaMTX通常=在宿主机直接跑 main.py或容器挂载 /var/run/docker.sock
# DEMO_ORCHESTRATOR_ENABLED=false
# VIDEO_RTSP_URLS_JSON_FILE 必须设成**可写**的 JSON 文件Docker 中请 bind-mount 宿主机文件,与一键覆盖写入的映射一致
# DEMO_ORCHESTRATOR_RTSP_PORT=18554
# 手配假流、只改 JSON 给「另一进程」用时:可把 127.0.0.1 换成 host.docker.internal 等。
# 一键联调 orchestrate-and-start 在本进程起流+拉流,固定写 127.0.0.1,不读此项。
# DEMO_ORCHESTRATOR_RTSP_JSON_HOST=host.docker.internal
# 一键起 MediaMTX 后,等待本机 RTSP 端口可连接的最长时间(秒)
# MEDIAMTX_TCP_READY_SEC=30