Code 실전

코드 배포 없이 에이전트 행동을 즉시 바꾸는 런타임 토글 설계법

seunghyeonlab 2026. 5. 21. 10:02

hero

프로덕션 에이전트가 갑자기 이상하게 동작할 때, 대부분의 팀은 두 가지 선택지 앞에 선다. 코드를 고쳐 다시 배포하거나, 아니면 그냥 전체를 꺼버리거나. 둘 다 느리고, 둘 다 위험하다. 이 글은 세 번째 길 — 런타임 토글로 재배포 없이 에이전트 기능을 실시간 전환하는 방법 — 을 Mac Mini 클러스터 직접 운영 경험 기반으로 정리한다.


1. 왜 지금 이걸 봐야 하나

Claude Code 에이전트는 LLM 응답 시간, 툴 실행 결과, 외부 API 상태에 따라 예측 못한 행동을 한다. 프로덕션에서 이상 징후가 포착됐을 때, 재배포 사이클은 보통 4~10분이 걸린다. 그 사이에 에이전트는 계속 돌아간다.

티켓팅 사이트 서버 관리자가 트래픽 폭주 순간 '특정 좌석 잠금' 토글을 누르는 장면을 생각해보자. 코드 수정이 아니다. 설정값 하나가 시스템 행동을 바꾼다. Claude Code 에이전트도 정확히 이 구조로 운영할 수 있다.

Mac4 클러스터에서 Draw Things 동시 호출이 몰릴 때 특정 기능만 순간 off 해야 했던 상황이 실제로 여러 번 있었다. 재배포 없이, 프로세스 재시작 없이 말이다. 그때마다 런타임 토글이 조용히 역할을 해줬다.


2. 핵심 아이디어

결론부터: 툴을 '선택하지 않게' 만드는 게 아니라, 애초에 '존재하지 않게' 만들어야 한다.

LLM에게 "이 툴은 지금 쓰지 마"라고 프롬프트로 지시하는 방식은 불안정하다. LLM이 판단을 잘못 내릴 수 있고, 프롬프트가 길어질수록 지시가 묻힌다. 반면 툴 자체를 에이전트 등록 목록에서 빼버리면 LLM 입장에서 그 기능은 처음부터 없는 것이다.

아래 표는 두 접근 방식의 차이를 보여준다.

항목 프롬프트 지시 방식 런타임 토글 방식
동작 보장 불안정 (LLM 판단 의존) 완전 보장 (등록 자체 미포함)
전환 속도 재배포 필요 파일 수정 즉시 반영
추가 지연 프롬프트 길이 증가 0ms
이력 추적 어려움 로그로 자동화 가능

이 구조의 핵심은 외부 JSON 파일 하나다. 에이전트 초기화 시 이 파일을 읽어 툴 배열을 동적으로 조립한다. 파일을 수정하면 다음 에이전트 호출부터 즉시 반영된다.


3. 바로 따라하는 방법

토글 파일 작성

프로젝트 루트에 features.json을 만든다. 키는 기능 이름, 값은 true/false다.

{
  "web_search": true,
  "code_execution": false,
  "file_write": true,
  "long_context_mode": false,
  "last_modified": "2026-05-21T09:00:00Z"
}

이 파일이 에이전트 행동의 유일한 제어판이 된다.

에이전트 초기화 코드에 토글 주입

import json
from pathlib import Path

def load_active_tools(feature_path='features.json'):
    flags = json.loads(Path(feature_path).read_text())

    all_tools = {
        'web_search': web_search_tool,
        'code_execution': code_exec_tool,
        'file_write': file_write_tool,
        'long_context_mode': long_ctx_tool,
    }

    # 켜진 기능만 반환 — 꺼진 기능은 리스트에 포함되지 않음
    return [tool for key, tool in all_tools.items() if flags.get(key, False)]

active_tools = load_active_tools()
agent = ClaudeAgent(tools=active_tools)

load_active_tools()는 에이전트 초기화마다 호출된다. features.json이 바뀌면 다음 호출부터 새 목록이 적용된다.

전환 후 검증

# 현재 등록된 툴 목록 출력
active = load_active_tools()
print("현재 활성 툴:", [t.name for t in active])

기대 출력 예시 (code_execution: false 상태):

현재 활성 툴: ['web_search', 'file_write']

code_exec_tool이 목록에 없으면 정상이다.


4. 운영할 때 조심할 점

변경 이력 추적 — 반드시 붙여야 한다

토글을 아무렇게나 바꾸면 나중에 "언제 껐지?"를 아무도 모른다. 변경할 때마다 타임스탬프를 함께 기록하는 래퍼 스크립트를 쓴다.

# features.json 수정 후 타임스탬프 갱신
jq --arg ts "$(date -u +%Y-%m-%dT%H:%M:%SZ)" \
  '.last_modified = $ts' features.json > features.tmp \
  && mv features.tmp features.json

# 현재 활성 기능 확인
cat features.json | jq '{last_modified, active: [to_entries[] | select(.value==true) | .key]}'

기대 출력:

{
  "last_modified": "2026-05-21T09:32:11Z",
  "active": ["web_search", "file_write"]
}

n8n 자동화와 연결

Mac Mini n8n 2.8.4 워크플로우에서 이 스크립트를 훅으로 연결하면, Slack 알림까지 자동으로 붙는다. 토글 하나 바꿀 때마다 누가 언제 어떤 기능을 껐는지 팀 전체가 실시간으로 확인할 수 있다.

주의사항 정리

  • 파일 권한: features.json이 여러 프로세스에서 동시에 쓰이면 경쟁 상태가 생긴다. mv로 원자적 교체를 쓰는 게 안전하다 (위 스크립트에서 이미 적용).
  • 기본값 처리: flags.get(key, False)로 키가 없는 경우 항상 꺼진 상태로 처리한다. 새 기능 추가 시 JSON에 명시적으로 넣기 전까지는 비활성이다.
  • Docker 환경: 컨테이너 내부에서 파일을 수정해도 반영되지만, 볼륨 마운트로 외부에서 제어하는 구조가 더 깔끔하다. docker run -v ./features.json:/app/features.json ... 형태로 마운트하면 호스트에서 직접 편집할 수 있다.
  • 롤백 시나리오: 토글 전환 후 에이전트 동작이 더 이상해지면, features.json을 이전 버전으로 교체하면 된다. git으로 관리하면 git checkout features.json 한 줄로 되돌아간다.

마무리

툴 등록 자체를 동적으로 제어하면 LLM이 쓸 수 없는 기능은 처음부터 존재하지 않아, 오동작 가능성을 원천에서 막는다. Mac Mini 클러스터 4대를 직접 운영하면서 체감한 가장 조용한 안전망이다.

다음 글에서는 이 토글 구조에 원격 API 제어를 붙여, 배포 없이 웹 대시보드에서 에이전트 기능을 켜고 끄는 방법을 다룬다.


🐦 X에서 더 빠르게: @baegseungh7061
📚 이 시리즈 더 보기: Code 실전
💌 새 글 알림: X 팔로우 또는 블로그 RSS 구독