재시작하면 조용히 멈추는 AI 비서: 침묵 장애부터 세션 브리지까지

항상 켜져 있는(always-on) AI 에이전트를 운영하다 보면, "죽지 않는데 대답도 안 하는" 최악의 상태를 만나게 됩니다. 프로세스는 살아 있어 감시 장치가 못 잡고, 사용자에게는 그냥 무응답. 이 글은 헤드리스로 도는 대화형 AI 에이전트가 재시작할 때 대화형 프롬프트에 걸려 영원히 멈추던 침묵 장애를 진단하고, 재시작해도 직전 맥락을 이어받도록 "세션 브리지"를 붙인 기록입니다.

1. 증상: 살아있는데 무응답

메신저 채널에 붙여 24시간 도는 AI 에이전트가 있었습니다. 세션 연속성을 위해 재시작 시 이전 대화 세션을 resume(재개)하도록 해뒀는데, 어느 순간부터 서비스 재시작 후 메신저가 조용해지는 일이 생겼습니다. 그런데 이상했습니다.

  • systemd는 서비스가 active라고 보고
  • 프로세스도 멀쩡히 살아 있음
  • 그런데 메시지를 보내면 아무 반응이 없음

Restart=always도, "프로세스가 죽었나" 보는 감시 스크립트도 이 상황을 못 잡습니다. 죽지 않았으니까요.

2. 원인: 헤드리스에 뜬 대화형 선택창

터미널 멀티플렉서 안에서 에이전트 화면을 캡처해 보고 원인이 드러났습니다. 세션이 커지면(수만 토큰), CLI가 재개 시 대화형 선택 프롬프트를 띄웠습니다.

이 세션은 크고 오래됐습니다.
  ❯ 1. 요약본에서 재개 (권장)
    2. 전체 세션 그대로 재개
    3. 다시 묻지 않기
  Enter로 확인 · Esc로 취소

사람이 앞에 있으면 그냥 엔터를 치면 됩니다. 하지만 이건 사람 없는 헤드리스 환경입니다. 입력을 기다리며 영원히 멈추고, 그동안 프로세스는 살아 있으니 어떤 자동 복구 장치도 발동하지 않습니다. 설령 재시작돼도 세션은 그대로 크니 같은 선택창에 다시 걸립니다. 무한 침묵.

3. 1차 해법: 재개하지 말고 항상 새 세션

선택창은 "큰 세션을 재개할 때만" 뜹니다. 그러니 재개를 안 하면 됩니다. 시작 스크립트가 매 기동 시 새 세션으로 출발하도록 바꿨습니다.

# Before: 이전 세션 재개 (커지면 선택창 → 헤드리스 정지)
exec agent --resume "$PREV_SESSION_ID"

# After: 항상 새 세션 (선택창이 뜰 여지 자체가 없음)
NEW_ID=$(uuidgen)
exec agent --session-id "$NEW_ID"

덤으로 "이 폴더를 신뢰하십니까?" 같은 첫 실행 확인창도 설정으로 미리 수락 처리해 막았습니다. 이제 재시작을 몇 번을 반복해도 프롬프트 없이 곧장 대기 상태에 도달합니다.

대가는 있습니다. 새 세션으로 시작하니 직전 대화의 맥락이 사라집니다. 신뢰성과 맞바꾼 트레이드오프죠. 그래서 다음 단계가 필요했습니다.

4. 2차 해법: 세션 브리지로 맥락 이어받기

"재개는 안 하되, 맥락은 넘긴다." 핵심 아이디어는 직전 세션을 짧게 요약해 새 세션의 시작 컨텍스트로 주입하는 것입니다.

  1. 요약 생성: 새 세션을 만들기 직전, 직전 세션 기록을 파싱해 "최근 사용자 요청 몇 개 + 마지막 응답 요지"를 뽑습니다. 무거운 LLM 호출 대신 기록 tail 파싱으로 빠르게. 실패해도 기동을 막지 않도록 타임아웃과 예외 처리를 둡니다.
  2. 주입: 그 요약을 시스템 컨텍스트에 덧붙여 새 세션을 시작합니다("직전 세션 요약: ..."). 원래 인격/시스템 프롬프트는 그대로 두고 append만 합니다.
  3. 중복 방지: 한 번 주입한 요약은 마킹해서 다음 재시작 때 다시 주입되지 않게 합니다.

안전장치가 중요합니다. 요약에는 토큰·API 키·계좌·잔고 같은 민감값이 절대 들어가면 안 됩니다. 정규식으로 스크럽하고, 크기도 1KB로 제한해 컨텍스트 오염을 막았습니다. 요약 파일 권한도 최소로 잠급니다.

5. 감시 장치도 "응답 능력"까지 보게

1차 해법이 근본 원인을 없앴지만, 안전망을 하나 더 깔았습니다. 기존 감시 스크립트는 "프로세스 생존"만 봤는데, 침묵 장애는 그걸 통과합니다. 그래서 두 가지를 추가했습니다.

  • 정지 감지: 화면에 블로킹 프롬프트(선택창·신뢰창) 시그니처가 연속으로 보이면, 아무도 메시지를 안 보내도 정지로 판단해 재시작.
  • 재시작 루프 가드: 짧은 시간에 재시작이 반복되면(예: 1시간 3회 초과) 자동 재시작을 멈추고 관리자에게 직접 알림. 무한 재시작 폭주를 막습니다.

6. 교훈

  • "살아있음"은 "동작 중"이 아니다. 헬스체크는 프로세스 생존이 아니라 응답 능력을 봐야 합니다.
  • 대화형 CLI를 헤드리스로 돌릴 땐, 뜰 수 있는 모든 프롬프트를 전수 차단하라. 재개 선택창, 신뢰 확인, 업데이트 확인 — 하나라도 남으면 그게 정지 지점입니다.
  • 연속성과 안정성은 다른 방법으로 얻어라. "세션 재개"로 둘 다 잡으려다 침묵 장애를 얻었습니다. 안정성은 새 세션으로, 연속성은 요약 브리지로 분리하니 둘 다 안전해졌습니다.
  • 맥락 주입엔 반드시 시크릿 스크럽과 크기 제한을. 편의를 위해 과거 내용을 넣을수록 유출·오염 위험이 커집니다.

FAQ

Q. 그냥 세션을 주기적으로 압축(compact)해서 작게 유지하면 안 되나요?

완화는 되지만 근본 해결은 아닙니다. 압축 타이밍과 무관하게 "충분히 크면" 선택창이 뜰 수 있고, 그 순간 헤드리스는 멈춥니다. 결정적으로 막으려면 재개 자체를 안 하는 게 확실합니다.

Q. 새 세션마다 요약을 LLM으로 만들면 더 좋지 않나요?

품질은 좋아지지만 매 재시작마다 비용·지연이 붙고, 그 호출이 실패하면 기동이 늦어집니다. 기록 tail 파싱은 즉시·무비용이고, "마지막에 뭘 하고 있었나"를 넘기기엔 충분했습니다. 필요하면 나중에 LLM 요약으로 승급할 수 있습니다.

Q. 맥락을 더 오래 검색하고 싶으면?

세션 브리지는 "직전 한 번"만 넘깁니다. 더 과거를 뒤지려면 과거 대화를 임베딩해 두고 검색하는 별도 지식베이스(RAG)를 붙이는 게 맞습니다. 브리지는 단기 연속성, RAG는 장기 기억 — 역할을 나누세요.

이 글은 실제 운영 중인 개인 AI 에이전트의 안정화 작업을 일반화해 정리한 기록입니다. 특정 호스트·경로·자격증명 등 구체적 인프라 정보는 의도적으로 제외했습니다.