10장 · 루프 제어와 가드레일왜 가드레일이 필요한가 — 스스로 반복하는 루프에는 한도가 없다네 겹의 가드레일 — 코드가 보장하는 것과 모델에게 부탁하는 것비용 가드 — 스텝 상한과 토큰 상한입력 가드와 행동 가드 — 의심스러운 입력에 표시하고, 금지 행동을 적어 둔다따라하기 — 가드레일을 단 루프정리와 체크리스트실습문제와 해답
10장. 루프 제어와 가드레일
정리와 체크리스트
10장에서 배운 것
- 가드레일 — 에이전트의 비용과 행동을 정해 둔 범위 안에 묶는 안전장치.
- 다층 방어 — 한 겹이 뚫려도 다음 겹이 받도록 여러 겹을 쌓는 설계.
- 비용 가드 — 스텝 상한(
max_steps)과 문의 1건 토큰 상한(max_session_tokens). - 사용자 정의 예외 —
Exception을 물려받아 만든 우리만의 예외. 루프는 알리고, 부른 쪽이 대응합니다. - 입력 가드 — 정규식으로 의심 문구를 찾아 경고를 붙입니다. 막지 않고 표시합니다.
- 오탐 — 문제없는 입력을 문제로 잘못 잡는 것. 그래서 입력 가드는 막지 않고 표시합니다.
- 행동 가드 — 금지 행동과 거절 뒤의 행동을 시스템 프롬프트에 적습니다.
- 안전 설정 — Gemini 서비스가 유해 표현을 걸러 내는 기준.
한눈에 보는 핵심
guarded = input_guard(question) # 입력: 의심 문구 표시
config = types.GenerateContentConfig(
system_instruction=SYSTEM, # 행동: 금지 조항
tools=list(tools.values()), # 행동: 도구 목록 = 할 수 있는 일
safety_settings=[...], # 안전: 유해 표현 기준
)
for step in range(1, max_steps + 1): # 비용: 스텝 상한
response = client.models.generate_content(...)
spent += response.usage_metadata.total_token_count
if spent > max_session_tokens: # 비용: 토큰 상한
raise BudgetExceeded(...) # → 받은 쪽이 handoff_to_ticket
if not response.function_calls:
return response.text or EMPTY_ANSWER # 빈 답은 안내 문구로
...
return handoff_to_ticket(question, "스텝 초과") # 비용: 스텝을 다 쓰면 티켓 접수
| 코드가 지키는 것 | 모델에게 부탁하는 것 |
|---|---|
| 스텝 상한, 토큰 상한 | 금지 조항을 지키는 것 |
| 도구 목록(없는 도구는 실행되지 않는다) | 경고가 붙은 입력에 원칙대로 응대하는 것 |
| 한도에 닿으면 티켓으로 넘기는 것 | 거절한 뒤 정상 상담으로 돌아오는 것 |
체크리스트
- [ ] 루프에서 스텝이 늘수록 한 번의 호출이 커지는 이유를 설명할 수 있다.
- [ ] 토큰 상한을 무엇을 근거로 정하는지 말할 수 있다.
- [ ] 토큰을 세는 줄이 종료 판정보다 앞에 있는 이유를 설명할 수 있다.
- [ ] 토큰 상한 초과를 예외로 알리는 이유를 설명할 수 있다.
- [ ] 스텝 상한과 토큰 상한에 닿았을 때 각각 누가
handoff_to_ticket을 부르는지 말할 수 있다. - [ ] "티켓 전환"이라고 출력만 하고 티켓을 만들지 않으면 왜 문제인지 설명할 수 있다.
- [ ] 입력 가드가 막지 않고 표시하는 이유를 "오탐"이라는 말로 설명할 수 있다.
- [ ] 정규식 패턴 하나가 "지시를 무시", "지시 다 무시", "지시는 전부 무시"를 모두 잡는 까닭을 설명할 수 있다.
- [ ]
response.text가None일 수 있는 경우와 그때의 처리를 말할 수 있다. - [ ] 네 겹이 모두 뚫려도 실제 환불이 일어나지 않는 이유를 설명할 수 있다.
- [ ] 실행 결과에서 우리 코드가 한 일과 모델이 한 일을 가려낼 수 있다.
한 줄 핵심
보장해야 하는 것은 코드로 지키고, 프롬프트는 그 위에 얹습니다. 한도에 닿은 문의는 오류가 아니라 사람에게 넘길 문의입니다.