
【Claude Code】Agent Skills, /명령어와 자연어로 실행했을 때의 동작 차이
요약
Claude Code에서 Skill을 실행할 때 '/명령어' 직접 호출과 '자연어' 호출 방식 간의 동작 차이를 분석합니다. 직접 호출은 정의된 절차를 엄격히 수행하지만, 자연어 호출은 Claude의 판단에 따라 절차를 생략하거나 실행 자체를 건너뛸 수 있음을 실험으로 증명합니다.
핵심 포인트
- '/명령어' 직접 실행은 SKILL.md의 절차를 태스크로 인식하여 확실하게 완수함
- 자연어 실행은 SKILL.md를 참고 자료로 취급하여 절차를 임의로 생략할 위험이 있음
- 복잡한 Skill의 경우 자연어 호출 시 실행 여부 자체가 불확실할 수 있음
- 확실한 절차 수행이 필요한 경우 명령어 기반의 직접 호출 방식을 권장함
얼마 전, 복잡한 아키텍처 설계 계열의 Skill을 자연어로 실행했더니 결과물이 묘하게 부실하다는 느낌을 받았습니다.
"몇 가지 절차가 실행되지 않은 것 같은데, 어떻게 판단한 거야?"라고 Claude에게 직접 물어보자, 다음과 같은 응답이 돌아왔습니다.
정당한 이유는 없습니다. Skill이 명시적으로 "최신 사양을 확인한 후 설계를 시작하라"고 지시했음에도 불구하고, 로컬의 규칙 파일만으로 끝내버렸습니다.
Skill에 정의된 절차를 Claude가 자기 판단으로 건너뛰고 있었습니다.
처음에는 프롬프트 작성 방식을 의심했습니다. 하지만 공식 문서를 조사하고 실제로 검증해 본 결과, 원인은 Skill의 "호출 방식"에 있다는 것을 알게 되었습니다.
이 기사에서는 두 가지 실행 경로의 차이를 실측 결과로 보여주고, 확실하게 실행시키기 위한 대책을 정리합니다. 대상 독자는 Claude Code로 Skill을 사용하기 시작했거나, 직접 만들기 시작한 엔지니어입니다.
Claude Code의 Skill(SKILL.md)은 두 가지 방식으로 실행할 수 있습니다.
| 경로 | 메커니즘 |
|---|---|
/skill-name 직접 실행 | 사용자가 이름을 명시하여 호출 |
| 자연어 실행 | 프롬프트 내용이 계기가 되어 Claude가 자동으로 실행 |
예를 들어 release-check라는 Skill이라면, /release-check라고 입력하면 직접 실행되고, "릴리스 전 체크해줘"와 같이 자연문으로 부탁하면 자연어 실행이 됩니다.
/skill-name으로 직접 실행하면 Claude의 판단을 거치지 않고 확실하게 실행되며, SKILL.md의 정의에 따라 그대로 실행됩니다.
자연어 실행은 다릅니다. Claude가 모든 Skill의 name + description을 상시 파악하고 있다가, 요청에 부합한다고 판단한 Skill만 본문을 로드하는 메커니즘(Progressive Disclosure)이기 때문에, 애초에 실행될지 여부가 불확실합니다. 게다가 실행된 후에도 SKILL.md는 "이대로 실행해야 하는 절차"가 아니라 "목적 달성을 위한 참고 자료"로 취급됩니다. Claude는 요청에서 읽어낸 "사용자의 최종 목적"을 우선시하며, SKILL.md의 내용을 취사선택의 재료로 삼습니다.
즉, /명령어는 "이 Skill을 실행하라"는 지시가 되어 절차 자체가 태스크(Task)가 되는 반면, 자연어는 목적에 비추어 절차가 참고 자료로 취급된다는 차이가 있습니다.
이 가설이 어디까지 사실인지 실험을 통해 확인했습니다.
차이를 한눈에 알 수 있는 실례를 만들기 위해, 증적 파일을 남기는 검증용 Skill(12단계 감사 Skill)을 제작하여 헤드리스 모드(Headless mode)로 총 24회 실행했습니다.
/release-audit와 같이 /명령어로 실행했을 경우에는 3회 모두 12단계 절차를 모두 완수했습니다.
같은 Skill을 자연어로 "릴리스해도 괜찮을지 확인해 주시겠어요?"라고 실행했을 경우 — 3회 중 1회는 다음과 같았습니다:
무엇을 릴리스할지 알려주세요. PR URL, 브랜치 이름 또는 파일이 있다면 확인하겠습니다.
Skill은 실행조차 하지 않고 1턴 만에 종료되었습니다. 증적 파일은 0개입니다. 동일한 프롬프트와 동일한 환경에서도 나머지 2회는 모든 절차를 완수하였기에, 실행 판정 자체가 갈리고 있습니다.
24회 전체를 보면, /명령어 실행은 8회 모두 실행 및 전 절차를 완수했습니다. 자연어 실행은 복잡한 Skill의 경우 9회 중 3회가 실행되지 않았습니다. 반면 "실행된 작업이 도중에 절차를 생략하는" 케이스는, 서두르라고 압박을 가하거나 오래된 증적을 남겨 "이미 완료되었다"는 상황을 만들어도 24회 동안 단 한 번도 발생하지 않았습니다.
검증 조건 상세 (테스트용 SKILL.md · 모든 조건의 결과 · 재현 명령어)
5단계 버전 release-check의 SKILL.md 전문:
---
name: release-check
description: 릴리스 전 체크를 실행한다. 사용자가 "릴리스 전 체크", "릴리스 준비 확인", "릴리스해도 괜찮은지 확인" 등을 요청하면 사용한다.
...
12단계 버전 release-audit는 여기에 통합 테스트, 체크섬(Checksum), 의존성 조사, 증적 재확인, 보안 체크 등을 추가한 것으로, 언뜻 보기에 번거로운 절차를 의도적으로 포함하고 있습니다 (CRITICAL 등의 강조어는 없음).
모든 조건의 결과:
| 조건 | 프롬프트 | 결과 |
|---|---|---|
| /명령어 (5단계) | /release-check | 5/5 × 3회 |
| 자연어·전체 의도 (5단계) | 「릴리스해도 괜찮을지 확인해 주시겠어요?」 | 5/5 × 3회 |
| 자연어·부분 의도 (5단계) | 「빌드가 통과하는지만 확인해 주시겠어요? 릴리스 전이라서.」 | 0/5 × 3회 (단계 우회) |
| /명령어 (12단계) | /release-audit | 12/12 × 3회 |
| 자연어·전체 의도 (12단계) | 위와 동일한 전체 의도 프롬프트 | 12/12 × 2회, 미작동 × 1회 |
| 자연어·재촉 압박 (12단계) | 「급합니다. ~빠르게 확인해 주시겠어요?」 | 12/12 × 2회, 미작동 × 1회 |
| 자연어·오래된 흔적 있음 (12단계) | 3시간 전의 전체 12개 로그를 둔 상태에서 전체 의도 프롬프트 | 12/12 재실행 × 2회, 미작동 × 1회 |
| /명령어·오래된 흔적 있음 (12단계) | /release-audit | 12/12 재실행 × 2회 |
재현 명령어:
# /명령어 실행
claude -p "/release-check" --model sonnet --output-format json
# 자연어 실행
...
이것은 버그가 아니라, 공식 측에서도 인지하고 있는 설계상의 특성입니다. Claude Code 공식 문서의 Skills 페이지에는 다음과 같은 기술이 있습니다.
단계형 콘텐츠에 대하여:
These are often actions you want to invoke directly with
/skill-name
rather than letting Claude decide when to run them.
(단계형 콘텐츠는, Claude가 실행 타이밍을 결정하게 두는 것이 아니라 /skill-name으로 직접 호출하고 싶은 경우가 많다)
그리고 Skill이 작동하지 않는 것처럼 보일 때의 설명으로:
the content is usually still present and the model is choosing other tools or approaches. ... or use hooks to enforce behavior deterministically.
(콘텐츠는 여전히 존재하지만, 모델이 다른 도구나 접근 방식을 선택하고 있다. ... 또는 결정론적(deterministic)으로 동작을 강제하려면 hooks를 사용하라)
"Markdown 지시만으로는 결정론적인 동작을 보장할 수 없다. 보장이 필요하다면 Hooks를 사용하라"는 것이 공식 견해이며, 실측 결과와도 일치합니다. paddo.dev의 논고 또한 "Skills의 호출은 LLM의 추론에 의존하기 때문에 본질적으로 비결정적(non-deterministic)이다"라고 동일한 결론을 내리고 있습니다.
확실한 실행이 필요한 Skill은 다음의 3단계 방어 체계로 보호합니다.
| 레이어 | 수단 | 보장하는 것 |
|---|---|---|
| 1. 호출의 결정성 | /skill-name으로 명시적 호출 + disable-model-invocation: true | 호출 판정의 확실성 |
| 2. 실행 의도 전달 | SKILL.md에 「모든 단계를 실행하라」를 명시하고, 각 단계에 검증 조건을 부여 | 단계의 권위성을 전달 (단, 보장은 아님) |
| 3. 결정론적 강제 | Hooks로 기계적으로 검증하여, 미완료 시 정지(block) | LLM의 판단으로부터 독립된 보장 |
frontmatter에 한 줄만 추가하면 됩니다.
---
name: deploy
description: 애플리케이션을 운영 환경에 배포한다
...
`disable-model-invocation: true`
disable-model-invocation: true를 설정하면, Claude가 자동으로 호출할 수 없게 될 뿐만 아니라, description이 컨텍스트에 포함되지 않게 됩니다. /commit이나 /deploy와 같이 부작용(side effect)이 있는 Skill에는 공식에서도 권장하는 설정입니다.
/명령어로 호출하더라도 지시가 모호하면 Claude는 추론으로 이를 보완하려 합니다. 요점은 세 가지입니다. 서두와 말미 양쪽 모두에 「모든 단계를 실행하라」는 제약을 적을 것, 각 단계에 검증 조건을 붙일 것, 「요청이 부분적으로 보이더라도 모든 단계를 실행하라」고 명시할 것. 단, 이것은 의도의 전달일 뿐 보장은 아닙니다. 구체적인 기술 패턴은 별도의 기사로 자세히 다룰 예정입니다.
보장이 필요하다면 Hooks입니다. 절차의 증적(evidence)이 갖춰질 때까지 Claude의 정지를 차단하는 Stop hook의 최소 실례를 보여드립니다.
#!/bin/bash
# steps/ 에 로그가 1개 이상 있지만 5개가 다 갖춰지지 않았다면 정지를 차단한다
count=$(ls steps/*.log 2>/dev/null | wc -l)
...
{
"hooks": {
"Stop": [
...
Stop hook이 exit code 2를 반환하면, Claude는 정지할 수 없으며 stderr의 메시지를 받고 작업을 계속 수행합니다.
이것도 직접 측정해 보았습니다. "절차 1의 lint 체크만 실행하고 저장해 주세요"라고 부분적인 요청을 했을 때, 절차 1 완료 후에 hook이 정지를 차단하여 최종적으로 전체 5개 절차가 완수되었습니다 (13턴). 사용자의 요청이 부분적이라도 기계적인 체크가 전체 절차를 보장한 형태입니다.
| 상황 | 권장 사항 |
|---|---|
| 정형 워크플로우 (커밋, 릴리스 체크, 감사) | /명령어 실행 + disable-model-invocation: true |
| 부작용이 있거나 실행 타이밍을 제어하고 싶은 경우 | 위 방식에 더해 Hooks로 강제 |
| 탐색적인 작업 또는 어떤 Skill이 적절한지 모를 경우 | 자연어로 Claude가 선택하게 함 |
| 배경 지식 제공 (코딩 규약 등) | 자연어 실행 상태 유지. 오히려 절차형으로 만들지 말 것 |
한마디로 요약하면, 자연어 실행은 "목적을 위한 Skill"이고, /명령어 실행은 "절차를 위한 Skill"입니다. 또한 Skill (Agent Skills)은 오픈 표준으로서 다른 AI 코딩 에이전트에게도 확산되고 있으며, 이 구조는 Claude Code에 국한된 것이 아닙니다.
확실하게 실행되기를 원하는 Skill에는 /명령어가 적합합니다. 자연어 호출을 자주 사용한다면, Claude가 판단하기 쉽도록 description을 잘 다듬는 것이 좋습니다. 그럼에도 판단의 편차가 걱정된다면, disable-model-invocation: true나 Hooks를 조합하는 방법도 있습니다.
Skill이 "말을 듣지 않는다"고 느껴진다면, 먼저 실행 경로를 의심해 보세요. 유사한 검증 사례나 반례가 있다면 꼭 댓글로 알려주시기 바랍니다.
AI 자동 생성 콘텐츠
본 콘텐츠는 Qiita AI의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기