가드레일
adk-guardrail을 사용한 입력/출력 유효성 검사 및 콘텐츠 안전.
개요
가드레일은 안전, 규정 준수 및 품질을 보장하기 위해 Agent의 입력과 출력을 검증하고 변환합니다. 이들은 Agent 실행과 병렬로 실행되며 다음을 수행할 수 있습니다:
- 유해하거나 주제에서 벗어난 콘텐츠 차단
- PII (이메일, 전화번호, SSN, 신용카드) 수정
- 출력에 JSON 스키마 적용
- 콘텐츠 길이 제한
설치
[dependencies]
adk-guardrail = "0.2.0"
# JSON 스키마 유효성 검사를 위한 설정
adk-guardrail = { version = "0.2.0", features = ["schema"] }
핵심 개념
GuardrailResult
모든 가드레일은 세 가지 결과 중 하나를 반환합니다:
pub enum GuardrailResult {
Pass, // 콘텐츠가 유효함
Fail { reason: String, severity: Severity }, // 콘텐츠 거부됨
Transform { new_content: Content, reason: String }, // 콘텐츠 수정됨
}
심각도 수준
pub enum Severity {
Low, // 경고만 표시하며 차단하지 않음
Medium, // 차단하지만 다른 검사를 계속함
High, // 즉시 차단함
Critical, // 차단하고 빠르게 실패함
}
PII 수정
개인 식별 정보를 자동으로 감지하고 수정합니다:
use adk_guardrail::{PiiRedactor, PiiType};
// 기본값: 이메일, 전화번호, SSN, 신용카드
let redactor = PiiRedactor::new();
// 또는 특정 유형 선택
let redactor = PiiRedactor::with_types(&[
PiiType::Email,
PiiType::Phone,
]);
// 직접 수정
let (redacted, found_types) = redactor.redact("Email: test@example.com");
// redacted = "Email: [EMAIL REDACTED]"
// found_types = [PiiType::Email]
지원되는 PII 유형:
| 유형 | 패턴 | 수정 결과 |
|---|---|---|
Email | user@domain.com | [EMAIL REDACTED] |
Phone | 555-123-4567 | [PHONE REDACTED] |
Ssn | 123-45-6789 | [SSN REDACTED] |
CreditCard | 4111-1111-1111-1111 | [CREDIT CARD REDACTED] |
IpAddress | 192.168.1.1 | [IP REDACTED] |
콘텐츠 필터링
유해한 콘텐츠를 차단하거나 주제 제약을 적용합니다:
use adk_guardrail::ContentFilter;
// 유해한 콘텐츠 패턴 차단
let filter = ContentFilter::harmful_content();
// 특정 키워드 차단
let filter = ContentFilter::blocked_keywords(vec![
"forbidden".into(),
"banned".into(),
]);
// 주제 관련성 적용
let filter = ContentFilter::on_topic("cooking", vec![
"recipe".into(),
"cook".into(),
"bake".into(),
]);
// 콘텐츠 길이 제한
let filter = ContentFilter::max_length(1000);
사용자 지정 콘텐츠 필터
use adk_guardrail::{ContentFilter, ContentFilterConfig, Severity};
let config = ContentFilterConfig {
blocked_keywords: vec!["spam".into()],
required_topics: vec!["rust".into(), "programming".into()],
max_length: Some(5000),
min_length: Some(10),
severity: Severity::High,
};
let filter = ContentFilter::new("custom_filter", config);
스키마 유효성 검사
Agent 출력에 JSON 스키마를 적용합니다 (schema 기능 필요):
use adk_guardrail::SchemaValidator;
use serde_json::json;
let schema = json!({
"type": "object",
"properties": {
"name": { "type": "string" },
"age": { "type": "integer", "minimum": 0 }
},
"required": ["name"]
});
let validator = SchemaValidator::new(&schema)?
.with_name("user_schema")
.with_severity(Severity::High);
유효성 검사기는 다음에서 JSON을 추출합니다:
- 원시 JSON 텍스트
- Markdown 코드 블록 (
```json ... ```)
GuardrailSet
여러 가드레일을 결합합니다:
use adk_guardrail::{GuardrailSet, ContentFilter, PiiRedactor};
let guardrails = GuardrailSet::new()
.with(ContentFilter::harmful_content())
.with(ContentFilter::max_length(5000))
.with(PiiRedactor::new());
GuardrailExecutor
Guardrail을 실행하고 상세 결과를 얻습니다:
use adk_guardrail::{GuardrailExecutor, GuardrailSet, PiiRedactor};
use adk_core::Content;
let guardrails = GuardrailSet::new()
.with(PiiRedactor::new());
let content = Content::new("user")
.with_text("Contact: test@example.com");
let result = GuardrailExecutor::run(&guardrails, &content).await?;
if result.passed {
// Use transformed content if available
let final_content = result.transformed_content.unwrap_or(content);
println!("Content passed validation");
} else {
for (name, reason, severity) in &result.failures {
println!("Guardrail '{}' failed: {} ({:?})", name, reason, severity);
}
}
ExecutionResult
pub struct ExecutionResult {
pub passed: bool, // 전체 통과/실패 여부
pub transformed_content: Option<Content>, // 수정된 Content (있는 경우)
pub failures: Vec<(String, String, Severity)>, // (이름, 사유, 심각도)
}
Custom Guardrails
Guardrail trait를 구현합니다:
use adk_guardrail::{Guardrail, GuardrailResult, Severity};
use adk_core::Content;
use async_trait::async_trait;
pub struct ProfanityFilter {
words: Vec<String>,
}
#[async_trait]
impl Guardrail for ProfanityFilter {
fn name(&self) -> &str {
"profanity_filter"
}
async fn validate(&self, content: &Content) -> GuardrailResult {
let text: String = content.parts
.iter()
.filter_map(|p| p.text())
.collect();
for word in &self.words {
if text.to_lowercase().contains(word) {
return GuardrailResult::Fail {
reason: format!("Contains profanity: {}", word),
severity: Severity::High,
};
}
}
GuardrailResult::Pass
}
// 다른 Guardrail들과 병렬로 실행 (기본값: true)
fn run_parallel(&self) -> bool {
true
}
// 이 Guardrail 실패 시 빠르게 실패 처리 (기본값: true)
fn fail_fast(&self) -> bool {
true
}
}
Agents와의 통합
Guardrail은 LlmAgentBuilder와 통합됩니다:
use adk_agent::LlmAgentBuilder;
use adk_guardrail::{GuardrailSet, ContentFilter, PiiRedactor};
let input_guardrails = GuardrailSet::new()
.with(ContentFilter::harmful_content())
.with(PiiRedactor::new());
let output_guardrails = GuardrailSet::new()
.with(SchemaValidator::new(&output_schema)?);
let agent = LlmAgentBuilder::new("assistant")
.model(model)
.instruction("You are a helpful assistant.")
.input_guardrails(input_guardrails)
.output_guardrails(output_guardrails)
.build()?;
실행 흐름
사용자 입력
│
▼
┌─────────────────────┐
│ 입력 Guardrail │ ← PII 비식별화, Content 필터링
│ (병렬) │
└─────────────────────┘
│
▼ (변환되거나 차단됨)
┌─────────────────────┐
│ Agent 실행 │
└─────────────────────┘
│
▼
┌─────────────────────┐
│ 출력 Guardrail │ ← Schema 유효성 검사, 안전성 확인
│ (병렬) │
└─────────────────────┘
│
▼
최종 응답
예시
# 기본 PII 및 Content 필터링
cargo run --example guardrail_basic --features guardrails
# JSON Schema 유효성 검사
cargo run --example guardrail_schema --features guardrails
# Agent 전체 통합
cargo run --example guardrail_agent --features guardrails
모범 사례
| 실천 | 설명 |
|---|---|
| 가드레일 계층화 | 안전을 위해 입력 가드레일을 사용하고, 품질을 위해 출력 가드레일을 사용합니다. |
| 입력 PII 처리 | 모델에 도달하기 전에 PII를 제거합니다. |
| 출력 스키마 | JSON schema를 사용하여 구조화된 출력을 검증합니다. |
| 적절한 심각도 | Critical은 아껴서 사용하고, Low는 경고에 사용합니다. |
| 철저히 테스트 | 가드레일은 보안에 중요한 코드입니다. |