HTTP 200 is not a usable transcript: a meeting-audio acceptance checklist

2026-09-09 · Application engineering · 8 minute read

A transcription request returned text. Is it ready for meeting notes, subtitles or speaker-based editing? Treat transport, words, speakers and timing as separate acceptance questions. This article uses a FunASR MOSS result and a small standard-library Python checker to show what can be automated, and what still needs listening.

FunClip video, subtitle and editing workspace
An existing FunClip workspace illustrates the downstream application. This is not a screenshot of the synthetic example below.

Four questions, four kinds of evidence

LayerQuestionNot a substitute
Request and structureDid the request complete with the expected fields, units and ordering?HTTP 200 and nonempty JSON do not establish correctness.
WordsAre names, numbers, negations and decisions transcribed correctly?Fluent text can still omit or invent speech.
SpeakersAre turns and overlapping speech assigned consistently?Two output labels do not prove correct separation of two people.
Timing and coverageDo subtitles locate the right sound? Are the beginning, middle and end covered?An end timestamp near the recording duration does not prove complete transcription.

VAD detects regions that may contain speech; ASR transcribes words; speaker diarization assigns anonymous speakers to speech regions. They answer different evaluation questions. A label such as S01 is local to a recording, not a name, an enrolled voiceprint or a cross-recording identity.

Choose the output contract for the application

MOSS-Transcribe-Diarize is a third-party model maintained by OpenMOSS. FunASR normalizes its output into text and timed segments with anonymous speaker labels. This path does not require an additional external VAD or speaker model. Do not arbitrarily split a meeting into independent requests and then assume that every chunk's S01 identifies the same person.

Version-specific installation belongs in the maintained MOSS deployment page and integration guide, not a second recipe that drifts independently. See the speaker-clipping walkthrough for FunClip boundaries and the model catalogue for selection.

A structural checker, not a quality evaluator

Download transcript-audit.py or read its source. It reads one file, makes no network requests and does not modify results. The input is one MOSS result object from FunASR AutoModel.generate(...)[0], also written as .moss-vllm.json by the repository's offline example. It is not raw vLLM diarized_json, SRT or the Qwen3 example's segments schema.

This deliberately constructed result represents a hypothetical 12-second recording. It is not model output and carries no accuracy claim:

{
  "text": "Let's start. Agreed.",
  "sentence_info": [
    {"start": 1000, "end": 3500, "spk": "S01", "text": "Let's start."},
    {"start": 6000, "end": 8000, "spk": "S02", "text": "Agreed."}
  ]
}

Use the example JSON as result.json. Units are milliseconds: pass 12000 for 12 seconds, not 12.

python3 transcript-audit.py result.json --duration-ms 12000

The report has segment_count=2, last_end_ms=8000 and tail_not_covered_ms=4000. It always returns quality_verified=false: it has neither heard the audio nor received human reference annotations. Synthetic cases, invalid fields and CLI behavior are regression-tested on Python 3.12. Those tests validate the checker, not the model. Rejecting zero-length segments is an additional acceptance policy here, not a guarantee that the model adapter always emits positive-length intervals.

Move from structural checks to acceptance

  1. Freeze input and configuration. Retain the audio hash, duration, sample rate/channels, model revision, server version, response format and generation limit. Record resampling, channel mixing and trimming. Removing silence requires an offset map if timestamps must locate the original video.
  2. Listen at high-risk positions. Check the start and end, both sides of long silence, speaker changes, interruptions, quiet speech and critical numbers. Sampling can reveal problems; a successful sample is not proof of whole-recording accuracy.
  3. Evaluate text and speakers separately. With a human reference transcript, measure CER/WER under consistent punctuation, case and tokenization rules. With human speaker/time annotations, evaluate diarization. The number of distinct output labels is not a diarization acceptance test.
  4. Freeze the diarization scoring policy. pyannote.metrics provides speaker metrics. Boundary collars and whether overlapping speech is scored affect results. Compare systems on the same reference and configuration, not detached numbers.
  5. Diagnose incomplete output before tuning. Inspect termination due to generation limits, structural completeness and whether the client consumed the whole response. Raising a token limit may address some truncations, but cannot guarantee recall. A successful request is not grounds to close an unresolved user issue.

What should travel with the result?

Follow consent and retention requirements, store only necessary data, and restrict access to audio and speaker annotations. Retain the source-audio identifier, fixed configuration, original output, structural report and human acceptance notes. Link summaries back to transcript passages. Check subtitle readability, positioning and privacy. Replay edited clips instead of only checking interval lengths. Known-person identification needs a separate consent, enrollment and verification design; renaming anonymous cluster labels does not provide it.

This checker is an entry point into acceptance, not a leaderboard or a substitute for listening and annotation. Start a real workload through the deployment centre, then use the FunASR repository for implementation details or a minimal reproducible issue.

Sources and reproduction