지난 기사에서는 로컬 AI Gateway에 **감사 로그 (Audit Log)**를 구현했습니다.
이번에는 해당 게이트웨이에
Policy Engine
을 추가로 구현한 내용을 소개합니다.
왜 Policy Engine이 필요한가
AI 요청 처리에서,
"무엇을 탐지했는가"와 "그것을 어떻게 처리할 것인가"는 본래 별개의 책임입니다.
- 탐지(Detection)는 "무엇이 있는지"를 찾는 것
- 제어(Control)는 "그것을 어떻게 다룰지"를 결정하는 것
이 두 가지가 혼재되어 있으면,
제어를 변경할 때마다 코드를 수정해야 합니다.
예를 들어:
- 이메일 주소 처리를
warn에서 block으로 바꾸고 싶을 때
- 향후 운영 환경과 개발 환경에서 서로 다른 정책(Policy)을 적용하고 싶을 때
이러한 변경을 설정만으로 수행할 수 있도록,
제어 판단을 코드로부터 분리하는 것
이 이번 구현의 목적입니다.
문제는 "탐지와 액션이 분리되어 있지 않다는 것"
이전까지의 구현에서는 Detector가
"무엇을 탐지했는가"와 "어떻게 다룰 것인가 (block/warn/info)"
를 모두 담당하고 있었습니다.
즉, 이메일 주소를 warn에서 block으로 바꾸려면,
Detector의 코드를 수정해야 했습니다.
환경마다 동작을 다르게 할 수도 없었으며,
설정이 아닌 구현을 변경하는 것이 전제되어 있었다
라는 문제가 있습니다.
구현한 내용
탐지 결과(Finding)와 제어 판단(PolicyResult)을 분리하는
Policy Engine
을 구현했습니다.
구현한 내용은 다음 세 가지입니다.
- 탐지 타입별 액션(Action)을 YAML 파일로 설정
- 사용 가능한 모델을 허용 리스트(Allowlist)로 관리
- 감사 로그에 탐지 결과와 제어 판단을 모두 기록
아키텍처
Detector는 "무엇을 탐지했는가"를 반환하고,
Policy Engine이 "어떻게 다룰 것인가"를 결정하며, Gateway가 block/forward를 실행합니다.
정책 설정
정책은 YAML 파일로 관리합니다.
findings:
openai_api_key: block
email_address: warn
...
설정할 수 있는 내용은 두 가지입니다.
findings 섹션
탐지 타입별로 block / warn / info 중 하나를 지정합니다.
models 섹션
사용을 허용할 모델을 나열합니다. 리스트에 없는 모델은 차단됩니다.
이는 비용 관리뿐만 아니라, 의도하지 않은 모델로의 전송을 방지하려는 목적도 있습니다.
평가 메커니즘
Policy Engine은 요청에 대해 두 가지 종류의 평가를 수행합니다.
모델 평가
요청에서 지정된 모델이 허용 리스트에 포함되어 있는지 확인합니다.
func (e *Engine) EvaluateModel(model string) PolicyResult {
for _, allowed := range e.config.Models.Allowed {
if model == allowed {
...
허용 리스트가 비어 있는 경우에도 모든 모델을 차단합니다.
탐지 결과 평가
탐지 결과(Finding)의 타입별로 YAML 설정을 참조하여 액션을 결정합니다.
func (e *Engine) EvaluateFindings(findings []detector.Finding) []PolicyResult {
for _, f := range findings {
action, ok := e.config.Findings[f.Type]
...
최종적으로, 평가 중 하나라도 block이 포함되어 있으면 요청을 거부합니다.
구현 시 의식한 점
탐지와 판단의 책임 분리
Detector는 "무엇을 탐지했는가"만을 반환합니다.
type Finding struct {
Type string
Confidence string
...
}
액션은 Policy Engine이 결정합니다.
이를 통해 코드를 변경하지 않고 YAML만 변경하여 제어를 바꿀 수 있습니다.
페일 세이프 (Fail-safe)
YAML에 정의되지 않은 탐지 유형 (detection type)이 나타날 경우,
기본적으로 block 처리합니다.
action, ok := e.config.Findings[f.Type]
if !ok {
action = ActionBlock // 설정에 없는 유형은 안전한 방향으로 처리
...
"누락된 설정은 통과시킨다"가 아니라, "누락된 설정은 차단한다"라는 설계입니다.
탐지와 판단을 모두 감사 로그 (Audit Log)에 남기기
감사 로그에는 findings (탐지 결과)와 policy_results (제어 판단)를 모두 기록합니다.
{
"findings": [
{"type": "email_address", "confidence": "medium", "matched": "test***"}
...
나중에 YAML을 변경하더라도,
"탐지 결과는 동일하지만, 판단만 바뀌었다"
라는 점을 추적할 수 있습니다.
SSK 설계 패턴과의 대응
이번 구현은 SSK에서 AI Gateway의 "입력을 제어한다"를 구현한 사례입니다.
제어 판단을 코드가 아닌 설정으로 외부화함으로써,
"어떻게 제어할 것인가"를 바꾸기 쉬운 상태로 만드는 것
이 이번의 주요 목적입니다.
요약
- 탐지와 제어 판단이 분리되어 있지 않으면, 정책 변경 시마다 코드 수정이 필요함
- Policy Engine을 도입하여 YAML로 제어 규칙을 관리할 수 있도록 함
- 모델의 허용 목록 (allowlist)과 탐지 유형별 액션 (action)을 YAML로 설정함
- 설정에 없는 유형은 페일 세이프 (fail-safe)로서 차단함
- 감사 로그에 탐지 결과와 제어 판단을 모두 남겨 정책 변경을 추적 가능하게 함
Discussion