AI 에이전트 스트리밍 실전: 끼어들기, 인간 인계, 세션 연속성
출처: Dev.to
TL;DR: AI 에이전트 스트림은 대부분의 프레임워크가 처리하지 못하는 방식으로 끊깁니다: 연결 끊김, 작업 중단, 기기 간 인간 인계. 이 글에서는 Ably AI Transport가 세 가지 상황—명시적인 취소 신호를 통한 바지인(barge‑in), 내구성 있는 조직 차원의 HITL, LiveObjects를 통한 다중 에이전트 진행—을 어떻게 처리하는지 실시간 데모를 통해 살펴봅니다.
당신은 AI 지원 에이전트와 대화 중입니다. 문제를 설명했고, 에이전트가 답변을 절반 정도 내놓은 상태에서 연결이 끊깁니다. 재연결하면 답변이 사라집니다.
같은 질문을 다시 입력하면 에이전트가 같은 추가 질문을 다시 합니다. 3분간의 컨텍스트가 사라진 겁니다. 모델이 잊어버린 것이 아니라, 전달 계층이 아무것도 저장하지 않았기 때문입니다.
연결 끊김, 페이지 새로 고침, 기기 전환 모두 같은 이유로 실패합니다: 세션 상태가 전달 연결에만 존재하고, 그와 독립적으로 존재하지 않기 때문입니다. Ably AI Transport는 세션을 개별 연결을 초월하는 채널에 저장함으로써 이를 해결합니다. 아래 데모는 바지인, 인간 인계, 다중 에이전트 조정을 깊이 있게 다루며, 실제 프로덕션 팀이 처음부터 직접 구현하게 되는 기본 요소들을 보여줍니다.
핵심 정리
- 연결 끊김은 대부분의 AI 스트림을 처음부터 다시 시작하게 만듭니다. Ably AI Transport는 채널에 세션 출력을 버퍼링하므로, 클라이언트가 재연결해도 추론을 다시 실행하지 않고 따라잡을 수 있습니다.
- 바지인은 양방향 채널이 필요합니다. SSE는 사용자의 중단을 네트워크 끊김과 구분하지 못하지만, AI Transport는 취소와 리다이렉트를 명시적인 채널 신호로 전달해 에이전트가 바로 처리하도록 합니다.
- 조직 차원의 인간 인계—예를 들어, 감독자가 몇 시간 뒤 다른 기기에서 실시간 세션에 참여하는 경우—는 대부분의 프레임워크가 해결하지 못하는 HITL(인간‑인-루프) 상황입니다. AI Transport의 내구성 있는 세션은 채널 히스토리에 보류 중인 승인을 저장해, 적절한 사람이 응답할 때까지 유지됩니다.
Mike Christensen (Ably Pub/Sub 팀 리드)은 다중 에이전트 휴일 계획 앱을 통해 위의 모든 기본 요소를 실시간으로 시연합니다. 아래 섹션들은 영상과 동일한 챕터 구성을 따릅니다.
연결 끊김 중 스트림
표준 HTTP 스트리밍은 서버 측에 세션 상태를 저장하지 않습니다. 연결이 닫히면, 그 사이에 생성된 토큰들은 사라집니다: 전달 계층이 이를 보관하도록 요청받지 않았기 때문이죠. 클라이언트는 빈 상태로 재연결하고 다시 프롬프트를 보냅니다.
페이지 새로 고침 시 스트림 손실
대부분의 AI 구현은 브라우저에 토큰 상태를 저장합니다: React 컴포넌트 상태, 부분 응답을 추적하는 JavaScript 변수 등. 페이지를 새로 고치면 그 상태가 사라집니다. 에이전트는 클라이언트가 생성 중에 사라졌다는 사실을 인식하지 못하고, 이미 만든 출력을 재스트리밍할 메커니즘도 없습니다.
기기 전환 시 세션 손실
세션은 연결에, 연결은 기기에 묶여 있습니다. 노트북에서 휴대폰으로 옮기면 대화가 이어지지 않습니다. 새로운 기기는 세션 히스토리에 접근할 경로가 없습니다.
세 경우 모두 근본 원인이 동일합니다. 생성 상태가 단일 전달 연결에 결합돼 있기 때문이죠. 이를 분리—세션을 개별 연결을 초월하는 채널에 저장—하면 세 가지 문제를 한 번에 해결할 수 있습니다. 타임아웃 원인과 프로토콜 폴백에 대한 자세한 내용은 Is WebSockets enough for AI chat? 를 참고하세요.
서버‑사이드 버퍼링 및 오프셋 기반 재생
에이전트가 발행하는 모든 토큰은 클라이언트가 연결돼 있든 없든 세션 채널에 바로 기록됩니다. 재연결 시 AI Transport는 untilAttach 를 사용해 끊김 동안 발행된 모든 토큰을 순서대로 전달하고, 이후 실시간 스트림을 재개합니다. LLM은 절대 재실행되지 않으며, 클라이언트만 따라잡습니다.
세션은 채널에 존재, 연결이 아니다
세션은 채널에 존재하고, 이를 연다든 연결에 존재하지 않습니다. 같은 채널 이름을 구독하는 모든 기기는 동일한 세션에 참여합니다: 전체 대화 히스토리와 현재 위치부터 시작되는 실시간 스트림을 모두 받습니다. 두 개의 브라우저 탭, 노트북과 휴대폰, 혹은 응답 도중 페이지 새로 고침—all receive the same uninterrupted state.
채널 히스토리를 통한 컨텍스트 제공
클라이언트가 실시간 복구 창을 넘어 오프라인 상태라면, 채널 히스토리가 전체 대화를 제공합니다. 클라이언트는 view.loadOlder() 로 오래된 메시지를 페이지네이션하며 세션 전체 컨텍스트를 로드합니다. 완전히 오프라인인 사용자는 FCM, APNs, Web Push 등을 통해 에이전트 응답을 푸시 알림으로 받을 수 있습니다. 현재 푸시 알림 기능은 Partial 로 제공됩니다.
데모 요약
Mike는 스트림 도중 페이지를 새로 고치고, 응답이 정확히 중단된 지점부터 이어지는 것을 보여줍니다. 두 개의 창을 나란히 열면 동일한 진행 중인 응답이 동시에 업데이트됩니다.
세션 연속성은 인프라 레이어다
그 위에서 일어나는 일—사용자가 에이전트를 어떻게 보며, 어떻게 중단·리다이렉트하고, 인간 운영자가 전체 컨텍스트를 가지고 인계받으며, 여러 전문 에이전트가 독립적으로 진행 상황을 표시하는가—는 모두 세션이 살아있고 가시적이어야 가능합니다.
다음 네 섹션은 데모가 보여주는 상호작용 패턴을 다룹니다: 에이전트가 작업 중일 때 사용자가 보는 화면, 사용자가 어떻게 중단하거나 리다이렉트하는지, 인간 운영자가 전체 컨텍스트와 함께 인계받는 방법, 그리고 여러 전문 에이전트가 독립적으로 진행 상황을 표시하는 방법. 네 가지 모두 세션이 실시간으로 가시적이어야 합니다.
가시성 없이는 중단이 의미 없다
사용자는 보이는 에이전트만 의미 있게 중단할 수 있습니다. 진행 상황이 보여야 바지인과 인간 인계가 모두 가능해집니다. 가시성이 없으면 사용자는 “보이지 않는 프로세스를 취소한다”는 상황에 처하게 되며, 기다릴지 리다이렉트할지 판단할 근거가 없습니다.
데모가 보여주는 네 종류의 진행 신호
- 토큰 스트리밍 – 오케스트레이터가 생성 중인 토큰을 실시간으로 보여줍니다.
- Ably LiveObjects – 세 전문 에이전트(항공편, 호텔, 액티비티)의 구조화된 진행 상태를 전달합니다.
- Presence – 세션에 어떤 에이전트가 활성화돼 있는지 표시합니다.
- Task History – 각 에이전트가 완료한 작업을 기록합니다.
각 신호는 서로 다른 소스에서 나오며 독립적으로 도착합니다. 세 전문 에이전트는 진행 상황을 직접 발행하고, 오케스트레이터를 거치지 않으므로 사용자는 각 에이전트의 실시간 상태를 동시에 볼 수 있습니다. 또한 각 에이전트는 원시 쿼리 파라미터를 별도 모델 호출을 통해 자연어로 변환합니다. 예를 들어 진행 카드에 “14일 직항 항공편 검색 중”이라고 표시되며, 이는 바지인에 큰 도움이 됩니다. 사용자는 최신 실시간 정보를 기반으로 중단 여부를 판단하게 됩니다.
고객 탐색 연구에서 드러난 ‘중단’의 중요성
Ably CEO Matthew O’Riordan이 발표한 고객 탐색 연구에 따르면, 팀이 비동기 에이전트 경험으로 전환하면서 중단 기능이 핵심 요구사항으로 떠올랐습니다. 한 팀은 사용자 입력을 완전히 차단했는데, 이는 SSE에서는 사용자의 정지 신호가 네트워크 끊김과 구분되지 않아 안전하게 처리할 방법이 없었기 때문입니다.
AI Transport가 해결하는 이유
채널이 양방향이기 때문에, 사용자 입력은 특정 채널 이벤트로 도착합니다. 이는 연결 부수 효과가 아니라 명시적인 이벤트이므로, 에이전트는 이를 신뢰하고 실시간으로 처리할 수 있습니다.
두 가지 중단 패턴
사용자에게 보여주고 싶은 방식에 따라 선택합니다.
1️⃣ Cancel‑then‑send (취소 후 전송) – 일반적인 패턴
await transport.cancel()
transport.cancel() 은 채널에 명시적인 취소 신호를 발행합니다. 서버는 abort를 실행하고 LLM 스트림을