LabHub
시작하기
배우기 러닝패스 코스

음성 AI 에이전트 — 듣고, 찾고, 말하는 파이프라인

추적 하나에서 모든 지표를 — 품질·실패율·SLO·관문

LabHub 에서 이어서 보기

목표

파이프라인의 턴마다 구간을 기록하고, 같은 원자료에서 지연 분포·WER·응답 품질·실패율을 계산해 SLO 로 판정한 뒤, 퇴행을 막는 배포 관문을 만든다.

왜 중요한가

평균 지연과 오류율만 보면 '근거를 통과한 오답' 과 '조용히 잘 넘긴 오류' 가 보이지 않는다. 지표를 같은 원자료에서 계산해야 숫자끼리 맞고, 관문은 막아야 할 것을 넣어 봐야 믿을 수 있다. 이 실습은 기준 파이프라인 voicekit.pipeline.Pipeline 을 씁니다 — 모듈 5·8 과 같은 줄기(확정 → 재작성·검색 → 문턱 → JSON 답 → 숫자 검사 → 추출 → TTS)이고, run(wav, qid, context, span=기록함수) 로 단계마다 구간을 알려 주며, faults={...} 로 단계를 일부러 실패시킬 수 있습니다. 채점기는 여러분의 구간 기록에서 모든 요약 숫자를 다시 계산해 대조합니다.

단계

  1. 질의 12개를 추적 12개(뿌리 turn + 단계 구간)로 /root/voice/eval/spans.jsonl 에 남기세요.
  2. 단계별 구간 길이의 p50·p95 를 /root/voice/eval/stages.json 에 적으세요.
  3. asr 구간의 글과 정답 원문으로 질의별·전체 WER 을 /root/voice/eval/wer.json 에 적으세요.
  4. 정답표로 사실 정확도·거절 정확도·근거 일치율을 /root/voice/eval/quality.json 에 적으세요.
  5. ASR 빈 글·LLM 시간 초과·TTS 실패를 한 질의씩 넣어 다시 돌린 /root/voice/eval/spans_faults.jsonl 과, 오류 턴·사용자 실패를 센 /root/voice/eval/failures.json 을 만드세요.
  6. SLO 목표를 정하고 측정과 비교한 /root/voice/eval/slo.json 을 만드세요.
  7. 기준·현재 지표를 비교해 퇴행이면 종료 코드 1 을 내는 /root/voice/eval/gate.py 를 만드세요.
  8. 이번 판의 지표 /root/voice/eval/metrics.json 을 만들고 기준 판(/opt/lab/fixtures/voice/eval/baseline.json)과 관문을 돌린 /root/voice/eval/report.json 을 만드세요.

참고

턴 하나 = 추적 하나

voice-llm up 뒤 Pipeline() 으로 질의 12개(/opt/lab/fixtures/voice/rag/queries.jsonl 순서, 되물음의 context 는 앞 질의의 확정 글)를 돌리며 span 고리로 받은 단계 구간을 모으고, 턴마다 뿌리 구간 turn(start 0, end = 자식 끝의 최대, status = 턴 결과, attrs 에 say·source·mode·first_audio_ms·errors)을 더해 /root/voice/eval/spans.jsonl 에 한 줄씩 적으세요.

span 고리는 span(name, start_ms, end_ms, status, attrs) 로 불립니다. 뿌리의 span_id 를 먼저 정해 두고 자식의 parent_id 에 씁니다. uuid.uuid4().hex 가 간단한 id 입니다.

단계별 분포

spans.jsonl 의 자식 구간(뿌리 제외)을 이름별로 모아 길이(end − start)의 n·p50_ms·p95_ms(가까운 순위)를 /root/voice/eval/stages.json 에 적으세요.

p95 가 가장 긴 단계가 사용자를 가장 자주 기다리게 하는 곳입니다. 평균이 아니라 분포의 꼬리를 봅니다.

서비스와 닮은 자료의 WER

asr 구간의 attrs.text 와 /opt/lab/fixtures/voice/queries/refs.tsv 의 원문으로 질의별 WER 을 per_query 에, 오류 합 ÷ 단어 수 합을 wer 에 적은 /root/voice/eval/wer.json 을 만드세요(소문자·문장부호 정리, 아포스트로피 유지).

모듈 3 의 wer.py 를 가져와도 됩니다. 같은 모델이 LibriSpeech 에서 1% 남짓 틀렸던 것과 비교해 보세요.

응답 품질 — 근거 일치는 정확함이 아니다

뿌리 구간과 정답표로 fact_accuracy(답이 있는 질문 전부를 분모로, 거절하지 않았고 say 에 facts 가운데 하나가 들어 있으면 적중 — 대소문자 무시), refusal_accuracy(답이 없는 질문 가운데 status 가 refused 인 비율), grounded_rate(거절하지 않은 답 가운데 say 의 숫자가 모두 source 문서에 있는 비율)를 /root/voice/eval/quality.json 에 적으세요.

세 숫자를 나란히 두면 근거 일치율이 높아도 사실 정확도가 낮을 수 있다는 것이 보입니다. 숫자는 re.findall(r"\d+(?::\d+)?", …) 로 뽑습니다.

오류와 사용자 실패를 가른다

Pipeline(faults={"asr_empty": {"q09"}, "llm_timeout": {"q03"}, "tts_error": {"q07"}}) 로 01 을 다시 돌려 /root/voice/eval/spans_faults.jsonl 에 남기고, /root/voice/eval/failures.json 에 turns, error_turns(자식 구간 가운데 status 가 error 인 것이 있는 턴 수), user_failures(뿌리 status 가 failed 또는 reprompt 인 턴 수), user_failure_rate, by_type 을 적으세요.

LLM 이 죽은 턴은 검색한 문단의 문장을 읽어 주므로(degraded) 사용자는 답을 듣습니다 — 오류지만 실패는 아닙니다. TTS 가 죽으면 사용자는 침묵을 듣습니다.

SLO 로 판정한다

objectives 에 p95_first_audio_ms·fact_accuracy_min(0.5 이상)·user_failure_rate_max(0.1 이하)를 정하고, measured 에 spans.jsonl 뿌리의 first_audio_ms p95, quality.json 의 fact_accuracy, failures.json 의 user_failure_rate 를, pass 에 셋의 통과 여부를 적은 /root/voice/eval/slo.json 을 만드세요.

목표는 사용자가 겪는 것으로 적습니다. 실패율은 실패를 넣은 실행에서 옵니다 — 넣은 실패가 목표를 깨는지 보는 것이 이 단계의 요점입니다. 미달이 나와도 채점은 판정이 맞는지만 봅니다.

퇴행을 막는 관문

python3 gate.py 기준.json 현재.json 으로 부르는 /root/voice/eval/gate.py 를 만드세요. wer 가 0.02 넘게 오르거나, fact_accuracy 가 0.10 넘게 내리거나, p95_first_audio_ms 가 기준의 20% 넘게 오르거나, user_failure_rate 가 0.05 넘게 오르면 이유를 찍고 종료 코드 1, 아니면 0 으로 끝납니다. 채점기가 숨긴 기준·현재 쌍으로 양방향 시험합니다.

지표마다 (이름, 나빠지는 방향, 허용 폭, 비율인가) 표를 두면 규칙이 한눈에 보입니다. 좋아진 것은 얼마나 좋아졌든 통과입니다.

이번 판을 관문에 넣는다

/root/voice/eval/metrics.json 에 wer(wer.json)·fact_accuracy(quality.json)·p95_first_audio_ms(slo.json 의 measured)·user_failure_rate(평상시 실행이므로 0.0)를 적고, python3 gate.py /opt/lab/fixtures/voice/eval/baseline.json metrics.json 의 종료 코드를 gate_exit 에, 출력 줄을 gate_output 에 담은 /root/voice/eval/report.json 을 만드세요.

기준 판은 '지난 배포(가상)' 의 지표입니다. 관문이 막았다면 어느 지표 때문인지 출력에 있습니다 — 그 판을 내보낼지는 사람이 정합니다.