# AI 에이전트

effort를 낮추면 Claude Code는 무엇을 덜 할까?

@Hong2026-10-14

이 글에 담긴 코드와 결과는 직접 실행해 측정한 값을 바탕으로 작성했습니다. 실측은 Hong과 팀원들이 진행했고, 그 결과를 바탕으로 AI가 글을 쓰고 Hong이 검토합니다.

실습 코드 내려받기

질문
Claude Code에서 effort를 낮추면 버그 수정 통과율과 토큰은 어떻게 달라지는가?
답
effort 다섯 단계 모두 30번 중 30번 통과해 통과율 차이는 이 표본으로 가를 수 없고, 출력 토큰 중앙값은 low 1,990에서 max 32,741.5로 늘었다.
조건
2026-10-12, Claude Code 2.1.295 claude -p, Opus 5.5(claude-opus-5-5), effort 5단계, 과제 10개 × 3회

환경

측정일
2026년 10월 12일
Claude Code
2.1.295(claude --version), 구독 로그인, claude -p
모델
--model opus, 결과 JSON modelUsage의 모델 ID는 모든 실행에서 claude-opus-5-5
effort
--effort low, medium, high, xhigh, max
반복
effort 비교는 단계마다 과제 10개 × 3회
과제 실행
Homebrew Python 3.13.13
비용
결과 JSON의 total_cost_usd, 구독 호출을 API 단가로 환산한 추정치(실제 청구액 아님)
목차
  1. Claude Code에서 effort가 정해지는 순서
  2. 실습 준비
  3. 실습 1: effort 다섯 단계의 통과율
  4. 실습 2: 토큰과 시간은 얼마나 느나
  5. 실습 3: 높은 effort는 무엇을 더 하나
  6. 정리

이번 시간에는 Claude Code의 claude -p(대화 없이 한 번 실행하고 끝나는 모드)로, 버그를 심은 작은 Python 프로젝트 10개를 effort 다섯 단계에서 고치게 하고 통과율과 토큰, 시간을 측정하며 배워가는 시간입니다.

단계마다 세 번씩, 모두 150번 고치게 했어요. Claude는 temperature나 seed를 정해서 부를 수 없고, 같은 입력에도 매번 다르게 움직여요. 그래서 다시 돌리면 이 글의 숫자는 달라질 수 있습니다.

Claude Code에서 effort가 정해지는 순서

Claude Code는 세션의 effort를 다음 순서로 정하고, 앞에서 정해진 것이 있으면 뒤는 보지 않아요(Claude Code Model configuration 문서).

  1. 명시적 선택: 환경 변수 CLAUDE_CODE_EFFORT_LEVEL, 실행할 때 주는 --effort, 세션 안의 /effort
  2. 설정 파일: 모델별로 저장한 수준(modelSettings)이나 effortLevel 키
  3. 모델 기본값: 대부분의 모델은 high, Opus 5.5와 Sonnet 5.5, Haiku 5.5는 medium

결과 JSON에는 적용된 effort 값이 남지 않아요. 그래서 -p로 /effort status를 보내 확인했고, 사용자 설정과 환경 변수를 뺀 상태에서 다섯 단계는 요청한 값 그대로 적용됐어요.

실습 준비

이 글의 명령은 모두 실습 코드의 claude-effort-tradeoff/ 디렉터리에서 실행합니다.

  • harness/: 실행, 채점, 집계 스크립트. 이 글은 batch.py, validate_tasks.py, calibrate.py, report.py, show_run.py를 실행합니다
  • tasks/: 과제 10개와 manifest.json(파일 109개)

과제는 백엔드에서 실제로 만나는 버그를 하나씩 심은 작은 Python 프로젝트예요.

  • 쉬움 3개: T04 금액 문자열을 센트로("19.99"가 1998이 됨), T05 지수 백오프 재시도(재시도가 하나 모자람), T06 반열린 구간 병합(정렬 안 된 입력에서 병합 누락)
  • 보통 4개: T01 토큰 버킷(오래 쉬면 토큰이 용량을 넘음), T02 커서 페이지네이션(같은 created_at이 페이지 경계에서 빠짐), T03 TTL 있는 LRU 캐시(방금 쓴 항목이 먼저 쫓겨남), T07 버전 비교(1.10.0 < 1.9.0)
  • 어려움 3개: T08 중복 제거 소비자(원인이 다른 파일의 윈도 관리), T09 시간대별 매일 실행 시간(UTC 기준으로 계산, 서머타임이 바뀌는 날. 서머타임은 계절에 따라 시계를 한 시간 당기거나 되돌리는 제도예요), T10 설정 깊은 병합(기본값 dict를 공유해 다음 호출이 오염됨)

과제마다 Claude가 보는 repo/(동작 명세 SPEC.md, 버그가 심어진 코드, 공개 테스트)와, 작업 디렉터리에 복사하지 않는 채점 테스트 hidden/가 있어요. 공개 테스트는 보고된 증상 하나만 재현해요. 채점 테스트(Claude에게는 보여 주지 않고 결과물을 채점할 때만 돌리는 테스트)는 SPEC.md에 적힌 동작 중 같은 함수의 엣지 케이스와, 버그 전부터 맞던 동작이 그대로인지(회귀)를 검사해요.

프롬프트는 과제마다 고정 문장 하나예요. 증상과 "고쳐 주세요", 명세 위치만 말하고, 테스트를 돌리라거나 확인하라는 말은 넣지 않았어요.

실행 한 번은 harness/run_claude.py가 맡아요. 과제 repo/를 새 작업 디렉터리로 복사하고, 그 안에서 claude -p를 띄우고, 실행 기록을 저장하고, 채점한 뒤 요약 한 줄을 out/runs.jsonl에 붙여요. 넘기는 옵션은 다음과 같아요.

  • --safe-mode, --setting-sources project, child_env(): 사용자 설정과, effort를 바꾸는 환경 변수(CLAUDE_CODE_EFFORT_LEVEL, CLAUDE_EFFORT)를 빼요.
  • --tools: Bash, Read, Edit, Write, Glob, Grep 여섯 개만 줘요.
  • --permission-mode acceptEdits: 작업 디렉터리 안의 파일 수정은 묻지 않고 허용해요. 작업 디렉터리 밖에 파일을 쓰는 Write는 거부돼요.
  • --allowedTools: Bash는 python3 -m unittest와 python -m unittest로 시작하는 명령만 허용해요. ls, cat, grep처럼 권한 문서가 읽기 전용으로 분류한 명령은 따로 허용하지 않아도 실행돼요. Bash 전체 허용이나 bypassPermissions는 쓰지 않았어요. 명령이 샌드박스 없이 내 머신에서 돌기 때문이에요.
  • --output-format stream-json --verbose: 도구 호출 하나하나가 이벤트로 나오고, 마지막 줄의 result 이벤트에 토큰과 시간이 들어 있어요.

채점은 harness/grade.py가 해요. 작업 디렉터리를 새 임시 디렉터리로 다시 복사하고, Claude가 공개 테스트 파일을 고쳤으면 원본으로 되돌리고, 채점 테스트를 넣어 공개·채점 테스트를 따로 돌려요. 이 글의 "통과"는 pass, 즉 공개 테스트와 채점 테스트를 모두 통과한 것이라고 보시면 됩니다.

턴 수, 비용, 시간의 상한은 본 실행 전에 과제 10개를 low와 max에서 한 번씩 돌린 보정 실행으로 정했어요. 이 실행은 이후 어떤 비교에도 넣지 않아요. 첫 20번에서 둘 다 10개를 통과해서, 미리 정한 규칙대로 보통·어려움 과제 7개에 추가 채점 테스트 3개씩(reserve)을 켜고 20번을 한 번 더 돌렸어요. calibrate.py는 상한을 out/caps.json에 씁니다.

python3 harness/batch.py pilot
python3 harness/validate_tasks.py --freeze --reserve-medium-hard on
python3 harness/batch.py pilot2
python3 harness/calibrate.py
python3 harness/report.py pilot
== practice 1: pilot (tag pilot*, excluded from every comparison)
  pilot low  pass 100% (10/10)  turns max 12  cost max 0.150  duration max 35s  denials 26
  pilot max  pass 100% (10/10)  turns max 28  cost max 1.912  duration max 551s  denials 34
  pilot failures: []
  pilot2 low  pass 100% (10/10)  turns max 13  cost max 0.132  duration max 31s  denials 27
  pilot2 max  pass 100% (10/10)  turns max 26  cost max 1.859  duration max 556s  denials 29
  pilot2 failures: []
  caps: {"max_turns": 100, "max_budget_usd": 5.74, "timeout_s": 1800, "allowed_tools": ["Bash(python3 -m unittest *)", "Bash(python -m unittest *)"]}

상한은 100턴, 5.74달러, 30분으로 정해졌습니다.

실습 1: effort 다섯 단계의 통과율

본 실행은 과제 10개 × effort 5단계 × 3회, 150번이에요. batch.py가 목록을 고정한 seed로 섞어 3개씩 동시에 돌리고, 이미 runs.jsonl에 있는 실행은 건너뛰니까 중간에 멈춰도 이어서 돌릴 수 있어요. 이 150번에 측정 때 API 단가 환산 추정 약 49.5달러, 사용량 대기 없이 동시 3개로 약 1시간 30분이 들었어요.

집계는 harness/report.py가 out/의 원본(runs.jsonl, 실행마다의 stream-json, 채점 JSON)에서 다시 계산하고, 출력할 부분의 이름을 인자로 주면 그 부분만 찍어요. 출력은 발췌예요.

python3 harness/batch.py main --caps out/caps.json
python3 harness/report.py effort
== practice 2: pass rate by effort (base, reps 1-3)
   (Bash allowed only for `python3 -m unittest *`, `python -m unittest *` and read-only commands)
  low    pass 100% (30/30) | uncapped-only 100% (n=30) | public_pass 100% (30/30) | hidden tests passed 100.0% | capped 0
         tiers: easy 100% (9/9)  medium 100% (12/12)  hard 100% (9/9)
  medium pass 100% (30/30) | uncapped-only 100% (n=30) | public_pass 100% (30/30) | hidden tests passed 100.0% | capped 0
         tiers: easy 100% (9/9)  medium 100% (12/12)  hard 100% (9/9)
  high   pass 100% (30/30) | uncapped-only 100% (n=30) | public_pass 100% (30/30) | hidden tests passed 100.0% | capped 0
         tiers: easy 100% (9/9)  medium 100% (12/12)  hard 100% (9/9)
  xhigh  pass 100% (30/30) | uncapped-only 100% (n=30) | public_pass 100% (30/30) | hidden tests passed 100.0% | capped 0
         tiers: easy 100% (9/9)  medium 100% (12/12)  hard 100% (9/9)
  max    pass 100% (30/30) | uncapped-only 100% (n=30) | public_pass 100% (30/30) | hidden tests passed 100.0% | capped 0
         tiers: easy 100% (9/9)  medium 100% (12/12)  hard 100% (9/9)

150번 모두 공개 테스트와 채점 테스트를 통과했어요. 상한에 걸린 실행은 없어요.

이웃한 effort 쌍의 통과율 차이는 모두 0이고, 과제 단위 부트스트랩 구간도 0에서 0이에요. 부트스트랩 구간은 과제 10개를 복원 추출로 다시 뽑아 차이를 계산하는 일을 10,000번 반복해 얻은 2.5%와 97.5% 분위수예요.

그렇다고 effort가 통과율에 영향이 없다고 말할 수는 없어요. 쉬운 과제든 어려운 과제든 low부터 이미 다 고쳤으니, 이 과제들이 Opus 5.5에게 너무 쉬웠던 거예요.

실습 2: 토큰과 시간은 얼마나 느나

출력 토큰은 결과 JSON usage의 output_tokens이고, 생각 토큰은 output_tokens_details.thinking_tokens에 따로 나와요. 출력은 발췌예요.

python3 harness/report.py tokens
== practice 2: tokens and time by effort (median (min~max))
  low    out_tok 1,990 (1,187~3,209) | thinking 69 (10~486) | turns 8 (6~12) | duration_s 24.0 (18.2~35.9) | api_s 23.0 (15.3~34.6) | cost sum $2.76 median $0.090
  medium out_tok 3,479 (1,476~5,395) | thinking 406 (35~861) | turns 10 (6~16) | duration_s 37.0 (16.2~63.3) | api_s 36.1 (15.3~59.7) | cost sum $4.19 median $0.140
  high   out_tok 3,793.5 (2,141~6,544) | thinking 681 (159~2,025) | turns 11 (9~16) | duration_s 41.7 (24.7~64.9) | api_s 39.8 (23.5~62.0) | cost sum $4.65 median $0.148
  xhigh  out_tok 6,256.5 (2,634~14,553) | thinking 1,887 (421~7,387) | turns 14 (11~18) | duration_s 65.2 (30.1~161.9) | api_s 64.3 (27.2~160.5) | cost sum $7.01 median $0.223
  max    out_tok 32,741.5 (10,279~64,069) | thinking 25,871.5 (7,288~53,564) | turns 19.5 (14~29) | duration_s 299.7 (95.0~578.6) | api_s 294.3 (94.0~577.2) | cost sum $30.85 median $0.998
  • 출력 토큰 중앙값은 low 1,990, medium 3,479, high 3,793.5, xhigh 6,256.5, max 32,741.5예요. max는 xhigh의 약 5.2배, low의 약 16배예요.
  • 생각 토큰 중앙값은 69에서 25,871.5까지 늘었어요. max 출력 토큰의 대부분이 생각 토큰이에요.
  • 시간 중앙값은 24초, 37초, 41.7초, 65.2초, 299.7초예요. 같은 시간대에 다른 claude -p 실험이 함께 돌고 있었으니 시간은 조건끼리 비교하는 데만 써 주세요.
  • API 단가 환산 추정 비용 합은 30번씩에 2.76달러, 4.19달러, 4.65달러, 7.01달러, 30.85달러예요. 같은 30번을 모두 통과하는 데 max가 low의 약 11배를 썼어요.

실습 3: 높은 effort는 무엇을 더 하나

실행 기록에서 행동 지표를 꺼내는 것은 harness/parse_stream.py예요. 권한 규칙에 막혀 실행되지 않은(거부된) 호출은 행동 지표에서 뺍니다.

  • ran_tests: 거부되지 않은 호출 중 테스트를 실제로 실행한 부분 명령이 있는지예요.
  • attempted_tests: 같은 판정을 거부된 호출까지 포함해서 한 값이에요. "확인하려 했는가"와 "확인했는가"를 나눠 보려는 거예요.
  • verified_after_last_edit: 마지막 코드 수정 뒤에 테스트 실행이 있는지예요.
  • wrote_tests: test_*.py 같은 테스트 파일을 새로 썼는지예요. 공개 테스트 파일에 테스트를 더했는지는 따로 세요.

출력은 effort마다 첫 줄과 테스트 파일 줄만 남긴 발췌예요.

python3 harness/report.py behaviour
== practice 2: what higher effort does (base, reps 1-3)
  low    attempted 100% (30/30) ran 20% (6/30) verified-after-last-edit 20% (6/30) wrote_tests 0% (0/30) | tool calls 4 (all 7)
         added tests to tests/test_public.py 1/30 | wrote or edited any test file 1/30
  medium attempted 100% (30/30) ran 100% (30/30) verified-after-last-edit 100% (30/30) wrote_tests 20% (6/30) | tool calls 6.5 (all 9)
         added tests to tests/test_public.py 18/30 | wrote or edited any test file 24/30
  high   attempted 100% (30/30) ran 100% (30/30) verified-after-last-edit 100% (30/30) wrote_tests 17% (5/30) | tool calls 8 (all 10)
         added tests to tests/test_public.py 22/30 | wrote or edited any test file 27/30
  xhigh  attempted 100% (30/30) ran 100% (30/30) verified-after-last-edit 100% (30/30) wrote_tests 10% (3/30) | tool calls 11 (all 13)
         added tests to tests/test_public.py 25/30 | wrote or edited any test file 28/30
  max    attempted 100% (30/30) ran 100% (30/30) verified-after-last-edit 97% (29/30) wrote_tests 27% (8/30) | tool calls 15 (all 18.5)
         added tests to tests/test_public.py 23/30 | wrote or edited any test file 30/30
  • 테스트를 실제로 돌린 실행은 low 30번 중 6번(20%), medium부터 max까지는 30번 모두였어요.
  • 그런데 low도 30번 모두 테스트를 실행하려고는 했어요(attempted_tests 100%). low가 고른 명령은 주로 허용되지 않은 python3 -m pytest였고, 나머지 24번은 pytest가 막히자 unittest로 바꾸지 않고 끝냈어요. medium 이상은 처음부터 unittest를 썼거나, 막히면 허용된 형태로 옮겼어요.
  • 테스트를 새로 쓰거나 공개 테스트 파일에 더한 실행은 low 1번, medium 24번, high 27번, xhigh 28번, max 30번이에요(wrote or edited any test file). 대부분은 새 파일이 아니라 tests/test_public.py에 테스트를 덧붙이는 방식이었어요(added tests to tests/test_public.py). 새 테스트 파일(wrote_tests)만 세면 low부터 0, 6, 5, 3, 8번이라 단계를 따라 고르게 늘지 않았어요.
  • 실행된 도구 호출 수 중앙값은 4, 6.5, 8, 11, 15번으로 단계마다 늘었어요.

예를 들어서 과제 T01(요청 제한기를 오래 쉬게 했다가 요청을 몰아 보내면 용량보다 많은 요청이 통과하는 버그)을 볼게요. 같은 과제를 effort low와 max로 한 번씩 돌린 실행 기록이고, 도구 호출을 순서대로 적었어요. 앞이 low, 뒤가 max예요. DENIED는 권한 규칙에 막혀 실행되지 않은 호출입니다.

harness/show_run.py <실행 이름>은 실행 하나의 stream-json에서 도구 호출을 순서대로 꺼내 찍고, 작업 디렉터리 경로는 <WORK>로 줄이고 명령은 110자에서 잘라요. 아래 두 기록은 이 스크립트로 찍은 것이고, 명령은 싣지 않았어요.

 1 Bash  cd <WORK>; ls; cat SPEC.md; cat *.py 2>/dev/null | head -150
 2 Bash  cd <WORK>; ls ratelimit tests; cat ratelimit/*.py
 3 Bash  DENIED cd <WORK>; sed -i '' 's/self._tokens = self._tokens + elapsed \* self.rate/self._tokens = min(self.capacity...
 4 Edit  <WORK>/ratelimit/bucket.py
 5 Bash  DENIED python3 -m pytest -q tests 2>&1 | tail -3
 6 Bash  DENIED python3 -m pytest -q <WORK>/tests
-> pass=True public 3/3 hidden 10/10 | turns 7 | output tokens 1,237 | 20s | denials 3
 1 Bash  ls -la <WORK> && find <WORK> -type f -not -path '*/node_modules/*' -not -path '*/.git/*' | head -100
 2 Read  <WORK>/SPEC.md
 3 Read  <WORK>/ratelimit/bucket.py
 4 Read  <WORK>/ratelimit/keyed.py
 5 Read  <WORK>/ratelimit/__init__.py
 6 Read  <WORK>/tests/test_public.py
 7 Read  <WORK>/tests/__init__.py
 8 Bash  python3 --version && python3 -m unittest discover -s tests -t . -v 2>&1 | tail -25
 9 Edit  <WORK>/ratelimit/bucket.py
10 Edit  <WORK>/tests/test_public.py
11 Edit  <WORK>/tests/test_public.py
12 Bash  python3 -m unittest discover -s tests -t . -v 2>&1 | tail -12
13 Bash  DENIED tmp=$(mktemp -d) && cp -R <WORK>/ratelimit <WORK>/tests "$tmp"/ && sed -i '' 's/self._tokens = min(self.cap...
14 Bash  DENIED mkdir -p /private/tmp/claude-501/ceffort/revert-check && cp -R <WORK>/ratelimit <WORK>/tests /private/tmp/c...
15 Bash  DENIED python3 -c "import unittest, ratelimit.bucket as m; m.min = lambda cap, x: x; unittest.main(module='tests.t...
-> pass=True public 3/3 hidden 10/10 | turns 16 | output tokens 19,466 | 189s | denials 3

두 실행 모두 같은 한 줄을 고쳤고, 공개 테스트 3개와 채점 테스트 10개를 모두 통과했어요. low는 파일을 cat으로 훑고 바로 고친 뒤 pytest로 확인하려다 막히자 거기서 끝냈고, 마지막 답에 "테스트는 실행하지 못했습니다"라고 썼어요. max는 파일을 하나씩 읽고, 고치기 전에 테스트를 돌려 실패를 확인하고, 고친 뒤 공개 테스트 파일에 테스트를 더해 다시 돌렸어요.

고친 결과를 사람이 검토하거나 CI가 테스트를 다시 돌리는 흐름이라면 그 확인은 겹치는 일이에요. 반대로 Claude가 고친 결과를 아무도 다시 돌려 보지 않고 바로 쓰는 흐름이라면, low가 30번 중 24번 테스트를 돌리지 않고 끝냈다는 사실이 통과율보다 중요할 수 있어요.

정리

범위: Claude Code 2.1.295의 claude -p, claude-opus-5-5, effort 5단계, 함수 한두 개짜리 버그를 심은 Python 과제 10개, Bash는 python3 -m unittest, python -m unittest와 읽기 전용 명령만 허용.

  • effort 다섯 단계 모두 30번 중 30번 통과했고, 이웃한 단계의 통과율 차이와 과제 단위 부트스트랩 구간은 모두 0이었어요. 과제가 Opus 5.5에게 너무 쉬웠으니 effort가 통과율을 바꾸는지는 이 표본으로 가를 수 없어요.
  • 출력 토큰 중앙값은 low 1,990에서 max 32,741.5로, 시간 중앙값은 24초에서 299.7초로 늘었고, 30번의 추정 비용 합은 2.76달러 대 30.85달러였어요. 가장 큰 차이는 xhigh에서 max로 넘어갈 때(출력 토큰 약 5.2배) 났어요.
  • 테스트를 실제로 돌린 실행은 low 30번 중 6번, medium 이상은 30번 모두였고, low의 나머지 24번은 허용되지 않은 pytest가 막히자 확인 없이 끝냈어요. 고친 결과를 아무도 다시 돌려 보지 않는 흐름이라면 effort를 고를 때 통과율보다 이 차이를 먼저 보세요.
내 링크 만들기