DEV Community

finaltype
finaltype

Posted on Originally published at finaltype.github.io

[260706] 로컬 LLM 3종 비교에서 찾아낸 숨은 실패 패턴들

등급 분포만 봐서는 안 보였던 두 가지 실패 유형, 그리고 프레임워크 마이그레이션으로 잡은 구조화 출력 문제

프로젝트 초반이라 하루 단위로 바뀌는 게 많습니다. 매일 올리진 못하겠지만, 굵직한 변화가 있었던 날은 이렇게 짧게 남겨보려고 합니다. 오늘은 세 가지 축으로 작업했습니다.

1. 로컬 LLM 3종 정식 비교

멀티에이전트 LLM 리서치 파이프라인에 쓸 로컬 모델을 하나 골라야 해서, 후보 3개를 같은 조건(같은 종목 목록, 같은 날짜)으로 순차 실행해서 비교했습니다.

비교는 두 단계로 했습니다.

  • 자동 확인: 실패율, 최종 등급이 한쪽으로 쏠리는지, 다른 종목명이 섞여 들어오는지, 소요 시간
  • 수동 스팟체크: 실제 생성된 글의 완성도, 등급과 근거가 실제로 맞는지 하나씩 열어서 확인

여기서 흥미로운 걸 발견했는데, 표면적인 지표만 봐서는 안 잡히는 문제가 두 가지 있었습니다.

  • 한 모델은 최종 등급의 대부분이 "보류(Hold)"로 쏠려 있었는데, 이유가 "신중해서"가 아니라 최종 판단 문장 생성 자체가 꽤 높은 확률로 실패해서 빈 값이 기본 등급으로 떨어진 것이었습니다. 등급 분포표만 봤으면 그냥 "이 모델은 보수적이네" 하고 넘어갔을 문제입니다.
  • 다른 모델은 겉보기엔 지표가 다 깨끗했는데, 종목명을 교차 검증해보니 낮은 확률로 완전히 다른 회사를 분석해서 결론을 내는 환각이 있었습니다. 등급 쏠림보다 훨씬 조용하고, 훨씬 위험한 실패 유형이라고 판단해서 탈락시켰습니다.

결국 두 후보 다 탈락하고 기존에 쓰던 모델을 유지하기로 했습니다. 지표만 믿지 않고 실제 출력을 까봐야 하는 이유를 다시 확인한 하루였습니다.

2. LLM 프레임워크 자체를 원본으로 갈아탐

멀티에이전트 리서치에 쓰는 프레임워크를 커스텀 포크에서 원본 오픈소스 프로젝트로 옮겼습니다. 계기는, 더 가벼운 모델로 테스트하던 중 최종 판단 단계에서 구조화된 출력(JSON 등)이 자꾸 파싱에 실패하는 문제를 겪은 것이었습니다.

원인을 추적해보니, 로컬 LLM 서버(Ollama)의 OpenAI 호환 API가 "이 형식으로만 답해줘"라는 요청을 사실상 무시하는 알려진 이슈가 있었습니다. 프레임워크가 이 경로로 모델을 호출하고 있어서, 모델이 가끔 정해진 형식 대신 자유 문장으로 답하면 그대로 실패로 이어졌던 것입니다.

우회 방법은 같은 서버의 네이티브 API를 쓰는 것이었습니다. 이쪽은 출력 형식 자체를 문법 수준에서 강제할 수 있어서, 같은 테스트를 돌렸을 때 마이그레이션 전엔 구조화 출력이 매번 실패해서 자유 텍스트로 겨우 통과했던 것이, 마이그레이션 후엔 안정적으로 통과했습니다.

3. 자동투자 기능 다듬기 + 배포 전 버그 하나 잡음

AI가 추천한 종목들에 금액을 자동으로 나눠 담아주는 기능을 손봤습니다. 배분 방식을 조금 더 정교하게 바꿨고, 매수 직전에 실시간 호가 스프레드가 비정상적으로 넓으면(유동성 문제 신호) 자동으로 매수를 보류하고 별도로 알려주는 안전장치도 추가했습니다.

리밸런싱 알림 로직도 몇 가지 손봤는데, 이 과정에서 배포 전에 잠재적인 버그를 하나 발견했습니다. 알림 조건을 바꾸면서 실제 주문을 넣는 부분이 예전 계산 방식을 그대로 참조하고 있다는 걸 놓칠 뻔했는데, 검토 중에 잡아서 실제 계좌에 반영되기 전에 고쳤습니다.

4. 인프라 버그 두 건

  • 무거운 계산 작업이 GPU를 쓰고 있는지 판단하는 로직에 구멍이 있어서, 특정 상황(임시 벤치마크 실행 중)에는 그 판단을 놓치고 GPU 메모리 부족으로 실제 크래시가 났습니다. 이미 다른 곳에 만들어뒀던 범용 체크 함수가 있었는데 이 스크립트만 그걸 안 쓰고 있었던 게 원인이라, 적용해서 재발을 막았습니다.
  • 로컬 LLM 하나가 리포트 작성을 끝낸 뒤에도 GPU 메모리를 계속 붙잡고 있는 문제가 있었습니다. 명시적으로 캐시를 비우는 호출을 이미 하고 있었는데도 안 풀려서, 근본 원인은 아직 확실치 않지만 프로세스를 재시작하면 확실히 회수된다는 걸 확인했고, 이후 작업 시작 전 메모리 여유를 자동으로 체크해서 필요하면 재시작하는 안전장치를 넣었습니다.

그 밖에

.env 같은 민감 설정 파일의 권한을 더 엄격하게 조정했습니다.


오늘은 이 정도로 정리합니다. 앞으로도 눈에 띄는 변화가 있으면 이런 형태로 가볍게 남겨볼 예정입니다.

Top comments (0)