안녕하세요! 퇴근 후 "되묻고 깊어지는 상담 대화"를 목표로 주역 상담 AI를 만들고 있는 직장인 바이브코더입니다.
이번 7일 차 작업에서는 단순한 '동작 여부'를 넘어, 실제 제품 출시를 위해 품질을 어떻게 계량화하고 디버깅하는지에 대한 날것의 기록을 나눕니다. 3개의 커밋, 4개 파일 변경(+132 / -13), 그리고 87건의 테스트를 올패스한 디버깅 일지입니다. 대략 이걸위해 약 이틀동안 퇵근후 계속 손봤던거 같네요
My Vibe Coding Stack
- Local Dev: Mac Mini M4 Pro + Ollama + Docker Compose
- Database: PostgreSQL 16 + pgvector (iching-db)
- Model (Production Live): Gemini 2.5 Flash (Vertex AI)
1. [해결] 프롬프트 예시 문장이 부른 화근: 상투구의 악순환
기존 prompts/counsel.md에 단정적 예언을 방지하기 위해 예시로 들어둔 문장("주역에서는 지금의 상황을 ~한 흐름으로 봅니다")이 화근이었습니다. 모델이 이를 서식(Template)으로 오인하여 전체 상담의 무려 45%(15/33턴)에서 조사까지 똑같이 출력해 버리는 버그를 발견했습니다. 전혀 다른사례, 다른 괘, 그리고 다른 질문이었음에도 불구, 특정한 문구(지금은 잠시 멈추고 되돌아봐야할때) 라는 언급이 동일하게 나오더군요
이에 예시 문장 한 줄을 삭제하고 전후 지표를 정교하게 실측했습니다.
| 지표 | 수정 전 | 수정 후 | 판정 |
|---|---|---|---|
| 프롬프트 예시 문자열 그대로 재생산 | 15 / 33 | 2 / 33 | −87% (개선) |
| 1턴 1질문 위반 (물음표 2개 이상) | 3 / 30 | 1 / 30 | 개선 |
| 근거 문단을 "주역…지금…"으로 시작 | 19 / 33 | 15 / 33 | 거의 그대로 |
| 근거 탈락 (본문 턴) | 1 / 30 | 1 / 30 | 변화 없음 |
| 질문이 답변 끝에 누락 | 8 / 30 | 8 / 30 | 변화 없음 |
💡 뼈아픈 교훈: 예시를 지웠더니 상투구 재생산은 87%나 사라졌지만, 모델이 빈자리를 채우기 위해 스스로 "주역의 지혜는~" 이라는 새로운 상투구를 지어냈습니다 (4회 ➔ 14회로 폭증).
결국 프롬프트 예시를 정교하게 깎는 행위는 증상 치료일 뿐이었습니다. 원인은 "관점 하나 + 질문 하나"를 매 턴 강제하는 아키텍처의 제약 구조에 있었습니다. 이 구조를 바꾸지 않으면 모델은 언제나 자신만의 클리셰로 도망치게 됩니다.
2. [해결] 정상 턴이 지연 에러 페이지로 끝나던 백엔드 버그
잘 진행되던 상담이 가끔 원인 모를 "서비스 연결 지연" 에러로 튕기는 치명적인 버그가 있었습니다. 네트워크 문제가 아닌 백엔드 Pydantic 스키마 처리 문제였습니다.
- 원인:
agents/safety.py에서data.get("reason", "")로 검증 값을 꺼내고 있었습니다. 문제는 파이썬의.get기본값은 해당 키 자체가 '없을 때만' 작동한다는 것입니다. Gemini API는 때때로 키(reason)는 포함하되 값만null로 보냈고, 이로 인해None이 그대로 유출되어 Pydantic 스키마 검증에서ValidationError를 발생시켰습니다. - 결과: 예외 처리기가 작동해 API 에러로 오판하고 정상 상담 턴에 "지연 안내" 차단 문구를 내보냈던 것입니다.
| 검증 항목 | 수정 전 | 수정 후 |
|---|---|---|
| 실제 모델 재현 테스트 (history 포함 5회) | 5 / 5 ERROR | 5 / 5 정상 |
| 위기 발화 판정 (history 유무 각 3건) | 6 / 6 정상 | 6 / 6 정상 |
| 회귀 테스트 (NORMAL · BLOCK_CRISIS 경로) | 2건 실패 | 2건 통과 |
이 에러는 대화의 2턴째 이후(history가 누적된 상태)에서만 약 1/33 확률로 터졌습니다. 정교하게 예외 처리 로직을 수정하여 완전히 박멸했습니다.
📊 품질 지표 분석: 의미(Semantics) vs 어휘(Vocabulary)
품질을 어휘와 의미의 두 트랙으로 쪼개서 지표를 측정한 결과, 재미있는 불일치 현상을 관찰했습니다.
🟢 의미(Meaning) 측면: 대폭 개선
서로 다른 괘가 같은 뉘앙스의 뻔한 말로 수렴하던 환각 문제가 완전히 사라졌습니다. 이제 모델은 괘 이름표를 떼고 효사의 구체적인 은유와 문맥을 활용합니다.
- 58번 태괘(兌) 동효 2 ➔ 孚兌 (믿음에서 오는 기쁨)
- 36번 명이괘(明夷) 동효 3 ➔ 不可疾貞 ("급히 바르게 할 수 없다")
- 61번 중부괘(中孚) 동효 2 ➔ 鳴鶴在陰 · 好爵 (그늘에서 우는 학, 좋은 술잔) 엉뚱한 괘 원문이 교차 오염되는 현상은 0건이었습니다.
🔴 어휘(Vocabulary) 측면: 클리셰의 이동
머리말의 편중도를 측정하기 위해 시작 어절 길이를 가변으로 측정했습니다.
| 기준 | 수정 전 최다 문구 | 수정 후 최다 문구 | 판정 |
|---|---|---|---|
| 2어절 일치율 | 51% (주역에서는 지금의) | 46% (주역의 지혜는) | 거의 그대로 |
| 3어절 일치율 | 48% | 28% | 개선 |
| 4어절 일치율 | 9% | 25% | 악화 |
| 머리말 총 가짓수 | 26가지 | 19가지 | 줄어듦 (획일화) |
모델은 클리셰를 고친 게 아니라 다른 클리셰로 '갈아탔을' 뿐입니다. 심지어 4어절까지 완벽하게 일치하는 머리말 비율이 25%로 늘었습니다. AI가 자기가 지어낸 문장 틀을 더 규칙적으로 지키고 있는 셈입니다.
최종 평가: 당초 문제는 해결했습니다. 다른 사례에 대해서 괘를 근거로 답하는건 확인했습니다. 문장 어휘는 해당 서비스 특유의 아이덴티티로 생각할 수도 있을거 같아 일단 넘어가기로 했습니다.
🔮 다음 여정 및 6가지 남은 과제
지표 측정 과정에서 새롭게 수집된 6건의 미해결 이슈를 벡로그에 담고 8단계 통합을 준비 중입니다.
- 차단 문구 검토: 기계적인 검증이 불가능한 '위기/주의 차단 메시지'는 실제 관련 경험이 있는 분이나 전문 상담사에게 피드백을 받아 정교하게 다듬을 예정입니다.
diff.md전수 검수: 판정 어법이 뒤집히거나 왜곡된 49건의 데이터 추가 대조.- 법률적 명문화: 이용약관 및 인앱 안전 고지문 작성.
- 크레딧 관리: 5단계 테스트 중 에이전트의 반복 호출로 개발 크레딧이 전량 소진되는 현상을 겪었습니다. 프로덕션 환경의 캐싱 정책을 더 예리하게 벼려야겠습니다.
💬 바이브코더분들께 드리는 질문!
- LLM이 출력물 서두에 반복해서 쓰는 고유의 머리말(클리셰)을 프롬프트 예시 제거 외에 효과적으로 분산시켜 보신 팁이 있으신가요?
- Pydantic 스키마 처리 시 API에서 null 값이나 예외 형태의 유실 데이터를 예측하고 방어하는 파이프라인 설계 꿀팁이 있다면 아낌없이 댓글로 훈수 부탁드립니다! 👇
점점 전문화되어 가니까 이제 뭘 피드백 드려야 할지보다 같이 고민하는 단계에 이른 것 같습니다 ㅋㅋㅋ
특히 이번 내용은 엄청 공감합니다. 모델을 너무 깎고 제약하다 보면 결국 정해진 규칙 안에서만 움직이는 룰베이스처럼 되고, 빠르게 변하는 환경을 못 따라가면서 과거의 답을 반복하게 되더라고요.
저도 이것저것 해보면서 차라리 모델은 모델답게 쓰는 게 낫다는 쪽입니다. 억지로 작은 모델을 깎아서 맞추기보다 더 발전된 모델을 쓰고, 모델이 가진 판단력과 유연성을 제대로 활용하는 쪽이 결과도 좋았고요.
읽다 보면 “나라면 이걸 어떻게 했을까?” 계속 생각하게 됩니다. 저도 서비스를 만들면 똑같이 고민해야 할 내용들이라 쉽게 답이 안 나네요 😂
그래도 이렇게 하나하나 고민해서 만든 서비스는 나중에 직접 써보면 확실히 다르더라고요. 겉으로 잘 안 보여도 디테일과 완성도에서 티가 납니다.
계속 과정 공유해주세요. 이제는 피드백보다 같이 고민하면서 보고 있습니다 ㅎㅎ