#!/usr/bin/env bash # # 验证 Whisper 转写服务: # 1. 检查 GPU 与容器运行状态 # 2. 用测试音频发起 verbose_json 转写请求,校验响应契约 # 3. (可选)检查 oral-trainer-api 的集成状态 # # 用法: # ./verify.sh # ./verify.sh --file /path/to/speech.wav # ./verify.sh --url http://127.0.0.1:9000 --model Systran/faster-whisper-large-v3 # ./verify.sh --api-url http://127.0.0.1:8000 set -euo pipefail WHISPER_URL="${WHISPER_URL:-http://127.0.0.1:9000}" WHISPER_MODEL="${WHISPER_MODEL:-Systran/faster-whisper-large-v3}" TEST_FILE="" API_URL="" usage() { sed -n '2,9p' "$0" | sed 's/^# \{0,1\}//' } while [[ $# -gt 0 ]]; do case "$1" in --url) WHISPER_URL="$2"; shift 2 ;; --model) WHISPER_MODEL="$2"; shift 2 ;; --file) TEST_FILE="$2"; shift 2 ;; --api-url) API_URL="$2"; shift 2 ;; -h|--help) usage; exit 0 ;; *) echo "未知参数: $1"; usage; exit 1 ;; esac done echo "==> Whisper 地址: $WHISPER_URL" echo "==> 模型: $WHISPER_MODEL" echo "==> 检查 GPU" if command -v nvidia-smi >/dev/null 2>&1; then nvidia-smi --query-gpu=name,memory.total,driver_version --format=csv,noheader | head -5 else echo "警告: 未找到 nvidia-smi,请确认 NVIDIA 驱动与容器运行时已安装(容器需要 --gpus 支持)。" fi echo "==> 检查容器状态" if command -v docker >/dev/null 2>&1 && [[ -f docker-compose.yml ]]; then docker compose ps --status running | sed -n '1,3p' else echo "警告: 未在当前目录发现 docker-compose.yml,跳过容器状态检查。" fi TMP_DIR="$(mktemp -d)" trap 'rm -rf "$TMP_DIR"' EXIT if [[ -n "$TEST_FILE" ]]; then AUDIO_FILE="$TEST_FILE" else echo "==> 生成测试音频(3 秒 440Hz 正弦波)" if ! command -v ffmpeg >/dev/null 2>&1; then echo "错误: 未找到 ffmpeg,请安装或用 --file 指定真实语音文件。" >&2 exit 1 fi AUDIO_FILE="$TMP_DIR/tone.wav" ffmpeg -hide_banner -loglevel error -f lavfi \ -i "sine=frequency=440:duration=3" -ar 16000 -ac 1 -y "$AUDIO_FILE" fi if [[ ! -f "$AUDIO_FILE" ]]; then echo "错误: 音频文件不存在: $AUDIO_FILE" >&2 exit 1 fi echo "==> 发起转写请求(verbose_json)" START_TS="$(date +%s)" RESPONSE="$(curl -sS --max-time 300 \ -X POST "$WHISPER_URL/v1/audio/transcriptions" \ -F "model=$WHISPER_MODEL" \ -F "file=@$AUDIO_FILE" \ -F "response_format=verbose_json" \ -F "temperature=0")" ELAPSED="$(( $(date +%s) - START_TS ))" echo "$RESPONSE" | python3 -c ' import json, sys payload = json.load(sys.stdin) text = payload.get("text") segments = payload.get("segments") assert isinstance(text, str), "响应缺少 text 字段" assert isinstance(segments, list), "响应缺少 segments 字段(需要 verbose_json)" for index, segment in enumerate(segments): start = segment.get("start") end = segment.get("end") if not (isinstance(start, (int, float)) and isinstance(end, (int, float)) and end > start >= 0): raise AssertionError(f"segments[{index}] 缺少合法 start/end") print(f"OK: text={text!r}") print(f"OK: segments={len(segments)} 条") ' echo "==> 转写耗时: ${ELAPSED}s" if [[ -n "$API_URL" ]]; then echo "==> 检查 API 集成 ($API_URL/healthz)" curl -sS --max-time 15 "$API_URL/healthz" | python3 -c ' import json, sys payload = json.load(sys.stdin) configured = payload.get("moss_configured") if configured is not True: raise SystemExit(f"错误: moss_configured={configured!r},请检查 .env 中的 MOSS_TRANSCRIBE_URL") print("OK: moss_configured=true") ' fi echo "全部检查通过。建议再用真实语音文件复核转写质量:" echo " $0 --file /path/to/speech.wav"