고급 인력에게 서빙을 시키지 마라 — Claude Fable과 Codex GPT-5.6 오케스트레이션 설정기
On this page
고급 인력에게 서빙을 시키지 마라
Fable이 처음 나왔을 때, 멋모르고 effort를 울트라코드(xhigh + workflow)로 놓고 돌렸습니다. 결과는 참담했어요. 명령 하나 제대로 못 끝낸 채 주간 Fable 한도를 다 써버렸거든요. 아, 이건 진짜 제대로 알고 써야 하는 물건이구나. 그때부터 Fable 잘 쓰는 법을 찾아다녔고, 성공지식백과님의 유튜브를 참고해서 지금의 세팅을 만들었습니다. 이 글은 그 기록입니다.
한도를 날려먹기 전까지, 가장 비싼 모델이 sed를 돌리고, 빌드 로그를 훑고,
한 줄짜리 오타를 고치고 있었습니다. Claude Code에 붙은 Fable 얘기입니다.
Opus보다 한 단계 위 티어, 지금 제가 쓸 수 있는 제일 비싼 모델이 그러고 있었어요.
미슐랭 헤드셰프를 데려다 서빙과 설거지를 시키는 식당은 없죠. 헤드셰프는 메뉴를 짜고 주방을 지휘하고 마지막 플레이팅만 봅니다. 접시는 홀 스태프가 나릅니다. 당연한 얘긴데, 제 터미널에서는 그 당연한 게 안 지켜지고 있었습니다. 토큰 단가를 생각하면 꽤 아까운 그림이고요. 그 뒤에 ChatGPT/Codex에서 GPT-5.6 오케스트레이션을 새로 마무리하면서, 같은 원칙이 제품이 달라도 그대로 통한다는 걸 한 번 더 확인했습니다. 다만 강제하는 방법은 제법 달랐어요.
아이디어: 지휘와 실행을 분리한다
그래서 Fable은 지휘만 하게 했습니다. 계획 세우고, 일 쪼개서 나눠주고, 결과 받아서 종합하고, 중요한 판단 내리는 것. 딱 거기까지. 실제 작업 토큰은 아래 티어에서 나오게 했습니다.
| 역할 | 에이전트 | 담당 |
|---|---|---|
| 심층 추론 | deep-reasoner (Opus, effort max) | 설계·아키텍처 결정·복잡한 근본원인 분석 |
| 일반 실행 | executor (Opus) | 구현·수정·테스트·디버깅·리뷰 |
| 잡무 | runner (Haiku, 도구 제한) | 명령 실행·빌드·로그/파일 조회 |
runner는 도구를 Bash/Read/Grep/Glob으로 잘라놨어요. 판단이 필요 없는 일에
판단할 수 있는 도구를 쥐여줄 이유가 없으니까요. 조회하고 명령을 돌리는 데
Edit 권한까지는 필요 없습니다.
ChatGPT/Codex GPT-5.6에서는 어떻게 나눴나
Codex 쪽도 처음엔 비슷하게 생각했습니다. 대화창의 루트에게 일을 쪼개서 native collaborator로 넘기면, 자연스럽게 가벼운 작업은 가벼워지겠지 하고요. 그런데 그 가정이 틀렸습니다. 당시 Codex 0.144.1의 제 환경에서는 native collaborator가 루트의 Sol 모델과 추론 설정을 그대로 물고 태어났어요. 결국 Sol이 Sol을 여러 개 부르는 같은 모델 fan-out이 됐고, 짧은 시간에 5시간 한도를 빠르게 태웠습니다.
여기서 말하는 건 로컬 로그의 raw token 수를 돈처럼 환산한 얘기가 아닙니다. Codex 사용량은 그 숫자만으로 청구 비용을 계산할 수 없고, 저는 그런 식의 절감률도 만들지 않았습니다. 실패 원인은 훨씬 단순했어요. 지휘자와 실무자의 모델·권한·추론 강도가 분리되지 않았다는 것.
그래서 최종 V5는 역할을 이렇게 고정했습니다.
| 자리 | 모델 / 추론 | 권한 | 맡기는 일 |
|---|---|---|---|
| 대화형 루트 | gpt-5.6-sol / xhigh | 지휘 | 사용자와 대화, 계획·배분·종합 |
deep-reasoner | gpt-5.6-sol / max | read-only | 설계, 위험 검토, 복잡한 원인 분석 |
executor | gpt-5.6-terra / medium | workspace-write | 구현, 수정, 테스트, 디버깅 |
runner | gpt-5.6-luna / low | read-only | 검색, 상태·로그 조회, 가벼운 명령 |
핵심은 모델 이름만 적어 놓는 게 아니었습니다. Codex native collaboration은 아예
꺼두고, 모든 역할 호출을 ~/.codex/orchestration/bin/codex-role로만 지나게 했습니다.
각 역할의 모델은 profile과 실행 인자에 함께 고정해 두니, executor를 부른다고 해
놓고 루트 Sol이 복제되는 길이 없습니다.
# ~/.codex/orch-executor.config.toml (요지)
model = "gpt-5.6-terra"
model_reasoning_effort = "medium"
sandbox_mode = "workspace-write"# 루트가 역할을 시작하는 유일한 경로
printf '%s' '구현과 집중 테스트를 맡아줘' \
| ~/.codex/orchestration/bin/codex-role --cwd "$PWD" executor처음에는 자식이 다시 일을 나누는 걸 전부 막을까도 생각했습니다. 그런데 그러면
"전문가가 더 알맞은 전문가에게 넘긴다"는 처음 목적이 사라집니다. 그래서 V5는
무제한 재귀 대신 의미가 있는 한 번의 중첩만 허용합니다. 깊이 1의 전문가는
role profile과 policy에 따라 codex-role을 직접 호출할 수 없지만, 필요하면 엄격한
한 번짜리 요청 봉투를 반환할 수 있습니다. 그 봉투로 깊이 2를 이어서 실행하는 것은
루트의 continuation 경로뿐이며, 이때만 role·depth·route·cwd를 신뢰된 registry에서
결정합니다.
deep-reasoner (depth 1) → runner
executor (depth 1) → runner 또는 deep-reasoner
runner (depth 1) → 없음
모든 역할 (depth 2) → 없음이건 모든 자식을 leaf로 만들어 버리는 정책이 아닙니다. 예를 들어 executor가 구현 중에 설계 판단이 막히면 deep-reasoner를, 분석가는 근거를 모아야 하면 runner를 부를 수 있습니다. 다만 그 위임은 루트라는 한 곳에서만 실제 실행돼서, 어느 모델이 어떤 권한으로 몇 단계까지 갔는지를 놓치지 않습니다.
안전장치는 글을 무겁게 만들 만큼 길게 적을 건 아니지만, 이 정도는 남겨둘 만합니다.
요청에는 task hash와 TTL을 넣고, 원자적으로 한 번만 소비합니다. cwd와 route/depth는
루트의 continuation에서만 호출자가 적은 말을 믿지 않고 신뢰된 레코드에서 정규화해
읽습니다. 자식 실행 전에는 일반 환경은 보존하되 신뢰된 CODEX_HOME을 강제하고,
호출자가 넣은 CODEX_ROLE_* 권한값은 제거합니다. executor는 workspace 잠금을 잡고,
시작과 종료는 감사 기록으로 남기며, 형태가 틀린 봉투는 열어보지 않고 fail-closed로
막습니다.
이 정책은 Codex가 시작될 때 읽히는 쪽이라, 바꾼 뒤에는 이미 열려 있던 대화에 기대하면 안 됩니다. 새 Codex chat을 열거나 Codex를 재시작해야 합니다.
~/.codex/orchestration/bin/codex-orchestration verify
# startup policy를 바꾼 뒤: 새 Codex chat을 열거나 Codex 재시작검증도 따로 해뒀습니다. 단위 테스트는 28/28 통과, py_compile 통과,
codex-orchestration verify 통과. 마지막으로 루트 → executor → runner로 이어지는
depth-2 E2E도 실제로 한 번 끝까지 돌렸습니다. 말로만 중첩을 허용한 게 아니라,
최대 깊이와 권한이 그 경로에서 작동하는지 확인한 셈입니다.
Claude Fable 구현: 본체는 한 곳, 설정에는 얇은 로딩 코드만
원칙은 하나였습니다. 본체는 ~/.claude/fable/ 한 곳에만 두고, 각 설정 파일엔
그걸 불러오는 얇은 로딩 코드만 남긴다. 설정을 여기저기 흩어놓으면 나중에 켜고
끄기가 지옥이 되거든요.
~/.claude/
├── CLAUDE.md ──────── @~/.claude/fable/active.md (선언: import 한 줄)
├── agents/
│ ├── deep-reasoner.md ─┐
│ ├── executor.md ├─(심링크)─▶ fable/agents/* (역할 배정)
│ └── runner.md ────────┘
├── settings.json ─────── PreToolUse ─▶ fable/hooks/orchestration-gate.py (강제)
└── fable/ ← 본체는 여기 한 곳
├── active.md ─(심링크)─▶ fable.md 또는 empty.md
├── fable.md (지침 본문)
├── empty.md (빈 파일 = 지침 없음)
├── agents/ (에이전트 정의 원본)
└── hooks/orchestration-gate.py (게이트 스크립트)- 선언:
~/.claude/CLAUDE.md맨 끝의@~/.claude/fable/active.mdimport 한 줄이 전부입니다. 이active.md는fable/안의 심링크라, 지침이 담긴fable.md를 가리키거나 빈empty.md를 가리킵니다. - 역할:
~/.claude/agents/의 서브에이전트 3종은 전부 본체(fable/agents/)로 이어진 심링크고요. - 강제: 나머지는
settings.json에 등록된 PreToolUse 훅 하나가 담당합니다.
Claude Fable 게이트: 부탁만으로는 안 지킨다
여기까지는 흔한 프롬프트 엔지니어링이죠. "너는 지휘자야, 실행은 위임해"라고 지침에 써두는 것. 그런데 써보면 압니다. 모델은 이걸 자주 잊어요. 급하면 그냥 자기가 파일을 고쳐버립니다. 눈앞에 칼이 있는데 홀 스태프를 부르러 가는 게 귀찮은 겁니다.
그래서 부탁을 강제로 바꿨습니다. PreToolUse 훅(도구가 실행되기 직전에 끼어드는
훅)인 orchestration-gate.py가 메인 에이전트의 직접 코드 수정을 물리적으로
막습니다. 규칙 자체는 단순합니다. 한 턴(prompt_id, 사용자 입력 하나가 만드는
턴 식별자)에 코드 파일은 2개까지만 직접 손대게 하고, 3개째부터 막습니다.
그리고 막을 때는 어디로 위임하라는 안내까지 에러 메시지에 실어 돌려줍니다.
if len(gate["files"]) < LIMIT: # LIMIT = 2
gate["files"].append(path)
allow() # 2개까지는 직접 수정 통과
# 실제 메시지는 더 길다 — 요지만 발췌
deny("[fable 게이트] 이번 턴 직접 수정이 이미 %d개입니다. "
"추가 수정은 서브에이전트에 위임하세요: %s" % (LIMIT, ROUTES))ROUTES에는 "일반 구현·수정은 executor(Opus), 심층 추론·설계는 deep-reasoner,
단순 명령·조회는 runner(Haiku)"가 담겨 있습니다. 차단당한 모델은 이 문구를 읽고
곧바로 알맞은 에이전트에게 일을 넘기고요. 막기만 하는 게 아니라 갈 길까지
같이 알려준다는 게 이 방식의 포인트입니다.
만드는 김에 몇 군데는 좀 더 신경 썼습니다. 우선 Bash로 새는 걸 막았어요. Edit
도구만 조여봤자 sed -i, perl -i, 리다이렉트(>), tee로 코드 파일에 쓰면
그만이니까, 그런 셸 패턴은 정규식으로 따로 잡아냅니다.
서브에이전트는 반대로 전부 통과시킵니다. payload에 agent_id나 agent_type가
있으면 게이트를 그냥 건너뛰어요. 위임받은 쪽이 실제로 일하는 경로인데 여기서
막아버리면 앞뒤가 안 맞으니까요.
md·json·yaml 같은 비코드 파일은 지휘자가 직접 고쳐도 됩니다. 그리고 혹시라도 게이트에서 예외가 터지면 무조건 통과(fail-open)시킵니다. 강제 장치 하나가 버그로 세션 전체를 마비시키는 것보다는 그게 낫죠.
try:
main()
except Exception:
sys.exit(0) # fail-open — 게이트가 죽어도 세션은 산다Claude Fable 토글: 설정 파일은 건드리지 않는다
켜고 끄는 건 fable on / off / status 스크립트 하나로 합니다. 여기서 중요한 건,
이 스크립트가 settings.json이나 CLAUDE.md를 절대 건드리지 않는다는 점이에요.
로딩 코드는 한 번만 심어두고, 켜졌는지 꺼졌는지는 상태 파일과 심링크만으로 갈립니다.
fable on # .fable-state=on, active.md -> fable.md (지침 로드)
fable off # .fable-state=off, active.md -> empty.md (지침 없음)
fable status # 상태 + 로딩 코드 설치 여부 점검on이면 상태 파일에 on을 쓰고 active.md 심링크를 지침 파일로 돌립니다.
게이트 훅도 이 상태 파일을 먼저 읽어서 off면 바로 통과시키고요. 등록은
한 번, 동작 여부는 스위치가 쥐고 있는 이 구조 덕에 껐다 켜는 게 안전합니다.
바뀐 설정은 다음에 새로 여는 세션부터 적용됩니다.
그 "한 번 등록"이 이렇게 생겼습니다. 코드 수정 계열 도구에 게이트를 걸어두면 끝이에요.
// ~/.claude/settings.json
"hooks": { "PreToolUse": [{ "matcher": "Write|Edit|NotebookEdit|MultiEdit|Bash",
"hooks": [{ "type": "command", "command": "python3 ~/.claude/fable/hooks/orchestration-gate.py" }] }] }게이트 스크립트 전체 코드는 글 맨 아래 부록에 붙여놨어요. 이 스니펫과 부록 코드만 있으면 그대로 재현할 수 있습니다.
status는 on/off만 보여주고 끝나는 게 아니라 3층이 제대로 깔렸는지까지 훑어줍니다.
CLAUDE.md의 import 한 줄, agents 심링크 3종, settings.json의 게이트 등록,
스크립트 실행 권한을 각각 [OK]/[--]로 찍어주거든요. 어디가 빠졌는지 한눈에 보입니다.
Claude Fable만의 반나절 실측: 얼마나 아꼈나
솔직히 이 글을 쓰는 지금, 게이트를 올린 지 반나절밖에 안 됐습니다. 오늘 낮에
설치했거든요. "한 달 굴려본 소감" 같은 걸 말하기엔 이른데, 대신 오늘 하루치
로컬 세션 트랜스크립트(~/.claude/projects/*/*.jsonl, 12개 파일)를 긁어 실제
토큰을 뽑아봤습니다. 표본은 딱 반나절입니다.
아래 표와 그래프는 Claude Fable 구현의 관측치와 Anthropic 단가만 다룹니다. Codex의 절감액이나 사용량을 주장하는 자료가 아니며, 앞 절의 Codex 구성과 숫자를 섞어 비교하면 안 됩니다.
Claude Fable에서 오늘 쓴 토큰
| 모델 | 역할 | 메시지 | 입력 | 출력 | 캐시 읽기 | 캐시 쓰기 |
|---|---|---|---|---|---|---|
| Fable 5 | 오케스트레이터 | 612 | 211,034 | 489,475 | 103,342,291 | 8,274,907 |
| Opus 4.8 | 서브에이전트(executor 등) | 88 | 96,815 | 95,820 | 4,120,721 | 571,918 |
runner(Haiku)는 오늘 호출 0건이에요. 설치한 지 반나절이라 "빌드 돌려라,
로그 봐라" 같은 잡무를 넘길 일이 아직 없었습니다. 3층 중 맨 아래 Haiku는 오늘
하루 종일 놀았습니다.
Claude Fable의 Anthropic 단가와 계산
Anthropic 공식 단가(per MTok, MTok은 100만 토큰) 기준으로, Fable은 모든 항목에서 Opus의 정확히 2배, Haiku의 10배입니다.
| 모델 | 입력 | 출력 | 캐시 읽기 | 캐시 쓰기 |
|---|---|---|---|---|
| Fable 5 | $10 | $50 | $1.00 | $12.50 |
| Opus 4.8 | $5 | $25 | $0.50 | $6.25 |
| Haiku 4.5 | $1 | $5 | $0.10 | $1.25 |
캐시 쓰기 1.25×는 기본 5분 TTL 기준입니다(1시간 TTL은 2×).
executor(Opus)한테 위임한 작업의 실제 비용은 $8.51이었습니다
(입력 $0.48 + 출력 $2.40 + 캐시 읽기 $2.06 + 캐시 쓰기 $3.57). 이 토큰들을 그대로
Fable 단가로 환산하면 정확히 2배, $17.03이 나옵니다. 같은 일을 한 티어
아래에서 태운 덕에 위임분만 놓고 보면 값이 절반에서 끝난 거예요. 물론 위임을
안 했으면 저 캐시 쓰기·읽기 자체가 일부는 애초에 안 생겼을 겁니다. 그러니 이
$8.5를 '순수하게 아낀 돈'이라 부르기보단, 같은 노동을 어느 티어에서 태웠느냐의
단가 차이로 보는 게 정직해요. 잡무가 runner(Haiku)까지 내려갔다면 그 몫은 Fable
직접 대비 10분의 1이었을 거고요.
뽑아보니 눈에 띈 것
뽑은 숫자에서 세 가지가 눈에 걸렸어요.
- 아직 손은 Fable이 제일 많이 놀리고 있습니다. 출력 토큰이 Fable 48.9만, Opus
9.6만 — 실제 '생산' 토큰의 84%쯤이 여전히 지휘자에서 나와요. 반나절짜리
표본이라 위임 비중이 낮은 거고, 이게
executor랑runner쪽으로 넘어갈수록 절감 폭은 더 벌어질 겁니다. 오늘 숫자는 그 곡선의 출발점이고요. - 진짜 돈은 출력에서 나가지, 캐시에서 나가는 게 아닙니다. Fable의 캐시 읽기가 1억 토큰을 넘는데, 오케스트레이터란 게 컨텍스트를 계속 다시 읽는 존재라 그렇습니다. 근데 캐시 읽기 단가가 출력의 50분의 1이라 총액에선 생각보다 조용해요. 지휘자를 비싼 모델로 앉혀도 캐시 덕에 버틸 만하다는 뜻이고, 정작 아까운 건 출력 — '노동' 토큰입니다. 그걸 싼 티어로 내려보내는 게 이 구조의 전부인 이유가 여기 있죠.
- 어쨌든 반나절짜리라는 것. Haiku는 아예 안 썼고, 세션이 설계 위주냐 잡무 위주냐에 따라 이 비율은 크게 흔들립니다. '이렇게 아낄 수 있다'는 방향이지, '항상 절반 아낀다'는 약속은 아니에요.
남는 이야기
Claude Fable 쪽에도 한계는 있습니다. 게이트를 턴당 2파일까지 열어둔 건 순전히 타협이에요. 오타 하나, 설정 한 줄 고치자고 서브에이전트를 띄우면 위임 오버헤드(에이전트 기동에 컨텍스트 전달까지)가 정작 그 수정보다 비싸지거든요. 그래서 사소한 건 지휘자가 직접 처리하게 두고, 진짜 노동이 시작되는 지점부터 막습니다. 딱딱한 상한선이라기보단 적당히 걸쳐둔 균형점인 셈이죠.
결국 하고 싶었던 건 하나였어요. 비싼 사람한테 비싼 일을 시키는 것. 헤드셰프는 메뉴랑 최종 검수를 보고, 접시는 홀 스태프가 나르게 하는 것. 이렇게 당연한 걸 AI한테는 말로 부탁해선 안 먹히고 게이트까지 세워야 했다는 게 이번 설정기의 전부입니다. 반나절 치 숫자로도 위임분이 절반으로 깎였으니, 주방이 제대로 돌기 시작하면 어떤 그림이 나올지는 좀 더 굴려보고 다시 들고 오겠습니다.
부록: Claude Fable 게이트 스크립트 전체 코드
~/.claude/fable/hooks/orchestration-gate.py로 저장하고 실행 권한(chmod +x)을
주면 됩니다. 위의 settings.json 등록 스니펫과 합치면 재현 끝.
#!/usr/bin/python3
# ~/.claude/fable/hooks/orchestration-gate.py — fable 강제 게이트 (PreToolUse)
#
# 메인 에이전트가 한 턴(prompt_id)에 코드 파일을 2개까지만 직접 수정하게 허용하고,
# 3개째부터 차단하며 위임 지시를 돌려준다. Bash 를 통한 코드 파일 수정은 항상 차단.
# 서브에이전트(payload 에 agent_id/agent_type 존재)는 전부 통과 — 위임이 실행 경로다.
#
# fable off 상태(~/.claude/.fable-state != on)면 즉시 통과. 등록은 settings.json 에
# 1회(로딩 코드), 동작 여부는 스위치가 지배한다.
# 오류 시 fail-open(통과) — 게이트 버그가 세션을 마비시키지 않게 한다.
import json
import os
import re
import sys
import time
LIMIT = 2
STATE_FILE = os.path.expanduser("~/.claude/.fable-state")
GATE_STATE_DIR = os.path.expanduser("~/.claude/fable/state")
CODE_EXTS = {
"ts", "tsx", "js", "jsx", "mjs", "cjs", "py", "go", "rs", "java", "kt",
"kts", "swift", "c", "h", "cc", "cpp", "hpp", "cs", "rb", "php", "vue",
"svelte", "astro", "css", "scss", "sass", "less", "html", "sh", "bash",
"zsh", "sql", "lua", "dart", "scala", "ex", "exs", "zig", "ipynb",
}
EXT_RE = "|".join(sorted(CODE_EXTS))
ROUTES = (
"일반 구현·수정·테스트·리뷰 → executor(Opus), "
"심층 추론·설계·근본원인 분석 → deep-reasoner(Opus, max), "
"단순 명령·조회 → runner(Haiku)."
)
def allow():
sys.exit(0)
def deny(msg):
sys.stderr.write(msg)
sys.exit(2)
def is_code(path):
return path.rsplit(".", 1)[-1].lower() in CODE_EXTS if "." in path else False
def main():
try:
state = open(STATE_FILE).read().strip()
except OSError:
state = "off"
if state != "on":
allow()
data = json.load(sys.stdin)
if data.get("agent_id") or data.get("agent_type"):
allow() # 서브에이전트는 게이트 대상이 아님
tool = data.get("tool_name", "")
tool_input = data.get("tool_input") or {}
if tool == "Bash":
cmd = tool_input.get("command", "")
writes_inplace = re.search(r"\b(sed|perl)\s+[^|;&]*-\w*i", cmd)
writes_redirect = re.search(
r"(?:>>?|\btee\b(?:\s+-\w+)*)\s*['\"]?[^\s'\"|;&<>]+\.(?:%s)\b" % EXT_RE, cmd
)
touches_code = re.search(r"\.(?:%s)\b" % EXT_RE, cmd)
if writes_redirect or (writes_inplace and touches_code):
deny(
"[fable 게이트] 메인 에이전트는 Bash로 코드 파일을 수정할 수 없습니다. "
"코드 수정은 Edit/Write 도구(턴당 %d개 파일까지)를 쓰거나 서브에이전트에 위임하세요: %s"
% (LIMIT, ROUTES)
)
allow()
if tool not in ("Edit", "Write", "NotebookEdit", "MultiEdit"):
allow()
path = tool_input.get("file_path") or tool_input.get("notebook_path") or ""
if not path or not is_code(path):
allow() # 설정·문서 등 비코드 파일은 직접 수정 허용
session = re.sub(r"[^a-zA-Z0-9-]", "", data.get("session_id", "nosession"))
prompt = data.get("prompt_id", "")
os.makedirs(GATE_STATE_DIR, exist_ok=True)
gate_file = os.path.join(GATE_STATE_DIR, session + ".json")
gate = {"prompt_id": prompt, "files": []}
try:
saved = json.load(open(gate_file))
if saved.get("prompt_id") == prompt:
gate = saved
else: # 새 턴 — 카운터 리셋 + 오래된 세션 상태 정리
now = time.time()
for fn in os.listdir(GATE_STATE_DIR):
p = os.path.join(GATE_STATE_DIR, fn)
if now - os.path.getmtime(p) > 86400:
os.remove(p)
except (OSError, ValueError):
pass
if path in gate["files"]:
allow() # 같은 파일 재수정은 개수에 안 침
if len(gate["files"]) < LIMIT:
gate["files"].append(path)
json.dump(gate, open(gate_file, "w"))
allow()
deny(
"[fable 게이트] 이번 턴에 메인 에이전트가 직접 수정한 코드 파일이 이미 %d개입니다 (%s). "
"추가 코드 수정은 반드시 서브에이전트에 위임하세요: %s "
"지금 하려던 수정(%s)을 명확한 지침과 함께 Agent(Task) 도구로 위임하면 됩니다."
% (LIMIT, ", ".join(gate["files"]), ROUTES, path)
)
if __name__ == "__main__":
try:
main()
except SystemExit:
raise
except Exception:
sys.exit(0) # fail-open참고 자료