PHP에서의 LLM Provider Fallback: Neuron AI Router의 자동 페일오버 (Automatic Failover)
요약
Neuron AI Router를 사용하여 PHP 기반 AI 에이전트에서 LLM 제공자의 장애 발생 시 자동으로 다음 제공자로 전환하는 페일오버 전략을 소개합니다. 속도 제한이나 타임아웃 같은 일시적 오류 발생 시 대화 상태를 유지하며 투명하게 재시도하는 방법을 다룹니다.
핵심 포인트
- LLM 제공자의 장애는 에이전트 애플리케이션의 핵심 기능 중단을 초래함
- 단순 재시도(retry) 방식은 제공자 자체의 장애 상황에서 효과가 낮음
- Neuron AI Router는 지정된 목록에 따라 자동으로 다음 제공자로 전환함
- 페일오버 과정이 투명하게 이루어져 에이전트의 상태를 유지할 수 있음
제가 Neuron AI Router에 관한 첫 번째 기사를 게시했을 때, 라우팅 규칙(routing rules)에 대한 질문들이 나올 것이라 예상했습니다. 구조화된 출력(structured output)을 위해 어떤 규칙을 사용해야 하는지, 커스텀 규칙을 어떻게 작성하는지, 부하 상황에서 라운드 로빈(round robin)이 어떻게 동작하는지 같은 질문들 말이죠. 그런 질문 중 일부가 도착했지만, 가장 빈번했던 질문은 달랐으며, 사실 라우팅에 관한 것도 아니었습니다. 그것은 바로 장애(failure)에 관한 것이었습니다. 제공자(provider)가 다운되면 내 에이전트(agent)는 어떻게 되나요?
이는 타당한 질문이며, AI 애플리케이션 구축이 처음이라면 코드를 살펴보기 전에 적절한 답변을 들을 가치가 있습니다.
요약하자면 이렇습니다. Neuron AI Router의 새로운 폴백(fallback) 전략을 사용하면 PHP 에이전트를 위한 LLM 제공자(LLM providers)의 순서가 지정된 목록을 정의할 수 있습니다. 속도 제한(rate limit), 타임아웃(timeout), 또는 과부하된 서버와 같은 일시적인 오류(transient error)로 인해 추론 호출(inference call)이 실패하면, 동일한 요청이 목록의 다음 제공자에서 자동으로 재시도됩니다. 이 페일오버(failover)는 투명하게 이루어집니다. 에이전트는 이 일이 일어났다는 사실을 전혀 알지 못하며, 대화는 상태(state)를 잃지 않고 계속됩니다. 이 글의 나머지 부분에서는 왜 이런 문제가 존재하는지, 왜 일반적인 해결책들이 부족한지, 그리고 이를 어떻게 구성하는지 설명합니다.
프로덕션 환경에서 LLM 제공자가 실패하는 이유
LLM 제공자는 HTTP를 통해 통신하는 외부 서비스입니다. 에이전트가 생각할 때마다, 여러분은 직접 제어할 수 없는 기계에 네트워크 호출을 보내며, 그 기계는 현재 수백만 개의 다른 요청을 처리하고 있는 회사에 의해 운영됩니다. 이러한 서비스들은 매우 평범한 방식으로 실패합니다. 트래픽이 급증하여 속도 제한(rate limit)에 걸리기도 합니다. 제공자의 트래픽이 급증하여
전형적인 웹 애플리케이션에서 제3자 API 호출이 실패하는 것은 보통 시스템의 구석진 문제(corner case)에 불과합니다. 로그를 남기고, 아마도 큐(queue)에서 재시도(retry)를 수행하면 나머지 페이지는 여전히 정상적으로 작동합니다. 하지만 에이전트 기반 애플리케이션(agent based application)에서 추론(inference) 호출은 구석진 문제가 아니라 심장부입니다. 호출이 실패하면 해당 기능 자체가 중단됩니다. 그리고 에이전트는 미묘한 방식으로 상황을 악화시킵니다. 에이전트가 도구 실행(tool executions), 추론 단계(reasoning steps), 후속 메시지(follow up messages)를 루프(loop)하며 진행되기 때문에, 단 한 번의 사용자 요청이 수많은 추론 호출을 트리거할 수 있기 때문입니다. 루프의 매 반복(iteration)은 제공자(provider) 측의 좋지 않은 순간을 마주할 또 다른 기회가 됩니다. 에이전트가 더 유능해질수록, 노출되는 위험도 더 커집니다.
본능적인 해결책은 재시도(retry)를 포함한 try/catch 문을 사용하는 것입니다. 이는 단일 타임아웃(timeout) 상황에는 도움이 되지만, 구조적인 약점이 있습니다. 제공자가 5분 동안 상태가 좋지 않을 때, 동일한 제공자에게 재시도하는 것은 단지 더 느리게 실패할 뿐이라는 점입니다. 그 순간 실제로 원하는 것은 다른 제공자입니다. 그리고 이 지점이 과거에는 매우 불편했던 부분입니다. 왜냐하면 제공자를 전환한다는 것은 에이전트의 두 번째 설정을 유지하거나, 실패 상황을 중심으로 애플리케이션 코드를 분기(branching)해야 함을 의미했기 때문입니다. 실패는 에이전트 실행의 깊은 내부, 즉 도구 루프(tool loop) 중간에서 발생하므로, 이를 애플리케이션 레벨에서 잡아 다시 시작하는 것은 에이전트가 이미 수행한 모든 작업을 버리는 결과를 초래합니다.
제공자 경계에서의 자동 페일오버 (Automatic failover)
제가 이 내용을 router 패키지의 맥락에서 작성하는 이유는, router가 이 문제를 해결하기에 정확히 적절한 레이어(layer)에 이미 위치하고 있기 때문입니다. RouterProvider는 모든 Neuron 제공자와 동일한 계약(contract)인 AIProviderInterface를 구현하는 프록시(proxy)입니다. 이는 에이전트와 외부 세계 사이의 경계에서 모든 chat(), stream(), structured() 호출을 가로챕니다(intercept). 만약 그 경계에서 제공자가 실패한다면, router는 에이전트가 아무 일도 일어났다는 것을 전혀 모르는 상태에서 에러를 포착하고 정확히 동일한 요청을 다른 제공자에게 전달할 수 있는 시스템 내 유일한 컴포넌트입니다.
그것이 바로 새로운 폴백 (Fallback) 전략이 수행하는 역할입니다. 사용자는 제공자(Provider)의 순서가 지정된 목록을 정의합니다. 라우터는 첫 번째 제공자에게 요청을 보냅니다. 만약 호출이 일시적인 오류 (Transient error)로 인해 실패하면, 요청은 목록의 다음 제공자에게 투명하게(transparently) 전달됩니다. 에이전트(Agent)는 아무 일도 일어나지 않은 것처럼 응답을 받게 됩니다. 도구 루프 (Tool loop)는 계속 실행되며, 대화 상태 (Conversation state)는 전혀 영향을 받지 않습니다.
여기서 '일시적인 (Transient)'이라는 단어가 중요합니다. 모든 오류가 재시도할 가치가 있는 것은 아닙니다. 만약 API 키가 유효하지 않거나 요청 형식이 잘못되었다면 (Malformed), 동일한 요청을 동일한 제공자에게 다시 보내더라도 똑같이 실패할 것이며, 라우터는 그렇지 않은 척하며 시간을 낭비하지 않습니다. 속도 제한 (Rate limits), 타임아웃 (Timeouts), 그리고 서버 측 오류 (Server side errors)는 이야기가 다릅니다. 이러한 오류들은 특정 제공자의 특정 시점과 결부된 것이므로, 동일한 요청이 지금 당장 다른 곳에서는 성공할 가능성이 높습니다. 폴백 로직은 오직 이 두 번째 범주에만 반응합니다.
PHP에서 폴백 전략 구성하기
이번 업데이트를 통해 라우팅 규칙 (Routing rules)은 선택 사항이 되었습니다. 오직 회복 탄력성 (Resilience)만을 원한다면, 구성은 두 개의 제공자와 폴백 순서만 있으면 됩니다:
use NeuronAI\Agent\Agent;
use NeuronAI\Router\RouterProvider;
use NeuronAI\Providers\Anthropic\Anthropic;
...
모든 호출은 Anthropic으로 전달됩니다. 만약 Anthropic이 일시적인 오류로 응답하면, 동일한 호출이 OpenAI로 전달됩니다. 여러분의 에이전트 코드, 도구, 지침, RAG 설정 등 그 어떤 것도 변경할 필요가 없습니다. 이는 라우터가 출시 첫날부터 가졌던 '드롭인 (Drop-in)' 속성이 새로운 문제에 적용된 것과 같습니다.
폴백과 라우팅 규칙은 서로 결합될 수도 있습니다. 규칙은 정상적인 조건에서 요청이 어디로 가야 하는지를 결정하고, 폴백 순서는 그 결정이 실패하는 제공자를 만났을 때 어떤 일이 일어날지를 결정합니다:
use NeuronAI\Router\Rules\MethodRule;
RouterProvider::make()
...
여기서는 구조화된 출력(structured output)은 OpenAI로 보내고, 그 외의 모든 것은 Anthropic으로 보내도록 설정되어 있지만, 만약 둘 중 하나가 일시적으로 사용할 수 없는 상태가 되면 요청은 페일오버 (fallback) 순서에 따라 작동 중인 제공업체로 전달됩니다. 이 두 메커니즘은 서로 다른 두 가지 질문에 답합니다. 규칙(rule)은 “누가 이것을 처리해야 하는가?”에 답하고, 페일오버 (fallback)는 “지금 당장 누가 이것을 처리할 수 있는가?”에 답합니다.
한 가지 실무적인 참고 사항이 있습니다. 조용히 작동하는 페일오버 (fallback)는 사용자에게는 훌륭하지만, 사용자의 인지 측면에서는 위험할 수 있습니다. 만약 Anthropic이 2시간 동안 장애가 발생했고 모든 요청이 OpenAI에 의해 조용히 처리되었다면, 여러분은 이를 반드시 알아야 합니다. 왜냐하면 아마도 서로 다른 비용을 지불하고 있을 것이며, 서로 다른 모델 동작 (model behavior)을 얻고 있을 것이기 때문입니다. 페일오버 (fallback)를 도입할 때는 모니터링을 주의 깊게 살펴보고, 페일오버 활성화가 급증하는 것을 단순히 시스템이 이미 해결한 문제가 아니라 조사할 가치가 있는 신호로 취급하십시오.
이것이 Composer 패키지로 존재할 수 있는 이유
다른 생태계에서 이 작업은 보통 애플리케이션 외부에서 이루어집니다. 코드와 제공업체 사이에 OpenRouter와 같은 게이트웨이 서비스 (gateway service)를 배치하면, 게이트웨이가 페일오버 (failover)를 대신 처리해 줍니다. 그것도 작동하지만, 이는 또 다른 네트워크 홉 (network hop), 또 다른 벤더, 또 다른 대시보드, 그리고 여러분의 프롬프트가 이동하는 또 다른 장소를 추가한다는 것을 의미합니다.
여기서 전체 메커니즘은 여러분의 코드베이스 내부에 있는 Composer 패키지입니다. 애플리케이션과 함께 버전이 관리되며, 순수 PHP로 설정되고, 자체 테스트 스위트 (test suite)로 테스트할 수 있습니다. 이것이 가능한 이유는 제가 이 시리즈의 첫 번째 글에서 설명했던 것과 동일한 이유, 즉 통합 메시징 레이어 (Unified Messaging Layer) 때문입니다. 모든 Neuron 제공업체가 동일한 메시지 표현을 각자의 API 형식으로 변환하는 방법을 알고 있기 때문에, 라우터 (router)는 Anthropic에서 방금 실패한 요청을 중간에 별도의 변환 작업 없이 OpenAI로 전달할 수 있습니다. 페일오버 (fallback) 전략은 라우터에 억지로 붙여넣은 기능이 아니라, 동일한 아키텍처 특성에서 비롯된 두 번째 결과물입니다. 라우팅 (routing)이 첫 번째 결과물이었던 것처럼 말입니다.
시작하기
Composer를 사용하여 패키지를 업데이트하세요:
composer require neuron-core/router
GitHub의 README에서는 폴백 (fallback)이 각 내장 규칙 (built-in rule)과 어떻게 상호작용하는지를 포함한 모든 설정 옵션을 다루고 있으며, 처음부터 시작하는 경우라면 Neuron AI 문서에서 프로바이더 (providers)와 에이전트 (agents)에 관한 모든 내용을 확인할 수 있습니다. 만약 프로덕션 (production) 환경에서 에이전트를 실행 중이며, 어떤 에러가 폴백 (fallback)을 트리거해야 하거나 트리거하지 말아야 하는지에 대한 의견이 있다면, 이슈 (issue)를 생성하거나 커뮤니티 채널을 통해 경험을 공유해 주세요. 이 기능은 Neuron AI를 프로덕션 (production) 환경에서 운영하는 사람들과의 대화에서 탄생했으며, 저는 바로 그런 방식으로 이 기능이 계속 진화하기를 바랍니다.
AI 자동 생성 콘텐츠
본 콘텐츠는 Dev.to AI tag의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기