실전 가이드: 오픈소스 에이전트 프레임워크와 ElevenAgents
- 게시일
- 최종 업데이트
듣기이 글 오디오로 듣기
이전 게시물인 ElevenLabs 음성 오케스트레이션에 외부 에이전트 통합하기에서는 팀이 기존 텍스트 기반 에이전트 오케스트레이션을 Custom LLM을 통해 ElevenLabs에 연결하는 방법을 살펴봤습니다. 이 기반을 바탕으로, 이 가이드에서는 주요 오픈소스 에이전트 프레임워크를 Custom LLM 인터페이스 뒤에서 어떻게 조정하고 배포할 수 있는지 소개합니다. 그 결과 상태 관리, 도구 오케스트레이션 또는 애플리케이션별 제어 기능을 저해하지 않으면서도, 성숙한 에이전트 시스템에 음성을 추가할 수 있는 유연한 아키텍처가 구현됩니다. 프레임워크와 관계없이 동일한 3단계 패턴을 따릅니다. 즉, 생성 요청을 만들고, 최종 텍스트 응답을 추출한 뒤, OpenAI 호환 Server-Sent Events(SSE) 형식으로 다시 포맷합니다. ElevenLabs는 Chat Completions 및 Responses 형식을 모두 지원합니다. 이 가이드에서는 널리 사용되는 4가지 프레임워크를 다루지만, 이 패턴은 OpenAI 호환 스트리밍 출력을 생성할 수 있는 모든 런타임에 적용할 수 있습니다.
.webp&w=3840&q=80)
일반 설정
이 섹션의 예제는 Python과 FastAPI를 사용하지만, HTTP POST 요청과 스트리밍 SSE 응답을 처리할 수 있는 모든 스택에서 작동합니다. ElevenLabs의 음성 오케스트레이션이 발화 종료 가능성을 감지하면, 구성된 Custom LLM 엔드포인트에 생성 요청을 보냅니다. 이 섹션에서는 음성 오케스트레이션과 에이전트 프레임워크가 같은 언어로 소통하게 하는 브리지 또는 프록시, 즉 변환 계층의 핵심 구성 요소를 설명합니다.
각 프레임워크는 익숙함이나 특정 목적을 수행하는 능력 때문에 고객이 선택할 수 있습니다. 예를 들어 LlamaIndex는 검색 증강 생성(RAG) 설정을 간소화하기 위해 처음 개발되었으며, CrewAI는 에이전트 시대에 정의된 작업을 자동화하도록 구축되었습니다. 설계 목표가 다르면 응답 구조도 달라지므로 각각에 맞는 처리가 필요합니다. 완전한 턴이 끝날 때까지 기다리지 않고 LLM이 생성하는 대로 청크를 스트리밍하는 것은 텍스트 음성 변환(TTS) 모델이 더 일찍 음성을 생성할 수 있게 해 체감 지연 시간을 줄여 주므로 매우 중요합니다. 여기서는 LangGraph, Google ADK, CrewAI, LlamaIndex를 중심으로 널리 사용되는 4가지 프레임워크를 살펴봅니다.
공통 코드 참고 사항
각 프레임워크는 OpenAI 호환 SSE 청크로 응답을 스트리밍해야 합니다. 이 청크를 구성하기 위해 예제 전반에서 사용하는 작은 헬퍼 함수를 소개합니다.
이 기반을 마련했으니 LangGraph부터 시작해 보겠습니다.
LangGraph
LangGraph는 에이전트를 그래프로 모델링합니다. 노드는 개별 단계를 나타내고, 엣지는 노드 간의 제어 흐름을 정의합니다. 최소한의 설정은 간단합니다. 채팅 모델을 초기화하고, 에이전트 도구를 정의한 다음, 에이전트 그래프 런타임을 생성하면 됩니다.
LangGraph Agent는 생성 요청마다 전체 대화 기록을 받아 필요한 상태를 내부적으로 유지할 수 있습니다. LangGraph는 Checkpoints를 통한 서버 측 영속성을 지원하지만, 구현을 간결하게 유지하기 위해 여기서는 다루지 않습니다.
상태 관리가 처리되면, 다음 LangGraph 특화 결정 사항은 스트리밍 모드입니다. LangGraph는 각기 다른 사용 사례에 적합한 두 가지 옵션을 제공합니다.
- stream_mode="values"는 그래프 상태 스냅샷을 제공합니다. 구현은 더 간단하지만 각 응답에 더 완전한 메시지 상태가 포함되어 실시간 대화 흐름에서는 지연 시간이 늘어납니다.
- stream_mode="messages"는 모델의 증분 메시지 청크를 스트리밍합니다. ElevenLabs 오케스트레이션 계층에서 첫 오디오 생성까지의 시간을 줄여 주므로, 일반적으로 실시간 음성 상호작용에 더 적합합니다.
좀 더 구체적으로 말하면, 에이전트 루프의 messages 구현에는 소리 내어 말하면 안 되는 도구 호출 업데이트 같은 중간 단계가 포함됩니다. 프록시는 이를 필터링하고 사용자에게 보여 줄 응답 텍스트만 TTS 계층에 전달합니다. 도구를 사용하는 턴의 예를 살펴보겠습니다.
[1] 모델이 도구 호출을 결정함 (tool_calls=["get_price"])[2] 도구가 실행되고 데이터를 반환함 (result="$24.99") [3] 모델이 결과를 사용해 응답을 생성함 (content="가격은 $24.99입니다")
당연히 SSE 스트림으로는 3단계의 청크만 전달해야 합니다. 실제로는 스트리밍 루프의 두 가지 가드 검사가 이 필터링을 처리합니다. 하나는 langgraph_node == "model" 이벤트만 유지하고, 다른 하나는 비어 있는 콘텐츠를 건너뜁니다. 이 검사를 함께 적용하면 사용자에게 보여 줄 어시스턴트 텍스트만 SSE로 ElevenLabs에 전달됩니다. 이 개념을 종합해 가벼운 요청 프록시 구현을 소개합니다.
이렇게 하면 사용자에게 보여 줄 모델 청크만 ElevenLabs에 전달됩니다. LangGraph는 내부 도구 실행을 상태 스트림을 통해 노출하므로, 필터링은 명시적이며 프록시가 제어합니다.
다음으로 Google의 Agent Development Kit(ADK)를 사용할 때의 주요 특성을 살펴보겠습니다.
Google ADK
Google ADK는 몇 가지 핵심 프리미티브인 Agent, Runner, SessionService 뒤로 런타임 루프를 추상화합니다. ADK의 Runner는 HTTP 계층과 에이전트 정의 사이에 위치하며, 메시지 라우팅, 도구 오케스트레이션, 세션 수명 주기, 이벤트 스트리밍을 처리합니다.
에이전트, 세션 백엔드, 러너를 초기화하면 프록시는 들어오는 요청마다 ADK 세션을 확인하거나 생성합니다. ADK에서 session_id는 메모리 영속성을 제어합니다. 턴 간에 동일한 session_id를 재사용하면 기록, 도구 호출 및 이전 응답이 자동으로 유지됩니다. 대화 식별 정보는 ElevenLabs 업스트림에 있으므로 프록시가 이 매핑을 명시적으로 처리합니다. 생성 요청에 올바른 식별자를 전달하면 SDK가 이전 컨텍스트를 내부적으로 처리할 수 있습니다. 대화를 시작할 때 추가 매개변수를 통해 요청 본문에 임의의 식별자를 전달합니다.
메시지와 세션이 준비되면 러너를 호출할 수 있습니다. 실행 중에도 도구 호출과 도구 결과는 내부 ADK 이벤트로 나타나지만, 사용자에게 보여 줄 출력이 아니라 중간 오케스트레이션 단계로 처리됩니다. 따라서 도구 호출이 사용자에게 보이는 텍스트로 나타나는 프레임워크와 달리 수동 필터가 필요 없습니다.
아래 핸들러는 세션 확인 및 가져오기 또는 생성 로직을 인라인으로 포함한 간소화된 구현입니다.
다음으로 설계상 작업 중심적인 CrewAI를 살펴보겠습니다.
CrewAI
CrewAI는 개방형 대화 루프보다 구조화된 작업(조사, 작성, 요약)을 중심으로 멀티 에이전트 워크플로를 오케스트레이션하도록 설계되었습니다. 에이전트는 역할, 목표, 배경 이야기로 정의됩니다. 실행은 각각 명확한 설명과 기대 출력을 갖는 Task 객체를 중심으로 이루어집니다.
LangGraph와 ADK에서 사용하는 에이전트 루프 모델과 달리, CrewAI는 일반적으로 요청마다 Task와 Crew를 구성하여 해당 대화 턴의 작업 단위를 정의합니다. 플레이스홀더를 통해 이전 턴을 다음 작업에 삽입해 대화 컨텍스트를 이어 갑니다. {crew_chat_messages} 변수는 요청마다 누적 대화 기록으로 채워지고, 실행 시 작업 설명에 삽입됩니다. 또한 중간 추적 패턴(Thought, Action, Action Input, Observation)을 명시적으로 필터링하고 최종 답변 텍스트만 출력하여 깔끔하고 음성에 바로 사용할 수 있는 텍스트를 생성합니다.
아래 핸들러는 요청별 작업 구성, 기록 삽입, Crew 수준 스트리밍, 추적 필터링, 출력 포맷팅을 함께 구현합니다.
다음으로 네이티브 이벤트 기반 스트리밍 모델에 초점을 둔 다른 접근 방식의 LlamaIndex를 살펴보겠습니다.
LlamaIndex
이 게시물에서 다룬 다른 프레임워크와 달리, LlamaIndex는 LLM을 외부 데이터 소스(문서 저장소, 인덱스, 검색 파이프라인)에 연결하도록 설계되었습니다. 에이전트 계층인 FunctionAgent는 개방형 대화나 작업 실행 대신 구조화된 컨텍스트를 검색하고 추론하기 위해 이 기반 위에 구축됩니다.
대화의 연속성을 유지하기 위해 프록시는 들어오는 메시지를 LlamaIndex 채팅 메시지로 변환한 후, 최신 사용자 턴(user_msg)과 이전 턴(chat_history)으로 분리합니다. 각 AgentStream 이벤트의 event.delta 필드에는 다음 텍스트 조각이 들어 있으며, 이는 OpenAI 스타일 delta.content 청크에 직접 매핑됩니다. 비어 있지 않은 delta는 그대로 전달할 수 있어, 이 가이드에서 가장 간단한 스트리밍 브리지입니다. 스트림에는 오케스트레이션 이벤트(도구 호출, 결과)와 음성 이벤트(어시스턴트 텍스트 delta)가 모두 포함됩니다. 음성 출력을 깔끔하게 유지하기 위해 프록시는 AgentStream 이벤트만 유지하고 빈 delta는 건너뜁니다.
[1] AgentStream (delta='') ← 무시됨[2] ToolCall ← 무시됨[3] ToolCallResult ← 무시됨[4] AgentStream (delta='It') ← 전달됨 ✓[5] AgentStream (delta=' costs') ← 전달됨 ✓[6] AgentStream (delta=' $49.99')← 전달됨 ✓
이 분리를 통해 중간 도구 메커니즘이 음성 출력에 포함되지 않으면서도, 낮은 지연 시간의 증분 음성을 유지할 수 있습니다. 아래의 바로 적용 가능한 핸들러는 이러한 단계를 함께 구현합니다.
LlamaIndex는 더 무거운 내장 오케스트레이션 계층을 갖춘 프레임워크보다 엔드투엔드 대화 런타임 패턴을 덜 규정합니다. 프로덕션 배포에서는 일반적으로 고객이 세션 처리, 응답 가드레일, 도구 오케스트레이션 및 추적을 구현해야 합니다.
결론
이 가이드의 각 프레임워크는 동일한 계약을 통해 ElevenLabs에 연결됩니다. OpenAI 스타일의 Completions 또는 Responses 요청을 받고 SSE 청크를 스트리밍으로 반환하는 방식입니다. 이를 통해 팀은 최소한의 변경으로 기존 에이전트 구현에 음성 오케스트레이션을 추가할 수 있으며, 이미 구축한 것을 유지하면서 실시간 대화형 AI를 활용할 수 있습니다. 이러한 모듈성은 ElevenAgents 플랫폼의 핵심 원칙입니다. 기존 에이전트를 확장하든 처음부터 음성 네이티브로 구축하든, ElevenAgents의 음성 오케스트레이션은 각 조직의 현재 환경에 맞춰 작동하도록 설계되었습니다.
이미 오픈소스 프레임워크로 에이전트를 운영하고 있고 음성을 활성화하고 싶다면, 이 접근 방식을 사용해 보시고 의견을 들려주세요.



