AIM AI
경력근거: analyze call data, transcripts, and performance metrics to continuously improve
※ 실제 산출물이 아닌 AI 예측 시안입니다.
Aim AI는 자동차·홈서비스·금융 같은 여러 업종에 음성 에이전트를 붙이고 있고, 공고는 고객사마다 과제가 다르다는 점을 앞세웁니다. 업종과 지역이 섞이면 통화 허용 시간과 재시도 간격이 고객사마다 달라지는데, 그 규칙을 데이터로 분리해야 같은 판정기를 여러 고객사에 돌릴 수 있습니다. 이 구조는 고객사마다 다른 운영 규칙을 상대하는 파견 엔지니어 자리에서만 문제가 됩니다. 공고 업무는 대화 설계부터 연동, 배포, 개선까지 걸쳐 있는데 1주 범위에서 고를 수 있는 것은 하나라 대상 선별 축을 골랐습니다. 실제 발신과 결제 연동은 사업자 계정이 있어야 열리는 기능이라 제품 골조에서 뺐습니다. 이 포트폴리오는 지역 표기를 표준으로 맞추고 시각 규칙을 계산해 근거를 보여주는 기술을 보여줍니다. 음성 인식이나 통화 품질 자체의 전문성은 주장하지 않습니다. 그 깊이는 면접에서 모른다고 말하는 편이 낫습니다.
고객사마다 다른 과제 중 하나로 아웃바운드 통화 대상 선별 자동화가 적혀 있습니다. 어느 대상을 언제 다시 접촉할지 정하는 일이 그 자동화의 앞단입니다.
CRM과 잠재 고객 관리 시스템, 일정 도구에 REST API와 JSON으로 붙이는 일이 업무입니다. 고객사가 내려주는 시도 이력을 규칙과 대조하는 것이 그 연동의 실제 내용입니다.
배포 후 통화 데이터와 지표를 분석해 계속 개선하는 일이 업무로 적혀 있습니다. 시도 이력을 사유별로 나눠 보는 화면이 그 분석의 첫 판입니다.
대상별 통화 시도 이력과 지역별 통화 규칙을 대조해, 기준 시각에 다시 걸어도 되는 통화 대상과 안 되는 대상을 사유별로 나눠 보여주는 관리자 페이지입니다. 판정은 마지막 시도 시각과 재시도 간격, 허용 시간대, 시도 횟수 한도를 견주는 계산으로 정합니다. 실제 발신은 하지 않고 대상 목록까지만 만듭니다.
고객사에 음성 에이전트를 붙인 뒤 아웃바운드 운영을 도와주는 파견 엔지니어입니다. 매일 아침 CRM에서 시도 이력을 내려받아 지역별 통화 가능 시간과 재시도 규칙을 머릿속으로 맞춰 대상 명단을 추리는데, 지역 표기가 시스템마다 달라 어느 규칙을 적용할지부터 헷갈립니다.
지역마다 통화 허용 시간과 재시도 간격이 정해져 있으면, 지금 걸어도 되는지는 사람이 판단하지 않아도 시각 계산으로 갈라집니다.
시도 이력과 지역 규칙 파일을 올리고 기준 시각을 정하면 지금 걸어도 되는 통화 대상과 안 되는 대상을 사유별로 보여주는 관리자 페이지입니다. CRM 연동은 두지 않고 내려받은 파일만 읽습니다.
재시도 간격과 허용 시간대를 함께 계산해 각 대상이 몇 시부터 가능한지 시각을 함께 보여주는 화면입니다.
지역 사전과 통화 규칙을 파일로 분리해 고객사마다 다른 규칙을 같은 판정으로 돌리고, 결과를 JSON으로 내보내 다음 단계 시스템이 받아 갈 수 있게 합니다.
담당자가 시도 이력 50행과 지역 규칙을 올려 지금 걸 수 있는 대상이 사유별로 갈린 화면을 보는 것을 시연합니다.
결측·중복·미등록 지역이 섞인 입력 30종을 올려도 화면이 깨지지 않고, 판정하지 못한 대상은 사유가 붙어 남습니다.
시도 시각이 비어 있는 대상은 가능으로 세지 않고 시각 누락으로 따로 답니다. 재지 못한 것을 가능으로 만들지 않습니다.
판정과 손으로 만든 정답표를 맞춰 사유별로 어긋난 건을 셉니다. 단, 이력과 정답을 모두 작성자가 만든 합성 기준이라 성능 측정이 아니라 데모 조건 동작입니다.
진짜 성능 검증은 여기서 멈춥니다. 위 수치는 더미데이터 자가채점이며, 실제 판정 정확도는 Aim AI 고객사의 실제 시도 이력과 실제 접촉 규칙이 있어야 측정됩니다. 이 포트폴리오는 그 검증을 바로 돌릴 수 있는 상태까지 만듭니다.
수치는 합성 데이터 자가채점이라 성능 증명이 아닙니다. 면접에서는 데모 동작과 실검증 준비 상태로 솔직히 설명합니다.
작성자가 정답을 만들었습니다. 채점이 잘 나오는 건 당연한 일이라, 지역 표기가 시스템마다 다른 건과 시도 시각이 빈 건, 규칙표에 없는 지역을 이력에 함께 넣었습니다.
규칙상 걸 수 있다고 해서 걸어도 좋다고 단정할 수 없습니다. 수신 거부 의사와 개별 동의 상태는 이 도구가 보지 않으므로, 판정은 시간 규칙까지만 읽어야 합니다.
실제 도입하면 고객사마다 접촉 규칙과 지역 체계가 다릅니다. 규칙표를 고객과 합의해 채우는 일이 도입 단계의 실제 과제입니다.
실제 발신과 결제 연동은 사업자 계정이 있어야 열리는 기능이라 이 포트폴리오에 넣지 않았습니다. 공고의 전화 연동이 담당하는 층은 제품이 맡고, 여기서는 그 앞단의 대상 선별만 다룹니다.
① 인라인 샘플 · ② seed.json 다운로드 · ③ 증강 프롬프트. 세 층 모두 동일한 schema 에서 파생.
고객사 CRM에서 내려받은 대상별 통화 시도 이력입니다. 지역 표기가 시스템마다 대소문자·띄어쓰기로 흔들리고, 사전에 없는 신규 지역과 같은 시도 번호가 두 번 들어온 건, 시각이 비어 있는 행을 일부러 섞었습니다.
| attempt_no | lead_no | region_raw | attempted_at | outcome | duration_sec |
|---|---|---|---|---|---|
| AT-001 | LD-001 | US-East | 2026-08-18T09:10 | no_answer | 0 |
| AT-002 | LD-002 | us-east | 2026-08-19T14:00 | voicemail | 18 |
| AT-003 | LD-003 | US-West | 2026-08-19T16:30 | no_answer | 0 |
| AT-004 | LD-004 | US-East | 2026-08-19T10:00 | connected | 212 |
| AT-005 | LD-005 | US-Central | 2026-08-19T11:00 | no_answer | 0 |
아래 JSON Schema에 맞는 통화 시도 이력 행을 더 만들어 주세요. {"type":"object","properties":{"attempt_no":{"type":"string"},"lead_no":{"type":"string"},"region_raw":{"type":"string"},"attempted_at":{"type":["string","null"]},"outcome":{"type":"string","enum":["no_answer","voicemail","connected","failed"]},"duration_sec":{"type":["integer","null"]}},"required":["attempt_no","lead_no","region_raw","attempted_at","outcome","duration_sec"]} 한 lead_no에 시도가 여러 건 쌓이도록 만들어 주세요. 쉬운 데이터만 만들지 말 것. 다음 하드 케이스를 의도적으로 섞어라. 첫째 미매핑으로 region_raw가 사전에 없어 region_id가 null인 행, 둘째 표기흔들림으로 같은 지역을 US-East와 us-east와 us east처럼 대소문자·띄어쓰기만 다르게 적은 행, 셋째 중복으로 같은 attempt_no가 두 행에 있는 재적재 건, 넷째 결측으로 attempted_at과 duration_sec이 null인 발신 실패 행, 다섯째 경계극단으로 허용 시간의 시작과 끝 시각에 정확히 걸린 행. 또한 형식 깨뜨리기 시도 3건을 별도 파일로 만들어 outcome에 enum 밖 값을 넣거나 lead_no를 빼서 스키마 검증이 걸러내는지 확인하게 해 주세요.{"type":"object","properties":{"attempt_no":{"type":"string"},"lead_no":{"type":"string"},"region_raw":{"type":"string"},"attempted_at":{"type":["string","null"]},"outcome":{"type":"string","enum":["no_answer","voicemail","connected","failed"]},"duration_sec":{"type":["integer","null"]}},"required":["attempt_no","lead_no","region_raw","attempted_at","outcome","duration_sec"]}지역별 통화 허용 시간과 재시도 규칙입니다. 재시도 간격이 아직 정해지지 않은 지역을 일부러 섞었습니다.
| region_id | region_name | allow_from_hour | allow_to_hour | min_retry_hours | max_attempts |
|---|---|---|---|---|---|
| us_east | 미국 동부 | 9 | 20 | 24 | 4 |
| us_west | 미국 서부 | 8 | 19 | 24 | 4 |
| us_central | 미국 중부 | 9 | 19 | 12 | 3 |
| us_mountain | 미국 산악 | 9 | 18 | — | 3 |
| uk | 영국 | 9 | 18 | 48 | 2 |
아래 JSON Schema에 맞는 지역별 통화 규칙 행을 더 만들어 주세요. {"type":"object","properties":{"region_id":{"type":"string"},"region_name":{"type":"string"},"allow_from_hour":{"type":"integer"},"allow_to_hour":{"type":"integer"},"min_retry_hours":{"type":["integer","null"]},"max_attempts":{"type":"integer"}},"required":["region_id","region_name","allow_from_hour","allow_to_hour","min_retry_hours","max_attempts"]} region_name은 한글로 적어 주세요. 쉬운 데이터만 만들지 말 것. 다음 하드 케이스를 의도적으로 섞어라. 첫째 결측으로 min_retry_hours가 null인 규칙 미정 지역, 둘째 경계극단으로 max_attempts가 1인 지역과 허용 시간이 한 시간뿐인 지역. 또한 형식 깨뜨리기 시도 3건을 별도 파일로 만들어 allow_from_hour에 문자열을 넣거나 region_id를 빼서 스키마 검증이 걸러내는지 확인하게 해 주세요.{"type":"object","properties":{"region_id":{"type":"string"},"region_name":{"type":"string"},"allow_from_hour":{"type":"integer"},"allow_to_hour":{"type":"integer"},"min_retry_hours":{"type":["integer","null"]},"max_attempts":{"type":"integer"}},"required":["region_id","region_name","allow_from_hour","allow_to_hour","min_retry_hours","max_attempts"]}| Python | 시도 이력과 규칙을 읽어 다음 통화 가능 여부를 계산하는 판정 본체입니다. |
| JavaScript | 판정 결과를 사유별로 묶어 보여주는 관리자 화면입니다. |
| API | 판정 결과를 JSON으로 내보내 고객사 잠재 고객 관리 시스템이 받아 가는 3주차 경로입니다. |
| JSON | 고객사가 내려주는 시도 이력과 규칙 파일의 교환 형식입니다. |
| LLM | 지역 표기를 표준 이름으로 맞출 때 후보를 제시하는 보조입니다. 판정에는 쓰지 않습니다. |
| 자동화 프레임워크 | 판정과 내보내기를 하나의 실행으로 묶어 매일 같은 시각에 돌리는 구성입니다. |
✓ pass → 여기서 멈춰도 완결입니다. 더 키우려면 안 되는 대상이 몇 시부터 가능한지 함께 계산합니다.
✓ pass → 시각 계산까지가 한 덩어리입니다. 더 키우려면 규칙을 고객사별로 분리하고 결과를 JSON으로 내보냅니다.
위 예측 UI 는 3주차까지 완주했을 때의 화면입니다. 1주차에 실제로 만드는 것은 아래 표시된 화면 하나입니다.
verdict사유 라벨 · lead_no + 한 줄 요약(근거 수치 포함)화면·코드·면접에서 이 단어를 그대로 씁니다. 키는 출력 스키마의 enum 과 같습니다.
ready지금 가능retry_too_soon재시도 간격 미달outside_window허용 시간 밖max_attempts_reached시도 한도 초과attempt_time_missing시도 시각 누락region_unmapped지역 미매핑duplicate_attempt시도 번호 중복통화 대상 한 건이 기준 시각에 다시 걸어도 되는지의 판정 결과입니다. 안 되면 왜인지를 근거와 함께 담습니다.
{"type":"object","properties":{"lead_no":{"type":"string"},"verdict":{"type":"string","enum":["ready","retry_too_soon","outside_window","max_attempts_reached","attempt_time_missing","region_unmapped","duplicate_attempt"]},"attempts_used":{"type":["integer","null"]},"next_allowed_hour":{"type":["integer","null"]},"evidence":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"agg":{"type":["string","null"]},"value":{},"quote":{"type":["string","null"]}},"required":["field","value"]}},"next_action":{"type":"string"}},"required":["lead_no","verdict","attempts_used","next_allowed_hour","evidence","next_action"]}[
{
"lead_no": "LD-001",
"verdict": "ready",
"attempts_used": 1,
"next_allowed_hour": 9,
"evidence": [
{
"field": "attempted_at",
"value": "2026-08-18T09:10"
},
{
"field": "min_retry_hours",
"value": 24
},
{
"field": "max_attempts",
"value": 4
},
{
"field": "attempt_no",
"agg": "count",
"value": 1
}
],
"next_action": "재시도 간격이 지났고 허용 시간 안입니다. 오늘 대상 명단에 넣습니다."
},
{
"lead_no": "LD-013",
"verdict": "ready",
"attempts_used": 1,
"next_allowed_hour": 8,
"evidence": [
{
"field": "attempted_at",
"value": "2026-08-15T11:00"
},
{
"field": "min_retry_hours",
"value": 24
},
{
"field": "max_attempts",
"value": 4
},
{
"field": "attempt_no",
"agg": "count",
"value": 1
}
],
"next_action": "지난 통화가 연결됐지만 후속이 필요합니다. 오늘 대상 명단에 넣습니다."
},
{
"lead_no": "LD-002",
"verdict": "retry_too_soon",
"attempts_used": 1,
"next_allowed_hour": 14,
"evidence": [
{
"field": "attempted_at",
"value": "2026-08-19T14:00"
},
{
"field": "min_retry_hours",
"value": 24
}
],
"next_action": "마지막 시도로부터 재시도 간격이 아직 지나지 않았습니다. 내일 같은 시각 이후로 미룹니다."
},
{
"lead_no": "LD-012",
"verdict": "outside_window",
"attempts_used": 1,
"next_allowed_hour": 9,
"evidence": [
{
"field": "attempted_at",
"value": "2026-08-19T20:30"
},
{
"field": "allow_from_hour",
"value": 9
},
{
"field": "allow_to_hour",
"value": 20
}
],
"next_action": "허용 시간이 지난 뒤에 걸린 기록입니다. 다음 허용 시작 시각으로 미룹니다."
},
{
"lead_no": "LD-005",
"verdict": "max_attempts_reached",
"attempts_used": 3,
"next_allowed_hour": null,
"evidence": [
{
"field": "attempt_no",
"agg": "count",
"value": 3
},
{
"field": "max_attempts",
"value": 3
}
],
"next_action": "시도 한도를 다 썼습니다. 명단에서 빼고 다른 접촉 수단을 고객사와 상의합니다."
},
{
"lead_no": "LD-007",
"verdict": "attempt_time_missing",
"attempts_used": 1,
"next_allowed_hour": null,
"evidence": [
{
"field": "attempted_at",
"value": null
},
{
"field": "outcome",
"value": "failed"
}
],
"next_action": "시도 시각이 비어 있어 간격을 재지 못했습니다. 가능으로 세지 않고 이력을 다시 내려받습니다."
},
{
"lead_no": "LD-006",
"verdict": "region_unmapped",
"attempts_used": 1,
"next_allowed_hour": null,
"evidence": [
{
"field": "region_raw",
"value": "CA-Ontario",
"quote": "CA-Ontario"
}
],
"next_action": "사전에 없는 지역 표기입니다. 사전에 추가하기 전에는 명단에서 빼 둡니다."
},
{
"lead_no": "LD-005",
"verdict": "duplicate_attempt",
"attempts_used": 3,
"next_allowed_hour": null,
"evidence": [
{
"field": "attempt_no",
"agg": "count",
"value": 2
}
],
"next_action": "같은 시도 번호가 두 번 적재됐습니다. 앞 건으로만 세고 뒤 건을 이력에서 뺍니다."
}
][자동 검증 · 1주차] 1. verdict가 enum 일곱 값 안에 있는지 확인합니다. 2. lead_no·verdict·attempts_used·next_allowed_hour·evidence·next_action이 모두 있는지 확인합니다. 3. allow_from_hour와 allow_to_hour가 0 이상 23 이하인지 확인합니다. 4. attempted_at이 null이면 verdict가 attempt_time_missing인지 확인합니다. 5. evidence의 field가 두 입력 표의 컬럼에 실재하는지 확인합니다. 6. evidence의 quote가 그 행 region_raw의 일부인지 확인합니다. 7. next_action이 존댓말로 끝나는지 확인합니다. [판정 규칙] 1. 지역 표기은 대소문자와 띄어쓰기, 구분자를 없애 소문자로 모은 뒤 calling_windows의 region_id와 맞춥니다. 맞는 것이 없으면 미매핑으로 남기고 그 행은 나머지 판정에서 뺍니다. 2. 같은 행 번호가 두 번 들어오면 앞 건만 남기고 뒤 건은 적재 단계에서 사유와 함께 뺍니다. [수동 검증] 1. 한도를 다 쓴 대상을 정말 접어도 되는지 고객사 영업 담당자가 확인합니다. 2. LD-002·LD-009처럼 지역 표기가 흔들렸지만 사전으로 맞춰진 대상은 미매핑 사유를 달지 않는 것이 맞는지 확인합니다. [평가 러너 · 2주차] 1. 손으로 만든 정답표와 맞춰 사유별로 어긋난 건을 세고 어느 사유에서 많이 틀리는지 나눕니다. 2. 이력과 정답을 작성자가 만든 합성 기준이라 이 수치는 성능 측정이 아니라 데모 조건 동작입니다.
근거: analyze call data, transcripts, and performance metrics to continuously improve