SeungjoonLee 14 1
게시물 메뉴

안녕하세요! 퇴근 후 "되묻고 깊어지는 상담 대화"를 목표로 주역 상담 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단계 통합을 준비 중입니다.

  1. 차단 문구 검토: 기계적인 검증이 불가능한 '위기/주의 차단 메시지'는 실제 관련 경험이 있는 분이나 전문 상담사에게 피드백을 받아 정교하게 다듬을 예정입니다.
  2. diff.md 전수 검수: 판정 어법이 뒤집히거나 왜곡된 49건의 데이터 추가 대조.
  3. 법률적 명문화: 이용약관 및 인앱 안전 고지문 작성.
  4. 크레딧 관리: 5단계 테스트 중 에이전트의 반복 호출로 개발 크레딧이 전량 소진되는 현상을 겪었습니다. 프로덕션 환경의 캐싱 정책을 더 예리하게 벼려야겠습니다.

 

💬 바이브코더분들께 드리는 질문!

  1. LLM이 출력물 서두에 반복해서 쓰는 고유의 머리말(클리셰)을 프롬프트 예시 제거 외에 효과적으로 분산시켜 보신 팁이 있으신가요?
  2. Pydantic 스키마 처리 시 API에서 null 값이나 예외 형태의 유실 데이터를 예측하고 방어하는 파이프라인 설계 꿀팁이 있다면 아낌없이 댓글로 훈수 부탁드립니다! 👇