서버 앞에 뭘 하나 더 만들려다 제지당했다
범용 엔드포인트를 신설하려는 걸 끊었다. 그리고 그 김에 프로젝트 하나를 접기로 했다
하려던 것
공시 말고 법안·회의록 요약도 하고 싶어졌다. 같은 모델을 쓰면 되는 일이다.
그런데 에이전트가 내놓은 안은 이거였다. 공시 요약 서비스에 범용 /api/generate 엔드포인트를 신설한다. 그리고 이미 구현 중이었다. 끊었다.
“이건 모델 서버 쪽에서 구현되어야 하는 거라고 생각한다. 공시 서비스는 이런 거 신경 안 써도 된다.”
어디서 막혔나
왜 만들려고 했는지는 이해가 간다. “여러 도메인이 LLM을 쓴다 → 공통 진입점이 필요하다”는 자연스러운 추론이고, 공시 서비스에 이미 모델을 부르는 코드가 있으니 거기에 하나 더 얹는 게 제일 빨라 보인다.
문제는 그게 이미 있다는 거다. vLLM이 OpenAI 호환 /v1/chat/completions를 그대로 제공한다. 법안 요약 서비스가 자기 시스템 프롬프트를 들고 그걸 직접 부르면 끝난다.
래퍼를 하나 두면 이렇게 된다.
- 응답 포맷이 래퍼 스키마에 고정된다 → 다른 도메인이 쓰기 불편해짐
- vLLM이 새 기능을 내놔도 래퍼를 고쳐야 쓸 수 있다
- 유지보수 면적이 는다. 그것도 도메인 서비스 안에 는다
서버를 따로 뺀 이유가 정확히 이거였다. 경계를 지키자고 만든 구조인데 2주 만에 경계 안쪽에 우회로가 생길 뻔했다.
어떻게 풀었나
규칙 세 줄로 못 박았다. 새 LLM 기능을 만들기 전에:
- 표준
/v1/chat/completions로 직접 되나? → 되면 만들지 마라 - 도메인 로직이 진짜 복잡한가(구조화 파싱, 검증, 복수 호출 오케스트레이션)? → 아니면 만들지 마라
- 여러 도메인이 공유할 공통 로직인가? → 그때만 공용 래퍼를 고려
그리고 이 생각이 오늘 한 단계 더 나갔다. 공시 요약 서비스 자체가 필요 없는 것 같다고, 프롬프트까지 서비스 API가 직접 들고 처리하면 되지 않냐고 물었다. 원래 여기서 하려던 게 뭐였는지 되짚어보니 “도메인 로직 + FAISS 검색 + 프롬프트 관리”였는데, 하나씩 없어져 있었다. 프롬프트는 호출부가 들고 있는 게 낫고, API는 이미 Go로 갈아탔고, FAISS는 쓰는 데가 없다.
“FAISS/RAG 이것들의 사용처가 없어. 그리고 멀리 내다 봤을 때에도 FAISS 검색이 공시 앱에 필요할까?”
여기 임베딩은 multilingual-e5-large로 이미 서버에 올려 뒀는데, 정작 공시는 키로 조회하는 데이터다. “이 회사의 이번 분기 공시”는 종목코드와 기간으로 찾는 거지 의미 유사도로 찾는 게 아니다. 벡터 검색을 끼우면 정확한 조회가 부정확해지기만 한다. RAG는 “비슷한 걸 찾을 때” 쓰는 거지 “정확한 걸 찾을 때” 쓰는 게 아니다.
그래서 걷어내기로 했다.
지금까지 정리
모델 서버를 세운 지 18일 됐다.
만들어서 좋았던 것 — 모델 서버를 앱에서 분리한 것, 첫날 붙인 모니터링(두 번 살렸다. 로그가 없는데 지표가 있었고, 장비가 죽었을 때 분 단위 궤적이 남아 있었다), earlyoom과 학습 프로토콜.
안 만들어서 좋았던 것 — 범용 래퍼, 콜백 큐/gRPC 레이어, FAISS 검색, 공시 전용 서비스 자체.
만들었는데 헛수고였던 것 — 파인튜닝 5일.
배운 점
- 개선의 절반이 “빼는 것”이었다. 그리고 뺀 것 중 상당수는 내가 만들려던 게 아니라 에이전트가 자연스럽게 제안한 것이었다.
- 에이전트는 기본적으로 뭔가 만드는 쪽으로 기운다. 물어보면 만들 이유를 잘 찾아 준다. 그래서 요즘 내 역할은 대체로 “그래서 뭐가 좋아지는데?”라고 묻는 거다.
- 이미 있는 표준 인터페이스 위에 자기 스키마를 얹지 마라. 얹는 순간 상류 업데이트가 내 일이 된다.
- RAG는 유사도 문제에만 쓴다. 키로 찾을 수 있는 걸 임베딩으로 찾으면 손해다.
위의 판단 기준 세 줄은 지금 메모에만 적어 뒀다. 프로젝트 문서에 박아 놔야 하는데 아직 안 했다.