Agent Safety Boundary

Safety boundary는 "위험한 응답을 하지 말라"는 안전 prompt가 아니라, 위험한 input, action, output이 반드시 통과하는 structural control point다. 직접 source는 Leafy 프로젝트 한 사례이며, 특정 class topology를 보편 표준으로 선언하지 않고 "우회 불가능한 review path"라는 원칙만 재사용한다.

Leafy 사례: 단일 챗봇에서 Router + Moderator로

flowchart LR
    U[User] --> R[AgentRouter]
    R --> C[Crisis agent]
    R --> E[Emotion agent]
    R --> F[Fallback agent]
    C --> S[SafetyModerator]
    E --> S
    F --> S
    S --> O[Response or escalation]

SafetyModerator가 하는 일

Warning

Leafy 사례는 의료 안전성이나 WHO guideline 준수를 독립 검증한 연구가 아니다. Project-specific routing 사례로만 사용한다.

경계는 input/authorization/output 어디에나 있을 수 있다

경계 검사 실패 시
Input crisis, injection, prohibited data route, redact, reject
Authorization tool, consent, scope deny or approve
Observation schema, provenance validate, quarantine
Output harm, privacy, policy rewrite, block, escalate
Publication source link, human approval, visibility draft 유지, reject, 재검수
Retrieval role, department, published status 검색·답변 근거에서 제외

Leafy 사례는 이 표의 Input/Output 행만 직접 다루고, Authorization 행은 아래 장애 관제 Agent 사례가 직접 다룬다. Irreversible action은 output rewrite로 되돌릴 수 없으므로 실행 전 authorization이 필요하다는 것과, moderator도 model이면 false negative가 있어 deterministic rule·allowlist·human escalation·evaluation을 병행해야 한다는 것, fail-open과 fail-closed는 위험도에 따라 달라야 한다는 것은 이 페이지가 Leafy 사례로부터 일반화한 해석이다.

Authorization 경계: 장애 관제 Agent 사례

도구 결과에 섞인 지시: 간접 프롬프트 인젝션

// CommandExecutorService
private static final Pattern ALLOWED_CONTAINER_NAME =
        Pattern.compile("^[a-zA-Z0-9][a-zA-Z0-9_.\\-]*$");

public boolean isAllowed(String command) {
    String[] parts = command.trim().split("\\s+");
    return parts.length == 3
            && "docker".equals(parts[0])
            && "restart".equals(parts[1])
            && ALLOWED_CONTAINER_NAME.matcher(parts[2]).matches();
}

Publication과 Retrieval 경계: 사내 위키 요구사항 사례

흔히 겪는 실패

테스트 질문

관련

출처


  1. multi-agent-safety-moderator.md — "일반 공감... 자기성찰 요청... 명시적 도움 요청", "일반 대화 맥락에서 갑작스러운 위기 발화를 감지할 보조 레이어 없음", "에이전트 응답 품질이 프롬프트 설계에만 의존 — 구조적 보장 없음", "위험 수준 분류 및 단계별 대응 로직 부재", "AgentRouter (의도 분류)... ChatBot... ReflectionAgent... ActionAgent... SafetyModerator (후처리)" (L61-68), "에이전트 응답이 SafetyModerator를 거치지 않고 사용자에게 도달하는 경로가 존재하지 않는다.", "위험 수준 분류 (none / monitor / high)", "high: 응답 재작성 + 상담 리소스 강제 삽입" (L70-71), "escalationRequired: boolean; // 즉각 인적 개입 필요 여부" (L115), "high 판정 시 SafetyModerator가 응답을 재작성하며, 반드시 포함하는 요소:... 직접 질문... 상담 리소스 안내" (L119-121), "fallbackRouting... API 장애 시: 키워드로 reflection / action 판별" ↩︎ ↩︎ ↩︎ ↩︎ ↩︎ ↩︎ ↩︎

  2. 01-프로젝트-개요.md — "Alertmanager 웹훅을 수신해 AI가 자율적으로 분석하고 Discord를 통해 결과와 조치를 제안하는 에이전트" (L18), "AlertPlaybook (알람별 허가된 조치 목록 조회)" (L35), "운영자가 yes를 입력해야 실행된다. 허가된 명령 패턴(docker restart <name>)만 실행 가능하다." ↩︎ ↩︎

  3. 03-도구를-두-종류로-나눈-이유.md — "AgentTools는 읽기 전용으로 유지했다.", "ConversationTools는 ConversationAgent 전용으로 만들고, execute_command 하나만 넣었다.", "에이전트가 스스로 결론을 내리는 것과, 그 결론에 따라 직접 실행하는 것 사이에 경계를 두기로 했다.", "docker restart <container-name> 형식만 허용한다.", "PendingApprovalStore에 30분 TTL로 저장되고, Discord에서 "yes"가 들어와야 실행된다.", "에이전트의 출력이 스스로 실행을 트리거하는 건 불가능하다.", "혼자 운영하는 서버라도", "채널 ID를 키로 저장하기 때문에, 같은 채널에서 "yes"를 입력한 사람만 해당 채널의 대기 명령을 실행할 수 있다.", "에이전트가 프롬프트 인젝션을 당하거나 예상치 못한 명령을 생성하더라도 이 검증을 통과하지 못하면 거부된다.", AgentTools의 query_loki(containerName, timeRange) // 로그 조회, execute_command의 if (!commandExecutorService.isAllowed(command)) 뒤 pendingApprovalStore.store(command, alertId, channelId);, CommandExecutorService.isAllowed() 코드, "검증을 통과한 명령도 바로 실행되지 않는다." ↩︎ ↩︎ ↩︎ ↩︎ ↩︎ ↩︎ ↩︎ ↩︎ ↩︎ ↩︎

  4. monitoring-agent-authorization-code-2026-10-09-a1ca1b7.md — clean local checkout a1ca1b7de5065ee50e5d0bca9a19ad5ea4753c2c의 선택 발췌. AlertAnalysisChain.java L66–99·L116–133은 playbook 조회와 AUTO 직접 실행, AlertPlaybook.java L16–25·L88–89는 승인 템플릿과 고정 AUTO 조치, DiscordNotificationService.java L73–92는 NEEDS_APPROVAL의 대기 저장, ConversationAgent.java L59–80 및 AgentTools.java L112–150은 조회·제안 도구 등록과 Loki 문자열 반환, ConversationTools.java L14–50은 형식 검사 뒤 대기 저장, CommandExecutorService.java L22–32·L53–88은 공통 형식 검사와 인자 분리 실행, DiscordBotListener.java L49–90·L92–126 및 PendingApprovalStore.java L14–58은 조건부 채널·role 검사와 명령·채널 기반 승인이다. 전체 파일 해시는 snapshot manifest에 있다. 배포 버전과 런타임 설정은 확인하지 않았다. ↩︎ ↩︎ ↩︎ ↩︎ ↩︎ ↩︎ ↩︎ ↩︎ ↩︎

  5. Backend-Agent-Developer-Roadmap.md — AgentDojo 항목 "외부 도구가 반환한 데이터에 악성 명령이 포함되는 간접 프롬프트 인젝션을 평가하는 환경입니다. 이메일, 웹페이지, 검색 결과를 읽고 실제 액션을 수행하는 에이전트를 만든다면 반드시 읽어야 합니다." (L42), "함께 읽을 논문은 Not What You’ve Signed Up For입니다. LLM 애플리케이션에서 데이터와 명령의 경계가 흐려질 때 발생하는 간접 프롬프트 인젝션을 설명합니다." (L43). 논문 자체가 아니라 로드맵 글의 소개다. ↩︎

  6. 03-에이전트-파이프라인.md — DiscordNotificationService 분기 "[NEEDS_APPROVAL] → 분석 결과 + 명령 실행 승인 요청", "[AUTO] → 명령 자동 실행 후 결과 전송" (L38-39), "@Tool 어노테이션이 붙은 메서드를 Gemini가 tool call하면, Spring AI가 해당 메서드를 실행하고 결과를 다음 Gemini 호출에 포함시키는 과정을 자동으로 반복한다." (L71), ConversationAgent 예시 "에이전트: [query_prometheus 즉시 호출]" (L135) ↩︎

  7. 요구사항 명세서.md — FR-WIKI-003 "공개 전 위키는 사원에게 노출하지 않으며 챗봇 검색 대상으로 사용하지 않는다.", FR-USR-008 "권한이 없는 자료는 목록, 검색, 상세, 다운로드 및 챗봇 검색 대상에서 제외한다.", NFR-SEC-003 "삭제되거나 비공개 처리된 문서와 비공개 처리된 위키는 사원 화면, 검색 결과, 챗봇 검색 대상, 찜하기 목록, 최근 본 기록에서 조회 가능한 대상으로 노출되지 않아야 한다." (L156), FR-QNA-004 "근거 문서를 찾지 못한 경우 시스템은 정보 부족을 안내해야 한다.|추측 답변을 생성하지 않는다." ↩︎ ↩︎ ↩︎

  8. 요구사항을 기반으로한 로직 프로세스.md — 챗봇 질의응답 흐름 확인 항목 "근거 없는 답변 금지" ↩︎