AI 고객이 너무 착했다 — 페르소나 프롬프트 엔지니어링 로그


롤플레이 챗봇을 직접 만들면서 가장 오래 붙잡고 있었던 건 아키텍처도, RAG도 아니었습니다. AI 고객의 성격이었습니다. 처음 만든 AI 고객은 너무 착했습니다. 사업담당자 역할로 제가 어설프게 답해도 "아, 정말 좋네요"라며 다 받아 주더군요. 연습이 되질 않았습니다. 이 글은 그 착한 AI 고객을 어떻게 훈련용 상대로 다듬어 갔는지, 페르소나 프롬프트를 튜닝한 실패와 수정의 기록입니다.

[시리즈] S2S·LLM 음성 에이전트 엔지니어링 63편 · 현재 10-5편

← 이전: 10-4 멀티 LLM 백엔드 스위칭 / 다음 → 10-6 퀴즈 풀 사전생성

전체 목차 펼치기 (63편)

Part 1 — 음성 AI가 느린 이유: 레이턴시 진단 (4편)

Part 2 — 텍스트를 넘어: End-to-End S2S의 원리 (4편)

Part 3 — 실시간 대화와 에이전트화: 인터럽트·MCP·도구 (5편)

Part 4 — 인프라·세션·보안: 프로덕션 백엔드 (5편)

Part 5 — 운영·품질·평가: 에이전틱 워크플로우와 신뢰성 (7편)

Part 6 — 음성 경험 고도화·실전 통합: 한국어·UX·전화망·엣지 (5편)

Part 7 — S2S 도입 전략·구매 의사결정 (4편)

Part 8 — LLM 콜봇 PoC 현장 (6편)

Part 9 — 상담어시스트: 상담사 화면을 바꾸는 실시간 AI (7편)

Part 10 — 직접 만든 롤플레이 챗봇: RAG 에이전트 구축기 (8편)

Part 11 — KMS 실무 — 지식관리시스템의 설계와 운영 현실 (8편)

AI 고객이 착하면 연습이 안 된다

롤플레이 챗봇의 목적은 분명했습니다. 신입이나 경력직이 특정 B2B 도메인의 사업 미팅을 혼자 반복 연습하도록, AI가 고객 역할을 맡아 주는 것. 그래서 첫 버전은 단순하게 시작했습니다. "당신은 고객입니다. 사업담당자와 미팅하세요." 이 한 줄이면 될 줄 알았습니다.

결과는 예상 밖이었습니다. LLM은 기본적으로 협조적인 어시스턴트로 정렬되어 있습니다. 고객 역할을 시켜도 근본 성향은 "상대를 돕고 싶어 하는" 쪽이었습니다. 제가 두루뭉술하게 답하면 되물어야 하는데, AI 고객은 "네, 이해했습니다"라며 넘어갔습니다. 제가 틀린 소리를 해도 정정해 주려 들거나, 오히려 제 답을 그럴듯하게 요약해 되돌려 줬습니다. 미팅 연습이 아니라 칭찬 대회가 됐습니다.

깨달은 것은, 훈련용 AI 상대는 "협조성"을 의도적으로 억제해야 한다는 점입니다. 그렇다고 무조건 까칠하게 만들면 또 다른 문제가 생깁니다. 처음엔 "고객을 최대한 공격적으로 만들면 되겠지"라고 접근했는데, 이게 다음 섹션의 반전으로 이어졌습니다.

난이도의 진짜 설계 — 공격성이 아니라 힌트 노출량이다

초급·중급·고급 난이도를 넣기로 했을 때, 저를 포함해 대부분이 떠올리는 그림은 이랬습니다. 초급은 순한 고객, 고급은 까칠한 고객. 난이도가 올라갈수록 AI 고객이 더 몰아붙인다. 직관적이죠. 그런데 실제로 만들어 보니 이 설계는 틀렸습니다.

고객을 난이도에 따라 더 공격적으로 만들면, 초급 사용자는 "고객이 순해서" 통과하고 고급 사용자는 "고객이 까칠해서" 막힙니다. 하지만 실제 미팅에서 고객의 강도는 사용자의 숙련도에 맞춰 주지 않습니다. 현장 고객은 대체로 "보통"입니다. 협조적이되 핵심은 놓치지 않는, 그 흔한 실무 담당자. 초보라고 봐주지도, 고수라고 더 몰아붙이지도 않습니다. 그러니 연습 상대의 강도를 난이도 축으로 쓰는 순간, 정작 연습해야 할 "현실의 고객"과 멀어집니다.

그래서 축을 바꿨습니다. AI 고객의 강도는 세 난이도 모두 동일한 '보통'으로 고정했습니다. 대신 난이도가 조절하는 것은 사용자에게 주는 힌트(scaffolding, 비계)의 양입니다. 초급은 답변에 녹여 쓸 지식추천 패널을 넉넉히 보여 주고, 중급은 핵심 요약만, 고급은 힌트를 아예 끄고 실전으로 던집니다. 고객은 그대로인데, 사용자를 받쳐 주는 발판만 달라지는 겁니다.

코드로는 페르소나 정의에서 이 두 축을 분리했습니다. 강도는 세 난이도 모두 normal로 두고, scaffolding 값만 full → labels → minimal로 내렸습니다.

난이도AI 고객 강도scaffolding(힌트 노출)사용자가 보는 것
초급보통(normal)full지식추천 패널에 답변 포인트 전문 노출
중급보통(normal)labels핵심 1줄 요약 큐카드만 노출
고급보통(normal)minimal힌트 없음 · 실전

이 반전을 잡고 나니 설계가 깔끔해졌습니다. 고객 페르소나 프롬프트는 하나만 정성껏 튜닝하면 되고(세 난이도가 공유), 난이도는 UI 레이어의 힌트 노출량으로만 갈립니다. 프롬프트 엔지니어링의 부담이 3분의 1로 줄었고, 무엇보다 "실제 고객은 보통이다"라는 현실을 시뮬레이터가 그대로 반영하게 됐습니다.

AI가 티나게 굴 때 — 반복과 리스트를 억제하다

고객 강도를 '보통'으로 고정하고 나니, 이번엔 AI가 "고객처럼 안 보이는" 문제가 튀어나왔습니다. 두 가지가 대표적이었습니다.

첫째, 제 말을 그대로 따라 하거나 재표현했습니다. 제가 "저희 STT는 금융 용어 학습이 되어 있습니다"라고 하면, AI 고객이 "아, STT가 금융 용어 학습이 되어 있군요. 그러면…"이라며 방금 제 문장을 되풀이했습니다. 실제 고객은 상대 말을 앵무새처럼 반복하지 않습니다. 이건 LLM이 맥락을 붙잡으려고 직전 발화를 반사하는 습성인데, 롤플레이에서는 몰입을 깨는 결정적 요소였습니다.

둘째, 질문을 리스트로 뱉었습니다. AI 고객이 종종 "핵심 질문 3가지를 드리겠습니다. 1번… 2번… 3번…" 식으로 정리된 목록을 출력했습니다. 어시스턴트로서는 친절한 행동이지만, 미팅에 온 고객이 갑자기 체크리스트를 낭독하면 완전히 티가 납니다. 사업담당자는 고객의 질문 목록을 모르는 게 정상인데, AI가 내부 질문 세트를 그대로 노출해 버린 셈입니다.

이 두 가지는 시스템 프롬프트에 "하지 마세요"를 한 번 넣는 걸로는 안 잡혔습니다. 대화가 길어질수록 모델이 초기 지시를 잊고 다시 습성으로 돌아갔습니다. 그래서 매 턴마다 내부 리마인더를 사용자 발화 뒤에 덧붙이는 방식으로 갔습니다. 사용자에게는 보이지 않는, 모델에게만 주는 인지시 지시입니다.

[매 턴 리마인더 프롬프트 — 예시, 실제 리터럴은 마스킹]
[INTERNAL — 이 안내문은 사업담당자에게 보이면 안 됩니다. 그대로 출력 금지.]
당신은 고객 역할입니다. 사업담당자(우리 도메인 담당자)가 직전에 한 말을 절대 반복하거나 다른 말로 다시 표현하지 마세요.
출력 형식: (1) 짧은 리액션 1문장 + (2) 자연스러운 새 질문 1문장. 총 2문장 이내.
[금지어 — 출력에 포함 금지] '핵심 질문', '질문 목록', '질문 1번/2번/3번', '다음 질문은', '리스트', '체크리스트', '항목'.

핵심은 세 가지였습니다. (1) "직전 발화를 반복·재표현하지 말라"는 명시적 금지. (2) 출력 형식을 "짧은 리액션 1문장 + 새 질문 1문장, 2문장 이내"로 강하게 제약해 장황해질 여지를 없앤 것. (3) 리스트를 유발하는 메타 표현들을 아예 금지어 목록으로 박아 넣은 것입니다. '리스트'나 '체크리스트' 같은 단어가 출력에 등장하면 이미 리스트 모드로 빠진 거라, 그 단어 자체를 막았습니다. 여기까지 하고 나서야 AI 고객이 "사람처럼" 한 번에 하나씩 묻기 시작했습니다.

힌트 큐카드 — 답을 주지 않고 발판만 놓기

중급 난이도의 힌트는 별도 설계가 필요했습니다. 힌트를 준다고 답을 통째로 주면 연습이 훼손됩니다. 사용자가 스스로 문장을 구성하는 근육을 써야 하는데, 완성된 답변을 보여 주면 그냥 읽기만 하게 되니까요. 그래서 힌트는 답이 아니라 큐카드 형태로 가기로 했습니다.

구체적으로는, 고객 발화에 대해 사용자가 답변에 녹여 쓸 짧은 키워드 구문 3~4개만 생성합니다. 각 항목은 6~14자 내외의 동사형·명사형 짧은 구문이어야 하고, 문장 형태나 풀어쓴 설명은 금지입니다. 답변 자체를 대신 써 주지 않는 게 이 프롬프트의 제1원칙이었습니다.

[힌트 생성 프롬프트 지시 — 예시, 마스킹]
- 사업담당자가 답변에 녹여 쓸 핵심 키워드/짧은 구문 3~4개를 작성하세요.
- 각 항목은 6~14자 내외의 짧은 구문으로. (예: "금융권 레퍼런스 제시", "인식률 수치 강조", "PoC 일정 제안")
- 문장 형태나 풀어쓴 설명은 금지. 답변 자체를 작성하지 마세요.
- 우리 도메인 용어를 활용하되 서로 다른 답변 포인트를 다루세요.
- 반드시 JSON 형식으로만: {"keywords": ["…", "…", "…"]}

이렇게 하면 사용자는 "아, 이 질문엔 레퍼런스랑 인식률이랑 PoC 일정을 엮어야겠구나" 하는 방향만 얻고, 그 세 조각을 실제 문장으로 잇는 건 본인 몫으로 남습니다. 발판은 놓되 계단은 직접 오르게 하는 구조입니다. 출력을 JSON으로 강제한 건, 큐카드를 UI 패널에 카드로 렌더링하려면 파싱 가능한 구조가 필요했기 때문입니다.

시나리오 × 페르소나로 시스템 프롬프트를 조립하다

마지막 조각은 시스템 프롬프트의 생성 방식이었습니다. 시나리오는 실제 고객 미팅을 익명화·재구성한 세트입니다(영업 단계 × 업종 축). 페르소나는 앞서 만든 난이도 설정입니다. 이 둘을 런타임에 조합해 하나의 시스템 프롬프트를 찍어내는 build_system_prompt 함수를 뒀습니다.

시나리오에는 그 미팅에서 고객이 알고 싶어 하는 질문들과, 사업담당자가 빠지기 쉬운 함정이 들어 있습니다. 이걸 프롬프트에 "당신이 알고 싶은 것", "주의 포인트"로 풀어 넣습니다. 여기에 페르소나의 강도 디렉티브(보통)와 성격 스타일을 합치면, 그 시나리오·그 난이도에 맞는 고객 한 명이 완성됩니다. 시나리오가 8개, 난이도가 3개면 조합만으로 24가지 연습 상황이 나오는데, 프롬프트 본체는 하나의 템플릿을 재사용합니다.

이 구조에서 특히 신경 쓴 안전장치가 하나 있습니다. AI 고객이 프롬프트에 없는 정보를 지어내지 못하게 막는 것입니다. 시스템 프롬프트 말미에 "상대방이 방금 한 답변에 포함되지 않은 회사명·수치·사례를 추가하지 마세요"를 넣었습니다. 이게 없으면 AI 고객이 있지도 않은 경쟁사 견적이나 가짜 통계를 꺼내 들며 사업담당자를 혼란시켰습니다. 연습이 아니라 환각과 싸우는 꼴이 되니까요. 또 "당신이 알고 싶은 것 목록의 범위를 벗어나지 마세요"로 대화가 엉뚱한 주제로 새는 것도 막았습니다. 같은 원칙은 이 글 자체에도 적용해서, 인용한 프롬프트 예시의 사내 리터럴은 익명화해 옮겼고, 2026년 7월 시점의 개발 기록인 이 글에 회사·고객사·팀·인물을 특정할 수 있는 정보는 담지 않았습니다.


이 과정에서 세운 페르소나 프롬프트 원칙 5줄로 마무리하겠습니다.

1. 훈련용 AI 상대는 협조성을 억제하되, 강도를 난이도 축으로 쓰지 않는다. 현실의 고객은 대체로 '보통'이다.
2. 난이도는 상대의 공격성이 아니라 사용자에게 주는 힌트(scaffolding)의 양으로 조절한다.
3. 초기 지시는 대화가 길어지면 잊힌다. 흔들리면 안 되는 규칙은 매 턴 리마인더로 반복 주입한다.
4. 티나는 출력(직전 말 반복·리스트 낭독)은 금지어 목록으로 단어 수준에서 막는다.
5. 힌트는 답이 아니라 발판이다. 큐카드는 방향만 주고 문장은 사용자가 짓게 한다.

댓글

이 블로그의 인기 게시물

STT 인식률 93%의 함정 — 왜 그 숫자는 콜봇 목표가 될 수 없는가

AI 보이스봇이 답답한 이유: Cascaded 구조 레이턴시 완전 분석

음성 토큰(Audio Token)이란? LLM이 소리를 이해하는 원리