Extract clear win and loss reasons from deal notes, calls, and CRM fields. — Claude Skill
Claude Code용 Claude 스킬 · 제공: Louis Blythe · 실행: /win-loss-reason-extraction (Claude 내)·업데이트: 2026년 6월 14일·vmain@e0f13a6
승패 근거를 가격, 시기, 경쟁사, 무결정, 기능 공백, 적합도, 내부 프로세스 같은 사유로 분류하고 신뢰도와 근거를 붙입니다.
- 정돈되지 않은 거래 메모와 통화 발췌를 일관된 승패 사유 카테고리로 바꿉니다.
- 명시된 사유, 추론된 사유, 경쟁사 영향, 무결정 위험을 분리합니다.
- 리더가 무엇을 신뢰할 수 있는지 볼 수 있도록 신뢰도와 원천 근거를 표시합니다.
- 영업 코칭, 제품, 가격, 마케팅을 위한 후속 조치를 만듭니다.
실패 종료 사유가 일관되지 않고 모호해 담당자 간 비교가 어렵습니다.
/win-loss-reason-extraction을 실행해 사유, 근거, 신뢰도, 후속 조치를 정규화합니다.
대상
기능
세그먼트, 경쟁사, 금액별로 거래를 왜 이겼거나 졌는지 요약합니다.
실제 거래 사유를 사용해 경쟁사 대화 흐름을 갱신합니다.
패배가 자격 검증, 디스커버리, 가격, 시기, 제품 공백 중 어디에서 왔는지 식별합니다.
작동 방식
성공 종료 및 실패 종료 메모, CRM 필드, 통화 발췌, 세그먼트, 거래 규모, 경쟁사 맥락을 수집합니다.
사유를 일관된 분류 체계로 정규화합니다.
각 사유에 근거, 신뢰도, 세그먼트, 담당자 후속 확인을 붙입니다.
승률, 제품 공백, 가격, 메시지에 중요한 패턴을 요약합니다.
사유가 너무 모호해 영업 담당자 검증이 필요한 사례를 표시합니다.
입력 옵션
CRM 필드, 거래 메모, 통화 발췌, 종료 이유, 결과, 금액입니다.
예시
실패 종료 거래 메모: 1. $62k ARR, 경쟁사 LearnPro. 구매자는 템플릿을 좋아했습니다. LearnPro의 보고 기능은 약하지만 7월까지 출시해야 한다고 말했습니다. 2. $18k ARR, 경쟁사 없음. CFO가 모든 신규 도구를 중단했습니다. 담당자는 '예산'이라고 적었습니다. 3. $90k ARR, 경쟁사 GuidePilot. SSO와 관리자 제어가 조달 준비가 되지 않아 패배했습니다. 4. $42k ARR, 경쟁사 LearnPro. 구매자는 우리 가격이 불명확하고 구현 서비스가 별도라고 말했습니다. 성공 종료 메모: 5. $75k ARR, 경쟁사 LearnPro. 고객 성공 VP가 경영진 보고와 CSM 워크플로를 필요로 해 승리했습니다. 필요: 정규화된 사유, 신뢰도, 근거, 영업/제품/제품 마케팅 조치.
| 거래 | 결과 | 1차 사유 | 2차 사유 | 신뢰도 | 근거 | |---|---|---|---|---|---| | 1 | 패배 | 시기 / 구현 속도 | 경쟁사 템플릿 강점 | 높음 | 7월 출시 필요, LearnPro 템플릿 선호 | | 2 | 패배 | 무결정 / 예산 동결 | 실제 경쟁 패배 아님 | 중간 | CFO가 신규 도구 중단 | | 3 | 패배 | 제품 공백 | 엔터프라이즈 준비도 | 높음 | SSO와 관리자 제어가 조달을 막음 | | 4 | 패배 | 가격 명확성 | 서비스 패키징 | 중간 | 가격 불명확, 서비스 별도 | | 5 | 승리 | 보고 차별화 | CS 워크플로 적합성 | 높음 | 경영진 보고와 CSM 워크플로 필요 |
Q2를 단순히 '가격 때문에 졌다'고 요약하지 마세요. 더 강한 패턴은 엔터프라이즈 준비도와 구현 속도입니다. 가격 명확성은 한 거래에만 나타나므로 더 많은 근거가 필요합니다.
영업: 출시 기한과 조달 요구사항을 더 일찍 확인합니다. 제품: SSO와 관리자 제어를 엔터프라이즈 차단 요인으로 검토합니다. 제품 마케팅: LearnPro 대응 카드에서 템플릿과 보고의 차이를 갱신하고 가격 대화에서 구현 서비스를 명확히 합니다.
2번과 4번 거래 담당자에게 '예산'과 '가격 불명확'가 구매자 발언인지 담당자 추론인지 확인하세요. 검증 전까지는 중간 신뢰도로 유지합니다.
개선되는 지표
지원 도구
승패 사유 추출을(를) 사용해 보시겠어요?
시작 방법을 선택하세요.
이 스킬을 컴퓨터에 로컬로 설치하고 실행합니다.
컴퓨터에서 터미널을 열고 이 명령을 붙여넣으세요:
이 명령은 스킬과 모든 파일을 컴퓨터에 다운로드합니다:
모든 프로젝트에서 사용하려면 끝에 -g를 추가하세요.
Claude Code를 시작한 다음 명령을 입력하세요:
승패 이유 추출
당신은 거래가 성사되거나 실패한 이유를 자동으로 추출하고 분류하는 영업 봇 구축 전문가입니다. 목표는 개발자가 결과에서 학습해 향후 성과를 개선하는 시스템을 만들도록 돕는 것입니다.
승패 추출이 중요한 이유
지식 격차
추출이 없을 때:
- "왜 졌나요?" "모르겠습니다"
- 같은 실수 반복
- 패턴 가시성 없음
- 직관 기반 전략
추출이 있을 때:
- 분류된 실패 이유
- 추세 식별
- 데이터 기반 개선
- 실행 가능한 인사이트
승리 이유 범주
일반적인 승리 이유
WIN_REASONS = {
"product_fit": {
"keywords": ["우리에게 꼭 필요한 것", "완벽하게 맞음", "우리 문제를 해결함"],
"indicators": ["feature_match_high", "use_case_alignment"]
},
"price_value": {
"keywords": ["공정한 가격", "좋은 가치", "가치가 있음", "ROI가 타당함"],
"indicators": ["price_accepted", "roi_discussed_positively"]
},
"trust_relationship": {
"keywords": ["신뢰함", "함께 일하기 좋음", "우리를 이해함"],
"indicators": ["high_engagement", "personal_connection"]
},
"competitive_advantage": {
"keywords": ["더 나음", "대신 선택함", "차별화됨"],
"indicators": ["competitor_comparison_won", "unique_feature_mentioned"]
},
"timing": {
"keywords": ["딱 맞는 시점", "긴급한 필요", "완벽한 타이밍"],
"indicators": ["fast_sales_cycle", "urgency_expressed"]
},
"champion_advocacy": {
"keywords": ["옹호함", "팀을 설득함", "내 추천"],
"indicators": ["strong_champion", "internal_selling"]
}
}
실패 이유 범주
일반적인 실패 이유
LOSS_REASONS = {
"price": {
"keywords": ["너무 비쌈", "예산 초과", "더 저렴한 옵션", "감당할 수 없음"],
"indicators": ["price_objection", "budget_constraints"],
"severity": "high"
},
"timing": {
"keywords": ["타이밍이 나쁨", "지금은 아님", "나중에 가능", "내년에"],
"indicators": ["timing_objection", "delayed_decision"],
"severity": "medium"
},
"competitor": {
"keywords": ["다른 곳으로 감", "선택함", "경쟁사가 이김", "[경쟁사] 사용"],
"indicators": ["competitor_mentioned", "comparison_lost"],
"severity": "high"
},
"no_decision": {
"keywords": ["아무것도 하지 않음", "현재 상태 유지", "우선순위 아님"],
"indicators": ["stalled", "no_urgency"],
"severity": "medium"
},
"feature_gap": {
"keywords": ["기능 누락", "지원하지 않음", "당신들에게 없는 X가 필요함"],
"indicators": ["feature_request_unmet", "requirement_gap"],
"severity": "high"
},
"bad_fit": {
"keywords": ["우리에게 맞지 않음", "적합하지 않음", "필요한 것이 아님"],
"indicators": ["poor_icp_match", "misaligned_use_case"],
"severity": "medium"
},
"internal_issues": {
"keywords": ["조직 개편", "합병", "예산 동결", "리더십 변경"],
"indicators": ["external_factors", "company_change"],
"severity": "low"
},
"lost_champion": {
"keywords": ["담당자가 떠남", "더 이상 없음", "보고 체계 변경"],
"indicators": ["champion_departed", "stakeholder_change"],
"severity": "medium"
}
}
추출 방법
대화 분석
def extract_reasons_from_conversation(conversation, outcome):
reasons = []
# 마지막 메시지 분석
final_messages = conversation.messages[-5:]
for message in final_messages:
if message.sender == "prospect":
# 이유 키워드와 대조
if outcome == "lost":
matched_reasons = match_loss_reasons(message.text)
else:
matched_reasons = match_win_reasons(message.text)
reasons.extend(matched_reasons)
# 대화 패턴 분석
pattern_reasons = extract_pattern_based_reasons(conversation, outcome)
reasons.extend(pattern_reasons)
# 중복 제거와 순위화
return rank_reasons(reasons)
def match_loss_reasons(text):
matches = []
for reason_code, config in LOSS_REASONS.items():
for keyword in config["keywords"]:
if keyword.lower() in text.lower():
matches.append({
"reason": reason_code,
"confidence": 0.8,
"source": "keyword_match",
"evidence": text
})
return matches
패턴 기반 추출
def extract_pattern_based_reasons(conversation, outcome):
reasons = []
if outcome == "lost":
# 가격 패턴
if had_price_objection(conversation) and not resolved_price_objection(conversation):
reasons.append({
"reason": "price",
"confidence": 0.7,
"source": "pattern",
"evidence": "해결되지 않은 가격 반론"
})
# 경쟁사 패턴
competitor = detect_competitor_mention(conversation)
if competitor and conversation.last_stage == "evaluation":
reasons.append({
"reason": "competitor",
"confidence": 0.6,
"source": "pattern",
"evidence": f"평가 중 경쟁사 {competitor} 언급"
})
# 미결정 패턴
if conversation.days_stalled > 30:
reasons.append({
"reason": "no_decision",
"confidence": 0.5,
"source": "pattern",
"evidence": f"거래가 {conversation.days_stalled}일 동안 정체됨"
})
return reasons
LLM 기반 추출
def extract_reasons_with_llm(conversation, outcome):
prompt = f"""
이 {outcome} 영업 대화를 분석하고 결과의 주요 이유를 식별하세요.
대화:
{format_conversation(conversation)}
제공할 내용:
1. 주요 이유(다음 중 선택: {list_reasons(outcome)})
2. 보조 이유(해당하는 경우)
3. 신뢰도 수준(높음/중간/낮음)
4. 대화에서 나온 증거
JSON 형식으로 작성하세요.
"""
response = llm.generate(prompt)
return parse_reason_response(response)
이유 검증
신뢰도 점수화
def calculate_reason_confidence(reason, conversation):
confidence = 0.5 # 기본 신뢰도
# 직접 진술 보너스
if reason["source"] == "explicit_statement":
confidence += 0.3
# 다중 신호 보너스
signal_count = count_supporting_signals(reason, conversation)
confidence += min(signal_count * 0.1, 0.3)
# 최신성 보너스(최근 진술이 더 신뢰 가능)
if reason.get("message_index") and reason["message_index"] >= len(conversation.messages) - 3:
confidence += 0.1
# 결과 설문과 교차 확인
if matches_survey_response(reason, conversation.deal_id):
confidence += 0.2
return min(confidence, 1.0)
사람 검증 루프
def queue_for_validation(deal_id, extracted_reasons):
"""불확실한 추출 결과를 사람 검토 대상으로 표시합니다"""
needs_review = []
for reason in extracted_reasons:
if reason["confidence"] < 0.7:
needs_review.append(reason)
if needs_review:
create_review_task(
deal_id=deal_id,
reasons=needs_review,
priority="medium" if len(needs_review) == 1 else "high"
)
집계와 분석
추세 분석
def analyze_loss_trends(time_period):
losses = get_lost_deals(time_period)
# 이유별 집계
reason_counts = Counter()
for deal in losses:
for reason in deal.loss_reasons:
reason_counts[reason["reason"]] += 1
# 비율 계산
total = len(losses)
reason_pcts = {r: c/total for r, c in reason_counts.items()}
# 이전 기간과 비교
prev_period = get_previous_period(time_period)
prev_reason_pcts = analyze_loss_trends(prev_period)
# 의미 있는 변화 식별
trends = {}
for reason, pct in reason_pcts.items():
prev_pct = prev_reason_pcts.get(reason, 0)
change = pct - prev_pct
if abs(change) > 0.05: # >5% change
trends[reason] = {
"current": pct,
"previous": prev_pct,
"change": change,
"direction": "increasing" if change > 0 else "decreasing"
}
return {
"distribution": reason_pcts,
"trends": trends,
"top_reasons": sorted(reason_pcts.items(), key=lambda x: -x[1])[:5]
}
세그먼트별 분석
def analyze_by_segment(time_period):
"""세그먼트별 승패 이유를 분석합니다"""
segments = ["smb", "mid_market", "enterprise"]
analysis = {}
for segment in segments:
deals = get_deals(time_period, segment=segment)
won = [d for d in deals if d.outcome == "won"]
lost = [d for d in deals if d.outcome == "lost"]
analysis[segment] = {
"win_rate": len(won) / len(deals) if deals else 0,
"top_win_reasons": get_top_reasons(won, "win"),
"top_loss_reasons": get_top_reasons(lost, "loss"),
"deal_count": len(deals)
}
return analysis
경쟁사 분석
def analyze_competitor_losses(time_period):
"""특정 경쟁사에 대한 패배를 분석합니다"""
competitor_losses = get_deals(
time_period,
outcome="lost",
reason="competitor"
)
by_competitor = {}
for deal in competitor_losses:
competitor = deal.competitor_name
if competitor not in by_competitor:
by_competitor[competitor] = {
"count": 0,
"reasons": [],
"deal_sizes": []
}
by_competitor[competitor]["count"] += 1
by_competitor[competitor]["reasons"].extend(deal.loss_details)
by_competitor[competitor]["deal_sizes"].append(deal.deal_size)
# 요약
for competitor in by_competitor:
by_competitor[competitor]["avg_deal_size"] = mean(
by_competitor[competitor]["deal_sizes"]
)
by_competitor[competitor]["common_reasons"] = Counter(
by_competitor[competitor]["reasons"]
).most_common(3)
return by_competitor
실행 가능한 인사이트
인사이트 생성
def generate_win_loss_insights(analysis):
insights = []
# 가격 인사이트
if analysis["top_loss_reasons"][0][0] == "price":
insights.append({
"type": "alert",
"topic": "pricing",
"insight": f"가격이 {analysis['top_loss_reasons'][0][1]:.0%}로 최상위 실패 이유입니다",
"recommendation": "가격 전략 또는 가치 커뮤니케이션을 검토하세요"
})
# 경쟁사 인사이트
competitor_losses = [r for r in analysis["distribution"] if r.startswith("competitor_")]
if sum(analysis["distribution"].get(r, 0) for r in competitor_losses) > 0.3:
insights.append({
"type": "alert",
"topic": "competitive",
"insight": "패배의 30% 이상이 경쟁사 때문입니다",
"recommendation": "경쟁 포지셔닝과 배틀카드를 업데이트하세요"
})
# 승리 인사이트
if analysis.get("win_analysis", {}).get("top_win_reasons", []):
top_win = analysis["win_analysis"]["top_win_reasons"][0]
insights.append({
"type": "positive",
"topic": "winning",
"insight": f"주요 승리 동인: {top_win[0]} ({top_win[1]:.0%})",
"recommendation": f"메시징에서 {top_win[0]}을 강조하세요"
})
return insights
연동
CRM 업데이트
def update_crm_with_reasons(deal_id, reasons):
"""추출된 이유로 CRM을 업데이트합니다"""
primary_reason = reasons[0] if reasons else None
secondary_reason = reasons[1] if len(reasons) > 1 else None
crm_update = {
"Loss_Reason__c": primary_reason["reason"] if primary_reason else None,
"Loss_Reason_Secondary__c": secondary_reason["reason"] if secondary_reason else None,
"Loss_Details__c": "; ".join([r["evidence"] for r in reasons]),
"Competitor_Lost_To__c": extract_competitor(reasons),
"Reason_Confidence__c": primary_reason["confidence"] if primary_reason else None
}
crm_client.update_opportunity(deal_id, crm_update)
참조 문서
name: win-loss-reason-extraction description: 사용자가 영업 봇이 거래가 성사되거나 실패한 이유를 자동 분류하는 능력을 만들거나 개선하려 할 때 사용합니다. "승패 분석", "거래 결과", "실패 이유", "종료 이유", "거래 분류"를 언급할 때도 사용합니다.
승패 이유 추출
당신은 거래가 성사되거나 실패한 이유를 자동으로 추출하고 분류하는 영업 봇 구축 전문가입니다. 목표는 개발자가 결과에서 학습해 향후 성과를 개선하는 시스템을 만들도록 돕는 것입니다.
승패 추출이 중요한 이유
지식 격차
추출이 없을 때:
- "왜 졌나요?" "모르겠습니다"
- 같은 실수 반복
- 패턴 가시성 없음
- 직관 기반 전략
추출이 있을 때:
- 분류된 실패 이유
- 추세 식별
- 데이터 기반 개선
- 실행 가능한 인사이트
승리 이유 범주
일반적인 승리 이유
WIN_REASONS = {
"product_fit": {
"keywords": ["우리에게 꼭 필요한 것", "완벽하게 맞음", "우리 문제를 해결함"],
"indicators": ["feature_match_high", "use_case_alignment"]
},
"price_value": {
"keywords": ["공정한 가격", "좋은 가치", "가치가 있음", "ROI가 타당함"],
"indicators": ["price_accepted", "roi_discussed_positively"]
},
"trust_relationship": {
"keywords": ["신뢰함", "함께 일하기 좋음", "우리를 이해함"],
"indicators": ["high_engagement", "personal_connection"]
},
"competitive_advantage": {
"keywords": ["더 나음", "대신 선택함", "차별화됨"],
"indicators": ["competitor_comparison_won", "unique_feature_mentioned"]
},
"timing": {
"keywords": ["딱 맞는 시점", "긴급한 필요", "완벽한 타이밍"],
"indicators": ["fast_sales_cycle", "urgency_expressed"]
},
"champion_advocacy": {
"keywords": ["옹호함", "팀을 설득함", "내 추천"],
"indicators": ["strong_champion", "internal_selling"]
}
}
실패 이유 범주
일반적인 실패 이유
LOSS_REASONS = {
"price": {
"keywords": ["너무 비쌈", "예산 초과", "더 저렴한 옵션", "감당할 수 없음"],
"indicators": ["price_objection", "budget_constraints"],
"severity": "high"
},
"timing": {
"keywords": ["타이밍이 나쁨", "지금은 아님", "나중에 가능", "내년에"],
"indicators": ["timing_objection", "delayed_decision"],
"severity": "medium"
},
"competitor": {
"keywords": ["다른 곳으로 감", "선택함", "경쟁사가 이김", "[경쟁사] 사용"],
"indicators": ["competitor_mentioned", "comparison_lost"],
"severity": "high"
},
"no_decision": {
"keywords": ["아무것도 하지 않음", "현재 상태 유지", "우선순위 아님"],
"indicators": ["stalled", "no_urgency"],
"severity": "medium"
},
"feature_gap": {
"keywords": ["기능 누락", "지원하지 않음", "당신들에게 없는 X가 필요함"],
"indicators": ["feature_request_unmet", "requirement_gap"],
"severity": "high"
},
"bad_fit": {
"keywords": ["우리에게 맞지 않음", "적합하지 않음", "필요한 것이 아님"],
"indicators": ["poor_icp_match", "misaligned_use_case"],
"severity": "medium"
},
"internal_issues": {
"keywords": ["조직 개편", "합병", "예산 동결", "리더십 변경"],
"indicators": ["external_factors", "company_change"],
"severity": "low"
},
"lost_champion": {
"keywords": ["담당자가 떠남", "더 이상 없음", "보고 체계 변경"],
"indicators": ["champion_departed", "stakeholder_change"],
"severity": "medium"
}
}
추출 방법
대화 분석
def extract_reasons_from_conversation(conversation, outcome):
reasons = []
# 마지막 메시지 분석
final_messages = conversation.messages[-5:]
for message in final_messages:
if message.sender == "prospect":
# 이유 키워드와 대조
if outcome == "lost":
matched_reasons = match_loss_reasons(message.text)
else:
matched_reasons = match_win_reasons(message.text)
reasons.extend(matched_reasons)
# 대화 패턴 분석
pattern_reasons = extract_pattern_based_reasons(conversation, outcome)
reasons.extend(pattern_reasons)
# 중복 제거와 순위화
return rank_reasons(reasons)
def match_loss_reasons(text):
matches = []
for reason_code, config in LOSS_REASONS.items():
for keyword in config["keywords"]:
if keyword.lower() in text.lower():
matches.append({
"reason": reason_code,
"confidence": 0.8,
"source": "keyword_match",
"evidence": text
})
return matches
패턴 기반 추출
def extract_pattern_based_reasons(conversation, outcome):
reasons = []
if outcome == "lost":
# 가격 패턴
if had_price_objection(conversation) and not resolved_price_objection(conversation):
reasons.append({
"reason": "price",
"confidence": 0.7,
"source": "pattern",
"evidence": "해결되지 않은 가격 반론"
})
# 경쟁사 패턴
competitor = detect_competitor_mention(conversation)
if competitor and conversation.last_stage == "evaluation":
reasons.append({
"reason": "competitor",
"confidence": 0.6,
"source": "pattern",
"evidence": f"평가 중 경쟁사 {competitor} 언급"
})
# 미결정 패턴
if conversation.days_stalled > 30:
reasons.append({
"reason": "no_decision",
"confidence": 0.5,
"source": "pattern",
"evidence": f"거래가 {conversation.days_stalled}일 동안 정체됨"
})
return reasons
LLM 기반 추출
def extract_reasons_with_llm(conversation, outcome):
prompt = f"""
이 {outcome} 영업 대화를 분석하고 결과의 주요 이유를 식별하세요.
대화:
{format_conversation(conversation)}
제공할 내용:
1. 주요 이유(다음 중 선택: {list_reasons(outcome)})
2. 보조 이유(해당하는 경우)
3. 신뢰도 수준(높음/중간/낮음)
4. 대화에서 나온 증거
JSON 형식으로 작성하세요.
"""
response = llm.generate(prompt)
return parse_reason_response(response)
이유 검증
신뢰도 점수화
def calculate_reason_confidence(reason, conversation):
confidence = 0.5 # 기본 신뢰도
# 직접 진술 보너스
if reason["source"] == "explicit_statement":
confidence += 0.3
# 다중 신호 보너스
signal_count = count_supporting_signals(reason, conversation)
confidence += min(signal_count * 0.1, 0.3)
# 최신성 보너스(최근 진술이 더 신뢰 가능)
if reason.get("message_index") and reason["message_index"] >= len(conversation.messages) - 3:
confidence += 0.1
# 결과 설문과 교차 확인
if matches_survey_response(reason, conversation.deal_id):
confidence += 0.2
return min(confidence, 1.0)
사람 검증 루프
def queue_for_validation(deal_id, extracted_reasons):
"""불확실한 추출 결과를 사람 검토 대상으로 표시합니다"""
needs_review = []
for reason in extracted_reasons:
if reason["confidence"] < 0.7:
needs_review.append(reason)
if needs_review:
create_review_task(
deal_id=deal_id,
reasons=needs_review,
priority="medium" if len(needs_review) == 1 else "high"
)
집계와 분석
추세 분석
def analyze_loss_trends(time_period):
losses = get_lost_deals(time_period)
# 이유별 집계
reason_counts = Counter()
for deal in losses:
for reason in deal.loss_reasons:
reason_counts[reason["reason"]] += 1
# 비율 계산
total = len(losses)
reason_pcts = {r: c/total for r, c in reason_counts.items()}
# 이전 기간과 비교
prev_period = get_previous_period(time_period)
prev_reason_pcts = analyze_loss_trends(prev_period)
# 의미 있는 변화 식별
trends = {}
for reason, pct in reason_pcts.items():
prev_pct = prev_reason_pcts.get(reason, 0)
change = pct - prev_pct
if abs(change) > 0.05: # >5% change
trends[reason] = {
"current": pct,
"previous": prev_pct,
"change": change,
"direction": "increasing" if change > 0 else "decreasing"
}
return {
"distribution": reason_pcts,
"trends": trends,
"top_reasons": sorted(reason_pcts.items(), key=lambda x: -x[1])[:5]
}
세그먼트별 분석
def analyze_by_segment(time_period):
"""세그먼트별 승패 이유를 분석합니다"""
segments = ["smb", "mid_market", "enterprise"]
analysis = {}
for segment in segments:
deals = get_deals(time_period, segment=segment)
won = [d for d in deals if d.outcome == "won"]
lost = [d for d in deals if d.outcome == "lost"]
analysis[segment] = {
"win_rate": len(won) / len(deals) if deals else 0,
"top_win_reasons": get_top_reasons(won, "win"),
"top_loss_reasons": get_top_reasons(lost, "loss"),
"deal_count": len(deals)
}
return analysis
경쟁사 분석
def analyze_competitor_losses(time_period):
"""특정 경쟁사에 대한 패배를 분석합니다"""
competitor_losses = get_deals(
time_period,
outcome="lost",
reason="competitor"
)
by_competitor = {}
for deal in competitor_losses:
competitor = deal.competitor_name
if competitor not in by_competitor:
by_competitor[competitor] = {
"count": 0,
"reasons": [],
"deal_sizes": []
}
by_competitor[competitor]["count"] += 1
by_competitor[competitor]["reasons"].extend(deal.loss_details)
by_competitor[competitor]["deal_sizes"].append(deal.deal_size)
# 요약
for competitor in by_competitor:
by_competitor[competitor]["avg_deal_size"] = mean(
by_competitor[competitor]["deal_sizes"]
)
by_competitor[competitor]["common_reasons"] = Counter(
by_competitor[competitor]["reasons"]
).most_common(3)
return by_competitor
실행 가능한 인사이트
인사이트 생성
def generate_win_loss_insights(analysis):
insights = []
# 가격 인사이트
if analysis["top_loss_reasons"][0][0] == "price":
insights.append({
"type": "alert",
"topic": "pricing",
"insight": f"가격이 {analysis['top_loss_reasons'][0][1]:.0%}로 최상위 실패 이유입니다",
"recommendation": "가격 전략 또는 가치 커뮤니케이션을 검토하세요"
})
# 경쟁사 인사이트
competitor_losses = [r for r in analysis["distribution"] if r.startswith("competitor_")]
if sum(analysis["distribution"].get(r, 0) for r in competitor_losses) > 0.3:
insights.append({
"type": "alert",
"topic": "competitive",
"insight": "패배의 30% 이상이 경쟁사 때문입니다",
"recommendation": "경쟁 포지셔닝과 배틀카드를 업데이트하세요"
})
# 승리 인사이트
if analysis.get("win_analysis", {}).get("top_win_reasons", []):
top_win = analysis["win_analysis"]["top_win_reasons"][0]
insights.append({
"type": "positive",
"topic": "winning",
"insight": f"주요 승리 동인: {top_win[0]} ({top_win[1]:.0%})",
"recommendation": f"메시징에서 {top_win[0]}을 강조하세요"
})
return insights
연동
CRM 업데이트
def update_crm_with_reasons(deal_id, reasons):
"""추출된 이유로 CRM을 업데이트합니다"""
primary_reason = reasons[0] if reasons else None
secondary_reason = reasons[1] if len(reasons) > 1 else None
crm_update = {
"Loss_Reason__c": primary_reason["reason"] if primary_reason else None,
"Loss_Reason_Secondary__c": secondary_reason["reason"] if secondary_reason else None,
"Loss_Details__c": "; ".join([r["evidence"] for r in reasons]),
"Competitor_Lost_To__c": extract_competitor(reasons),
"Reason_Confidence__c": primary_reason["confidence"] if primary_reason else None
}
crm_client.update_opportunity(deal_id, crm_update)