← 블로그 목록
가이드2026-05-21

통화 녹음 전사 & AI 요약 API: 상담 내용을 자동으로 정리하는 법

통화 녹음 전사 & AI 요약 API: 상담 내용을 자동으로 정리하는 법

ClawOps - AI 전화 에이전트 플랫폼

콜센터에서 하루 수백 건의 통화가 이뤄지지만, 실제로 내용을 다시 들어보는 비율은 극소수입니다. 녹음 파일을 들으려면 통화 시간만큼의 시간이 다시 필요하기 때문입니다.

ClawOps는 통화 녹음을 자동으로 텍스트로 변환(Transcript)하고, AI가 핵심 요약(Summary)까지 생성해주는 API를 제공합니다. 이 글에서는 SDK로 전사·요약을 조회하는 방법과, 웹훅으로 실시간 알림을 받는 방법을 다룹니다.

전사(Transcript)와 요약(Summary)이란?

기능설명출력
전사 (Transcript)통화 녹음을 STT로 텍스트 변환화자 분리된 대화 스크립트 (타임스탬프 포함)
요약 (Summary)전사된 텍스트를 LLM이 구조화핵심 요지, 결정사항, 후속 조치, 감정 분석

전사는 통화 종료 후 자동으로 시작되며, 요약은 전사 완료 후 자동으로 이어집니다. 두 기능 모두 사용량 기반으로 과금됩니다.

활성화 방법

  1. ClawOps 대시보드에 로그인
  2. 조직 설정통화 받아쓰기 활성화
  3. 요약을 사용하려면 요약(Summary) Add-on도 활성화

SDK로 전사 결과 조회하기

Python

from clawops import ClawOps

client = ClawOps()

# 전사 상태 조회
state = client.calls.get_transcript("CA1a2b3c4d5e6f7890")

if state.status == "completed":
    for seg in state.segments or []:
        print(f"[{seg.speaker}] {seg.start:.2f}s  {seg.text}")
elif state.status == "pending":
    print(f"전사 진행 중 (시작: {state.started_at})")
elif state.status == "failed":
    print(f"전사 실패 ({state.stage}): {state.error}")
elif state.status == "not_requested":
    print("전사가 요청되지 않았습니다.")

Node.js

import ClawOps from '@teamlearners/clawops';

const client = new ClawOps();

const state = await client.calls.getTranscript('CA1a2b3c4d5e6f7890');

if (state.status === 'completed') {
  for (const seg of state.segments ?? []) {
    console.log(`[${seg.speaker}] ${seg.start.toFixed(2)}s  ${seg.text}`);
  }
} else if (state.status === 'pending') {
  console.log(`전사 진행 중 (시작: ${state.startedAt})`);
} else if (state.status === 'failed') {
  console.log(`전사 실패 (${state.stage}): ${state.error}`);
}

전사 상태 (status)

상태설명
completed전사 완료 — segments 배열에 결과 포함
pending전사 진행 중
failed전사 실패 — stageerror에 원인
not_requested전사가 요청되지 않음

개별 통화 전사 요청

조직 설정에서 "통화 받아쓰기"가 꺼져 있어도, 특정 통화 1건에 대해 전사를 명시적으로 요청할 수 있습니다.

Python

from clawops import ClawOps, ConflictError

client = ClawOps()

try:
    result = client.calls.request_transcript("CA1a2b3c4d5e6f7890")
    print(result.status, result.call_id)  # "pending", "CA..."
except ConflictError:
    # 이미 요청된 통화 — 현재 상태 확인
    state = client.calls.get_transcript("CA1a2b3c4d5e6f7890")
    print(f"이미 요청됨: {state.status}")

Node.js

import ClawOps, { ConflictError } from '@teamlearners/clawops';

const client = new ClawOps();

try {
  const result = await client.calls.requestTranscript('CA1a2b3c4d5e6f7890');
  console.log(result.status, result.callId);
} catch (e) {
  if (e instanceof ConflictError) {
    const state = await client.calls.getTranscript('CA1a2b3c4d5e6f7890');
    console.log(`이미 요청됨: ${state.status}`);
  }
}

이미 요청된 통화에 다시 요청하면 ConflictError가 발생합니다. 전사된 오디오 길이만큼 사용량 기반으로 과금됩니다.

SDK로 AI 요약 결과 조회하기

Python

summary = client.calls.get_summary("CA1a2b3c4d5e6f7890")

if summary.status == "completed":
    data = summary.result_json or {}
    print(f"요지: {data.get('coreSummary')}")
    for d in data.get("decisions", []):
        print(f"  - 결정: {d}")
    for f in data.get("followUps", []):
        print(f"  - 후속: {f}")
    print(f"감정: {data.get('sentiment')}")
elif summary.status == "failed":
    print(f"요약 실패: {summary.failed_reason}")

Node.js

const summary = await client.calls.getSummary('CA1a2b3c4d5e6f7890');

if (summary.status === 'completed') {
  const data = summary.resultJson;
  console.log(`요지: ${data.coreSummary}`);
  data.decisions?.forEach(d => console.log(`  - 결정: ${d}`));
  data.followUps?.forEach(f => console.log(`  - 후속: ${f}`));
  console.log(`감정: ${data.sentiment}`);
}

기본 요약 스키마 (default:v1)

{
  "coreSummary": "고객이 환불 절차를 문의함. 7일 이내 처리 안내됨.",
  "decisions": ["환불 신청서 이메일 발송"],
  "followUps": ["배송 운송장 번호 확인 후 회신"],
  "sentiment": "neutral"
}
필드타입설명
coreSummarystring핵심 요지 1~2문장
decisionsstring[]주요 합의·결정사항 (없으면 빈 배열)
followUpsstring[]후속 조치 (없으면 빈 배열)
sentimentstringpositive / neutral / negative

조직에서 커스텀 프롬프트나 스키마를 등록하면 출력 필드를 자유롭게 변경할 수 있습니다.

웹훅으로 실시간 알림 받기

SDK 폴링 대신, 전사·요약 완료 시점에 웹훅으로 자동 알림을 받을 수 있습니다.

웹훅 등록

POST /v1/accounts/{accountId}/webhooks

{
  "url": "https://my-app.com/webhooks/transcript",
  "events": ["transcript.completed", "transcript.failed",
             "summary.completed", "summary.failed"]
}

전사 웹훅 수신 (Python + Flask)

from flask import Flask, request
import urllib.request
import json

app = Flask(__name__)

@app.route("/webhooks/transcript", methods=["POST"])
def transcript_webhook():
    event = request.form.get("Event")
    call_id = request.form.get("CallId")

    if event == "transcript.completed":
        url = request.form.get("TranscriptUrl")
        duration = request.form.get("DurationSec")
        count = request.form.get("SegmentCount")
        print(f"전사 완료 [{call_id}]: {count}개 세그먼트, {duration}초")

        # 서명 URL로 전사 본문 다운로드
        with urllib.request.urlopen(url) as r:
            segments = json.loads(r.read())
        for seg in segments:
            print(f"  [{seg['speaker']}] {seg['text']}")

    elif event == "transcript.failed":
        stage = request.form.get("Stage")
        err = request.form.get("ErrorMessage")
        print(f"전사 실패 [{call_id}] ({stage}): {err}")

    return "", 204

요약 웹훅 수신

@app.route("/webhooks/summary", methods=["POST"])
def summary_webhook():
    event = request.form.get("Event")
    call_id = request.form.get("CallId")

    if event == "summary.completed":
        provider = request.form.get("Provider")     # 예: "anthropic"
        model = request.form.get("Model")            # 예: "claude-sonnet-4-6"
        summary = json.loads(request.form.get("SummaryJson") or "{}")
        print(f"요약 완료 [{call_id}] ({provider}/{model})")
        print(f"  요지: {summary.get('coreSummary')}")
        for d in summary.get("decisions", []):
            print(f"  - 결정: {d}")
        for f in summary.get("followUps", []):
            print(f"  - 후속: {f}")

    elif event == "summary.failed":
        stage = request.form.get("Stage")
        err = request.form.get("ErrorMessage")
        print(f"요약 실패 [{call_id}] ({stage}): {err}")

    return "", 204

웹훅 파라미터

transcript.completed:

파라미터설명
CallId통화 ID
TranscriptUrl전사 결과 JSON GCS 서명 URL (7일 만료)
DurationSec오디오 길이(초)
SegmentCount전사된 세그먼트 개수

summary.completed:

파라미터설명
CallId통화 ID
Provider사용된 LLM 제공자 (예: anthropic)
Model사용된 모델 (예: claude-sonnet-4-6)
SummaryJson요약 결과 JSON 문자열

TranscriptUrl은 7일 후 만료됩니다. 장기 보관이 필요하면 수신 즉시 JSON을 다운로드해 자체 스토리지에 저장하세요.

서명 검증

모든 웹훅 요청에는 X-Signature 헤더가 포함됩니다. 프로덕션에서는 반드시 검증하세요:

from clawops import ClawOps, WebhookVerificationError

client = ClawOps()

try:
    client.webhooks.verify(
        url="https://my-app.com/webhooks/transcript",
        params=request.form.to_dict(),
        signature=request.headers["X-Signature"],
        signing_key="your_signing_key",
    )
except WebhookVerificationError:
    return "Unauthorized", 401

Voice Agent SDK 녹음 기능

SDK Agent를 사용하는 경우, recording=True로 통화 오디오를 로컬에 자동 저장할 수도 있습니다:

from clawops.agent import ClawOpsAgent, OpenAIRealtime

agent = ClawOpsAgent(
    from_="07012341234",
    session=OpenAIRealtime(system_prompt="고객 상담원입니다."),
    recording=True,
    recording_path="./recordings",
)

통화별로 3개 파일이 생성됩니다:

파일설명
{call_id}_in.wav상대방(발신자) 음성
{call_id}_out.wavAI 에이전트 음성
{call_id}_mix.wav양쪽 혼합 스테레오

프로덕션에서는 recording_path를 적절한 스토리지로 설정하고, S3 등 외부 스토리지로 업로드하는 로직을 추가하세요.

실전 활용 시나리오

1. CS 팀 대시보드

통화 종료 → 전사·요약 웹훅 수신 → DB에 저장 → CS 대시보드에서 검색·필터링. 매니저가 "이번 주 부정적 감정 통화"만 필터링해서 품질 개선에 활용.

2. CRM 자동 연동

요약의 followUps 배열을 CRM의 Task로 자동 생성. "고객에게 견적서 이메일 발송" 같은 후속 조치를 놓치지 않음.

3. 컴플라이언스 기록

금융·보험 업종에서 필수인 통화 기록 보관. 전사 텍스트를 구조화해서 감사 추적(audit trail)에 활용.

4. AI 에이전트 품질 개선

전사 데이터를 분석해 AI가 잘못 응답한 패턴을 파악. 프롬프트를 개선하는 피드백 루프.

비용

  • 전사: 전사된 오디오 길이 기반 사용량 과금
  • 요약: 요약 건당 사용량 과금
  • Trial 기간(3일) 동안 무료 사용 가능

자세한 요금은 claw-ops.com/#pricing을 확인하세요.

마무리

통화 녹음이 "있기만 한" 파일에서 "검색 가능한 데이터"로 바뀌면, CS 품질 관리, CRM 연동, 컴플라이언스 준수까지 한 번에 해결됩니다. ClawOps의 전사·요약 API를 활용해 상담 데이터의 가치를 극대화하세요.

3일 무료 Trial로 바로 시작 → claw-ops.com


관련 글: AI 전화 에이전트 성과 측정 KPI 10가지 · AI 전화 서비스의 보안과 컴플라이언스 · 웹훅으로 전화 자동화 구현하기

관련 글 더 보기

ClawOps AI 전화 API로 시작하기

070 번호 발급부터 AI 음성 통화까지, REST API 몇 줄이면 됩니다.