토스, 코딩 에이전트 Skill 품질 관리 시스템을 만들다

by DD
1일 전
조회수 0

코딩 에이전트용 Skill이 본문에는 트리거 정보를 담지만 Description에는 없어서 호출 실패(Trigger Failure)가 발생하는 구조적 문제를 발견함\n• 30개 항목을 규칙 검사 17개 + 모델 판정 13개로 분리하여 결정적 결함은 정규식으로, 의미적 품질은 LLM으로 검증하는 이원화 검증 구조(Two-Phase Verification)를 도입함\n• BLOCKER가 하나라도 있으면 무조건 F 등급으로 Merge를 차단하는 단순화 게이트(Simplified Gate) 전략으로 리뷰어 부담을 경감함\n• GitHub Actions의 Sticky Comment와 로컬 Claude Code 플러그인으로 CI/CD 파이프라인과 개발자 워크플로우를 통합함\n• references/ 분리 미흡으로 호출 시 4-5천 토큰을 소비하는 케이스를 줄이기 위해 3단 분리 원칙(3-Tier Separation)을 권장

이원화 검증 아키텍처: 규칙 vs 모델의 책임 분리

본 시스템의 핵심 설계 원칙은 결정적(Deterministic) 결함과 의미적(Semantic) 결함의 분리에 있다. name이 kebab-case가 아닌 경우나 frontmatter 누락은 정규식 한 줄로 100% 검출되지만, "description이 본문의 트리거 조건을 커버하는가"는 모델의 문맥 이해가 필요하다.

규칙 검사를 먼저 실행하여 BLOCKER를 거르면 LLM 비용이 발생하지 않는 구조로, 비용 효율성과 정확도를 동시에 달성한다. 이는 컴파일 타임 vs 런타임 체크의 분리처럼 검증 시점을 분리하는 전략과 유사하다. 실무에서 트리거 패턴을 "when", "할 때", "사용 시" 등의 키워드로 정규식 검출을 시도했으나, 이모지나 완곡한 표현 등 False Positive가 급증하여 결국 모델 판정으로 전환했다. 키워드 매칭의 한계가 드러난 사례이다.

BLOCKER 게이트의 단순화 전략

{"deep_dive": [{"content": "등급 체계에서 가장 중요한 설계는 BLOCKER 하나 = 무조건 F 규칙이다. 이는 미세한 등급 차이로 인한 논쟁을 차단하고, Merge 차단 여부를 단일 비트로 결정하게 만든다.\n트리거 실패, 형식 위반, secret 노출은 모두 사람의 눈으로 발견하기 어려운 결함이다. 코드라면 컴파일러가 잡아주지만, Skill에는 1차 게이트가 없어 조용히 누적된다. 블록커 게이트를 두는 이유는 이러한 잠복 결함(Latent Defect)을 자동화 없이 발견하기 거의 불가능하기 때문이다. 섹션별 BLOCKER 분포(구조 5개, 트리거 1개, 안전성 2개)를 보면, 형식과 보안에 가장 강한 제약을 둔 것이 특징적이다."}]}

3단 분리 원칙과 컨텍스트 비용 관리

{

"content": "my-Skill/\n├── Skill.md # 호출하면 바로 로드. 본문은 짧게.\n├── references/ # 무거운 참고 자료. 단일 계층으로 정리.\n└── scripts/ # 항상 같은 형태로 실행해야 하는 작업 스크립트\n\n이 구조의 핵심은 호출 빈도별로 토큰 비용을 분리하는 것이다. Skill.md는 매번 호출될 때마다 로드되므로 1~2천 토큰 이내로 유지해야 하고, references/는 LLM이 필요할 때만 읽는다. 스크립트는 실행할 때마다 형태가 달라지면 안 되는 작업의 결과를 일관되게 보장한다.\n실무에서 references/를 분리하지 않은 Skill이 4~5천 토큰을 소비하는 경우가 있었는데, 분리한 뒤 평시 호출 비용이 1/3 이하로 줄었다. 이는 Lazy Loading 패턴과 비슷하며, LLM 컨텍스트 점유로 인한 숨은 비용을 보여주는 사례이다."

}

정규식에서 모델 판정으로의 전환 과정

{

"content": "트리거 판정에서 정규식(WHEN_SIGNALS_DESC 리스트)을 포기하고 모델 판정으로 바꾼 건 표현의 다양성 문제 때문이었다. 영어 'when'만 체크하면 한국어 Skill이 전부 BLOCKER로 잡히고, 한국어 표현('할 때', '사용 시')을 추가하면 반대로 누락이 발생했다.\n이모지나 완곡한 동의 표현처럼 정규식으로 담을 수 없는 케이스가 계속 나왔다. 결국 description이 본문의 트리거 조건을 커버하는지 묻는 의미적 질문에는 모델이 의미를 파악하는 쪽이 훨씬 안정적이었다. 이는 구문 분석 vs 의미 분석의 경계를 보여주는 좋은 사례인데, 두 영역의 경계를 잘못 긋으면 오탐(False Positive)이 폭발한다는 걸 보여준다."

}

GitHub Actions 통합과 Reusable Workflow 설계

PR마다 변경된 Skill.md 디렉토리만 골라 평가하는 Selective Evaluation 전략이 인상적이다. 전체 평가가 아닌 변경분만 평가하여 비용을 절감하고, 결과를 Sticky Comment로 남겨 작성자가 컨텍스트 전환 없이 즉시 액션을 취할 수 있게 했다.

workflow_dispatch 모드를 통해 전체 또는 특정 Skill 평가를 수동으로 트리거할 수 있으며, 결과를 Slack DM으로 전송하는 output_channel 옵션도 제공한다. Skill_path_filter로 plugins/*/Skills/* 같은 glob 패턴을 지정하면 불필요한 LLM 호출을 방지한다. caller 레포에는 .github/workflows/Skill-review.yml 파일 하나만 추가하면 되며, 평가 스크립트와 Rubric, LLM 지침은 모두 Reusable Workflow 쪽에 번들되어 있다.

안전성 섹션의 False Negative-zero 전략

평문 secret 노출과 destructive 권한 허용은 모두 BLOCKER로, False Negative를 0에 가깝게 만드는 튜닝 방향을 채택했다. 다른 섹션이 False Positive를 줄이는 쪽으로 임계값을 잡는 것과 대조적이다.

이 선택의 배경에는 secret 노출의 비가역성(Unrecoverable)이 있다. 한 번 Merge되면 회수가 사실상 불가능하지만, 작성자가 한 번 더 확인하는 비용보다 훨씬 크다. 외부 도구인 gitleaks를 활용하여 자체 정규식보다 안전한 검출을 구현했으며, rm -rf, dd if=, mkfs, chmod -R 777, fork bomb 등의 destructive 패턴을 정규식으로 검출한다.

Skill 품질 관리를 위한 Rubric 설계와 시스템 구현