day5_practice/guards_input.py · guards_refusal.py · guards_output.py · guards.py · grade_guard.py

가드레일 플레이어

문장 하나가 ① 규칙 검사(정규식) → ② LLM 판별(구조화 출력) → 모델 → ③ 출력 가드 중 어느 층에서 걸리고 어느 층을 통과하는지 한 단계씩 따라갑니다. 공격 문장, 정상 문장, 규칙을 빠져나가는 공격, 개인정보가 담긴 응답, 공격·정상 세트 채점을 탭으로 나눴습니다. 규칙 검사와 마스킹 결과는 함수 본문을 떼어 실제로 돌린 값이고, LLM 판별과 모델 응답은 (예시) 입니다.

차단 차단 (확신도 > 0.7) handler 없이 반환 tool_calls 있음 도구 결과를 들고 다시 (입력 가드도 다시) 최종 입력 Human InputGuardrailMiddleware.wrap_model_call ① rule_check 정규식 · 금지 주제 ② llm_check 구조화 출력 model handler(request) ③ OutputGuardrail .after_model PII → 범위 → 근거 사용자 refusal_message(reason) 고정 문구 + guard_audit.log tools 도구 실행

검사 상태

노란 줄이 이번 단계에서 바뀐 값

State["messages"]

guard_audit.log

시각 ⇥ 종류 ⇥ 사유 (사용자에게는 안 보임)

터미널 출력

층마다 무엇을 보고, 걸리면 무엇을 하나

층자리보는 것비용 · 성질걸리면
① 규칙rule_check (guards_input.py)정규식 8개, 금지 주제 문자열 4개. 처음 걸린 것에서 멈춤공짜, 빠름, 같은 입력엔 항상 같은 결과. 적어 둔 표현만 잡음(True, "[규칙] …"). LLM 판별을 부르지 않음
② LLMllm_check (guards_input.py)표현이 아니라 의도. InjectionCheck(is_injection, confidence, reason)요청마다 모델 호출 1번. 판별 모델도 틀리거나 속을 수 있음is_injection 이고 confidence > 0.7 일 때만 (True, "[LLM] …"). 예외면 통과(fail-open)
거절refusal_message (guards_refusal.py)사유에 "금지 주제" 가 있는지모델이 쓰지 않는 고정 문자열이라 주입될 수 없음종류별 고정 문구 AIMessage. 사유는 guard_audit.log 에만
③ 출력OutputGuardrailMiddleware.after_modeltool_calls 없는 최종 응답만. PII → 금지 주제 → 근거(LLM) 순서, 걸리면 즉시 return3단계만 모델 호출. 판별 모델은 응답 본문만 받음부분 마스킹 / 통째로 고정 문구 / 안내 문구 덧붙임. 모두 같은 id 로 교체

입력 가드는 before_model 이 아니라 wrap_model_call 입니다. before_model 은 State 를 고칠 수는 있어도 모델 호출 자체를 대신할 수 없지만, wrap 은 실제 호출을 하는 handler 를 쥐고 있어 부르지 않기로 할 수 있습니다. 그래서 차단된 요청에는 본 모델 비용이 들지 않고 공격 문장이 본 모델에 닿지도 않습니다.

공격 세트 10문항을 규칙에만 넣어 본 결과

rule_check 와 패턴 목록만 그대로 복사해 실행한 결과입니다. LLM 을 부르지 않으므로 항상 같습니다. 정상 세트 10문항은 모두 규칙을 통과했습니다(오탐 0). 따라서 정상 세트에서 오탐이 나온다면 LLM 판별에서 나온 것입니다.

함정과 주의

관련 파일