
고객사 망 안에 띄운 모델과 외부 API 모델을 한 설치에 함께 두면, 쓸 모델을 정해 두는 것만으로는 사내 데이터가 밖으로 나가는 일을 막지 못합니다. 내부 모델로 인사 DB의 연봉을 조회한 대화를 외부 모델로 이어 가면, 앞서 조회한 결과가 대화 맥락에 실려 외부 프로바이더로 갑니다. 처음부터 외부 모델로 연 대화도 위험합니다. 대화 첫머리에는 쓸 수 있는 DB 목록과 업무 요약이 실리는데, 거기 「인사 DB — 임직원 연봉·평가」라는 줄이 있으면 그 이름과 설명이 이미 외부로 나간 것입니다. 웹 페이지에 숨겨 둔 지시가 그 DB를 조회하라고 시킬 수도 있습니다.
이 글은 모델과 데이터를 잇는 판정 하나를 다룹니다. 프로바이더에는 데이터가 망 안에 남는지를 뜻하는 등급을, DB 연결·문서 컬렉션·커넥터에는 「내부 모델 전용」 표시를 붙입니다. 턴(질문 하나를 받아 답을 내기까지의 한 번)을 시작할 때 그 턴의 모델과 등급을 먼저 확정하고, 외부 모델로 도는 턴에는 「내부 모델 전용」 자원을 이름까지 싣지 않습니다. 대화는 시작한 등급으로 끝까지 갑니다.
모델 허용과 역할 권한은 대화 하나 안을 보지 못한다
관리자가 쓸 수 있는 설정은 이미 둘 있습니다. 모델 관리 화면에서 모델마다 사용 허용을 켜고 끄면, 끈 모델은 새 대화의 모델 목록과 에이전트 기본값 목록, 분석 작업에서 빠집니다. 에이전트 기본값은 채팅, DB 분석, 회의 요약처럼 모델을 부르는 기능마다 기본 모델을 정합니다. DB 분석과 회의 요약은 내부 모델에 배정하고 일반 채팅에서만 외부 모델을 고르게 둘 수 있습니다.
두 설정은 어떤 모델을 쓸 수 있는지를 정할 뿐, 그 모델이 받는 대화에 무엇이 들어 있는지는 보지 않습니다. 첫 문단의 두 경우는 모두 허용된 모델과 권한이 있는 사용자 사이에서 일어납니다. 역할 권한도 마찬가지입니다. 역할 권한은 누가 인사 DB를 볼 수 있는지를 정하고, 그 사용자가 어느 모델로 보는지는 묻지 않습니다. 인사 담당자는 내부 모델로도 외부 모델로도 같은 DB를 조회할 수 있습니다.
실행 직전에 막는 방법도 있습니다. 외부 모델 턴에서 인사 DB 조회를 거절하면 행 값은 나가지 않습니다. 하지만 거절은 모델이 그 DB를 알고 부른 뒤에야 일어납니다. 그때는 DB 이름과 업무 요약이 이미 프롬프트에 실려 나갔습니다. 실행 직전에 막는 방법은 대화 첫머리에 싣는 권한 없는 DB의 이름 줄도 어쩌지 못합니다. 그 줄은 모델이 「그 DB는 볼 수 없다」고 답할 수 있게 싣는 것인데, 외부 모델에게는 그 이름들도 밖으로 나가는 정보입니다.
등급은 프로바이더에 붙고, 설치 설정이 정한다
모델 쪽 표시는 프로바이더 등급이고 값은 둘입니다. private은 데이터가 고객사 망 안에 남는 프로바이더, public은 망 밖으로 나가는 프로바이더입니다. 이 글에서 내부 모델은 private 프로바이더의 모델을, 외부 모델은 public 프로바이더의 모델을 말합니다. 모델은 자기 프로바이더의 등급을 그대로 따릅니다.
등급은 설치자가 설정 파일에 프로바이더마다 적고, 관리 화면은 보여 주기만 합니다. 그 주소가 사내 서버인지 외부 서비스인지는 base URL을 적는 사람이 압니다. 등급을 화면에서 바꿀 수 없으므로, 운영 중에 잘못 누른 한 번으로 외부 프로바이더가 내부로 바뀌지 않습니다.
등급을 정하는 기준은 프로바이더 종류가 아니라 주소입니다. vLLM은 대개 사내 GPU 서버에 띄우지만 클라우드에 띄울 수도 있습니다. 같은 vLLM이라도 사내 주소면 private, 클라우드 주소면 public입니다.
「내부 모델 전용」은 자원 자체에 붙는 표시다
데이터 쪽 표시는 자원마다 하나입니다. 관리자는 DB 연결, 문서 컬렉션, 커넥터(MCP 서버처럼 회사가 연결한 외부 서비스)마다 「모든 모델」과 「내부 모델 전용」 가운데 하나를 고릅니다. 이 표시는 역할 권한과 따로 있습니다. 외부 모델 턴에서 어떤 자원을 쓰려면 사용자에게 그 자원의 권한이 있고, 자원이 「모든 모델」이어야 합니다.
private 프로바이더가 있는 설치에서는 새 자원의 기본값이 「내부 모델 전용」입니다. 관리자가 칸을 빠뜨려도 그 자원은 외부 모델에 보이지 않고, 외부 모델에 열어도 되는 자원만 관리자가 직접 엽니다.
한 번 「모든 모델」로 연 자원은 「내부 모델 전용」으로 되돌릴 수 없습니다. 열려 있던 동안 그 자원의 데이터가 외부 모델 대화에 실렸을 수 있기 때문입니다. 이미 나간 데이터를 내부 모델 전용이라고 부르면 표시가 사실과 어긋납니다. 외부 모델에 닿으면 안 되는 데이터가 나중에 생기면 새 DB 연결이나 새 컬렉션을 만들어 「내부 모델 전용」으로 둡니다.
외부 모델 턴에는 그 자원이 어디에도 나타나지 않는다
턴이 외부 모델로 돌면 「내부 모델 전용」 자원은 그 턴이 모델에게 보내는 내용 어디에도 없습니다. 빠지는 자리는 다음과 같습니다.
- 대화 첫머리에 싣는 DB·문서 목록과 업무 요약
- 권한이 없는 DB의 이름 목록
- @멘션으로 고를 수 있는 대상
- 도구 목록과 도구 설명, 대화 중에 나중에 불러오는 도구
- 「내부 모델 전용」 커넥터가 내는 도구 전부
외부 모델은 인사 DB가 있다는 사실을 모르므로 조회를 시도할 수 없습니다. 웹 페이지나 문서에 숨겨 둔 지시도 모델이 모르는 DB를 가리킬 수 없습니다. 모델이 다른 경로로 이름을 알아내 직접 부르더라도, 도구를 실행하기 직전에 같은 판정이 한 번 더 돌아 그 호출을 거절합니다.
노출은 도구 목록을 만드는 한 곳에서, 실행은 모든 도구 호출이 지나는 실행부 한 곳에서 막습니다. 채팅, 플레이북, 딥 리서치가 같은 실행부를 쓰므로 표면마다 따로 막을 자리가 생기지 않습니다. 두 곳 모두 판정 도중 오류가 나면 그 자원을 빼거나 호출을 거절합니다.
딥 리서치에서 총괄이 띄우는 조사원은 총괄 턴의 실행 문맥을 그대로 물려받으므로 같은 자원이 빠집니다. 회의 요약은 사용자가 고른 요약 모델의 등급으로 같은 판정을 거칩니다.
대화는 시작한 등급으로 끝까지 간다
판정은 턴을 시작하기 전에 합니다. 먼저 이 턴이 돌 모델을 확정합니다. 대화에 고른 모델이 있으면 그 모델, 없으면 그 기능의 기본 모델입니다. 모델에서 프로바이더를, 프로바이더에서 등급을 읽어 턴의 실행 문맥에 싣고, 대화 첫머리 조립과 @멘션 해석은 그 뒤에 합니다. 모델을 확정하지 못하면 등급을 짐작하지 않고 턴을 거절합니다.
대화는 처음 정해진 등급으로 고정됩니다. 내부 모델로 시작한 대화에는 「내부 모델 전용」 자원에서 읽은 결과와 사용자가 붙여 넣은 사내 자료가 섞여 있을 수 있으므로, 끝까지 내부 모델로만 이어 갑니다. 외부 모델로 시작한 대화는 외부 모델로 이어 갑니다. 첫 문단의 첫 번째 경우가 이 규칙으로 막힙니다.
대화의 모델 목록에는 처음부터 같은 등급의 모델만 뜹니다. 대화 도중 모델을 바꿀 때도 이 목록 안에서 고릅니다. 다른 등급의 모델로 요청이 들어오면 턴 시작 판정이 거절하고, 그 모델을 쓰려면 새 대화를 열라고 안내합니다. 대화에서 만든 플레이북도 그 대화의 등급을 따릅니다.
사용자가 직접 입력하거나 붙여 넣은 텍스트에는 표시가 없어 판정할 근거가 없습니다. 그래도 내부 모델 대화에 붙여 넣은 내용은 대화가 고정되어 있어 내부 모델 밖으로 나가지 않습니다.
임베딩과 음성 인식은 턴이 아니라 설치가 고른다
문서를 검색하려면 문서를 임베딩 모델로 보내야 하고, 회의를 받아 적으려면 음성을 음성 인식 모델로 보내야 합니다. 이 모델들은 턴마다 고르는 것이 아니라 설치 설정이 역할마다 하나씩 정합니다. 그래서 턴 시작 판정이 아니라 설치 단계에서 확인합니다.
구축형 설치는 채팅, 임베딩, 리랭커, 음성 인식을 모두 망 안의 자체 호스팅 모델로 돌릴 수 있습니다. 설정 예시는 외부로 나가는 선택마다 무엇이 나가는지 적어 둡니다. 관리형 회의 전사 서비스를 쓰면 회의 음성이, 호스팅 음성 인식을 쓰면 마이크 음성이 외부로 갑니다. 임베딩도 같습니다. 「내부 모델 전용」 자원이 있는 설치에서 임베딩을 public 프로바이더에 두면 그 자원의 문서가 임베딩할 때 밖으로 나가므로, 기동할 때와 자원을 등록할 때 경고가 뜹니다.
설계에서 고른 것
거절만 하는 대신 숨기기를 골랐습니다. 실행 직전에 거절하는 쪽이 구현은 단순하고, 자원 목록을 모델마다 다르게 만들 필요도 없습니다. 하지만 거절은 모델이 자원의 이름과 용도를 이미 안 뒤에 일어나고, 그 이름과 용도는 프롬프트에 실려 나간 뒤입니다. 숨기면 외부로 나갈 것이 처음부터 없습니다. 실행부의 거절은 숨김을 비켜 간 호출을 받는 두 번째 확인으로 남겼습니다.
대화에 들어온 데이터를 보고 모델을 바꿔 주는 대신 대화를 시작한 등급으로 고정했습니다. 데이터를 따라 모델을 바꾸려면 호출마다 대화 맥락의 어디에 어느 자원의 결과가 남았는지 따져야 하고, 압축이나 편집이 끼면 그 판단은 틀리기 쉽습니다. 고정하면 판정은 턴 시작에 한 번이고, 화면에 보이는 모델이 곧 답한 모델입니다. 대가는 외부 모델을 쓰려면 새 대화를 열어야 한다는 점입니다. 내부 모델 대화의 모델 목록에는 외부 모델이 처음부터 뜨지 않으므로, 사용자가 거절을 만나는 일은 드뭅니다.
등급은 관리 화면이 아니라 설치 설정이 정합니다. 그 위에 쌓은 판정을 믿으려면 등급이 운영 중 클릭 한 번으로 바뀌면 안 됩니다. 등급을 private과 public 둘로만 둔 것도 같은 이유입니다. 「데이터 보호 계약이 있는 외부 모델」 같은 구분은 계약서가 보증하는 일이라 코드가 확인할 수 없습니다. 그런 프로바이더를 쓸지는 관리자가 모델 사용 허용으로 정합니다.
표시는 테이블이 아니라 DB 연결과 컬렉션 단위로 붙입니다. 테이블마다 붙이면 관리할 칸이 테이블 수만큼 늘고, 빠뜨린 칸이 곧 구멍이 됩니다. 한 DB 안에 민감한 테이블이 몇 개만 있으면 그 테이블만 보는 DB 계정으로 연결을 하나 더 만들어 「내부 모델 전용」으로 둡니다. 그러면 DB 계정의 권한과 자원 표시가 같은 경계를 가리킵니다.
내부 모델과 외부 모델을 함께 쓰는 설치라면, 어느 모델을 허용할지 정하기 전에 외부 모델에 닿으면 안 되는 DB와 문서의 목록부터 만듭니다. 그 목록이 「내부 모델 전용」으로 둘 자원이고, 목록에 든 테이블이 다른 테이블과 한 연결에 섞여 있으면 연결을 나눌 자리입니다.
어떤 문제를 해결하나요?
보안과 감사의 다른 글

엔지니어링 노트 · 보안과 감사
도구 결과에 나온 주소도 모델이 먼저 썼다면 열지 않는다
웹 페이지를 읽거나 주소에서 파일을 받는 도구는 대화에 먼저 나온 주소만 엽니다(「권한은 코드로 막고, 판단은 모델에 맡긴다」).

엔지니어링 노트 · 보안과 감사
답변에서 지운 전화번호도 모델은 이미 읽었다, 도구 결과에서 가린다
「지난주에 환불을 세 번 넘게 요청한 고객을 찾아 문의 내용을 정리해 줘」라는 질문은 숫자 하나로 끝나지 않습니다.

엔지니어링 노트 · 보안과 감사
답변만 검사하면 엑셀과 이슈 댓글로 샌다, 세 경로를 한 곳에서 검사한다
고객 문의를 분석하는 대화에서, 원문 열람 권한이 없는 사람이 보는 답변에는 전화번호가 [전화번호-1]로 가려져 있습니다(「답변에서 지운 전화번호도 모델은 이미 읽었다, 도구 결과에서 가린다」).

