ไฟล์ข้อมูลระดับคำ — result.words.json
result.words.json เป็นไฟล์ผลลัพธ์เสริมที่ worker สร้างให้ทุกงานที่เปิด ForceAligner (forcealigner_level ไม่ใช่ none) และ GET /status คืนลิงก์เป็น words_url (presigned URL อายุจำกัด เหมือนไฟล์อื่น) ไฟล์นี้มีไว้สำหรับ วิเคราะห์จังหวะการพูดและฝึกโมเดลตัด cue — ไม่ใช่ไฟล์ซับไตเติลสำหรับเล่น ขนาดประมาณ 0.8 MB ต่อชั่วโมงเสียง (≈1.5 MB เมื่อใช้ llmsplitting)
ไฟล์เป็น JSON หนึ่งออบเจ็กต์ (UTF-8 ไม่ escape ภาษาไทย, ตัวเลขเวลาเป็นวินาทีจากต้นไฟล์ ปัด 3 ตำแหน่ง) ช่วงดัชนีทุกช่วงเป็นแบบครึ่งเปิด [a, b)
โครงสร้าง
{
"format": "loma-fastsrt-words", "version": 1,
"job_id": "…", "language": "th", "forcealigner_level": "default | llmsplitting",
"config": { "...job config snapshot ที่ worker ใช้จริง..." },
"audio": { "duration_seconds": 600.0 },
"tokenizer": { "name": "nlpo3", "version": "1.4.0", "dict": "loma_worker_words_th", "dict_sha256": "…" },
"chunks": [
{ "i": 0, "start": 0.0, "end": 23.42, "avg_vad": 0.87, "tokens": [0, 61],
"asr_text": "…", "asr_segments": [ { "s": 0.0, "e": 5.2, "text": "…" } ],
"llm": "ok | fallback | none", "llm_error": "" }
],
"tokens": [ { "i": 0, "t": "ขณะเดียวกัน", "s": 10.192, "e": 11.952, "sp": true } ],
"cue_tokens": [ { "i": 0, "t": "…", "s": 10.208, "e": 11.936, "k": "p2 | p1 | line", "ref": 412, "line": 12 } ],
"llm": {
"model": "@preset/fast", "prompt_sha256": "…",
"attempted": 40, "succeeded": 39, "fallback": 1, "fallback_chunks": [17],
"lines": [ { "i": 0, "c": 0, "n": 0, "text": "ขณะเดียวกัน ถ้าเทียบกับเงินเฟ้อน่ะครับ",
"tokens": [412, 419], "tokens_match": "exact | resync | null", "cue_tokens": [412, 419] } ]
},
"cues": [
{ "index": 1, "start": 10.192, "end": 14.752, "tokens": [412, 419],
"text": "ขณะเดียวกัน ถ้าเทียบกับเงินเฟ้อนะครับ", "raw_text": "…",
"merged_from": [6], "speakers": ["A"], "speaker": "A" }
],
"cues_index": "tokens | cue_tokens",
"revised": false, "tokens_reordered": 0,
"vad": { "sample_rate": 16000, "frame_size": 512, "frame_seconds": 0.032,
"vad_threshold": 0.4, "effective_vad_threshold": 0.4, "used_vad_fallback": false,
"digits": "0000135899…",
"speech_coverage_ratio": 0.9995, "uncovered_speech_seconds": 0.35,
"gated_speech_seconds": 0.35, "gated_candidates": 11 }
}
ความหมายของฟิลด์
| ฟิลด์ | ความหมาย |
|---|---|
tokens |
token จาก ForceAligner รอบแรก (คำจาก NLPo3 / คำละติน) เรียงตามลำดับที่ตัวตัด cue อ่าน (เรียงตาม chunk แล้วตามเวลา) s/e คือเวลาเริ่ม-จบของคำ ช่องว่างระหว่าง e ของคำหนึ่งกับ s ของคำถัดไปคือช่วงเงียบที่ aligner เห็น (quantum 80 ms) |
tokens[].sp |
true เมื่อข้อความจาก Whisper มีช่องว่างหลัง token นี้ (ไม่มีคีย์เมื่อไม่มีช่องว่าง) ตัวตัด cue ใช้ช่องว่างนี้เป็นสัญญาณจบวลีเฉพาะรอยต่อไทย-ไทย ไฟล์ที่สร้างก่อน ก.ย. 2569 ไม่มีคีย์นี้ ให้ดูจาก chunks[].asr_text แทน |
chunks[].tokens |
ช่วงดัชนีของ tokens ที่อยู่ใน chunk นั้น asr_segments คือ segment จาก ASR (เวลาเป็นวินาทีจากต้นไฟล์) llm บอกว่า chunk นี้ผ่าน LLM สำเร็จ ใช้โค้ด fallback หรือไม่ได้ส่ง |
cues |
cue สุดท้ายตรงกับ result.srt ทุกบรรทัด (index, start, end, text เท่ากัน) tokens คือช่วงดัชนีของ token ที่ประกอบเป็น cue นี้ ใน array ที่ระบุโดย cues_index ช่วงของ cue ทั้งหมดต่อกันพอดีและครอบคลุม array นั้นทั้งหมด raw_text คือข้อความก่อนใส่ป้ายผู้พูด/ขัดเกลา merged_from คือดัชนี cue ก่อนขั้น final merge speakers/speaker คือป้ายผู้พูดจาก diarization (null เมื่อปิด) |
cues_index |
tokens เมื่อ cue สร้างจาก token รอบแรกโดยตรง (โหมด default) หรือ cue_tokens เมื่อ cue สร้างจาก token ที่จัดเวลาใหม่ (โหมด llmsplitting ซึ่งจัดตำแหน่งรอบสอง) |
cue_tokens |
มีเฉพาะเมื่อ cues_index = cue_tokens — token ที่ cue ประกอบขึ้นจริง k = p2 (token จากการจัดตำแหน่งรอบสอง, line คือดัชนีบรรทัด LLM), p1 (token รอบแรกที่ถูกใช้ต่อเพราะ chunk นั้น fallback, ref ชี้ไป tokens), line (บรรทัดที่ไม่มีเวลาระดับคำ) |
llm |
มีเฉพาะโหมด llmsplitting: บรรทัดดิบที่ LLM ตอบกลับ ก่อน ขั้น merge ใด ๆ lines[].tokens คือช่วงของ tokens (รอบแรก) ที่บรรทัดนี้ครอบคลุม (tokens_match = exact ตรงทุกตัวอักษร, resync จับคู่แบบยืดหยุ่นเพราะ LLM แก้คำ, null จับคู่ไม่ได้) และ cue_tokens คือช่วงใน cue_tokens |
vad.digits |
ค่าความน่าจะเป็นเสียงพูดจาก Silero VAD ทั้งไฟล์ หนึ่งหลัก (0–9) ต่อเฟรม 32 ms (round(prob*9)) เฟรมที่ i ครอบคลุมเวลา [i*frame_seconds, (i+1)*frame_seconds) |
tokens_reordered |
จำนวน token ที่ลำดับตามเวลาไม่ตรงกับลำดับที่ aligner คืนมา (ปกติ 0) |
revised |
true เมื่อข้อความ cue ผ่านขั้น revise_srt สำเร็จ (text อาจต่างจาก raw_text) |
ข้อควรรู้
- ขอบเขตเวลาของ cue ไม่จำเป็นต้องเท่ากับช่วงเวลาของ token ในนั้น (cue แรกของ chunk เริ่มที่ต้น chunk, การยืดเวลาสิ้นสุดไปถึง cue ถัดไป (end-fill) และให้ครบ
min_srt_duration_ms, การตัดขอบที่เงียบ, การบังคับให้เวลาไม่ถอยหลัง) - ไฟล์นี้เป็นผลลัพธ์เสริม: ถ้าสร้างไม่สำเร็จ งานยังเสร็จตามปกติและ
GET /statusจะไม่มีwords_url vad.digitsคือค่า VAD ทั้งไฟล์ ส่วนค่า VAD เฉพาะช่วงของแต่ละ cue (หลักเดียวกัน) อยู่ในบล็อกNOTE loma-fastsrt-vadของresult.debug.vtt- สำหรับการฝึกโมเดลตัด cue ให้ใช้
tokensเป็นอินพุต (เทียบเท่าสิ่งที่ worker เห็นตอนทำงานจริง) และใช้llm.lines[].tokens/cues[].tokensเป็นป้ายกำกับ