본 기사는 "Zoom AI Services를 깊이 있게 다루는 시리즈"의 2번째 글입니다.
1편에서 그린 지도를 바탕으로, 새로운 API인 Speech Summarization과 Decisions를 실제로 코드로 호출하여 파이프라인을 구축합니다.
1편을 공개한 후 다음과 같은 취지의 댓글을 받았습니다.
협상 도중 조건이 바뀌는 경우—예를 들어 한 번 결정했던 일정이나 조건을 나중에 "역시 이렇게 하자"라고 수정하는 대화에서, 요약이나 판정은
"수정된 내용"을 제대로 포착할 수 있는가? 그리고 인용(근거)도 수정된 발언을 가리키는가? 나중에 대화를 참조하는 용도를 생각하면 그 부분이 궁금합니다.
이는 AI로 회의/협상을 처리할 때 핵심을 찌른 질문이라고 생각했습니다. 회의록이나 요약이 "처음에 말한 내용"에 끌려가다가 도중의 수정을 놓치면, 후속 의사결정 전체가 틀어지기 때문입니다.
이에 본 기사에서는 다음 파이프라인을 구현하고 실제 데이터로 검증합니다.
할 일: 협상 음성을 Speech Summarization으로 요약하고, 그 결과를 Decisions에 전달하여 "수주 예상(점수)", "다음 액션(선택)" 등을 형식화된 구조로 판정하기 -
검증 질문:-
대화 도중 조건이 바뀌어도 판정이
수정된 값을 선택할 수 있는가 -
인용은 수정된 발언(즉, 그 시점)을 가리키는가 -
한국어 협상 음성으로 일련의 처리가 가능한지 (1편에서 예고한 사전 검증)
소재로는 가상의 협상 음성 2개를 준비했습니다. 하나는 "값의 수정(일정/계약 조건이 나중에 변경되는 경우)", 다른 하나는 머리색을 바꿔서 "전제의 수정(판단의 전제가 도중에 뒤집히는 경우)"입니다.
녹음 내용은 모두 가상입니다. Claude로 가상의 협상 스크립트를 만들고, VoiceStudio에서 T2S(Text-to-Speech)를 진행한 것을 ffmpeg으로 각각 Customer/SalesRep의 음성만 분리하여, 다시 Zoom Meeting에서 실제로 회의에 흘려 녹음한 것을 기반으로 합니다.
1편에서 "한국어 음성으로 가능한지 먼저 확인한다"고 예고했으므로, 먼저 결과만 말씀드립니다.
- 한국어 대응: 합격✅ — 문자 변환(Transcribe)・요약・Decisions 모두
ja-JP로
처리하여 문제없이 작동했습니다. 요약은 한국어 Markdown 형식으로, 인용 마커 [^1]
이 붙어서 반환됩니다. -
모델:— Decisions에는 zoom-decisions
(표준)을 사용합니다. zoom-decisions
과 zoom-decisions-pro
가 있지만, 전자의 모델로 진행했기 때문에 본 기사는 표준 모델로 처리했습니다. 결론적으로, 협상 판정 용도에서는 zoom-decisions-pro는 작성 시점에는 준비 중이며 표준 모델만으로 충분한 정확도를 보여주었습니다.
이번 핵심은 Decisions를 2개 경로로 흘려서 비교하는 부분입니다.
왜 두 경로인가 하면, 서두의 댓글에 있던 **"요약하면 정보가 손실되는 것 아닌가?"**를 직접 검증하고 싶기 때문입니다.
경로 A (요약 → Decisions): 구조화된 요약을 입력으로 사용합니다. 정보는 압축되지만, 노이즈도 줄어듭니다. -
경로 B (문자 변환 → Decisions): 원본 전체 텍스트를 입력으로 사용합니다. 정보는 최대이지만, 노이즈도 최대입니다.
같은 질문을 두 경로 모두에 던져보고, 답변이 일치하는지/어디서 갈라지는지를 확인합니다. 이것이 후반부의 고찰에서 중요하게 작용합니다.
엔드포인트는 POST /v2/aiservices/speech-summarization/summarize입니다. multipart/form-data로
config (JSON)와 file (음성)을 보내고, 응답은 **SSE(Server-Sent Events)**로 반환됩니다.
의존성이 전혀 없는 환경(Node 20+의 fetch / FormData / Blob만 사용)에서 작성할 수 있습니다.
// config는 file보다 앞에 append 하는 것이 포인트
const config = {
transcribe: { language: 'ja-JP', diarization: true, profanity_filter: false },
...
응답은 SSE이므로 스트림을 순차적으로 파싱합니다. 주요 이벤트는 transcribe_complete입니다.
(전사 완료)、summary_updated
(요약 업데이트. summary_result는 누적적으로 도착합니다), done
for await (const { event, data } of parseSSE(res.body)) {
if (event === 'transcribe_complete') {
transcript = data.transcript.result.text_display;
...
여기서 반환되는 요약은 제목/표/글머리 기호로 구조화된 Markdown 형식입니다. 그리고 곳곳에 [^1]와 같은 **인용 마커(citation marker)**가 붙으며, citations[]에는 각각의 시작 및 종료 시간이 포함되어 있습니다. 이 '인용에 시간이 부여되는 것'이 이번 검증에서 근거를 확보하는 데 효과적입니다.
엔드포인트는 POST /v2/aiservices/decisions/decide (동기식) 입니다. input에 판별하고 싶은 텍스트, questions[]에 타입이 지정된 질문을 나열합니다. 타입은 세 가지입니다.
predicate: Yes/No를 **확률(0~1)**로 반환 -
choice: 선택지 중 하나를 고르고, 각 선택지의 확률 분포도 반환 -
score: 레벨에 대한 가중치 점수 + 분포를 반환
협상용 질문 세트(발췌)는 다음과 같은 형태입니다.
핵심은 instructions에서 '정정이 있었다면 정정된 것을 선택하라'고 명시하는 것입니다. 이 작성 방식이 정확도를 크게 좌우합니다(후술).
호출 측 코드는 간단합니다.
const res = await fetch('https://api.zoom.us/v2/aiservices/decisions/decide', {
method: 'POST',
headers: { Authorization: `Bearer ${token}`, 'Content-Type': 'application/json' },
...
응답의 answers[]에는 질문별 판정이 타입에 따라 들어갑니다.
{
"answers": [
{ "type": "choice", "name": "sale_start_date", "choice": "nov_20",
...
첫 번째 음성은 도중에 조건이 두 번 정정되는 협상입니다.
- 세일 시작일: '11월 27일'이라고 말한 후 → '오류이며, 정확하게는 일주일 앞당긴 11월 20일'로 정정 -
계약: '1년 계약・연액 1,300만 엔' 안 → 최종적으로 '2년 계약・연액 1,200만 엔'으로 합의 (1년 안은 취소)
이를 요약하고 Decisions에 적용한 결과가 여기입니다(요약 경유/전사 완료 경유).
| 질문 | 경로 A (요약) | 경로 B (전사 완료) | 정답 |
|---|
sale_start_date | nov_20 | nov_20 | ✅ 정정 후 |
contract_term | two_year_1200 | two_year_1200 | ✅ 정정 후 |
start_date_was_revised | 0.997 | 0.998 | ✅ 정정 감지 |
next_action | send_quote | send_quote | ✅ |
정정된 값(11/20・2년 계약)을 요약 경유와 전사 완료 경유 모두에서 정확하게 포착했습니다. '정정이 있었는지?'도 0.99를 초과하여 감지하고 있습니다.
요약의 '11월 20일로 앞당김'이라는 설명에는 인용 마커 [^14]가 붙어 있으며, 그 citations를 보면——
[^14] 00:06:16 → 00:06:58
대화에서 실제로 날짜를 정정한 바로 그 구간을 가리키고 있었습니다. 즉, '정정된 결론'뿐만 아니라 '어디서 그렇게 결정되었는지'까지 시간으로 추적할 수 있습니다. 서두의 코멘트에서 제기된 우려(인용이 정정 후를 가리키는가)에 대한 답은 Yes입니다.
두 번째 음성은 머리색을 바꿔 '값'이 아니라 '전제'가 뒤집히는 협상으로 구성했습니다.
- 처음에는 '잠정 구성(월별 결제, 월 58만 엔)이라면 담당자 결재로 통과할 것'으로 보고 있었습니다.
- 그런데 대화 도중 경리 부서에서 **결제 기준이 '총 지급액'이 아니라 '연간 환산'**이라는 것으로 정정되었습니다. → 연간 환산 금액은 696만 엔으로, 사장님 결재 대상이 됩니다. - 결과적으로, 잠정 구성은 보류되고, 4월의 본 계약(연간 1,700만 엔)로 일원화하는 방침으로 전환되었습니다. - 아울러, 견적 유효 기간은 3월 말까지 연장하는 것으로 합의했습니다.
상담 ①용과는 별도로, 이 대화에 맞춰 질문 세트(provisional_setup_decision 등)를 준비하여 판별을 진행했습니다.
| 질문 | 경로 A (요약) | 경로 B (음성 녹취록) | 정답 |
|---|
provisional_setup_decision | skip (보류) | skip (보류) | ✅ 방침 전환 후 |
provisional_decision_was_reversed | 0.979 | 0.997 | ✅ 철회 감지 |
approval_basis_was_corrected | 0.999 | 0.9996 | ✅ 전제 수정 감지 |
estimate_valid_until | end_of_march | end_of_march | ✅ 연장 후 |
'금액의 수정'뿐만 아니라, '전제가 수정되어 방침 전체가 뒤집히는 경우'까지 양쪽 경로 모두에서 정확하게 포착했습니다. 특히 approval_basis_was_corrected (결제 기준 정정)을 0.999로 감지한 것은 회의록 용도로 매우 고무적인 결과입니다.
시험 삼아, 상담 ①용 질문(sale_start_date 등)을 전혀 다른 내용의 상담 ②에 그대로 적용해 보았습니다. 그 결과는 다음과 같습니다.
| 질문 | 상담 ②에 적용한 결과 |
|---|
sale_start_date | other_or_undecided (해당 없음) |
contract_term | undecided (해당 없음) |
start_date_was_revised | 0.03 (낮음) |
질문이 내용과 맞지 않을 때는, 무리하게 답을 지어내지 않고 '해당 없음/저확률'로 응답했습니다. 유형화된 질문을 재활용하는 운영에서도 오검지로 인해 폭주하기 어렵다는 의미입니다.
지금까지의 검증 A·B에는 사실 약점이 있습니다. 바로 질문문에 '수정이 있다면 수정된 것을 선택하라'고 명시되어 있었다는 점입니다. 이 경우 '수정을 포착했는지' 아니면 '수정을 찾으라고 지시했기 때문에 찾아낸 것인지' 구별이 불가능합니다.
이에, 힌트를 전혀 제거한 중립 버전을 준비했습니다.
instructions에서 수정과 관련된 문구를 모두 삭제하고, 그저 **'합의된 금액은 얼마인가?', '시작일은 언제인가? (미정이라면 미정)'**와 같이 소박하게 질문만 던집니다. - 두 상담 모두에서 공통 중립 스키마(금액/계약 기간/주요 날짜/프로비저닝 판단/제공 Tier/확도/다음 액션)로 통일합니다. - 선택지에는 수정 전의 값(11월 27일, 연간 1,300만 엔)도 의도적으로 혼재시켜, 힌트 없이 후자=수정된 값을 고를 수 있는지 확인합니다.
상담 ① (금액 수정) · 중립 버전
| 질문 | 경로 A (요약) | 경로 B (음성 녹취록) | 평가 |
|---|
contract_amount | y1200 | y1200 | ✅ 1,300만 엔에 현혹되지 않고 수정된 값 선택 |
contract_term | two_year | two_year | ✅ 수정된 값 |
key_scheduled_date | nov_20 | nov_20 | ✅ 11/27이 선택지에 있어도 수정된 값 |
next_action | send_quote | send_quote | ✅ |
상담 ② (전제 수정) · 중립 버전
| 질문 | 경로 A (요약) | 경로 B (음성 녹취록) | 평가 |
|---|
contract_amount | y1700 | y1700 | ✅ |
key_scheduled_date | apr_1 | apr_1 | ✅ |
provisioning_decided | 0.999 | 0.997 | ✅ |
provisioning_tier | vpc_dedicated | vpc_dedicated | ✅ 잠정 보류 후 |
아무런 힌트도 주지 않았음에도 불구하고, 수정 전 값을 선택지에 섞어 넣었을 때도 두 경로 모두 수정 후 값(11/20・2년 계약・1,200만 엔)을 선택했습니다. 즉, 프라이밍(priming)은 정확도의 '바닥을 높이는' 역할일 뿐 '비밀을 폭로하는' 것은 아니며, Decisions는 대화의 최신 상태를 본질적인 힘으로 추적하고 있다고 할 수 있습니다.
다만 중립 버전에서는 새로운 종류의 오류도 발견되었습니다. 이는 다음 고찰에서 정리하여 다루겠습니다.
두 경로를 비교한 결과, 명확한 경향이 나타났습니다.
날짜・계약・방침・'수정되었는지 여부'와 같은 사실 기반 질문은 요약을 거치든 음성 녹취록을 거치든 답변이 일치했습니다.
나아가 초기 검증에서 질문이 모호했을 때, 음성 녹취록 경로만 대화에 섞인 무관한 단어(예: 별건의 '다음 주 월요일')에 끌려가 오답을 내는 경우가 있었고, 요약 경로는 구조화 덕분에 이를 올바르게 걸러낼 수 있다는 사례도 있었습니다. 요약은 단순히 '정보를 누락시키는' 것뿐만 아니라, 노이즈 제거 역할로도 효과적인 것입니다.
명시적으로 언급된 사실은 instructions에 "수정이 있다면 수정 후를"이라고 적지 않아도 두 경로 모두 포착할 수 있었습니다(검증 C).
힌트의 명시는 정확도의 '바닥을 높이는' 데는 도움이 되지만, 필수적이지는 않습니다. 그렇다고 해도, 질문 문구와 선택지의 설계가 정확도를 좌우하는 것은 변함없으므로, 이 부분은 정교하게 작성할 가치가 있습니다.
중립 버전(검증 C)에서는 새로운 유형의 오류가 발견되었습니다. 이는 대화 속에서 한 문장으로 명확히 언급되지 않고 여러 발언에 분산되어 있는 사실입니다.
| 질문 | 경로 A (요약) | 경로 B (음성 녹취록) | 실제 상황 |
|---|
협상① provisioning_tier | undecided | shared | '공유 환경으로 진행'은 확정적이나, 대화에 분산됨 |
협상② contract_term | undecided | one_year | 계약 기간은 명시되지 않음 |
- 협상①의 경우, '공유 환경을 채택한다'는 결론이 대화 곳곳에 흩어져 있어 한 문장으로 단정되지 않았습니다.
요약 경로는 이를 압축하는 과정에서 누락하여 "미정"이라고 답변했습니다. 반면 음성 녹취록은 shared (정답)을 포착했습니다. - 협상②의 경우, 오히려 계약 기간 자체가 대화에서 명시되지 않았습니다. 여기서는 요약의 "미정"이 더 정직하며, 음성 녹취록은 '1년 차 금액'이라는 표현으로부터 one_year로 과도하게 추론했습니다.
➡️ 정리하자면, 사실 추출 경향은 다음 비대칭성으로 파악할 수 있습니다.
- 명시/수정된 사실 (금액・날짜 등 명확히 단정되는 것) → 두 경로 모두 견고함. 요약은 노이즈 제거 역할로도 효과적입니다. -
- 분산/암묵의 사실 (Tier・계약 기간 등 단정되지 않는 것) → 요약은 누락하여 "미정"이 되기 쉽습니다 (안전하지만 놓치는 부분이 있음), 음성 녹취록은 포착할 수 있으나 과도한 추론 위험이 있습니다.
'누락(recall)'과 '오탐지(precision)'의 트레이드오프, 정답은 용도에 따라 다르다고 생각합니다.
- 안전한 쪽으로 기울이기 (정밀도(Precision) 중시) — 요약 경로를 사용하고, 단정되지 않은 항목은 "미정" 상태로 받아들입니다. 판정을 그대로 자동 처리하는 등, 잘못된 확정을 피하고 싶을 때 적합합니다. 누락되는 부분은 후속 공정의 수동 검토에서 포착한다는 전제가 필요합니다.
- 과감하게 시도하기 (재현율(Recall) 중시) — 음성 녹취록 경로를 사용하고, 다소 과도한 추론을 허용하더라도 포착하는 것을 목표로 합니다. 초안 작성이나 후보 목록 생성 등, 사람이 마지막에 반드시 확인할 용도에 적합합니다.
- (권장) 항목을 분해하여 구체적인 instructions를 작성하기 — 'Tier는 무엇인가'라고 한 번에 묻기보다는, 판단에 도움이 되는 관련 항목들을 여러 개로 나누어 추출해야 합니다. 예를 들어
provisioning_tier (choice) 외에도, dedicated_env_required (predicate: 전용 환경이 필수인지), shared_env_agreed (predicate: 공유 환경으로 합의했는지)와 같이 분해하고, 선택지의 description과 instructions에 "어떤 발언이 있으면 무엇을 선택할지" 기준까지 작성합니다. 이렇게 하면 요약・음성 녹취록 어느 쪽에서도 정확도가 높아지고, "미정" 누락도 과도한 추론도 동시에 줄일 수 있습니다. = 트레이드오프 자체를 작게 만들 수 있습니다. 노력은 더 들지만, 실제 운영에서 효과를 보고 싶은 항목에는 이것이 가장 효과적입니다.
한편, deal_likelihood
(딜 예상 점수(Deal Probability Score), 0~2)의 동작은 명확히 구분되었습니다. 게다가, 힌트 제공 버전(A/B)과 힌트 미제공 버전(C) 모두에서 동일한 경향이 재현되고 있습니다.
| 경로 A (요약) | 경로 B (음성 녹취록) | 차이점 |
|---|
| 협상 ① (힌트 제공) | 1.99 | 1.05 |
| ... | | |
처음에는 “점수는 입력에 따라 달라지는 것”이라고 생각했지만, 2개의 협상 사례와 2가지 조건으로 비교하면서 해석이 바뀌었습니다.
- 협상 ①은 해지 조항이 보류되거나 결재권자의 승인을 기다리는 등, 미해결된 함의가 대화에 남아있었습니다. 음성 녹취록은 그 분위기까지 읽어 신중하게(1.05) 평가한 반면, 요약본은 이를 완화하여 낙관적으로(1.99) 판단했습니다.
- 협상 ②는 기준 수정도 방침도 유효 기한도 현장에서 결론이 났습니다. 그래서 두 경로 모두 높은 점수로 일치했습니다.
즉, **“요약 점수”와 “음성 녹취록 점수”의 차이점 자체가