ClawMux 라우팅 코어 완성과 분류기 안정화

26년 04월 04일

라우팅 코어 완성

며칠간 ClawMux를 만들어왔는데, 이날이 핵심 인프라를 완성하고 분류기를 안정화한 마일스톤이었다.

라우팅 코어를 마무리하고 며칠간 요동치던 분류기를 하나로 확정했다.

ClawMux는 LLM 에이전트 플랫폼과 업스트림 프로바이더 사이에 끼우는 프록시다.

핵심 문제는 포맷이 다르다는 거였다.

에이전트 플랫폼은 Anthropic 포맷으로 동작하는데, 연결하려는 프로바이더는 OpenAI 호환 API만 제공했다.

포맷이 다르면 그냥 연결할 수 없다.

그래서 ClawMux가 그 사이에서 세 가지 일을 한다.

요청 복잡도를 분류해서 싼 모델이랑 비싼 모델로 라우팅하고 Anthropic과 OpenAI 포맷을 양방향으로 변환하고 대화가 길어지면 컨텍스트를 압축한다.

이날 그 라우팅 코어를 완성했다.

6개 API 포맷 어댑터를 만들었다.

Anthropic, OpenAI Completions, OpenAI Responses 같은 포맷을 지원한다.

크로스 프로바이더 라우팅도 구현했다.

들어온 요청이 Anthropic 포맷이면, 업스트림이 OpenAI 계열일 때 변환 플래그를 세우고 변환을 수행한다.

세션 스토어, CJK 토큰 추정기, 설정 핫리로드, 설치 및 제거 스크립트까지 같이 만들었다.

프록시 하나가 제 구실을 하려면 이런 주변 인프라가 다

갖춰져야 한다.

7단계 파이프라인

요청이 ClawMux를 통과하는 흐름은 7단계로 정리됐다.

요청이 들어오면 먼저 복잡도를 분류한다.

그 다음 분류 결과에 따라 어느 프로바이더의 어느 모델로 보낼지 정한다.

포맷이 다르면 변환 플래그를 세운다.

Anthropic 포맷을 OpenAI 포맷으로 바꾸고 업스트림을 호출하고 응답을 다시 Anthropic 포맷으로 역변환해서 반환한다.

여기서 핵심은 이중 변환이 선택이 아니라는 거다.

업스트림 프로바이더가 OpenAI 호환 API만 제공하니까, Anthropic 포맷을 그대로는 보낼 수 없다.

변환이 ClawMux의 존재 이유 자체다.

분류기 진화의 끝

분류기는 며칠간 세 번이나 바뀌었다.

이날 그 요동을 끝내고 하나로 확정했다.

처음엔 14차원 키워드 점수 계산기였다.

단어가 같아야 점수가 오르니까 정확도가 낮았다.

그래서 로컬 임베딩 모델로 바꿨다.

의미 기반으로 분류하니까 키워드보다는 나았지만 경량 모델의 한계가 있었다.

그래서 결국 LLM 분류기로 왔다.

LLM 한 글자 분류기

최종 안은 싼 LLM에게 한 글자만 답하게 하는 거였다.

요청을 네 단계로 분류한다.

L은 단순, M은 보통, H는 복잡, Q는 컨텍스트 부족. 이 네 글자 중 하나만 반환한다.

추론은 끄고 최대 토큰 수를 1로 잡았다.

분류에 쓰는 토큰을 최소화한 거다.

비용을 최소화하면서도 키워드랑 임베딩 둘 다

이기는 정확도를 얻었다.

reasoning 비활성화

여기서 한 가지 더 만든 게 reasoning 비활성화 모듈이다.

LLM이 분류할 때 추론에 토큰을 쓰면 비용이 들고 속도도 느려진다.

그래서 6개 API 포맷별로 reasoning 비활성화 파라미터를 주입하는 모듈을 만들었다.

분류기 LLM이 추론에 토큰을 낭비하지 않게 막는 거다.

실패를 숨기지 않는다

분류기 설계에서 중요한 원칙이 하나 있었다.

실패를 조용히 넘기지 않는 거다.

포맷 에러가 나면 피드백을 주고 최대 두 번 재시도한다.

그래도 실패하면 503 에러를 반환한다.

조용히 기본값으로 폴백하지 않는다.

이게 왜 중요하냐면, 분류를 못 하면 잘못된 라우팅이 조용히 일어난다.

단순한 요청을 비싼 모델로 보내거나, 복잡한 요청을 싼 모델로 보내는 식이다.

그런 잘못된 라우팅이 조용히 일어나는 걸 막으려면, 분류 실패를 명시적으로 드러내야 한다.

품질 기준

이날 코드 품질 기준도 꽤 엄격하게 잡았다.

타입체크 에러 0개, 테스트 564개 전부 통과. 타입 단언이랑 빈 catch 블록도 0건이었다.

분류기 외부 의존성도 없앴다.

외부 의존성이 없으니까 분류기 자체는 자급자족한다.

컨텍스트 압축 시스템도 같은 시기에 완성됐다. 대화가 길어지면 자동으로 압축을 트리거하고 구조화된 프롬프트로 요약 품질을 검증한다.

필수 섹션 여섯 개를 확인하고 실패하면 피드백과 함께 최대 세 번 다시 만든다.

분류기가 라우팅을 담당한다면, 압축은 긴 대화를 유지하는 담당이다.

둘이 합쳐서 ClawMux의 양 축이 됐다.

마무리

며칠간 요동치던 분류기가 드디어 하나로 확정됐다.

키워드에서 임베딩으로, 임베딩에서 LLM으로. 세 번의 시도 끝에 가벼운 LLM이 내놓는 한 글자가 가장 정확한 분류를 만들어냈다.

복잡한 걸 쓰려다가 결국 가장 단순한 방식이 이긴 셈이다.

싼 모델에게 “한 글자로 답해”라고 하는 게, 14개 차원의 키워드 점수보다도, 118메가바이트짜리 임베딩 모델보다도 나은 결과를 냈다.

비용도 싸고 정확도도 높은, 양쪽을 다

잡은 해법이었다.

그리고 라우팅 코어가 완성되니까 ClawMux가 제 구실을 하기 시작했다.

들어온 요청을 분류하고 포맷을 변환하고 업스트림으로 보내고 응답을 다시 변환해서 돌려주는 전체 흐름이 하나로 이어졌다.