블로그

2026년 8월 15일 · 7분 읽기

Buzz로 AI 고객센터 만들기: 에이전트 6인을 사람과 같은 방에 넣었습니다

Nostr 릴레이 Buzz를 자체 클러스터에 올리고 역할이 다른 AI 에이전트 6인을 사람과 같은 프로토콜로 붙였습니다. 왜 Buzz를 골랐고 배포하면 무엇이 달라지는지 적었습니다.

  • Buzz
  • AI 에이전트
  • 고객센터
  • Nostr
  • 셀프호스팅

2026년 8월 23일 업데이트

혼자 앱을 만들면 고객 응대가 개발 시간을 직접 잠식합니다. 앱스토어 리뷰가 쌓이고, 메일이 들어오고, 커뮤니티에 질문이 올라옵니다. 답이 늦으면 평점으로 돌아옵니다.

사람을 뽑을 단계가 아니고, 상용 헬프데스크를 붙이면 대화가 남의 저장소에 쌓입니다. 그래서 고객센터를 직접 만들었습니다. 릴레이 하나를 클러스터에 세우고, 역할이 다른 AI 에이전트 6인을 사람과 같은 방에 넣었습니다.

문의가 들어와 응답이 나가는 경로

한눈에 보기

문제 혼자 앱을 만들면 고객 응대가 개발 시간을 직접 잠식합니다.
결정 역할을 6개로 나눴습니다.
결과 혼자 만드는 사람에게 가장 큰 이익은 마지막 항목입니다.
제약 에이전트에게 일을 넘기면 무엇을 했는지 확인할 방법이 필요합니다.

상용 헬프데스크를 쓰지 않은 이유는 두 가지입니다

첫째는 소유입니다. 고객 문의와 응답 이력은 제품에서 가장 오래 남는 자산입니다. 상용 서비스에 쌓으면 나중에 옮길 때 내보내기 기능이 허용하는 범위만 가져올 수 있습니다. 대화의 스레드 구조, 누가 무엇을 승인했는지, 어떤 판단으로 그 답을 보냈는지가 그 범위 밖에 있으면 사라집니다.

둘째는 AI를 붙이는 방식입니다. 대부분의 헬프데스크는 AI를 기능으로 제공합니다. 요약 버튼, 답변 추천, 감정 분석입니다. 저는 에이전트가 사람처럼 방에 들어와 대화하고, 필요한 것을 조회하고, 승인을 요청하고, 그 기록을 남기기를 원했습니다. 벤더가 그 자리를 열어 줄 때까지 기다릴 이유가 없었습니다.

Buzz를 고른 이유는 릴레이가 워크스페이스라는 점입니다

Buzz는 Block이 Apache-2.0으로 공개한 Rust 워크스페이스입니다. 한 문장으로 설명하면 사람과 AI 에이전트가 같은 방을 쓰는 자체 호스팅 협업 공간입니다.

핵심 설계가 프로젝트 소개에 그대로 있습니다.

모든 메시지, 리액션, 워크플로 단계, 리뷰 승인, git 이벤트가 하나의 로그에 담긴 서명된 이벤트입니다. 작성자가 사람이든 프로세스든 같은 모양, 같은 신원 모델, 같은 감사 추적을 갖습니다.

이 문장이 제가 필요했던 전부입니다. 에이전트에게 관리 API를 따로 열어 주면 그 통로만 감사 범위 밖으로 나갑니다. Buzz에서는 에이전트도 자기 키를 갖고, 자기 채널 멤버십을 갖고, 자기 감사 추적을 남깁니다. 권한 플래그가 아니라 신원으로 범위를 정합니다. 팀원을 대하는 방식과 같습니다.

실무에서 결정적이었던 강점을 꼽으면 이렇습니다.

강점 무엇이 달라지는가
릴레이가 워크스페이스 도메인 하나가 곧 작업 공간입니다. 서비스 5개를 엮지 않습니다
이벤트 로그가 정본 사람의 메시지와 에이전트의 메시지가 같은 종류입니다. 감사가 한 곳에서 끝납니다
신원 기반 범위 에이전트마다 키와 멤버십이 다릅니다. 권한을 프롬프트로 설명하지 않습니다
자체 호스팅 릴레이·저장소·검색 인덱스가 우리 클러스터 안에 있습니다
Helm 차트 제공 postgres·redis·minio를 번들로 받습니다. 하루 안에 뜹니다
Apache-2.0 벤더 로드맵을 기다릴 필요가 없습니다. 필요하면 crate를 읽습니다

crate가 30개로 나뉘어 있어 릴레이, 인증, 에이전트 어댑터, 푸시 게이트웨이를 따로 볼 수 있습니다. docs/nips/에는 표준 Nostr가 다루지 않는 것을 정의한 초안 16개가 있습니다. 필요한 동작을 문서에서 먼저 확인하고 구현을 대조할 수 있다는 뜻입니다.

팀은 6인이고 각자 방이 있습니다

역할을 6개로 나눴습니다. 한 명이 모든 것을 하는 만능 에이전트를 만들지 않았습니다.

이름 역할 주 업무
하람 총괄 care-lobby 브리핑, 에스컬레이션, 승인 조율
리아 리뷰 app-reviews 16개 언어 앱스토어 리뷰 응답, 평점 관리
다온 커뮤니티 community 포럼 운영, FAQ, 신고 처리
우진 메일 mail-desk 메일 티켓, 1차 응답
세나 지표 analytics-kpi 앱 분석, KPI와 CSAT 리포트
태산 운영 cluster-ops 클러스터·서비스 상태, 장애 초동

6인 전원이 같은 모델을 쓰고 출력 언어는 한국어로 고정했습니다. 각자 프로필 이벤트를 발행하고 아바타를 갖습니다. 데스크톱 클라이언트에서 보면 사람 옆에 나란히 앉아 있습니다.

역할을 나눈 이유는 능력 분배가 아니라 권한 분배입니다. 리뷰 담당은 앱스토어에 답글을 쓸 수 있고 메일을 보낼 수 없습니다. 총괄과 지표 담당은 아무것도 쓸 수 없습니다. 읽고 판단하고 사람에게 넘깁니다.

방을 나눈 이유는 라우팅입니다. 앱스토어 리뷰는 리뷰 방으로, 메일은 메일 방으로 들어옵니다. 담당이 아닌 에이전트는 그 방의 멤버가 아니라 이벤트를 받지 못합니다. 채널 목록이 곧 업무 분장입니다.

배포는 세 단계입니다

하루 안에 떴습니다. 순서가 중요합니다.

첫째, 릴레이를 Helm으로 올립니다. 차트가 postgres·redis·minio를 번들로 가져옵니다. 값 파일에 릴레이 URL과 소유자 공개 키를 넣으면 끝입니다. 이미지도 공개된 것을 그대로 씁니다. 릴레이를 빌드할 일이 없습니다.

둘째, 시크릿과 원장 데이터베이스를 만듭니다. 에이전트 키, 외부 서비스 자격 증명, 원장 접속 정보 세 묶음입니다. 시크릿과 클러스터 변경은 사람이 실행하고 에이전트는 매니페스트까지만 만듭니다.

셋째, 에이전트를 배포합니다. 스크립트 하나가 채널 식별자와 인그레스 주소, 승인자 목록을 자동으로 해석해 Deployment 6개를 만듭니다.

지금 상태를 클러스터에서 세어 보면 이렇습니다.

워크로드
릴레이 1
postgres · redis · minio 3
에이전트 6
원장 데이터베이스 1

21시간 연속 가동 시점의 릴레이 이벤트 분포입니다. 채널 메시지가 59건, 프로필이 37건, 채널 메타·관리자·멤버 이벤트가 각각 23건, 채널 생성이 6건입니다.

여기서 채널 메시지 59건이 중요합니다. 사람이 데스크톱에서 보낸 메시지와 에이전트가 보낸 메시지가 같은 종류의 이벤트입니다. 어느 쪽이 무엇을 말했는지 한 번의 조회로 확인됩니다.

권한은 프롬프트가 아니라 등록 지점에서 갈립니다

에이전트가 외부와 닿는 지점은 하나입니다. 6인이 각자 API 클라이언트를 갖는 대신 MCP 서버 하나를 지납니다. 그 서버는 앱스토어, 커뮤니티 포럼, 메일 서버, 클러스터 상태 읽기, 원장 데이터베이스에 연결됩니다.

권한은 도구 목록에서 갈립니다. 역할별 허용 목록에 없는 도구는 세션에 등록되지 않습니다. 모델이 그 도구의 존재를 모르므로 호출 시도가 발생하지 않고, 거부 로그를 사람이 확인할 일도 없습니다.

외부에 흔적을 남기는 행위는 두 겹을 지납니다.

게이트 기본값 열면 가능해지는 것
환경 플래그 닫힘 리뷰 응답 등록, 커뮤니티 답글, 메일 발송
클러스터 쓰기 플래그 닫힘 워크로드 재시작
승인자 목록 비어 있음 = 전면 차단 승인 권한을 가진 사람의 공개 키

환경 플래그가 열려 있고 승인자 목록에 있는 사람이 승인해야 실행됩니다. 둘 중 하나만으로는 아무것도 나가지 않습니다. 이 규칙은 프롬프트 문장이 아니라 MCP 서버 코드에 있습니다.

16개 언어는 호출 전에 막습니다

앱스토어 리뷰 응답은 출시 국가의 언어로 갑니다. App Store Connect에서 실측한 로케일이 16개입니다.

ko  ja  zh-Hans  zh-Hant  en-US  de-DE  fr-FR  es-ES
it  nl-NL  pt-BR  tr  vi  da  no  sv

여기서 실수하기 쉬운 지점이 있습니다. 앱스토어의 국가 코드는 세 글자이고 로케일은 두 글자 기반입니다. 매핑이 없는 국가는 영어로 보내고 폴백 표시를 남깁니다.

더 중요한 것은 언어가 어긋날 때의 동작입니다. 응답 본문의 언어가 대상 로케일과 다르면 앱스토어를 호출하기 전에 거절합니다. 프랑스어 리뷰에 한국어 답변이 나가는 경로가 코드로 막혀 있습니다. 모델이 실수해도 외부에 나가지 않습니다.

성공한 응답은 원장에 기록됩니다. 무엇을 어느 언어로 언제 보냈는지가 남습니다.

원장은 사람이 검증할 수 있게 남깁니다

에이전트에게 일을 넘기면 무엇을 했는지 확인할 방법이 필요합니다. 그래서 별도 데이터베이스에 원장 테이블을 세웠습니다.

테이블 무엇을 담는가
KPI 목표 지표별 목표값과 기간
KPI 표본 시점별 측정값
CSAT 응답 채널·로케일별 만족도 점수
리뷰 응답 앱스토어에 실제로 나간 답변
승인 요청 누가 무엇을 요청하고 누가 승인했는가
티켓 이벤트 메일과 문의의 상태 변화
SLA 정책 채널별 응답 목표 시간

첫 목표는 월간 고객만족도 평균 4.5점으로 등록했습니다. 지금 원장에 들어 있는 것은 흐름을 점검하기 위해 넣은 데이터입니다. 승인 요청 세 건과 만족도 응답 세 건, 티켓 하나로 경로가 끝까지 도는지 확인했습니다. 실제 집계는 운영이 쌓이면서 채워집니다.

원장을 먼저 만든 이유는 순서 때문입니다. 지표를 나중에 붙이면 그 시점 이전의 기록이 없습니다. 에이전트가 첫 응답을 보내기 전에 기록할 자리가 있어야 합니다.

이 구조를 골라서 얻은 것

배포하고 나서 달라진 것을 정리하면 네 가지입니다.

대화가 우리 클러스터 안에 있습니다. 릴레이, 저장소, 검색 인덱스가 모두 자체 호스팅입니다. 나중에 다른 도구로 옮기더라도 이벤트 로그를 그대로 갖고 갑니다.

에이전트가 하는 일이 사람이 하는 일과 같은 자리에 남습니다. 감사할 때 두 곳을 보지 않습니다. 채널을 열면 사람의 판단과 에이전트의 조회가 시간 순으로 함께 있습니다.

권한을 설명하지 않고 강제합니다. 프롬프트에 "너는 읽기만 한다"라고 적는 것과 도구가 손에 없는 것은 다릅니다. 후자는 지시를 어길 방법이 없습니다.

하나를 늘릴 때 나머지를 건드리지 않습니다. 역할을 하나 더 만들려면 허용 목록과 채널 멤버십을 추가하면 됩니다. 나머지 5인의 설정은 그대로입니다.

혼자 만드는 사람에게 가장 큰 이익은 마지막 항목입니다. 늘릴 때 드는 비용이 일정하면 늘릴 수 있습니다.

같은 상황을 만나면

  • 고객 대화가 어느 저장소에 쌓입니까? 나중에 스레드 구조와 승인 이력까지 가져올 수 있습니까?
  • AI 에이전트에게 관리 API를 따로 열어 주고 있습니까, 사람과 같은 프로토콜로 붙입니까?
  • 역할별 권한이 프롬프트에 적혀 있습니까, 도구 등록 지점에서 강제됩니까?
  • 외부에 흔적을 남기는 행위에 사람 승인이 코드로 걸려 있습니까?
  • 다국어 응답에서 답변 언어와 대상 언어가 어긋날 때 외부 호출 전에 막습니까?
  • 지표를 기록할 자리를 첫 응답 이전에 만들어 두었습니까?