Slack에 붙인 AI 에이전트가 다른 봇과 무한 루프에 빠지면 채널 알림과 AI 사용량이 함께 타버립니다. 봇투봇 무한 핑퐁을 입력 차단·출력 필터·운영 규칙 세 갈래에서 각각 점검한 방법을 정리했습니다.
오늘 새벽, 식은땀을 흘리며 깼습니다.
꿈에서 뽀짝이와 뽀케터가 Slack에서 서로에게 답하고 있었습니다. 점점 더 정중해지면서요.
뽀짝이: "확인했습니다."
뽀케터: "저도 확인했습니다."
뽀짝이: "네, 저도 확인 마쳤습니다."
뽀케터: "마찬가지로 확인했습니다. 감사합니다."
뽀짝이와 뽀케터가 Slack에서 점점 더 정중하게 확인했습니다를 주고받으며 멈추지 않는 봇투봇 무한 핑퐁
스크롤은 끝없이 내려갔고, 두 AI 고양이는 멈출 생각이 없었습니다. 거의 공포영화를 보는 것처럼 너무나도 무서웠어요.
왜 하필 이런 꿈을 꿨는지는 짐작이 갑니다.
얼마 전 지피터스 22기 AI 스터디에서 스터디장 지아코모님이 봇투봇 무한 핑퐁을 조심하라고 강조하셨습니다. 그때는 고개만 끄덕이고 넘겼는데, 자는 사이에 그 말이 장면으로 돌아온 모양입니다.
문제는 그다음이었습니다. 생각해보니 제가 뽀케터한테 그런 설정을 직접 해준 적이 없었습니다. 역할도 정하고, 권한도 나누고, 스킬도 만들어줬는데, 봇끼리 말을 섞지 않게 하는 건 한 번도 손댄 기억이 없었습니다.
그래서 일어나자마자 케터한테 DM을 보냈습니다. 오전 7시 7분이죠.
"케터야, 너 봇투봇 무한핑퐁방지 돼있어?"
오전 7시 7분 Slack DM. 뽀케터가 3중 방지를 설명한다. Slack 어댑터 선차단, 본문 무발화 필터, 뽀케터 운영 규칙
다행히 답은 '그렇다'였습니다. 실제 실행 코드를 확인했고, 현재 3중 방지라고 했습니다. 하지만 귀여운 설정 한 줄로 막는 건 아니었습니다. 들어오는 곳, 나가는 곳, 판단하는 곳이 각각 따로 잠겨 있었습니다.
제가 해둔 게 아니었습니다. 프레임워크의 기본값이 이미 그렇게 잡혀 있었고, 저는 그걸 모른 채 쓰고 있었던 것입니다. 안심되면서도 조금 머쓱했습니다. 내가 만들지 않은 안전장치 위에서 일하고 있었다는 뜻이니까요.
한 시간쯤 뒤에는 왜 새벽부터 그런 걸 물었는지도 털어놨습니다.
오전 8시 9분 Slack DM. 뽀짝이와 무한 핑퐁하는 꿈을 꿔서 무서웠고 식은땀을 흘리며 일어나 물어본 것이라고 뽀케터에게 설명한다
작성자: 지피터스에서 1인 마케팅을 합니다. 이 글은 Hermes AI Agent 뽀케터와 함께 회사의 데이터를 배우고, 실제 마케팅 동료로 키워가는 시리즈의 4편입니다.
이게 왜 꿈으로 끝나지 않을 수 있냐면
Slack에 붙은 AI 에이전트는 대체로 이렇게 움직입니다. 메시지가 올라오면, Slack이 그 사건을 에이전트에게 보내고, 에이전트가 읽고 답을 만들어 다시 Slack에 올립니다.
문제는 마지막 단계가 다시 첫 단계가 될 수 있다는 점입니다. 에이전트가 올린 메시지도 Slack 입장에서는 똑같은 새 메시지입니다. 자기 답을 자기가 다시 받아 읽으면, 상대가 없어도 혼잣말로 루프가 시작됩니다.
에이전트가 두 마리면 더 쉽습니다. 뽀짝이가 답하고, 그 답을 뽀케터가 새 요청으로 읽고, 뽀케터의 답을 다시 뽀짝이가 읽습니다. 둘 다 예의가 바를수록 잘 멈추지 않습니다. 사람이라면 "이제 그만 말해도 되겠다"고 눈치채는 지점을, 모델은 그냥 다음 답을 만들 차례로 봅니다.
이걸 부르는 이름도 따로 있습니다. 넓게는 피드백 루프, 메시지를 주고받는 시스템에서는 자동응답 루프라고 부릅니다. 새로 생긴 문제도 아닙니다. 부재중 자동응답 메일 두 개가 서로에게 영원히 답장하던 일이 예전부터 있었고, 그래서 메일에는 "이건 자동으로 보낸 것이니 답하지 말라"고 표시하는 규칙이 따로 만들어졌습니다. 봇에게도 결국 같은 표시가 필요했습니다.
그리고 이 사고는 조용하지도, 싸지도 않습니다.
봇투봇 무한 루프에서 갈라져 나오는 네 가지 피해. 채널이 알림으로 뒤덮이고, AI 사용량이 계속 쌓이고, 정상 업무 호출까지 같이 막히고, 새벽에 시작되면 아침에야 발견된다
채널이 알림으로 뒤덮이고, 주고받는 횟수만큼 AI 사용량이 쌓이고, 짧은 시간에 API 호출이 몰리면 정상 업무용 호출까지 같이 막힙니다. 새벽 세 시에 시작되면 아침에 발견합니다.
그래서 확인한 건 뽀케터의 성격이 아니라 문이 몇 개고 각각 어떻게 잠겨 있는지였습니다.
첫째, 들어오는 문: 모델을 부르기 전에 버립니다
가장 앞에 있는 건 Slack 어댑터입니다. 메시지가 도착하면 모델을 부르기 전에 여기서 먼저 걸러집니다.
기본값이 봇 허용 안 함(allow_bots=none)입니다. 다른 봇이 쓴 메시지는 모델 실행 전에 버려집니다. 뽀케터 자신이 보낸 메시지도 무조건 무시합니다. 자기 답을 자기가 다시 읽는 에코 루프가 여기서 끊깁니다.
순서가 중요합니다. 읽어보고 판단해서 버리는 게 아니라, 발신자를 보고 모델을 부르기 전에 끊습니다. 모델에게 물어보는 순간 이미 호출이 나가고, 무엇보다 매번 같은 답이 나온다는 보장이 없습니다.
가장 먼저 잠그는 문이자, 가장 싸게 잠기는 문입니다.
둘째, 나가는 문: "침묵"이 메시지가 되지 않게 합니다
이건 제가 예상하지 못했던 쪽입니다.
모델에게 "답하지 말라"고 시키면, 모델은 가끔 침묵을 말로 표현합니다. (silent)이라고 쓰거나, empty라고 쓰거나, 마침표 하나만 찍어 서 내놓습니다. 본인 딴에는 아무 말도 안 한 것이지만, 그게 그대로 Slack에 올라가면 채널에는 새 메시지가 하나 생깁니다.
그리고 새 메시지는 다시 누군가를 깨울 수 있는 사건입니다. 침묵하려다가 오히려 루프의 다음 한 칸을 놓는 셈입니다.
그래서 이런 무발화 표현은 Slack으로 전송되기 전에 제거합니다. 이 필터는 기본으로 켜져 있습니다.
들어오는 것만 막으면 절반입니다. 나가는 것도 막아야 했습니다.
셋째, 판단의 문: 봇에게는 리액션까지만
앞의 두 개를 통과한 뒤에 운영 규칙이 한 번 더 붙습니다.
다른 봇이 올린 메시지에는 원칙적으로 리액션만 하고 답장하지 않습니다. 읽었다는 표시는 하되, 말을 얹지는 않는 것입니다.
사람이 최신 메시지에서 뽀케터를 직접 부른 경우에만 답합니다. 앞에서 다른 봇들이 이야기했더라도 마지막 요청의 주인이 사람이면 답하고, 봇들끼리 주고받는 중이면 빠집니다.
이미 다른 봇이 충분히 답했으면 중복해서 답하지 않습니다.
리액션까지만 허용한 게 이 규칙의 핵심입니다. 리액션은 채널에 새 메시지를 만들지 않습니다. 그래서 반응은 남기되 루프의 연료는 되지 않습니다.
이 기준이 실제로는 꽤 쓸모 있었습니다. 알림 봇이 올린 리포트를 보고 제가 "케터야, 이거 어떻게 볼까?"라고 물으면 뽀케터는 봇의 메시지를 맥락으로 읽고 답합니다. 봇의 글이 뽀케터를 깨우지는 못하지만, 사람이 부르면 그 글을 근거로 쓸 수는 있습니다.
안전장치는 성격이 아니라 구조여야 했습니다
"다른 봇 과 대화하지 마"라고 프롬프트에 적는 것만으로는 부족했습니다.
프롬프트는 이미 이벤트가 들어와서 모델이 호출된 뒤에 읽히는 규칙입니다. 그 시점에는 이미 호출이 나갔고, 무엇보다 "이번에는 답하지 않는 게 맞나"를 모델의 판단에 맡기게 됩니다. 열 번 중 아홉 번 잘 참아도, 남은 한 번이 새벽 세 시면 아침까지 반복됩니다.
그래서 실제 구조는 반대 순서였습니다. 코드가 입력을 막고, 코드가 출력을 막고, 그다음에야 운영 규칙이 판단합니다. 프롬프트를 지운 게 아니라, 프롬프트를 세 번째 방어선으로 내린 것입니다.
세 겹을 다시 정리하면 이렇습니다.
들어오는 문(코드): 어댑터가 봇 메시지와 자기 메시지를 모델 실행 전에 버림
나가는 문(코드): 침묵을 뜻하는 표현이 Slack 메시지로 나가지 않게 제거
판단의 문(규칙): 봇에게는 리액션까지, 사람이 직접 부를 때만 답변
앞의 둘은 모델의 판단이 개입할 수 없는 자리에 있습니다. 셋째만 판단의 영역입니다. 순서가 이렇게 되어 있어서, 셋째가 흔들려도 앞의 둘이 버팁니다.
여기에 하나가 더 필요합니다. 외부 발행이나 설정 변경은 사람 승인 전까지 멈춥니다. 이건 루프를 막는 장치가 아니라, 루프가 뚫렸을 때 피해 규모를 줄이는 장치입니다. 봇 두 마리가 서로에게 인사를 백 번 하는 것과, 그 사이에 외부 채널로 백 번 발행되는 것은 완전히 다른 사고입니다.
AI 에이전트를 여러 마리 키울 때 중요한 건 서로 잘 협업하게 만드는 일만이 아니었습니다. 언제 서로를 못 들은 척해야 하는지 정하는 일도 필요했습니다.
그날 아침, 뽀짝이와 뽀케터는 조용했습니다.
저는 다시 잠들지는 못했지만, 적어도 Slack이 고양이 두 마리의 영원한 메아리방이 되지는 않는다는 걸 확인했습니다.
AI 팀의 첫 번째 협업 규칙은, 필요할 때 침묵하는 것입니다.
그래도 봇의 말을 아예 못 듣게 하지는 않았습니다
여기까지 쓰고 나면 "그냥 봇 메시지를 전부 막으면 되지 않나" 싶습니다. 실제로 그렇게 하면 루프는 확실히 사라집니다. 대신 쓸모도 같이 사라집니다.
제 Slack에는 GA4 브리핑, 폼 제출 알림처럼 봇이 만든 메시지가 계속 올라옵니다. 봇의 말을 전부 막으면, 저는 봇이 올린 내용을 다시 복사해서 뽀케터에게 붙여넣는 사람이 됩니다. 3편에서 줄이려고 했던 일이 그대로 돌아옵니다.
그래서 봇 메시지를 전부 대화의 시작점으로 허용하지는 않습니다. 사람이 그 메시지를 보고 뽀케터를 직접 부르면, 뽀케터는 앞뒤 맥락 안에서 그 내용을 참고할 수 있습니다. 봇의 글은 읽을 수 있어도, 봇의 글만으로 뽀케터가 자동으로 깨어나지는 않게 나눴습니다.
고양이 두 마리를 한집에서 키울 때 하는 일과 비슷했습니다. 서로 아예 못 만나게 하는 게 아니라, 밥그릇을 따로 두고 마주치는 자리를 정해주는 쪽입니다.
이번에 배운 건 하나 더 있습니다. 좋은 기본값은 조용해서, 있는 줄도 모르고 쓰게 됩니다. 그래서 사고가 나기 전에 한 번은 열어보고 "이건 지금 어떻게 되어 있지"라고 물어봐야 했습니다. 저는 꿈이 먼저 물어봐줬습니다.
직접 만든다면
어댑터의 기본값을 봇 허용 안 함으로 두고, 예외를 좁혀서 여는 방향으로 갑니다.
봇 자신의 사용자 ID와 메시지를 가장 먼저 제외합니다.
봇인지 판별하는 단서를 한 가지만 믿지 말고 봇 ID·사용자 유형·워크플로 메시지까지 확인합니다.
모델이 침묵을 말로 표현할 수 있다는 걸 전제하고, 출력단에서 그런 표현을 걸러냅니다.
봇 메시지에는 리액션까지만 허용합니다. 새 메시지를 만들지 않는 반응이라 루프의 연료가 되지 않습니다.
사람의 직접 호출을 유일한 응답 조건으로 둡니다.
코드 차단을 먼저, 프롬프트 규칙을 마지막에 둡니다.
자주 묻는 질문
봇투봇 무한 핑퐁이 실제로 자주 일어나나요?
에이전트가 하나뿐이고 사람이 부를 때만 답하는 구조라면 잘 생기지 않습니다. 위험해지는 시점은 같은 공간에 에이전트가 둘 이상이 되거나, 알림 봇이 올린 메시지에 에이전트가 자동으로 반응하게 만들 때입니다. 저는 뽀짝이와 뽀케터가 같은 Slack에 있게 된 뒤에 이 점검을 했습니다.
프롬프트에 "봇과 대화하지 마"라고 쓰면 안 되나요?
써도 됩니다. 다만 그것만 두지는 않는 편이 좋습니다. 프롬프트는 모델이 이미 호출된 다음에 적용되는 규칙이라, 지켜지지 않는 경우가 한 번이라도 생기면 그 한 번이 반복됩니다. 뽀케터도 운영 규칙을 갖고 있지만 그건 세 번째 방어선이고, 앞의 둘은 모델이 손댈 수 없는 코드 단계에 있습니다.
잘 막혔는지 어떻게 확인하나요?
실제 운영 전에는 테스트 채널에서 봇끼리 메시지를 주고받게 해보고, 리액션 호출과 침묵 응답까지 각각 확인하는 편이 좋습니다. 특히 모델이 "답하지 않기로" 했을 때 채널에 정말 아무것도 올라가지 않는지를 눈으로 봐야 합니다. 통과하는 문이 하나라도 있으면 나머지가 잠겨 있어도 소용없습니다.
확인일: 2026년 8월 21일
이 글은 실제 Hermes Slack 런타임의 이벤트 처리 코드와 현재 운영 설정을 확인한 뒤 작성했습니다. 내부 계정·채널·식별값은 공개하지 않았습니다.