본문으로 건너뛰기

© 2026 Molayo

Dev.to헤드라인2026. 05. 31. 02:48

실제로 작동하는 코딩 에이전트 스킬 설계하기

요약

AI 코딩 에이전트의 성능을 높이기 위해 프롬프트 제약 대신 결정론적인 도구(scripts)를 설계하는 스킬 설계 방법론을 제안합니다. 에이전트는 추론에 집중하고, 실제 동작은 예측 가능한 스크립트가 수행하도록 하여 컨텍스트 윈도우 문제를 해결합니다.

핵심 포인트

  • 에이전트의 프롬프트 제약보다 도구의 결정론적 설계가 더 중요함
  • 에이전트는 의도 파악과 추론에, 스크립트는 실행에 집중해야 함
  • 결정론적 스크립트 사용 시 컨텍스트 윈도우 효율성 증대
  • 반복 가능한 테스트를 통해 에이전트의 비결정론적 오류 감소

지난 몇 달 동안 저는 AI 코딩 에이전트(AI coding agents)에 집착해 왔습니다. 단순히 사용하는 것에 그치지 않고, 에이전트와 함께 구축하며 제 사용 사례(use-cases)에 맞춰 최적화해 왔습니다. 저는 Claude Code, VS Code Copilot, Codex, Cursor와 같은 에이전트들을 위해 다양한 스킬(skills)을 설계하고 테스트했으며, 이들이 어디에서 잘 작동하고 어디에서 조용히 무너지는지를 면밀히 관찰했습니다.

그리고 에이전트들은 실제로 무너집니다. 대개 비슷한 방식으로 말이죠. 에이전트는 세 단계까지는 아주 잘 수행하다가, 네 번째 단계에서 멍청한 실수를 저지릅니다. 그러고는 뒤로 돌아가며 무엇이 잘못되었는지 분석하기 시작하는데, 문제를 파악할 때쯤이면 컨텍스트 윈도우(context window)가 거의 가득 차 버립니다. 결국 작업이 완료되는 대신, what-have-I-done-so-far.md 파일 3개만 남게 되는 상황이 벌어집니다.

이런 순간들을 충분히 겪은 후, 저는 에이전트를 더 똑똑하게 만들려는 시도를 멈췄습니다. 대신 스킬 설계(skill design)에 대한 사고방식 자체를 완전히 바꾸었습니다. 이 블로그는 그 변화와 그 과정에서 도출된 패턴들에 관한 이야기입니다.

나에게 이를 가르쳐준 스킬

제가 계속해서 되돌아보게 되는 예시는 제가 만든 confluence-publisher라는 스킬입니다. 그 기원은 지루하지만 솔직합니다. 회사 차원에서 Confluence MCP 서버에 대한 접근 권한이 없었기 때문에, 제 에이전트에게는 페이지를 게시할 수 있는 기본 방법이 없었습니다. IT 부서의 처리를 기다리는 대신, 저는 직접 다리를 놓았습니다. 마크다운(markdown)을 가져와 Confluence에 게시하는 스킬을 만든 것입니다.

저는 이 스킬 설계를 새로운 스킬 프레임워크(skill framework)를 위한 샌드박스(sandbox)로 사용했습니다. 다른 스킬에서 겪었던 모든 고충이 여기서도 나타났기에, 무엇이 에이전트 스킬을 실제로 신뢰할 수 있게 만드는지 파악하는 데 이를 활용했습니다. 직접 따라 해보고 싶으시다면 전체 코드는 GitHub에 있습니다:

🔗 GitHub: link

사고방식의 전환

이제 제 작업 방식을 바꾼 부분에 대해 이야기하겠습니다.

스킬이 제대로 작동하지 않을 때, 본능적으로는 _에이전트(agent)_를 제약하려 합니다. 프롬프트(prompt)에 더 많은 규칙, 더 많은 가드레일(guardrails), 더 많은 "이것을 하지 마시오"를 추가하는 식이죠. 하지만 그것은 잘못된 조절 장치(knob)입니다. 결국 모델(model)과 싸우게 될 뿐입니다.

더 나은 방법은 에이전트(agent)가 아니라 **도구(tools)**를 제한하는 것입니다. 에이전트가 사용하는 것들, 즉 스크립트(scripts)를 결정론적(deterministic)이고 예측 가능하게 만드세요. 그런 다음 에이전트가 진정으로 잘하는 단 한 가지, 즉 사용자가 무엇을 원하는지 이해하고 그에 대해 추론(reasoning)하는 일을 맡기십시오. 에이전트는 의도(intent)를 읽고 결정합니다. 스크립트는 동작을 수행합니다.

이를 통해 두 가지를 얻을 수 있습니다. 위험한 작업이 반복(iterate) 가능하고 테스트를 작성할 수 있는 일반적인 테스트 완료 코드(tested code)로 실행되기 때문에, 비결정론적(nondeterministic)인 결과가 훨씬 줄어듭니다. 게다가 에이전트는 구현 세부 사항(implementation details)이 아닌 결정 사항만을 보유하게 되므로 컨텍스트 윈도우(context window)를 줄일 수 있습니다. 이후에 이어지는 대부분의 내용은 이 아이디어를 다양한 곳에 적용한 것에 불과합니다.

결정 경계로서의 스크립트 (Scripts as decision boundaries)

decision_boundaries

전체 설계에서 가장 중요한 선택은 다음과 같습니다: 에이전트는 런타임(runtime)에 코드를 작성하지 않습니다. 대신 미리 작성된 스크립트를 실행하고 적절한 컨텍스트(context)를 가지고 결정합니다.

워크플로(workflow)의 각 단계는 에이전트의 도구 모음(toolkit)에 있는 어떤 도구와 마찬가지로, 깨끗한 인터페이스를 가진 독립적인 파이썬(Python) 스크립트 — step_01_convert.py, step_02_publish.py — 로 구성됩니다. CLI 인자(arguments)가 입력되면, 구조화된 출력(structured output)과 종료 코드(exit codes)가 나옵니다. 에이전트의 역할은 다음 네 가지로 축소됩니다:

  1. 어떤 인자를 전달할지 결정하기
  2. 스크립트 실행하기
  3. 출력 읽기
  4. 다음에 무엇을 할지 결정하기

이렇게 하면 모호함이 제거됩니다. 모델에게 Confluence API의 의미론(semantics), 인증(authentication), HTTP 헤더, XHTML 포맷팅에 대해 즉석에서 추론하도록 요청하는 대신, 스크립트가 그 모든 복잡성을 보유하게 됩니다. 에이전트는 오직 '계약(contract)', 즉 무엇이 입력되고 무엇이 출력되는지만 이해하면 됩니다.

제 테스트 결과에 따르면, GPT-4o와 같이 추론 능력이 높지 않은 모델조차 길을 잃지 않고 이 방식을 따릅니다. 이는 SKILL.md가 마치 레시피처럼 읽히기 때문입니다. 변수를 설정하고, 1단계를 실행하고, 출력을 확인하고, 2단계를 실행합니다. 런타임(runtime) 시점에 개방형 문제 해결(open-ended problem-solving)이 필요하지 않으므로, 실수로 즉흥적인 행동을 할 여지가 거의 없습니다.

명확하게 드러나는 실패 사례

[

vocal_failures
]

모든 스크립트는 크고 구체적으로 실패하도록 설계되었습니다. 종료 코드(Exit code)는 단순히 0 또는 1이 아니라, 특정한 의미를 담고 있습니다:

종료 코드 (Exit Code)의미에이전트가 해야 할 일
0성공다음 단계로 진행
...

무언가 고장 났을 때, 메시지는 무의미한 "failed"가 아닙니다. 무엇이 누락되었는지 에이전트에게 정확히 알려줍니다:

ERROR: --parent-page-id is required for create/upsert mode.
ERROR: Missing environment variables: CONFLUENCE_URL, CONFLUENCE_API_TOKEN
ERROR: HTML artifact not found: /path/to/expected/file.html

이것이 에이전트의 자기 수정(self-correct)을 가능하게 합니다. 만약 --parent-page-id를 잊었다면 에러 메시지가 이를 알려주고, 에이전트는 이를 수정합니다. 자격 증명(credentials)이 누락되었다면, 에이전트는 추측하는 대신 사용자에게 요청해야 한다는 것을 알게 됩니다.

저는 diagnose_permissions.py 스크립트를 통해 이를 한 단계 더 발전시켰습니다. 2단계에서 인증 에러로 인해 실패할 경우, 에이전트는 연결성, 공간(space) 액세스, 페이지 수준의 권한을 순차적으로 테스트하는 전용 진단 스크립트를 실행할 수 있습니다. 각 테스트는 자체적으로 SUCCESS 또는 FAILED를 출력하므로, 에이전트는 추측하는 대신 권한 체인의 어느 지점이 끊어졌는지 정확히 찾아낼 수 있습니다.

메모리로서의 아티팩트 (Artifacts)

[

artifacts
]

에이전트 워크플로우 (Agentic workflows)에서 가장 어려운 문제 중 하나는 컨텍스트 윈도우 (Context window)입니다. 16,000자 길이의 HTML 문서를 에이전트의 메모리에 계속 유지할 필요는 없습니다. 그저 _접근 가능 (Reachable)_하기만 하면 됩니다.

artifacts/ 폴더가 이 역할을 수행합니다:

  • 1단계 (Step 1)에서 마크다운 (Markdown)을 XHTML로 변환하여 artifacts/{Title}_{RUN_TS}.html에 기록합니다.
  • 2단계 (Step 2)에서 해당 파일 경로를 읽어옵니다.
  • 에이전트는 오직 _경로 (Path)_만을 보유하며, 내용은 절대 보유하지 않습니다.

따라서 에이전트는 컨텍스트를 낭비하지 않고도 대규모 문서를 워크플로우를 통해 이동시킬 수 있습니다. 또한 필요한 경우 단계 사이에 아티팩트 (Artifact)를 검사할 수도 있습니다 (예: 게시하기 전에 변환이 제대로 되었는지 확인하기 위해 HTML을 읽는 경우).
네이밍 컨벤션 ({Title}_{RunTimestamp}.html) 덕분에 각 아티팩트는 자기 기술적 (Self-describing) 성격을 가지며, 몇 주 후에 실행 결과가 이상하게 나타나더라도 해당 실행의 정확한 파일을 찾아낼 수 있습니다.

거버넌스로서의 작업 로그 (Work-logs)

work_logs

모든 스크립트 호출은 공유 작업 로그인 work-logs/worklogs_{RUN_TS}.md에 내용을 추가합니다. 이것은 단순한 로깅 (Logging) 이상입니다. 해당 스킬 (Skill)이 실제로 무엇을 수행했는지에 대한 기록입니다.

전형적인 항목은 다음과 같습니다:

## Step 02: publish
**Script**: `script/step_02_publish.py`
**Started**: 2026-05-28T18:36:24.677335+00:00
...

이것이 중요한 이유는 몇 가지가 있습니다:

  • 에러 추적 (Error tracking): 실행이 중간에 실패할 경우, 로그는 어떤 단계가 성공했고 어떤 단계가 실패했는지, 타임스탬프(timestamps) 및 사용된 정확한 파라미터(parameters)와 함께 보여줍니다.
  • 부작용 추적 (Side-effect tracking): 페이지를 게시하는 것은 되돌릴 수 없습니다. 로그는 그것이 언제 발생했는지, 무엇이 나갔는지, 그리고 어디에 안착했는지를 기록합니다. 만약 스킬이 이후 단계에서 실패한다면, 에이전트가 어떤 상태를 남겨두었는지 정확히 알 수 있습니다.
  • 에이전트 자기 참조 또는 자기 개선 (Agent self-reference or self-improvement): 다음 실행 시, 에이전트는 과거의 작업 로그(work-logs)를 읽어 이전에 어떤 일이 일어났는지, 어떤 파라미터가 작동했는지, 무엇이 실패했는지, 출력이 어떤 모습이었는지를 확인할 수 있습니다. 이는 가공되지 않은 데이터로 컨텍스트(context)를 오염시키지 않으면서도, 과거의 실패 사례에 대해 에이전트가 스스로를 수정할 수 있게 해주는 일종의 장기 기억(long-term memory) 역할을 합니다.

비밀 정보는 컨텍스트 윈도우(context window) 외부에 유지합니다

secrets_stay_outside

자격 증명(Credentials)은 에이전트의 컨텍스트 윈도우(context window)에 노출되어서는 안 됩니다. 인증은 프로그래밍 방식으로 수행되어야 하며, 가급적 에이전트 자체를 위해 설계된 서비스 계정(service account)을 사용하는 것이 좋습니다. 자격 증명은 시크릿 매니저(secret managers)에서 읽어올 수 있으며, 에이전트의 컨텍스트와 별개로 인증을 수행할 수 있습니다. 이 예시에서는 데모의 편의를 위해 git에서 추적하지 않는 .env 파일을 사용하고 있습니다. 스크립트는 _load_env_file()을 통해 내부적으로 자격 증명을 로드합니다. 에이전트는 자신의 컨텍스트에서 토큰(token) 값을 절대 볼 수 없습니다.

def _load_env_file() -> None:
    """스킬의 .env 파일에서 변수를 os.environ으로 로드합니다."""
    env_path = Path(__file__).resolve().parents[1] / ".env"
...

스크립트는 필수 변수가 설정되어 있는지 확인하도록 설계되었으며, 변수를 식별할 수 없는 경우 "Missing environment variables: CONFLUENCE_API_TOKEN" 에러를 통해 에이전트에게 알립니다.

.template-env 파일은 실제 값 없이 예상되는 형식을 보여줍니다:

CONFLUENCE_URL=https://your-company.atlassian.net
CONFLUENCE_EMAIL=your-company-email
CONFLUENCE_API_TOKEN=developer-api-token-get-from-confluence-site

사람 운영자가 비밀 정보(secrets)를 관리하며, 에이전트는 인증된 앱이 이미 가지고 있는 권한 내에서 실행됩니다. 에이전트가 고민해야 할 OAuth 흐름이나 토큰 로테이션(token rotation)은 없습니다. 에이전트는 그저 접근이 허용된 샌드박스(sandbox) 내에서 작동할 뿐입니다.

천장이 아닌 바닥을 기준으로 설계하기

눈치채셨겠지만, 이 모든 과정을 관통하는 핵심은 SKILL.md가 사고 사슬 (Chain-of-Thought, CoT) 추론 능력이 없는 모델이라도 올바르게 실행할 수 있도록 혼란을 줄이는 방식으로 작성되었다는 점입니다. 이를 가능하게 하는 몇 가지 기술은 다음과 같습니다:

  • 정확한 명령어가 포함된 번호 매겨진 단계. 에이전트는 스크립트를 어떻게 호출할지 고민하지 않습니다. 정확한 명령어가 명확하게 표시된 플레이스홀더 변수(placeholder variables)와 함께 바로 그곳에 있습니다.
  • 단계당 하나의 결정. 각 단계는 확인해야 할 하나의 결과(종료 코드, exit code)와 선택해야 할 하나의 분기(계속 또는 중단)를 가집니다.
  • 중첩된 로직 배제. "만약 페이지가 존재하고, 모드가 upsert이며, 부모가 변경되었다면..."과 같은 로직은 없습니다. 각 모드는 독립적이며, 스크립트가 자체 로직을 처리합니다.
  • 구조화된 출력 (Structured output). 스크립트는 성공 시 JSON을 출력하므로, 어떤 모델이라도 추가적인 파싱 로직 없이 결과를 파싱할 수 있습니다.

이는 의도적인 트레이드오프 (tradeoff)입니다. 더 강력한 모델이라면 더 지저분한 인터페이스도 처리할 수 있겠지만, 바닥(최저 성능 기준)을 기준으로 설계함으로써 이 스킬은 다양한 모델 계층(심지어 소비자용 기기에서 실행되는 로컬 모델까지)에서 작동할 수 있습니다.

언제 이 방식을 사용해야 하는가 (그리고 언제 사용하지 말아야 하는가)

이 독자층에게 던질 만한 타당한 질문이 있습니다: MCP가 존재한다면, 왜 굳이 스크립트로 스킬을 만들어야 할까요? 제가 여기서 시작한 이유는 MCP 접근 권한이 없었기 때문이지만, 이 둘은 사실 경쟁 관계가 아닙니다. MCP 서버는 에이전트에게 도구(tools) 세트를 제공합니다. 반면 이 패턴은 에이전트에게 순서가 정해진 레시피와 더불어, 디스크 상의 메모리(on-disk memory) 및 수행한 작업에 대한 감사 추적(audit trail)을 제공합니다. 심지어 두 가지를 모두 실행하면서, 레시피가 로컬 스크립트 대신 MCP 도구를 가리키도록 설정할 수도 있습니다.

솔직한 트레이드오프(tradeoff)는 다음과 같습니다: 이 방식은 대부분 선형적(linear)이며 기록되기를 원하는 실제 부작용(side effects)이 있는 워크플로우에 가장 적합합니다. 게시(Publish), 배포(deploy), 마이그레이션(migrate), 생성 후 전송(generate-and-ship) 같은 작업들이 이에 해당합니다. 반면, 작업이 탐색적(exploratory)이거나 에이전트가 사전에 예측할 수 없는 방식으로 분기(branch)해야 하는 경우에는 적합하지 않습니다. 미리 작성된 스크립트는 스크립트로 정의되지 않은 상황에 적응할 수 없으며, 이를 작성하는 것 자체가 초기 비용(upfront cost)이 됩니다. 만약 작업이 일회성이라면, 복잡한 절차를 건너뛰고 에이전트가 즉흥적으로 처리하게 두십시오. 하지만 그 작업이 다시 실행될 가능성이 있고 결과가 중요하다면, 구조를 갖추는 것이 그 비용을 충분히 보상할 것입니다.

마무리하며

제가 배운 모든 것을 한 문장으로 압축해야 한다면 다음과 같습니다: 에이전트를 더 똑똑하게 만들지 마세요. 에이전트의 도구(tools)를 더 예측 가능하게 만드세요. 에이전트는 의도(intent)에 대해 추론하게 하고, 결정론적(deterministic)인 스크립트가 실행을 담당하게 하십시오.

AI 자동 생성 콘텐츠

본 콘텐츠는 Dev.to AI tag의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.

원문 바로가기
1

댓글

0