채팅에 상주하며 업무를 지원하는 AI 에이전트를 조직에 도입하면, 조만간 '특정 멤버는 수집 및 요약 대상에서 제외해 달라'는 요청이 들어옵니다. 근로 상담 채널의 당사자, 휴직 중인 멤버, 혹은 단순히 'AI가 자신의 발언을 읽었으면 좋지 않다'는 개인적인 희망 등 이유는 다양합니다.
겉보기에는 단순한 요청처럼 보이지만, 실제로 구현하려고 하면 '제외(除外)'라는 단어가 지칭하는 범위가 생각보다 모호하다는 것을 깨닫게 됩니다. 이 글에서는 제외 설정을 나중에 기능 부전 상태에 빠지지 않도록 하는 설계를 정리합니다.
왜 '사용자 ID로 필터링만 하는 것'으로는 불충분한가
처음에 떠오르는 구현은, 제외 대상 사용자 ID 목록을 가지고 메시지 수집 시 해당 ID의 메시지를 버리는 것입니다.
function collectMessages(rawMessages: Message[], excludedUserIds: Set<string>) {
return rawMessages.filter((m) => !excludedUserIds.has(m.authorId));
}
이 구현은 '제외 대상자 자신이 발언하지 않는 한' 기능합니다. 하지만 현실의 채팅은 대화이며, 제외 대상자는 다른 멤버에게 말을 걸어지거나 이름을 언급당하거나 인용됩니다. 단순 필터링으로는 다음 중 어느 하나의 사고가 발생합니다.
- 제외 대상자의 발언만 사라져서, 남은 대화가 문맥적으로 파탄한 요약이 됩니다. ('B님의 의견에 찬성입니다'라는 발언만 남아 핵심인 B님의 의견이 사라지는 경우)
- 제외 대상자에 대한 타인이 한 내용은 필터링되지 않기 때문에, '본인의 발언은 읽지 않는다'는 약속을 했더라도 본인에 관한 정보는 AI 측으로 거의 그대로 유입됩니다.
즉, '제외'를 '발언자 필터(発言者フィルタ)'로만 구현하면, 본인이 기대하는 보호('자신에 관한 정보가 AI의 판단 재료가 되지 않는 것')와 구현이 제공하는 보호('자신이 작성한 문자열만 AI 입력이 되지 않는 것') 사이에 갭이 생깁니다. 이 갭은 제외 설정을 한 본인에게는 보이지 않습니다.
패턴 1: '제외'를 하나의 플래그가 아닌 3단계의 스코프로 정의하기
먼저, 조직이나 멤버가 '제외해 달라'고 말할 때 실제로 기대하는 범위를 분해하여 선택할 수 있게 합니다.
| 스코프 | 의미 | 구현상의 처리 |
|---|
| 발언 비수집 | 본인의 발언만 AI에 전달하지 않음 | 발언자 필터(위의 단순 구현과 동일) |
| ... | | |
| 어떤 스코프를 선택하느냐에 따라 요약의 일관성과 보호의 강도가 트레이드오프가 된다는 것을 신청 양식 단계에서 명시해 두면, 나중에 '생각했던 것과 다르다'라는 불일치가 발생하지 않습니다. 근로 관련 사안처럼 보호를 최우선해야 하는 케이스는 '완전 제외', 단순히 개인의 심리적 저항감에 대한 배려라면 '발언 비수집'으로 충분하다는 운영이 현실적입니다. | | |
패턴 2: 제외 설정 변경은 '앞으로의 발언'과 '과거의 발언'을 별도로 다루기
제외 리스트에 사용자를 추가하는 순간, 많은 구현은 '지금부터 발생하는 이벤트'에만 효과를 미칩니다. 하지만 본인이 기대하는 것은 대개 '향후의 발언'뿐만 아니라 '지금까지 AI가 읽은 내용까지 포함하여 잊어달라'는 소급적 보호입니다.
이 두 가지는 기술적으로 완전히 다른 작업입니다.
async function applyExclusion(userId: string, scope: ExclusionScope) {
// 1. 향후 이벤트에 적용(즉시 유효)
await exclusionListStore.add(userId, scope);
...
'지금부터는 제외되지만, 과거에 수집된 분은 어떻게 되는가'를 운영 측이 파악하고 있지 못하면, 감사나 본인으로부터 문의가 왔을 때 대답할 수 없습니다. 이는 'AI를 초대한 후, 과거의 대화는 어디까지 읽히는가'라는 도입 시 자주 듣는 불안(AI를 초대하면 과거의 대화는 어디까지 읽히는가에 대한 불안)의 반작용이기도 합니다. 제외 설정 변경 이벤트에는 반드시 소급 처리의 필요 여부와 완료 상황을 묶어서 기록해 두어야 합니다.
패턴 3: '본인이 AI에게 직접 말을 건넨 경우'는 제외의 예외로 설계하기
완전 제외 스코프를 설정했더라도, 본인이 봇에게 직접 DM으로 질문하면 당연히 AI가 응답해야 합니다. 여기서 '제외 리스트에 들어 있으니 일률적으로 응답하지 않는다'라는 구현을 해버리면, 본인의 편의성을 불필요하게 떨어뜨립니다.
제외(除外)는 '본인의 발언이나 본인에 관한 정보가 본인이 관여하지 않는 곳에서 수집·분석·공유되는 것'에 대한 보호이지, '본인이 능동적으로 AI와 상호작용하는 것'까지 금지할 필요는 일반적으로 없습니다. 이 두 가지를 하나의 플래그로 표현하려고 하면 어느 쪽의 경험이 손상됩니다. 구현 측면에서는 제외 목록 체크를 '백그라운드 수집·요약 파이프라인'에만 적용하고, '본인으로부터의 직접 요청 응답 파이프라인'에는 적용하지 않는 경로 분리가 필요합니다.
저희가 hach.work에서 개발 중인 AI 에이전트 HACH 역시 채팅·메일·캘린더에서 정보를 자동 수집하여 아침 보고서를 만드는 특성상, '특정 사람만은 수집 대상에서 제외하고 싶다'는 요청에 대한 기술적 대비는 피할 수 없는 주제라고 생각합니다. 도입 기업마다 판단 기준이 다르기 때문에, 제외의 세밀도(粒度)를 하나의 온/오프가 아닌 단계로 설계하는 것이 나중에 운영상의 유연성을 좌우합니다.
패턴 4: 제외 대상이 '누구의 관점에서 제외되고 있는지' 시각화하기
제외 목록이 쌓이다 보면 관리자 자신도 '현재 어떤 범위(スコープ)에서 누가 제외되어 있는지'를 정확히 파악할 수 없게 됩니다. 제외 설정은 기본적으로 늘어나기만 하고 자연스럽게 줄어들지 않기 때문에, 분기별 한 번 등 정기적으로 목록을 출력하여 '아직 유효한 이유가 있는지'를 확인하는 재고(棚卸し) 메커니즘을 마련해 두는 것이 안전합니다. 특히 휴직이나 전근이 이유였던 제외 설정은 상황이 변했음에도 방치되기 쉬워서, 본인이 복직했는데도 오래된 제외 설정 때문에 회의록에 자신의 담당 태스크가 반영되지 않는 등 역방향의 불편함을 초래할 수 있습니다.
요약
- '제외하고 싶다'는 요청을 발언자 필터만으로 구현하면, 본인이 기대하는 보호와 실제 보호 사이에 격차가 발생한다.
- 제외는 발언 비수집·언급 마스킹·완전 제외의 3단계 범위(スコープ)로 선택할 수 있도록 한다.
- 제외 설정 변경은 '향후 이벤트'와 '과거 수집된 데이터'를 별도의 태스크로 취급하고, 소급 처리 필요 여부를 명시적으로 기록한다.
- 본인이 AI에게 직접 말하는 경로는 백그라운드 수집 파이프라인과는 별개의 경로로 제외 대상에서 제외한다.
- 제외 설정은 자연스럽게 줄어들지 않으므로, 정기적인 재고 메커니즘을 마련해야 한다.
'읽히고 싶지 않다'는 요청에 기술적으로 대응하려면, 그 요청이 실제로 어떤 범위를 지칭하는지를 사용자와 함께 분해하는 작업이 구현 자체보다 중요할 때가 많습니다.
논의(Discussion)