채팅창을 닫는 순간 나의 AI 어시스턴트가 작동을 멈췄다
요약
채팅창 기반의 AI 어시스턴트를 넘어, 백그라운드에서 독립적으로 작동하는 에이전트 시스템 구축 방안을 다룹니다. 예약된 작업과 독립적인 컨텍스트 관리를 통해 사용자의 개입 없이도 지속 가능한 AI 워크플로우를 구현하는 방법을 설명합니다.
핵심 포인트
- 채팅 중심 모델에서 벗어나 백그라운드 실행 가능한 에이전트 필요성 강조
- 단순 알림을 넘어 지연된 에이전트 실행(delayed assistant run) 개념 도입
- 반복 작업 시 컨텍스트 오염을 방지하기 위한 새로운 컨텍스트 사용 전략
- 로컬 AI 컨트롤 플레인 CliGate를 통한 모델 라우팅 및 스케줄링 구현
많은 AI 어시스턴트들은 채팅창이 열려 있는 동안에만 유용하게 느껴집니다.
무언가를 요청하고, 그것이 생각하는 과정을 지켜보고, 아마도 한 번의 후속 질문에 답하게 한 뒤, 당신은 떠납니다.
그 순간 환상이 깨집니다.
만약 어시스턴트가 나중에 스스로 깨어나거나, 백그라운드 (background)에서 무언가를 실행하고, 실제로 당신이 필요할 때만 당신에게 돌아올 수 없다면, 그것은 진정한 어시스턴트라고 보기 어렵습니다. 그것은 그저 예의 바른 채팅창일 뿐입니다.
그것은 상주 어시스턴트, 예약된 작업 (scheduled work), 채널, Claude Code, Codex, 그리고 하나의 localhost 서비스 뒤에서 모델 라우팅 (model routing)을 처리하는 나의 로컬 AI 컨트롤 플레인 (control plane)인 CliGate를 구축하면서 얻은 매우 실질적인 교훈 중 하나였습니다.
진짜 버그는 사용자가 어시스턴트를 돌봐줘야 한다는 점이었다
기존의 사고 모델은 너무 채팅 형태에 치우쳐 있었습니다.
사용자 메시지가 도착합니다. 어시스턴트가 작업을 수행합니다. 답변이 동일한 가시적인 스레드 (thread)로 돌아옵니다.
이 방식은 대화형 작업에는 작동하지만, 지루하고 실제적인 작업들에는 무너집니다:
- 아침 요약본을 보내줘
- 오늘 밤에 포스트를 게시해줘
- 내가 오프라인인 동안 무언가를 확인해줘
- 나중에 계속 진행하다가 막히면 나에게 핑 (ping)을 줘
다시 말해, 어시스턴트에게는 사용자가 여전히 그 자리에 앉아 있는 척하지 않고도 작업할 수 있는 방법이 필요했습니다.
알림만으로는 충분하지 않았다
예약된 작업 (scheduled work)의 첫 번째 순진한 버전은 그저 다음과 같습니다:
오후 8시 -> 메시지 전송
이것은 "물 마시기" 알림에는 괜찮습니다.
하지만 실제적인 추론 (reasoning)이나 도구 (tools)가 필요한 작업에는 쓸모가 없습니다.
대신 내가 원했던 것은 다음과 같습니다:
오후 8시 -> 지시 사항과 함께 어시스턴트를 깨움
-> 백그라운드 (background)에서 실행되도록 함
-> 나에게 결과를 전송함
...
사소해 보일 수 있지만, 이것은 예약된 작업 (scheduled task)의 본질을 바꿉니다. 그것은 타이머가 아니라 지연된 어시스턴트 실행 (delayed assistant run)이 됩니다.
어시스턴트는 각 실행(fire)을 위한 자신만의 숨겨진 작업 공간이 필요했다
한 가지 미묘한 버그가 빠르게 나타났습니다.
만약 모든 반복되는 실행이 영원히 동일한 숨겨진 대화 (conversation)를 재사용한다면, 어시스턴트는 나중에 발생하는 실행들을 중복된 것으로 취급하기 시작했습니다.
이는 반복적인 작업(recurring work)의 경우 특히 치명적입니다. 예약된 게시 작업(scheduled publishing task)이 단지 같은 날 이전에 실행된 작업이 동일한 범위(scope)를 사용하고 유사한 기록(history)을 확인했다는 이유만으로 "이미 완료됨"이라고 말해서는 안 됩니다.
그래서 저는 결국 두 가지 모드를 분리했습니다:
- 반복 작업의 경우 기본적으로 새로운 컨텍스트 (fresh context by default) 사용
- 명시적으로 유용할 때만 공유 컨텍스트 (shared context only when explicitly useful) 사용
이는 반복적인 작업이 사용자의 눈에 보이는 채팅을 오염시키지 않고, 지난 실행으로부터 잘못된 가정(assumptions)을 상속받지 않으면서도, 자신만의 백그라운드 범위 대화(background scope conversation) 내에서 어시스턴트를 깨울 수 있음을 의미합니다.
CliGate에서는 이를 통해 스케줄링 모델을 훨씬 더 신뢰할 수 있게 만들었습니다. 각 실행(fire)은 깨끗한 라운드(clean round)가 될 수 있는 반면, 일기 쓰기와 같은 기능은 여전히 공유 컨텍스트(shared context)를 선택하여 사용할 수 있습니다.
까다로운 부분은 실행을 시작하는 것이 아니라, 정직하게 일시 중지하는 것이었습니다.
백그라운드 실행이 항상 깔끔하게 종료되는 것은 아닙니다.
때때로 어시스턴트는 사용자를 필요로 합니다.
자격 증명(credential)이 누락되었을 수도 있고, 선택이 중요할 수도 있으며, 위험한 작업에 대한 확인이 필요할 수도 있습니다.
만약 이러한 일이 숨겨진 예약 작업 범위(scheduled-task scope) 내부에서 발생한다면, 해당 실행은 그 상태로 영원히 멈춰 있을 수 없습니다.
따라서 어시스턴트에게는 사용자 대면 대화(user-facing conversation)로 돌아갈 수 있는 가교(bridge)가 필요했습니다.
최종적으로 적절하다고 느껴진 모델은 다음과 같습니다:
- 예약된 실행은 숨겨진 범위 대화(hidden scope conversation)에서 발생함
- 보이는 대화(visible conversations)는 알림만 받음
- 실행이 사용자 단계에서 일시 중지되면, 어떤 예약 작업이 대기 중인지 기억함
- 사용자가 보이는 대화에서 답장하면, 그 답장을 일시 중지된 백그라운드 실행으로 라우팅(route)함
이것이 "백그라운드 자동화(background automation)"와 "조용한 막다른 길(silent dead end)"의 차이였습니다.
UI는 트레이드오프(tradeoff)를 명확하게 설명해야 했습니다
이 설정에서 제가 마음에 드는 점 중 하나는 사용자 대면 선택이 단순하다는 것입니다.
예약된 작업은 다음 중 하나를 수행할 수 있습니다:
- 정적 메시지로 사용자에게 알림 (notify user)
- 실제 지침과 함께 어시스턴트 호출 (invoke assistant)
그리고 어시스턴트를 호출할 때, 두 가지 제어 요소가 매우 중요합니다:
- 선택적 작업 디렉토리 (optional working directory)
- 실행 시 컨텍스트를 공유할지 아니면 새로 시작할지 여부
그 정도의 표면적만 있어도 스케줄링을 미니 워크플로우 엔진으로 만들 필요 없이 유용할 수 있습니다.
대부분의 사람들은 cron 관련 지식을 원하지 않습니다. 그들이 원하는 것은 다음과 같이 말하는 것입니다:
- 매일 아침, 이 프로젝트를 요약해 줘
- 오늘 밤에는, 초안을 발행해 줘
- 평일에 매일, 이것을 확인하고 실패하면 나에게 메시지를 보내 줘
어시스턴트는 이런 수준에서 작동해야 합니다.
그 결과는 더 믿음직한 어시스턴트였습니다
가장 큰 성과는 기술적인 우아함이 아니었습니다.
그것은 행동적 진실성이었습니다.
어시스턴트는 모든 작업이 실시간 채팅 교환인 척할 필요가 없어졌습니다.
이제 어시스턴트는 다음을 할 수 있습니다:
- 나중에 깨어나서 작동하기
- 사람의 개입 없이 실행되기
- 백그라운드 작업을 메인 대화에서 분리하기
- 작업이 완료되면 결과를 다시 전달하기
- 실제로 도움이 필요할 때만 도움을 요청하기
- 대화 흐름을 잃는 대신 사용자의 답변부터 이어가기
이것은 CliGate를 '추가 단계가 있는 채팅'처럼 느끼게 하는 것이 아니라, 실제 로컬 오퍼레이터(local operator)처럼 느끼게 했습니다.
만약 AI 어시스턴트를 구축하고 있다면, 이 선을 넘지 말아야 합니다. 즉, 예약된 알림은 예약된 어시스턴트 실행과 같지 않으며, 사용자는 그 차이를 즉시 느낍니다.
AI 자동 생성 콘텐츠
본 콘텐츠는 Dev.to AI tag의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기