본문으로 건너뛰기

© 2026 Molayo

Dev.to헤드라인2026. 06. 20. 21:31

모든 AI 제공업체가 동일하다고 가정하는 것을 그만두었습니다

요약

AI 게이트웨이 설계 시 모든 모델이 동일하게 작동한다고 가정하는 오류를 지적하며, 단순 라우팅을 넘어 모델별 기능(capability)에 따른 프로토콜 번역의 중요성을 강조합니다. 저자는 이를 해결하기 위해 CliGate라는 로컬 컨트롤 플레인을 구축한 경험을 공유합니다.

핵심 포인트

  • 모델 간 API 호환성만으로는 실제 동작의 차이를 해결할 수 없음
  • 단순 전송(Transport)이 아닌 기능 라우팅(Capability Routing)이 필요함
  • 프로토콜 번역은 라우팅의 후속 단계가 아닌 라우팅의 핵심 과정임
  • 모델별 기능 프로필에 따른 의도적인 요청 정규화가 필수적임

AI 게이트웨이 (AI gateway)가 불안정하게 느껴지게 만드는 가장 쉬운 방법은 모든 업스트림 (upstream) 모델이 동일하게 작동한다고 가정하는 것입니다.

서류상으로는 많은 도구들이 호환되는 것처럼 보입니다.

그들은 모두 프롬프트 (prompt)를 받습니다. 그들은 모두 텍스트를 반환합니다. 그들 중 일부는 심지어 OpenAI 형태의 API를 공유하기도 합니다.

실제로는 사용자들이 더 이상 용서하지 않는 바로 그 지점에서 차이점이 나타납니다:

  • 특정 도구 전용 필드가 누락됨
  • 이미지 페이로드 (image payload)가 한 경로에서는 작동하고 다른 경로에서는 깨짐
  • 모델 전환 (model switch)이 조용히 동작을 변경함
  • 요청은 성공했지만, 잘못된 기능 세트 (capability set)가 가정됨

그것은 제가 Claude Code, Codex CLI, Gemini CLI, OpenClaw, 상주 어시스턴트, 그리고 하나의 localhost 엔트리포인트 (entrypoint) 뒤에 있는 여러 모델/계정 소스를 위한 로컬 컨트롤 플레인 (control plane)인 CliGate를 구축하면서 얻은 가장 유용한 교훈 중 하나였습니다.

버그는 "라우팅 실패"가 아니었습니다

버그는 그보다 더 미묘했습니다.

라우팅은 종종 성공했습니다. 요청이 어딘가로 전송되었습니다. 응답이 돌아왔습니다. 명백하게 충돌(crash)이 발생한 것은 없었습니다.

하지만 그것이 게이트웨이가 정확하다는 것을 의미하지는 않았습니다.

만약 당신이 서로 다른 도구와 제공업체들을 마치 교체 가능한 것처럼 라우팅한다면, 로그만으로는 찾아내기 어려운 일련의 실패들을 겪게 됩니다:

  • 단순 전달 (passthrough)이 아닌 번역 (translation)이 필요한 Claude 스타일의 페이로드
  • 지원되지 않는 필드를 맹목적으로 전달하는 대신 성능을 저하시켜야(degrade) 하는 Codex 호환 흐름
  • 자체적인 기능 가정 (capability assumptions)이 필요한 Gemini 경로
  • 도달할 수는 있지만 기능적으로 동일하지는 않은 로컬 또는 폴백 (fallback) 경로

이것은 단순한 전송 라우팅 (transport routing)이 아닙니다.

이것은 기능 라우팅 (capability routing)입니다.

나는 "어디로 보낼 것인가"와 "이 목적지가 실제로 무엇을 할 수 있는가"를 분리해야 했습니다

처음에는 라우팅이 단순히 다음과 같다고 생각하기 쉽습니다:

제공업체 선택 -> 요청 전송

그 모델은 너무 단순합니다.

CliGate에서 실제로 중요했던 것은 이와 더 가까웠습니다:

호출자/도구 식별
-> 프로토콜 형태 (protocol shape) 식별
-> 제공업체/모델 소스 해결
...

제공업체에 도달할 수 있다는 것만으로는 충분하지 않습니다.

또한 해당 모델이 실제로 지원하는 기능(features)에 따라 처리되어야 합니다.

번역이 라우팅(routing)의 일부임이 밝혀짐

이 프로젝트를 통해 얻은 더 유용한 내부적 교훈 중 하나는, 프로토콜 번역(protocol translation)이 라우팅 이후에 수행되는 별도의 정리 단계가 아니라는 점입니다.

그것은 라우팅의 _일부_입니다.

어떤 경로는 더 풍부한 요청 형태(request shape)를 수용할 수 있습니다. 어떤 경로는 요청이 조용한 버그(silent bug)가 되기 전에 필드를 정규화(normalized)하거나 제거(stripped)해야 합니다.

이로 인해 안전한 멘탈 모델(mental model)이 다음과 같이 바뀌었습니다:

“업스트림(upstream)에서 불평하지 않았으니, 이 경로는 괜찮을 것이다.”

에서:

“이 경로는 특정 기능 프로필(capability profile)을 지원하므로, 의도적으로 정규화한다.”

로 말입니다.

사소하게 들릴 수 있지만, 이는 많은 “가끔 작동하는(works sometimes)” 식의 동작을 방지해 줍니다.

“호환 가능한 API”는 호환 가능한 동작과 동일하지 않음

이것이 함정입니다.

많은 시스템이 익숙한 엔드포인트 형태(endpoint shape)를 수용하기 때문에 호환성을 광고합니다.

하지만 HTTP 계층에서의 호환성은 시작일 뿐입니다.

만약 한 도구는 더 풍부한 추론(reasoning)이나 메타데이터 의미론(metadata semantics)을 기대하는데, 다른 백엔드가 해당 필드들을 다르게 처리한다면, 게이트웨이(gateway)에는 세 가지 나쁜 선택지가 있습니다:

  • 모든 것을 그대로 통과(pass through)시켜 정의되지 않은 동작(undefined behavior)이 발생하게 두거나
  • 너무 공격적으로 거부(reject)하여 시스템이 취약하게(brittle) 느껴지게 하거나
  • 기능(capability)별로 정규화하여 동작을 예측 가능하게 유지하거나

오직 세 번째 방식만이 확장 가능(scale)합니다.

이것이 제가 이제 보편적인 패스스루(passthrough) 설계보다 기능 인식 라우팅(capability-aware routing)을 선호하는 이유입니다.

호출자 식별(Caller identity)은 예상보다 더 중요함

claude-code, codex, gemini-cli, openclaw, 그리고 일반적인 OpenAI/Anthropic 호환 클라이언트들은 비슷해 보이는 경로를 호출할 수 있지만, 운영자 관점에서는 서로 교체 가능하지 않습니다.

사용자는 종종 실제로 다음 중 하나를 요구합니다:

  • Claude Code를 Claude 스타일의 흐름에 적합한 제공업체/모델 경로에 유지하기
  • Codex를 특정 계정이나 키에 바인딩(bind)하기
  • Gemini가 자체적인 기능 프로필(capability profile)을 사용하도록 하기
  • 소스를 사용할 수 없을 때 안전하게 폴백(fall back)하기

이것이 앱 인식 라우팅(app-aware routing)과 기능 인식 번역(capability-aware translation)이 별개의 관심사가 아니라 상호 보완적인 관계로 귀결된 이유입니다.

하나는 이 요청이 누구를 위한 것인지를 결정합니다.

다른 하나는 그 과정에서 어떻게 진실되게(truthful) 만들 것인지를 결정합니다.

의도적으로 저하시키되, 결코 우연히 발생하지 않게 하라

가장 최악의 실패는 우연히 발생하는 실패입니다.

만약 게이트웨이(gateway)가 목적지에서 무시되는 필드를 조용히 전달한다면, 사용자는 왜 결과가 일관되지 않은지 영원히 알 수 없을지도 모릅니다.

그래서 저는 명시적인 저하(degradation) 규칙을 선호하기 시작했습니다.

만약 특정 경로(route)가 필드를 준수할 수 없다면, 의도적으로 정규화(normalize)하십시오.

만약 제공업체(provider)가 특정 기능(capability)을 맞출 수 없다면, 정직하게 매핑(map)하십시오.

만약 모델 소스(model source)가 속도 제한(rate-limited)에 걸렸거나 유효하지 않다면, 활성화된 것처럼 보이는 모든 자격 증명(credentials)이 동일한 척하는 대신 이를 건너뛰십시오.

그렇게 하면 운영자 측면에서 훨씬 더 나은 스토리를 얻을 수 있습니다:

  • 왜 이 소스가 선택되었는지
  • 어떤 기능 프로필(capability profile)이 적용되었는지
  • 어떤 필드가 변환되거나 제거되었는지
  • 왜 다른 경로를 사용했을 때 다르게 동작했는지

"완벽한 추상화"를 쫓는 것을 멈췄을 때 신뢰성이 향상되었습니다

좋은 게이트웨이(gateway)는 반복적인 설정 작업을 숨겨주어야 합니다.

하지만 기능(capability)의 차이에 대해 거짓말을 해서는 안 됩니다.

그 사실을 받아들인 후, 아키텍처는 더 깔끔해졌습니다:

  • 앱 및 프로토콜(protocol)에 따른 경로 지정(route)
  • 제공업체(provider)/모델 소스(model source)에 따른 매핑(map)
  • 기능 프로필(capability profile)에 따른 번역(translate)
  • 로그와 설정에서 차이점을 명확하게 노출

이 방식은 덜 마법 같지만, 훨씬 더 신뢰할 수 있습니다.

내가 유지할 규칙들

만약 내일 다시 AI 게이트웨이(AI gateway)를 설계한다면, 저는 다음 규칙들을 유지할 것입니다:

  1. API 형태(shape)를 기능적 동등성(feature equivalence)과 동일시하지 말 것
  2. 호출자 신원(caller identity)을 일급 라우팅 입력(first-class routing input)으로 만들 것
  3. 번역(translation)을 라우팅(routing)의 일부로 취급할 것
  4. 지원되지 않는 필드는 의도적으로 저하시킬 것
  5. 운영자가 실패 원인을 설명할 수 있도록 기능(capability) 결정 사항을 노출할 것

이것이 제가 CliGate를 통해 밀어붙여 온 방향입니다.

이 프로젝트는 여전히 모델 라우팅(model routing), 계정, API 키, 로컬 런타임(local runtimes), 채널, 런타임 세션, 그리고 어시스턴트 레이어(assistant layer)를 위한 하나의 로컬 장소를 제공하는 것을 목표로 합니다.

하지만 모든 업스트림 제공업체(upstream provider)가 동일한 척하는 것을 멈추자, 시스템은 훨씬 더 신뢰할 수 있게 되었습니다.

만약 하나의 게이트웨이(gateway)를 통해 여러 AI 도구를 실행한다면, 단순히 엔드포인트 라우팅 (endpoint routing)을 수행하고 계신가요, 아니면 실제 역량 (capability)에 따른 라우팅을 수행하고 계신가요?

AI 자동 생성 콘텐츠

본 콘텐츠는 Dev.to AI tag의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.

원문 바로가기
0

댓글

0