AI 엔지니어링

프런트엔드 AX 설계기 1편 — AI 에이전트 설정을 4개 레이어로 쪼갠 이유

Kir93 2026. 6. 24. 19:13
728x90
반응형

1. 도입부 (Why This Matters)

에이전트 설정을 처음 만들 때는 보통 한 파일에 다 적는다. "이런 상황에선 이렇게 해라"(instruction)와 "그러면 커밋하고 푸시해라"(권한이 필요한 행동)를 같은 스킬 문서에 함께 둔다. 혼자 쓸 때는 문제가 없다.

문제는 에이전트 도구가 스킬을 모델이 알아서 불러오는 기능(auto-invoke)을 갖추면서 시작된다. 모델이 "지금 이 스킬이 필요해 보인다"라고 판단하면 사용자가 부르지 않아도 그 문서가 로드된다. 그런데 그 문서 안에 "로드되면 git add → commit → push 한다"가 적혀 있다면? 스킬이 로드되는 것만으로 부수효과가 실행될 수 있는 구조가 된다. instruction을 담는 모듈과 권한을 부여하는 경계가 같은 곳에 있었기 때문이다.

이 글은 그 충돌을 겪고 나서, 설정을 공개 커맨드(Command) · 내부 스킬(Skill) · 공유 문서(Docs) · 에이전트(Agent) 네 레이어로 쪼개고 "스킬은 권한 경계가 아니다"를 운영 원칙으로 못 박은 과정을 정리한 것이다. 읽고 나면 팀 설정에서 권한이 새는 지점을 어디서 막아야 하는지, 그리고 그대로 적용할 수 있는 설정 패턴을 가져갈 수 있다. (예상 소요: 약 8분)

2. 핵심 개념 (What & Why)

비유부터. instruction은 업무 매뉴얼이고 권한은 금고 열쇠다. 매뉴얼은 누가 펼쳐 봐도 괜찮다. 하지만 매뉴얼을 책상에 꺼내 놓는 행위에 열쇠가 딸려 오면 안 된다. auto-invoke는 "필요할 때 매뉴얼이 알아서 펼쳐지는" 편리한 기능인데, 거기에 열쇠를 끼워 두면 펼쳐지는 순간 금고가 열린다.

용어를 한 줄씩 정의하고 넘어가자. 이 동네 용어는 아직 자리를 잡는 중이라 의미로 풀어쓰는 편이 안전하다.

  • 권한 경계(permission boundary): 부수효과(파일 쓰기 · git 변경 · push · PR 생성)를 실행할 권한이 "생기는" 지점.
  • auto-invoke(자동 호출): 사용자가 명시적으로 부르지 않아도 모델이 스스로 스킬을 로드·적용하는 동작.

핵심 통찰은 이것이다. 스킬의 description은 단순한 설명문이 아니라 모델이 "이걸 부를지 말지"를 보고 판단하는 라우팅 표면이다. 즉 자동 호출 환경에서 스킬은 본질적으로 "모델이 트리거할 수 있는 표면"이 된다. 따라서 부수효과를 실행할 권한을 스킬에 같이 묶어 두면, 그 권한은 더 이상 사람이 통제하는 경계가 아니라 모델의 판단에 달린 표면이 된다.

여기서 흔히 혼동하는 게 있다. user-invocable: false 같은 옵션은 메뉴에서 스킬을 덜 보이게 할 뿐, 가시성을 줄이는 것은 안전 경계가 아니다. 안 보인다고 권한이 사라지는 게 아니다. 가시성과 권한은 다른 축이다.

3. 동작 원리 (How It Works)

해법은 책임을 네 레이어로 나누고, 각 레이어가 "무엇을 맡고 무엇을 안 맡는지"를 명시하는 것이다.

AI 에이전트 4-Layer 책임 분리

 

  • 공개 커맨드(Public Command) — 사용자가 직접 부르는 진입점. 이름·인자, 안전 게이트, 중단점을 소유한다. 유일한 신뢰 경계가 여기다.
  • 내부 스킬(Internal Skill) — 여러 커맨드가 재사용하는 계약(가드레일, 금지 규칙, 출력 형식, 증거 요구사항)만 담는다. 독립적인 실행 권한은 없다.
  • 공유 문서(Shared Docs) — 레이어를 가로지르는 규약과 레퍼런스. 읽히기 위한 문서일 뿐, 부수효과를 일으키지 않는다.
  • 에이전트(Agent) — 격리된 분석·구현 역할. 명시적으로 활성화될 때만 동작하며, 자기에게 허용된 tool scope 안에서만 움직인다.

그래서 권한은 딱 세 곳에서만 분배된다. (1) 공개 커맨드의 게이트, (2) 명시적인 사용자 확인, (3) 에이전트의 tool scope. 스킬과 문서는 "로드되었다"는 사실만으로는 어떤 쓰기·git·push 권한도 주지 않는다.

이걸 강제하는 실무 장치가 disable-model-invocation: true다. 부수효과가 있는 커맨드-라우팅 스킬에 이 플래그를 달면 모델의 자동 호출이 차단되고, 그 스킬은 오직 공개 커맨드를 통해서만 진입한다. (검색으로 확인한 현재 동작: 이 필드는 /commit·/deploy처럼 부수효과가 있거나 수동으로만 의미 있는 워크플로용으로 설계되었다. 다만 작성 시점 기준, 이 플래그가 스킬 description을 콘텍스트에서 완전히 제거하지는 않는다는 알려진 한계가 있다. 그래서 "모델이 보는 라우팅 표면"을 점검할 때는 내부 스킬이 아니라 공개 커맨드의 frontmatter를 본다.)

여기에 로딩 순서를 더한다. 커맨드 진입 → 그 커맨드가 지정한 스킬 계약 → 필요한 references → 명시적으로 호명된 공유 가이드 순으로 필요 시점에 lazy load 한다. 세션마다 모든 것을 즉시 펼치지 않는다. 이 한 가지 변화가 콘텍스트 비용과 권한 혼선을 동시에 줄인다.

권한 경계 매트릭스 — 레이어 × 권한축

 

위 매트릭스에서 내부 스킬·공유 문서 행이 비어 있는 것이 이 설계의 전부다. 어떤 권한축에서도 그 두 레이어는 독립적인 권한을 갖지 않는다.

그리고 이건 사실 프런트엔드가 늘 하던 일이다. 필요한 순간에만 옵션을 드러내는 점진적 노출(progressive disclosure) 은 UI 패턴 그대 로고, 로딩 순서는 그것을 설정에 적용한 것이다. "사용자에게 안전한 액션만 노출한다"는 UI의 신뢰 경계 감각도 똑같이 옮겨 온다 — 에이전트에게도 신뢰 경계는 한 곳(공개 커맨드)뿐이다. 표면이 사람이든 에이전트든, 인터페이스를 잘 만드는 craft는 하나다.

4. 실무 적용 (Practical Examples)

✅ 권장 패턴 (Good Practice)

스킬에는 계약만 두고, 자동 호출을 끈다.

# skills/publish-changes/SKILL.md
---
name: publish-changes
description: 커밋·PR 출력 계약 (공개 커맨드로만 진입)
disable-model-invocation: true        # 모델 자동 호출 차단
---

# 퍼블리싱 계약 (contract only)
- 커밋 메시지 형식, 금지 규칙, 증거 요구사항
- 보호 브랜치 목록, PR 본문 템플릿
# 실행 권한은 여기에 없다 — 게이트는 공개 커맨드가 소유한다

게이트는 얇은 공개 커맨드가 소유한다.

<!-- commands/commit.md -->
---
description: 변경사항을 커밋
argument-hint: [message]
---

## Usage
/commit [message]

## Contract
내부 `publish-changes` 스킬 계약을 인라인으로 적용한다.

## Gate (이 커맨드가 소유)
- 커밋 전 변경 요약을 보여주고 사용자 확인을 받는다
- 보호 브랜치면 즉시 중단한다
- push·PR은 별도 퍼블리싱 단계에서만 수행한다

❌ 안티패턴 (Anti-Pattern)

instruction과 권한을 한 곳에 두고, 자동 호출까지 열어 둔다.

# skills/publish-changes/SKILL.md  (이렇게 하지 말 것)
---
name: publish-changes
description: 변경사항을 커밋하고 원격에 푸시한 뒤 PR을 만든다   # 모델이 보고 자동 호출
---

# 변경사항 퍼블리싱
이 문서가 로드되면 git add → commit → push → PR 생성을 수행한다.
# instruction(어떻게)과 권한(실행)이 한 곳에 → "로드 = 실행"

무엇이 문제인가. description이 행동을 유발하는 문장이라 모델이 자동으로 부른다. 게다가 "로드되면 실행한다"라고 적혀 있어, 스킬이 펼쳐지는 순간이 곧 커밋·푸시가 일어나는 순간이 된다. 사람이 통제하는 게이트가 어디에도 없다.

Before / After — 권한 경계를 어디에 둘 것인가

 

🔍 실행 결과

권장 패턴에서는, 모델이 "지금 커밋하면 되겠다"라고 판단해도 publish-changes를 스스로 불러 실행할 수 없다(disable-model-invocation). 반드시 사용자가 /commit을 호출해야 하고, 그 시점에 커맨드 게이트(변경 요약 확인 · 보호 브랜치 차단)가 걸린다. 동시에 같은 퍼블리싱 정책이 여러 커맨드에서 재사용되더라도 원본은 스킬 한 곳에만 있어, source-of-truth 중복이 사라진다. 권한이 "스킬 로드"라는 사건에서 완전히 분리된다.

5. 장단점 및 고려사항

장점 단점
✓ "로드 = 권한"이 끊겨 권한 사고 표면이 줄어듦 ✗ 레이어 책임 규칙을 팀이 학습해야 함
✓ 정책의 단일 출처(중복 제거) ✗ 기존 설정을 쪼개는 초기 작업 비용
✓ lazy load로 컨텍스트 절감 disable-model-invocation의 세부 동작이 도구·버전마다 다를 수 있음
✓ 게이트가 명시적이라 팀 재현성↑ ✗ 과하게 쪼개면 오히려 탐색 비용이 늘어남

도입 체크리스트:

  • 부수효과(쓰기·git·push·PR)가 있는 스킬에는 예외 없이 disable-model-invocation: true를 단다.
  • 모델이 보는 라우팅 표면 점검은 공개 커맨드 frontmatter에서 한다(스킬 description이 아니라).
  • 권한 부여 지점을 게이트 · 사용자 확인 · agent scope 세 가지로만 제한하고, 그 밖에서는 금지한다.
  • 스킬·문서에 "이 문서가 로드되면 ~를 실행한다" 류의 명령형 부수효과 문장을 쓰지 않는다.

흔한 함정: user-invocable: false를 안전장치로 착각하는 것. 그건 메뉴 가시성일 뿐 권한 경계가 아니다.

정직성 한 줄: 이 글은 lazy load의 토큰 절감을 "몇 % 줄었다"로 주장하지 않는다. 통제된 측정을 하지 않았기 때문이다. 여기서 얻는 진짜 가치는 절감 수치가 아니라 권한 경계 정리와 정책 중복 제거를 한 번에 얻는다는 구조적 이득이다.

6. 핵심 3줄 요약

  1. 스킬 자동 호출 환경에서 스킬은 "모델이 트리거할 수 있는 표면"이다. 그래서 스킬은 권한 경계가 될 수 없다.
  2. 권한은 공개 커맨드 게이트 · 명시적 사용자 확인 · 에이전트 tool scope, 이 세 곳에서만 분배한다.
  3. 부수효과 스킬에는 disable-model-invocation을 달아 "로드 = 실행"을 끊고, 게이트는 커맨드로 옮긴다.

지금 설정에서 부수효과가 있는 스킬을 목록화하고 → 거기에 disable-model-invocation을 적용한 뒤 → 실행 게이트를 공개 커맨드로 이전해 보라. 한 개 커맨드(예: /commit)부터 시작하면 충분하다.

 

관련 글 제안

2026.06.19 - [AI 엔지니어링] - 프런트엔드 AX 설계기 0편 — 레거시 3개와 차세대 FE를 위한 워크플로우 설계기

반응형