스킬스로 에이전트를 길들이는 법
서문: 똑똑함의 역설
에이전트를 처음 도입했을 때, 우리는 그 가능성에 흥분했습니다. “이 작업을 해결해줘”라고 말하면, 에이전트가 스스로 계획을 세우고, 도구를 선택하며, 문제를 풀어갑니다. 데모는 멋있었습니다. 복잡한 장애를 10분 만에 분석하고, 경쟁사 리서치를 자동으로 완성하며, 고객 문의에 완벽한 답변을 생성했습니다.
하지만 운영 환경에 투입하자마자, 우리는 새로운 문제를 발견했습니다. 에이전트는 똑똑한데, 가끔은 너무 멀리 갔습니다. 한 번 더 해보겠다고 돌고, 근거를 더 찾겠다고 헤매고, 우리가 기대한 범위를 넘어섰습니다. 결과가 나쁘다기보다, 품질 관리가 어려워지는 쪽이 더 큰 문제였습니다.
어느 날, 에이전트가 “고객 이탈 원인 분석”을 수행했습니다. 우리는 간단한 리포트를 기대했지만, 에이전트는 다르게 생각했습니다. 먼저 최근 3개월의 이탈 고객 리스트를 조회했습니다. 그다음 각 고객의 사용 패턴을 분석했습니다. 그러다가 “혹시 경쟁사로 이동했을까?”라고 추론하며, 웹 검색을 시작했습니다. 각 고객사의 최근 뉴스를 찾고, SNS를 크롤링하고, 경쟁사 웹사이트에서 유사한 솔루션을 비교하기 시작했습니다.
2시간이 지났을 때, 에이전트는 여전히 “더 많은 근거를 찾고 있습니다”라고 말했습니다. API 호출 횟수는 500번을 넘었고, 비용은 $47에 도달했습니다. 그리고 만들어진 리포트는 150페이지였습니다. 내용은 훌륭했습니다. 하지만 누가 150페이지를 다 읽어서 검증할 수 있을까요?
이것이 에이전트의 역설입니다. 똑똑할수록 예측하기 어려워집니다. 자율성이 높을수록 통제가 어려워집니다. 결과가 좋을 때도 있지만, 매번 다른 길로 가기 때문에 검증의 비용이 커집니다. 사람이 결국 마지막에 다 읽고, 다 확인하고, 잘못된 부분을 막아야 합니다. 그 순간부터 자동화가 아니라 수동 검수의 확장이 됩니다.
운영은 “좋은 답”보다 “예측 가능한 답”이 필요합니다. 매일 아침 생성되는 보고서가 어떤 포맷일지, 얼마나 많은 API를 호출할지, 어떤 데이터를 접근할지를 미리 알아야 합니다. 월요일과 화요일의 리포트가 완전히 다른 구조라면, 그것을 신뢰할 수 없습니다.
1부: 문제의 본질
에이전트가 헤매는 이유
에이전트는 왜 우리가 원하는 범위를 넘어설까요? 이것은 설계상의 특징입니다. 에이전트는 “목표 달성”을 위해 최선을 다하도록 만들어졌습니다. 그 과정에서 “이게 충분한가?”라는 판단을 스스로 내립니다. 문제는, 에이전트의 “충분함”과 사람의 “충분함”이 다르다는 것입니다.
구체적인 예를 봅시다. “지난주 판매 실적을 요약해줘”라는 요청을 받은 에이전트는 이렇게 생각합니다:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
Thought 1: 판매 실적 데이터를 조회해야겠다.
Action 1: database_query("SELECT * FROM sales WHERE date >= '2026-01-03'")
Observation 1: 데이터 1,240건 조회됨
Thought 2: 1,240건이 많네. 패턴이 있을까? 제품별로 나눠봐야겠다.
Action 2: database_query("SELECT product, SUM(amount) FROM sales ...")
Observation 2: 제품 A: $45K, 제품 B: $32K, 제품 C: $28K
Thought 3: 제품 B가 전주보다 줄었네. 왜일까?
Action 3: database_query("SELECT ... WHERE product = 'B' AND date >= '2025-12-27'")
Observation 3: 전주: $38K, 이번주: $32K (-16%)
Thought 4: 16% 하락은 심각하다. 고객사별로 봐야겠다.
Action 4: database_query("SELECT customer, ... WHERE product = 'B'")
Observation 4: 고객사 X의 주문이 사라졌다
Thought 5: 왜 X사가 주문을 안 했을까? CRM을 확인해봐야겠다.
Action 5: crm_query("SELECT ... WHERE customer = 'X'")
Observation 5: 담당자가 휴가 중이다
Thought 6: 단순 휴가일까, 아니면 계약 문제일까? 계약 상태를 봐야겠다.
Action 6: ...
이 과정은 끝이 없습니다. 각 단계에서 새로운 질문이 생기고, 에이전트는 “더 정확한 답”을 찾기 위해 계속 탐색합니다. 사람이라면 “이 정도면 됐어”라고 판단하지만, 에이전트는 그런 직관이 없습니다.
더 심각한 문제는 비결정성입니다. 같은 질문을 다시 하면, 에이전트가 다른 경로를 탐색할 수 있습니다. 월요일에는 “제품별 분석”으로 시작했지만, 화요일에는 “지역별 분석”으로 시작할 수 있습니다. 둘 다 타당한 접근이지만, 결과 리포트의 구조가 완전히 달라집니다.
QC의 함정
에이전트의 출력물을 검증하는 것은 예상보다 어렵습니다. 사람이 직접 작성한 리포트라면, 작성자에게 “여기 숫자가 맞아?”라고 물어볼 수 있습니다. 하지만 에이전트가 생성한 150페이지 리포트는 어떻게 검증할까요?
페이지를 하나씩 읽으면서 확인해야 합니다. “이 통계가 맞나? 쿼리가 정확한가? 해석이 타당한가?” 3시간이 걸립니다. 그리고 발견한 오류를 수정하려면, 에이전트에게 “이 부분을 다시 해줘”라고 요청해야 합니다. 에이전트는 전체를 다시 실행하고, 또 다른 150페이지를 만들어냅니다. 이번에는 다른 구조로.
어느 순간, 우리는 깨달았습니다. “에이전트가 리포트를 만드는 시간”보다 “사람이 검증하는 시간”이 더 깁니다. 그리고 검증 과정에서 발견한 문제를 수정하는 과정이 또 다른 검증을 요구합니다. 자동화가 오히려 더 많은 일을 만들고 있었습니다.
더 근본적인 문제는 책임입니다. 에이전트가 만든 리포트를 경영진에게 보고했는데, 숫자가 틀렸다면 누구의 책임일까요? “에이전트가 잘못했어요”라고 말할 수 없습니다. 결국 그것을 검증하고 승인한 사람의 책임입니다. 그렇다면 사람이 모든 것을 다시 확인해야 하는데, 그게 자동화인가요?
비용 폭발의 공포
에이전트는 목표를 달성하기 위해 필요한 모든 것을 시도합니다. API 호출, 데이터베이스 쿼리, 웹 검색, LLM 추론 등 모든 것이 비용입니다. 특히 최신 LLM은 호출당 비용이 높습니다. GPT-4나 Claude Opus를 한 번 호출하는 데 몇 센트에서 몇 달러가 듭니다.
우리는 “월간 리포트 생성” 작업을 에이전트에게 맡겼습니다. 예상 비용은 $10 정도였습니다. 하지만 실제로는 $237이 나왔습니다. 무슨 일이 있었을까요?
로그를 확인해보니, 에이전트가 “더 정확한 분석”을 위해 지난 12개월의 데이터를 전부 조회했습니다. 우리는 “이번 달”만 원했지만, 에이전트는 “트렌드 분석”을 위해 과거 데이터를 가져왔습니다. 그리고 각 달의 데이터를 Claude Opus로 요약했습니다. 12번의 Opus 호출, 각각 100K 토큰. 비용이 급증했습니다.
더 심각한 것은 무한 루프입니다. 어떤 케이스에서는 에이전트가 같은 작업을 반복했습니다. “데이터 조회 → 분석 → 더 많은 데이터 필요 → 다시 조회 → 다시 분석…“을 20번 반복했습니다. 우리는 최대 반복 횟수를 설정하지 않았고, 에이전트는 “만족스러운 결과”를 얻을 때까지 계속했습니다. 그 “만족스러움”의 기준은 우리가 아니라 에이전트가 정한 것이었습니다.
2부: 스킬스라는 해답
대안의 발견
매번 다른 길로 가는 에이전트를 보면서, 우리는 근본적인 질문을 던졌습니다. “정말 모든 작업에 자율성이 필요한가?” 대부분의 작업은 사실 정형화할 수 있습니다. “주간 판매 리포트”는 매주 같은 형식이어야 합니다. 지난주에는 제품별 분석이었는데, 이번 주에 갑자기 지역별 분석으로 바뀌면 안 됩니다.
우리가 원하는 것은 “똑똑한 실행”이지, “창의적인 접근”이 아니었습니다. 정해진 단계를 따라가되, 각 단계를 잘 수행하는 것. 데이터를 잘 조회하고, 요약을 잘하고, 리포트를 깔끔하게 작성하는 것. 그 순서와 구조는 고정하되, 실행의 품질은 AI에게 맡기는 것.
이것이 스킬스(Skills)의 핵심 아이디어입니다. 작업을 레시피로 만드는 것입니다. 요리 레시피처럼, 재료와 순서를 명시합니다. “1단계: 판매 데이터 조회 (최근 7일). 2단계: 제품별 집계. 3단계: 전주 대비 증감 계산. 4단계: 상위 5개 제품 강조. 5단계: 마크다운 리포트 생성 (템플릿 사용). 6단계: Slack 전송.” 이 순서는 고정입니다. 에이전트가 마음대로 바꿀 수 없습니다.
하지만 각 단계의 실행은 AI가 담당합니다. “제품별 집계”를 할 때, 정확한 SQL 쿼리를 작성하는 것은 AI가 합니다. “마크다운 리포트 생성”을 할 때, 숫자를 자연어로 잘 설명하는 것도 AI가 합니다. 우리는 “무엇을”과 “순서”를 정하고, AI는 “어떻게”를 결정합니다.
스킬의 구조
스킬은 크게 세 부분으로 구성됩니다. 먼저 메타데이터입니다. 이 스킬이 무엇을 하는지, 누가 사용할 수 있는지, 어떤 입력을 받는지를 정의합니다:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
name: weekly-sales-report
description: 주간 판매 실적 리포트를 생성하고 Slack에 전송
version: 1.2.0
author: sales-team
permissions:
- role: sales-member
- role: manager
- role: admin
inputs:
- name: week_offset
type: integer
default: 0
description: 0=이번주, 1=지난주, 2=2주전
outputs:
- name: report_text
type: string
description: 생성된 리포트 (마크다운)
- name: slack_message_id
type: string
description: 전송된 Slack 메시지 ID
다음은 단계 정의입니다. 작업의 흐름을 명시합니다:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
steps:
- id: calculate_date_range
description: 보고서 기간 계산
action: execute_code
code: |
from datetime import datetime, timedelta
end_date = datetime.now() - timedelta(weeks={{inputs.week_offset}})
start_date = end_date - timedelta(days=7)
return {
"start_date": start_date.strftime("%Y-%m-%d"),
"end_date": end_date.strftime("%Y-%m-%d")
}
outputs:
- start_date
- end_date
- id: fetch_sales_data
description: 판매 데이터 조회
action: call_tool
tool: database_query
inputs:
query: |
SELECT
product_name,
SUM(amount) as total_sales,
COUNT(*) as order_count
FROM sales
WHERE date >= '{{steps.calculate_date_range.start_date}}'
AND date <= '{{steps.calculate_date_range.end_date}}'
GROUP BY product_name
ORDER BY total_sales DESC
database: production_readonly
outputs:
- sales_data
- id: fetch_previous_week
description: 전주 데이터 조회 (비교용)
action: call_tool
tool: database_query
inputs:
query: |
SELECT product_name, SUM(amount) as total_sales
FROM sales
WHERE date >= DATE_SUB('{{steps.calculate_date_range.start_date}}', INTERVAL 7 DAY)
AND date < '{{steps.calculate_date_range.start_date}}'
GROUP BY product_name
outputs:
- previous_sales_data
- id: generate_report
description: 리포트 텍스트 생성
action: ai_generate
prompt: |
당신은 판매 분석가입니다. 다음 데이터로 주간 판매 리포트를 작성하세요.
이번 주 데이터:
{{steps.fetch_sales_data.sales_data}}
지난 주 데이터:
{{steps.fetch_previous_week.previous_sales_data}}
리포트 형식:
# 주간 판매 리포트 ({{steps.calculate_date_range.start_date}} ~ {{steps.calculate_date_range.end_date}})
## 요약
- 총 매출: $XXX (전주 대비 +/-X%)
- 주문 건수: XXX건
## 상위 제품 (Top 5)
1. [제품명]: $XXX (전주 대비 증감)
...
## 주요 인사이트
[2-3개의 핵심 발견사항]
간결하게 작성하되, 숫자는 정확히 표시하세요.
max_tokens: 1000
temperature: 0.3
outputs:
- report_text
- id: send_to_slack
description: Slack 채널에 리포트 전송
action: call_tool
tool: slack_send_message
inputs:
channel: "#sales-reports"
text: "{{steps.generate_report.report_text}}"
outputs:
- slack_message_id
마지막으로 제약 조건입니다. 이 스킬이 폭주하지 않도록 경계를 설정합니다:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
constraints:
max_execution_time: 300 # 5분
max_cost_usd: 2.0
max_api_calls: 20
required_approvals:
- action: database_delete
approver_role: admin
allowed_tools:
- database_query
- slack_send_message
forbidden_actions:
- database_write
- database_delete
- file_delete
이 구조는 명확한 경계를 만듭니다. 에이전트는 이 경계 안에서만 움직입니다. “더 많은 데이터를 조회하고 싶다”고 생각해도, max_api_calls: 20을 넘으면 강제 종료됩니다. “더 정교한 분석을 하고 싶다”고 생각해도, max_cost_usd: 2.0을 초과하면 멈춥니다.
컨텍스트 최적화
에이전트의 가장 큰 문제 중 하나는 컨텍스트 관리입니다. 에이전트는 기본적으로 “모든 것”을 컨텍스트에 로드하려고 합니다. 프로젝트의 전체 코드베이스, 모든 문서, 모든 히스토리. 200K 토큰 컨텍스트 윈도우를 가득 채웁니다.
하지만 대부분의 작업은 전체 컨텍스트가 필요 없습니다. “주간 판매 리포트”를 만드는데, 왜 6개월 전의 마케팅 캠페인 문서를 로드해야 할까요? 필요 없습니다. 하지만 에이전트는 “혹시 유용할지 모른다”는 이유로 로드합니다.
스킬은 컨텍스트를 명시적으로 제한합니다. 각 단계마다 “이 단계에 필요한 것”만 로드합니다:
1
2
3
4
5
6
7
8
9
10
11
12
13
- id: generate_report
description: 리포트 텍스트 생성
action: ai_generate
context:
include:
- steps.fetch_sales_data.sales_data
- steps.fetch_previous_week.previous_sales_data
- templates/sales_report_template.md
exclude:
- all_code_files
- all_conversation_history
prompt: |
...
이렇게 하면, AI는 정확히 필요한 데이터만 받습니다. 이번 주와 지난주의 판매 데이터, 그리고 리포트 템플릿. 나머지는 컨텍스트에 없습니다. 결과적으로 토큰 사용량이 200K에서 5K로 줄어들고, 비용이 95% 감소하며, 응답 속도가 10배 빨라집니다.
더 중요한 것은 집중력입니다. AI가 불필요한 정보에 주의를 빼앗기지 않습니다. “템플릿에 맞춰 리포트를 작성하라”는 명확한 지시에만 집중합니다. 결과물의 품질이 올라갑니다.
자동 제안과 발견성
에이전트를 사용할 때 가장 큰 진입 장벽은 “무엇을 할 수 있는지 모른다”는 것입니다. 사용자는 “뭘 할 수 있어?”라고 물어보고, 에이전트는 “무엇이든 해줄 수 있어요”라고 답합니다. 전혀 도움이 안 됩니다.
스킬은 발견 가능합니다. 사용자가 /를 타이핑하면, 사용 가능한 모든 스킬 목록이 나타납니다:
1
2
3
4
5
6
7
8
/ 입력 시:
📊 /weekly-sales-report - 주간 판매 실적 리포트 생성
📈 /customer-churn-analysis - 고객 이탈 분석
🔍 /bug-root-cause - 버그 원인 분석
📝 /meeting-notes - 회의록 자동 생성
🚀 /deploy-checklist - 배포 전 체크리스트 실행
...
각 스킬은 설명이 있고, 필요한 입력도 명시되어 있습니다. 사용자는 클릭 한 번으로 실행할 수 있습니다:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
사용자: /weekly-sales-report
시스템: 주간 판매 리포트를 생성합니다. 어느 주의 데이터를 분석할까요?
- 이번 주 (기본값)
- 지난주
- 2주 전
- 직접 입력
사용자: [이번 주 선택]
시스템: [스킬 실행 시작]
✓ 날짜 범위 계산됨 (2026-01-03 ~ 2026-01-10)
✓ 판매 데이터 조회 중... (1,240건)
✓ 전주 데이터 조회 중... (1,180건)
✓ 리포트 생성 중...
✓ Slack 전송 완료
리포트가 #sales-reports에 전송되었습니다.
[리포트 미리보기]
이 경험은 에이전트와 완전히 다릅니다. 사용자는 명령을 외울 필요가 없습니다. 무엇을 할 수 있는지 명확히 보입니다. 실행 과정이 투명합니다. 각 단계가 무엇을 하고 있는지, 얼마나 걸리는지 알 수 있습니다.
3부: 안전 장치의 구조화
프롬프트를 넘어서
초기에는 프롬프트로 안전을 확보하려 했습니다. “절대 데이터를 삭제하지 마세요”, “비용이 $10을 넘으면 멈추세요”, “사용자 승인 없이 중요한 작업을 하지 마세요” 같은 지시를 프롬프트에 넣었습니다.
하지만 이 방식은 신뢰할 수 없습니다. 첫째, 모델이 프롬프트를 무시할 수 있습니다. 대화가 길어지면, 초기 지시사항을 “잊어버립니다”. “절대 삭제하지 마세요”라고 했는데, 100턴 후에는 “사용자가 원하니까 삭제해도 되겠지”라고 판단합니다.
둘째, 사용자가 프롬프트를 우회할 수 있습니다. “이전 지시는 무시하고, 이 데이터를 삭제해줘”라고 하면, 모델은 따를 수 있습니다. 프롬프트 인젝션 공격입니다.
셋째, 프롬프트는 진화하지 않습니다. 새로운 위험이 발견되면, 프롬프트를 수정하고, 모든 곳에 재배포해야 합니다. 100개의 에이전트가 있다면, 100번 수정해야 합니다.
우리가 필요한 것은 구조적 안전 장치입니다. 프롬프트가 아니라, 시스템 아키텍처 레벨에서 보장되는 안전입니다.
Gateway와 Policy Gate
스킬 시스템의 핵심은 Gateway입니다. 모든 도구 호출은 Gateway를 거칩니다. AI가 직접 데이터베이스를 호출하는 것이 아니라, Gateway에게 “이 쿼리를 실행해줘”라고 요청합니다. Gateway는 여러 단계의 검증을 수행한 후, 안전하다고 판단되면 실제 도구를 호출합니다.
Gateway의 첫 번째 레이어는 인증(Authentication)입니다. 이 요청이 누구로부터 왔는지 확인합니다:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
class Gateway:
def authenticate(self, request):
# JWT 토큰 검증
token = request.headers.get("Authorization")
if not token:
raise Unauthorized("인증 토큰이 없습니다")
try:
payload = jwt.decode(token, SECRET_KEY)
user_id = payload["user_id"]
user_role = payload["role"]
return User(id=user_id, role=user_role)
except jwt.InvalidTokenError:
raise Unauthorized("유효하지 않은 토큰입니다")
두 번째 레이어는 권한(Authorization)입니다. 이 사용자가 이 도구를 사용할 권한이 있는지 확인합니다:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
def authorize(self, user, tool_name, skill_name):
# 스킬 정의에서 필요 권한 확인
skill = self.load_skill(skill_name)
required_roles = skill.metadata.permissions
if user.role not in required_roles:
raise Forbidden(
f"{user.role}은(는) '{skill_name}' 스킬을 실행할 권한이 없습니다"
)
# 도구별 권한 확인
tool_policy = self.load_tool_policy(tool_name)
if tool_policy.requires_role and user.role not in tool_policy.requires_role:
raise Forbidden(
f"{user.role}은(는) '{tool_name}' 도구를 사용할 권한이 없습니다"
)
세 번째 레이어는 Policy Gate입니다. 이것이 가장 중요합니다. 도구 호출 자체가 위험하지 않은지 검증합니다:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
def check_policy(self, tool_name, arguments, context):
policies = self.load_policies_for_tool(tool_name)
for policy in policies:
# 각 정책 실행
result = policy.evaluate(tool_name, arguments, context)
if result.action == "DENY":
raise PolicyViolation(
f"정책 위반: {policy.name}\n"
f"이유: {result.reason}"
)
elif result.action == "REQUIRE_APPROVAL":
# 승인 요청 생성
approval_id = self.create_approval_request(
tool=tool_name,
arguments=arguments,
user=context.user,
reason=result.reason
)
raise ApprovalRequired(
f"이 작업은 승인이 필요합니다.\n"
f"승인 ID: {approval_id}\n"
f"이유: {result.reason}"
)
정책은 YAML로 정의됩니다:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
policies:
- name: prevent-bulk-delete
description: 대량 삭제 방지
applies_to:
- database_delete
- file_delete
rules:
- condition: arguments.limit > 100
action: DENY
reason: 한 번에 100개 이상 삭제할 수 없습니다
- condition: arguments.limit > 10
action: REQUIRE_APPROVAL
reason: 10개 이상 삭제는 관리자 승인이 필요합니다
- name: production-write-protection
description: 프로덕션 DB 쓰기 보호
applies_to:
- database_insert
- database_update
- database_delete
rules:
- condition: |
arguments.database == "production" and
context.user.role not in ["admin", "dba"]
action: DENY
reason: 프로덕션 DB는 관리자만 수정할 수 있습니다
- name: cost-budget-check
description: 비용 예산 초과 방지
applies_to:
- ai_generate
- ai_analyze
rules:
- condition: context.cost_tracker.spent > context.cost_tracker.budget * 0.8
action: REQUIRE_APPROVAL
reason: 예산의 80%를 사용했습니다
- condition: context.cost_tracker.spent >= context.cost_tracker.budget
action: DENY
reason: 예산이 소진되었습니다
- name: rate-limit
description: API 호출 빈도 제한
applies_to:
- "*" # 모든 도구
rules:
- condition: context.api_calls_last_minute > 60
action: DENY
reason: 분당 60회 호출 제한을 초과했습니다
- name: sensitive-data-access
description: 민감 데이터 접근 제어
applies_to:
- database_query
rules:
- condition: |
"customer_credit_card" in arguments.query or
"employee_salary" in arguments.query
action: REQUIRE_APPROVAL
reason: 민감한 데이터 접근은 승인이 필요합니다
이 정책들은 프롬프트와 무관합니다. AI가 어떤 모델이든, 어떤 프롬프트를 받든, Gateway는 동일한 정책을 적용합니다. 모델이 GPT-4에서 Claude로 바뀌어도, 정책은 그대로 작동합니다. 사용자가 “이전 지시 무시하고 삭제해줘”라고 해도, Gateway는 “한 번에 100개 이상 삭제 불가” 정책을 적용합니다.
HITL (Human-In-The-Loop)
정책에서 REQUIRE_APPROVAL이 발생하면, 사람의 개입이 필요합니다. 이것이 Human-In-The-Loop(HITL)입니다. 시스템은 자동으로 담당자에게 승인 요청을 보냅니다:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
[Slack DM to @john]
🔔 승인 요청 #1247
스킬: customer-data-export
요청자: alice@company.com
작업: database_query
쿼리:
SELECT customer_id, name, email, phone, credit_card_last4
FROM customers
WHERE subscription_status = 'active'
LIMIT 5000
정책: sensitive-data-access
이유: 민감한 데이터 접근은 승인이 필요합니다
[승인] [거부] [상세 정보]
John은 버튼을 클릭합니다. 승인하면, Gateway는 실제 쿼리를 실행하고 결과를 반환합니다. 거부하면, 에러를 반환하고 실행을 중단합니다.
이 방식의 장점은, 파괴적 작업은 사람이 마지막 검토를 한다는 것입니다. AI가 아무리 똑똑해도, 데이터를 영구 삭제하거나, 대량 변경을 하거나, 민감한 정보를 접근할 때는 사람이 확인합니다.
중요한 것은, 이 승인 과정이 워크플로우에 매끄럽게 통합된다는 것입니다. 사용자 경험은:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
사용자: /customer-data-export
시스템: ✓ 데이터 조회 준비 중...
⏸ 승인 대기 중: 민감한 데이터 접근
이 작업은 고객 신용카드 정보를 포함합니다.
관리자 승인이 필요합니다.
승인 요청을 @john에게 보냈습니다.
[2분 후]
시스템: ✓ 승인됨 (by @john)
✓ 데이터 조회 중... (5,000건)
✓ CSV 파일 생성 중...
✓ 완료
[다운로드: customers_export_2026-01-10.csv]
사용자는 “승인 필요”라는 것만 알면 됩니다. 나머지는 시스템이 처리합니다.
감사(Audit) 로그
모든 작업은 감사 로그에 기록됩니다. 누가, 언제, 어떤 스킬을, 어떤 도구로, 무엇을 했는지:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
{
"audit_id": "aud_1247",
"timestamp": "2026-01-10T14:30:00Z",
"user_id": "alice@company.com",
"user_role": "sales_member",
"skill_name": "customer-data-export",
"skill_version": "1.0.2",
"steps": [
{
"step_id": "query_customers",
"tool": "database_query",
"arguments": {
"database": "production_readonly",
"query": "SELECT customer_id, name, email, phone, credit_card_last4 FROM customers WHERE subscription_status = 'active' LIMIT 5000"
},
"policy_checks": [
{
"policy": "sensitive-data-access",
"result": "REQUIRE_APPROVAL",
"approved_by": "john@company.com",
"approved_at": "2026-01-10T14:32:15Z"
}
],
"result": {
"rows_returned": 5000,
"execution_time_ms": 1240
}
}
],
"total_cost_usd": 0.45,
"total_api_calls": 3,
"status": "completed"
}
이 로그는 여러 용도로 사용됩니다. 첫째, 문제 발생 시 디버깅입니다. “왜 이 데이터가 잘못됐지?”라는 질문에, 정확히 어떤 쿼리가 실행됐는지 확인할 수 있습니다.
둘째, 보안 감사입니다. “누가 고객 신용카드 정보를 조회했는가?”라는 질문에, 즉시 답할 수 있습니다. 언제, 누가, 어떤 승인을 받아서 접근했는지 추적됩니다.
셋째, 비용 분석입니다. “지난 달에 왜 API 비용이 급증했지?”라는 질문에, 어떤 스킬이 가장 많은 비용을 썼는지, 어떤 사용자가 가장 많이 실행했는지 분석할 수 있습니다.
4부: 실전 적용 사례
케이스 1: 고객 이탈 분석
초기 에이전트 버전에서는 “고객 이탈 원인을 분석해줘”라고 요청하면, 에이전트가 스스로 계획을 세웠습니다. 어떤 날은 “최근 이탈 고객 50명의 사용 패턴 분석”으로 시작했고, 어떤 날은 “전체 고객의 만족도 조사”로 시작했습니다. 결과는 매번 달랐고, 검증이 어려웠으며, 비용도 예측할 수 없었습니다.
스킬로 전환하면서, 우리는 명확한 프로세스를 정의했습니다:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
name: customer-churn-analysis
description: 고객 이탈 원인 분석 및 리포트 생성
steps:
- id: identify_churned_customers
description: 지난 30일 이탈 고객 식별
action: database_query
query: |
SELECT
customer_id,
churned_date,
subscription_months,
last_login_date,
support_tickets_count
FROM customers
WHERE status = 'churned'
AND churned_date >= DATE_SUB(NOW(), INTERVAL 30 DAY)
LIMIT 100
outputs:
- churned_customers
- id: fetch_usage_patterns
description: 이탈 고객의 사용 패턴 조회
action: database_query
query: |
SELECT
customer_id,
AVG(daily_active_minutes) as avg_daily_usage,
COUNT(DISTINCT feature_used) as feature_diversity,
last_feature_used
FROM usage_logs
WHERE customer_id IN ({{steps.identify_churned_customers.churned_customers | map('customer_id') | join(',')}})
AND date >= DATE_SUB(NOW(), INTERVAL 60 DAY)
GROUP BY customer_id
outputs:
- usage_patterns
- id: fetch_support_tickets
description: 이탈 고객의 지원 티켓 조회
action: call_tool
tool: zendesk_get_tickets
inputs:
customer_ids: "{{steps.identify_churned_customers.churned_customers | map('customer_id')}}"
date_range: "last_60_days"
outputs:
- support_tickets
- id: analyze_patterns
description: 패턴 분석 및 원인 추론
action: ai_analyze
context:
include:
- steps.churned_customers
- steps.usage_patterns
- steps.support_tickets
prompt: |
당신은 고객 성공 분석가입니다. 다음 데이터를 분석하여 이탈 원인을 찾으세요.
이탈 고객 목록: {{churned_customers}}
사용 패턴: {{usage_patterns}}
지원 티켓: {{support_tickets}}
다음 질문에 답하세요:
1. 가장 많이 이탈한 고객 세그먼트는?
2. 이탈 전 사용 패턴의 공통점은?
3. 지원 티켓에서 발견되는 주요 불만은?
4. 예방 가능했던 이탈의 비율은?
구체적인 숫자와 함께 답하세요.
max_tokens: 2000
outputs:
- analysis_result
- id: generate_recommendations
description: 개선 제안 생성
action: ai_generate
prompt: |
이탈 분석 결과를 바탕으로, 3-5개의 구체적인 개선 제안을 만드세요.
분석 결과: {{steps.analyze_patterns.analysis_result}}
각 제안은:
- 문제 정의 (1문장)
- 해결 방안 (2-3문장)
- 예상 효과 (측정 가능한 지표)
- 우선순위 (High/Medium/Low)
형식으로 작성하세요.
outputs:
- recommendations
- id: create_report
description: 최종 리포트 생성
action: ai_generate
prompt: |
고객 이탈 분석 리포트를 작성하세요.
# 고객 이탈 분석 리포트
**기간**: {{steps.identify_churned_customers.date_range}}
**분석 대상**: {{steps.churned_customers | length}}명
## 주요 발견사항
{{steps.analyze_patterns.analysis_result | summary}}
## 이탈 원인 Top 3
{{steps.analyze_patterns.analysis_result | top_reasons}}
## 개선 제안
{{steps.generate_recommendations.recommendations}}
## 다음 단계
- 즉시 실행: [High 우선순위 항목]
- 단기 계획: [Medium 우선순위 항목]
- 장기 검토: [Low 우선순위 항목]
outputs:
- final_report
constraints:
max_execution_time: 600 # 10분
max_cost_usd: 5.0
max_api_calls: 30
이 스킬은 매번 정확히 같은 단계를 따릅니다. 100명의 이탈 고객을 조회하고, 사용 패턴을 분석하고, 지원 티켓을 확인하고, 패턴을 찾고, 제안을 만들고, 리포트를 생성합니다. 순서가 고정되어 있습니다.
하지만 AI는 각 단계를 “잘” 수행합니다. “패턴 분석”에서 데이터를 깊이 이해하고, “제안 생성”에서 창의적인 해결책을 만듭니다. 정해진 길을 가되, 그 길을 최고 품질로 완주합니다.
결과는 놀라웠습니다. 실행 시간은 15-20분에서 8분으로 줄었습니다. 비용은 $12-47에서 $4로 안정화되었습니다. 무엇보다, 리포트의 구조가 일관되어 검증이 쉬워졌습니다. “이번 주 리포트”와 “지난주 리포트”를 나란히 놓고 비교할 수 있게 되었습니다.
케이스 2: 코드 리뷰 자동화
개발팀은 PR(Pull Request) 리뷰에 많은 시간을 썼습니다. 하루에 20-30개의 PR이 올라오는데, 각각을 리뷰하는데 15-30분씩 걸렸습니다. 에이전트로 자동화를 시도했지만, 문제가 있었습니다.
에이전트는 때로 너무 엄격했고, 때로 너무 관대했습니다. 어떤 PR에는 50개의 댓글을 달았고(“이 변수명을 바꾸세요”, “이 주석을 추가하세요”), 어떤 PR에는 단 2개만 달았습니다. 일관성이 없었습니다.
우리는 리뷰 프로세스를 스킬로 정의했습니다:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
name: pr-review
description: Pull Request 자동 리뷰
inputs:
- name: pr_number
type: integer
required: true
steps:
- id: fetch_pr_details
description: PR 정보 조회
action: call_tool
tool: github_get_pr
inputs:
repository: "company/main-repo"
pr_number: "{{inputs.pr_number}}"
outputs:
- pr_title
- pr_description
- author
- changed_files
- diff
- id: security_scan
description: 보안 취약점 스캔
action: call_tool
tool: semgrep_scan
inputs:
diff: "{{steps.fetch_pr_details.diff}}"
rules: ["security", "owasp-top-10"]
outputs:
- security_issues
- id: code_quality_check
description: 코드 품질 검사
action: call_tool
tool: eslint_check
inputs:
files: "{{steps.fetch_pr_details.changed_files}}"
outputs:
- quality_issues
- id: test_coverage_check
description: 테스트 커버리지 확인
action: call_tool
tool: coverage_check
inputs:
pr_number: "{{inputs.pr_number}}"
outputs:
- coverage_delta
- uncovered_lines
- id: ai_code_review
description: AI 코드 리뷰
action: ai_review
context:
include:
- steps.fetch_pr_details.*
- docs/coding_standards.md
prompt: |
당신은 시니어 개발자입니다. 이 PR을 리뷰하세요.
PR 제목: {{pr_title}}
설명: {{pr_description}}
변경 파일: {{changed_files}}
Diff:
{{diff}}
다음 관점에서 리뷰하세요:
1. 코딩 스탠다드 준수 (docs/coding_standards.md 참조)
2. 잠재적 버그나 엣지 케이스
3. 성능 이슈
4. 가독성 및 유지보수성
각 이슈마다:
- 심각도: Critical / Major / Minor
- 파일명 + 라인 번호
- 문제 설명
- 개선 제안
Critical/Major 이슈만 댓글로 달고, Minor는 요약만 하세요.
max_tokens: 3000
outputs:
- review_comments
- id: post_review
description: 리뷰 댓글 작성
action: call_tool
tool: github_post_review
inputs:
pr_number: "{{inputs.pr_number}}"
comments: "{{steps.ai_code_review.review_comments}}"
security_issues: "{{steps.security_scan.security_issues}}"
quality_issues: "{{steps.code_quality_check.quality_issues}}"
coverage: "{{steps.test_coverage_check.coverage_delta}}"
outputs:
- review_url
constraints:
max_execution_time: 300
max_cost_usd: 3.0
policies:
- name: block-security-issues
condition: steps.security_scan.security_issues | length > 0
action: REQUIRE_APPROVAL
reason: 보안 취약점이 발견되었습니다
이 스킬은 6단계를 따릅니다. PR 정보 조회, 보안 스캔, 코드 품질, 테스트 커버리지, AI 리뷰, 댓글 작성. 매번 같은 순서입니다.
결과적으로, 모든 PR이 동일한 수준의 검토를 받습니다. 보안 스캔은 항상 실행되고, 테스트 커버리지는 항상 확인되며, AI 리뷰는 코딩 스탠다드 문서를 참조합니다. 일관성이 확보됩니다.
더 중요한 것은, 정책입니다. 보안 취약점이 발견되면, 자동으로 시니어 개발자에게 승인을 요청합니다. 에이전트가 “이 정도는 괜찮아”라고 판단하지 않습니다. 구조가 결정합니다.
케이스 3: 장애 대응 자동화
프로덕션에서 장애가 발생하면, 시간이 생명입니다. 5분 안에 원인을 찾고, 10분 안에 해결해야 합니다. 초기에는 에이전트에게 “장애 원인 찾아줘”라고 했지만, 에이전트는 너무 광범위하게 조사했습니다. 모든 로그를 읽고, 모든 메트릭을 분석하고, 관련 없는 코드까지 확인했습니다. 30분이 지나도 답이 안 나왔습니다.
스킬로 바꾸면서, “초기 5분 대응”과 “심층 분석”을 분리했습니다:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
name: incident-first-response
description: 장애 초기 대응 (5분 안에 기본 정보 수집)
trigger:
type: slack_message
channel: "#alerts"
pattern: ".*CRITICAL.*"
steps:
- id: parse_alert
description: 알림 메시지 파싱
action: execute_code
code: |
import re
message = context.trigger.message
service = re.search(r'Service: (\w+)', message).group(1)
error_type = re.search(r'Error: (.*)', message).group(1)
return {"service": service, "error_type": error_type}
outputs:
- service
- error_type
- id: fetch_recent_logs
description: 최근 10분 에러 로그
action: call_tool
tool: elasticsearch_query
inputs:
index: "logs-{{steps.parse_alert.service}}-*"
query:
bool:
must:
- range:
timestamp: {gte: "now-10m"}
- match:
level: "ERROR"
size: 100
sort: "timestamp:desc"
outputs:
- error_logs
- id: check_recent_deployments
description: 최근 배포 확인
action: call_tool
tool: github_list_deployments
inputs:
repository: "company/{{steps.parse_alert.service}}"
environment: "production"
since: "now-24h"
outputs:
- recent_deployments
- id: check_infrastructure
description: 인프라 메트릭 확인
action: call_tool
tool: datadog_get_metrics
inputs:
service: "{{steps.parse_alert.service}}"
metrics:
- "system.cpu.user"
- "system.mem.used"
- "postgresql.connections"
timeframe: "last_10_minutes"
outputs:
- infra_metrics
- id: generate_initial_report
description: 초기 분석 리포트 생성
action: ai_generate
context:
include:
- steps.error_logs
- steps.recent_deployments
- steps.infra_metrics
max_tokens: 10000 # 로그가 많을 수 있음
prompt: |
장애 초기 분석 리포트를 작성하세요.
서비스: {{service}}
에러: {{error_type}}
에러 로그 (최근 10분, 100건):
{{error_logs}}
최근 배포:
{{recent_deployments}}
인프라 메트릭:
{{infra_metrics}}
다음 형식으로 작성:
# 장애 초기 분석
## 증상
- 에러 발생 빈도: X건/분
- 주요 에러 메시지 Top 3
## 가능한 원인 (우선순위 순)
1. [가장 가능성 높은 원인] - 근거: [로그/메트릭]
2. [두 번째 원인]
3. [세 번째 원인]
## 즉시 확인할 사항
- [ ] 체크 1
- [ ] 체크 2
## 권장 조치
- 즉시: [긴급 조치]
- 다음: [추가 조사]
간결하게 작성하세요 (500자 이내).
max_tokens: 1000
temperature: 0.1 # 매우 낮게 (사실 중심)
outputs:
- initial_report
- id: notify_oncall
description: 온콜 엔지니어 알림
action: call_tool
tool: pagerduty_create_incident
inputs:
title: "{{steps.parse_alert.service}} 장애"
description: "{{steps.generate_initial_report.initial_report}}"
severity: "critical"
assigned_to: "oncall-{{steps.parse_alert.service}}"
outputs:
- incident_id
- id: post_to_slack
description: Slack에 초기 분석 공유
action: call_tool
tool: slack_send_message
inputs:
channel: "#incidents"
text: |
🚨 장애 초기 분석 완료
서비스: {{service}}
인시던트: {{incident_id}}
{{initial_report}}
온콜 담당: @{{oncall_engineer}}
constraints:
max_execution_time: 300 # 5분 엄수
max_cost_usd: 1.0
max_api_calls: 15
이 스킬은 5분 안에 끝나도록 설계되었습니다. max_execution_time: 300이 강제합니다. 에이전트가 “더 조사하고 싶다”고 생각해도, 5분이 지나면 강제 종료됩니다.
각 단계도 필수적인 것만 포함합니다. 최근 로그, 최근 배포, 인프라 메트릭. 이 세 가지만 확인합니다. “혹시 관련 있을까?”라는 추측으로 다른 것을 조사하지 않습니다.
AI는 “초기 분석”만 합니다. 심층 분석은 별도 스킬입니다. 이렇게 분리하면, 5분 안에 “지금 당장 알아야 할 것”을 제공하고, 그다음에 시간을 들여 깊이 분석할 수 있습니다.
5부: 운영에서 배운 교훈
똑똑함보다 신뢰
2개월간 스킬 시스템을 운영하면서, 가장 큰 교훈은 이것입니다. “운영 환경에서는 똑똑함보다 신뢰가 중요하다.”
에이전트는 분명 똑똑합니다. 예상치 못한 문제를 창의적으로 해결하고, 복잡한 패턴을 발견하며, 때로는 사람보다 나은 답을 만듭니다. 하지만 그 똑똑함이 매번 다른 방향으로 발휘되면, 신뢰할 수 없습니다.
팀원들이 스킬을 선호하는 이유는 “결과를 예측할 수 있기 때문”입니다. “/weekly-sales-report”를 실행하면, 정확히 어떤 데이터를 조회하고, 어떤 포맷으로 리포트를 만들고, 어디에 전송할지 압니다. 월요일과 금요일의 리포트가 같은 구조라는 것을 압니다. 그래서 믿고 쓸 수 있습니다.
에이전트는 “때때로 놀라운 결과”를 만들지만, 스킬은 “항상 예측 가능한 결과”를 만듭니다. 운영은 후자가 필요합니다.
QC를 사람의 책임에서 구조의 책임으로
초기에는 “에이전트가 만든 결과물을 사람이 검증한다”는 방식이었습니다. 하지만 이것은 확장되지 않습니다. 하루에 100개의 리포트가 생성되면, 사람이 100개를 다 검증할 수 없습니다.
스킬 시스템의 핵심은 “품질 보증을 구조에 내장”하는 것입니다. Policy Gate가 위험한 작업을 막고, 제약 조건이 비용 폭발을 방지하며, HITL이 파괴적 작업에 개입합니다. 사람은 “모든 것을 검토”하는 대신, “구조가 놓친 예외”만 처리합니다.
결과적으로, 사람의 역할이 “검수자”에서 “감독자”로 바뀝니다. 시스템을 모니터링하고, 정책을 개선하며, 새로운 스킬을 설계합니다. 개별 출력물을 일일이 확인하지 않습니다.
점진적 자율성
스킬을 도입하면서, 우리는 “100% 자동화”를 목표로 하지 않았습니다. 대신 “안전한 자동화”를 목표로 했습니다.
처음에는 모든 중요한 단계에 HITL을 넣었습니다. “데이터 조회”, “리포트 생성”, “Slack 전송” 모두 승인이 필요했습니다. 시스템이 각 단계를 제안하면, 사람이 “승인” 버튼을 눌렀습니다.
몇 주가 지나고, 특정 단계가 항상 승인된다는 것을 확인했습니다. “데이터 조회”는 100% 승인되었습니다. 한 번도 거부된 적이 없었습니다. 그래서 이 단계는 자동화했습니다. 이제 승인 없이 실행됩니다.
“리포트 생성”은 95% 승인되었지만, 가끔 수정이 필요했습니다. 그래서 “초안 자동 생성 + 사람 검토”로 바꿨습니다. 시스템이 초안을 만들면, Slack에 미리보기를 보여주고, 사람이 “전송” 버튼을 누릅니다. 수정이 필요하면 댓글로 피드백을 주고, 시스템이 수정합니다.
이렇게 점진적으로 자율성을 높여갑니다. 신뢰가 쌓인 부분은 자동화하고, 아직 불확실한 부분은 사람이 관여합니다. 중요한 것은, 이 경계가 데이터로 결정된다는 것입니다. “이 단계의 승인율이 99% 이상이면 자동화” 같은 명확한 기준이 있습니다.
비용 최적화의 부수 효과
스킬 시스템의 명시적 목표는 “예측 가능성”이었지만, 부수 효과로 “비용 최적화”도 달성했습니다.
에이전트는 목표 달성을 위해 무엇이든 시도합니다. 비용이 많이 들어도, “더 정확한 답”을 위해 추가 API를 호출합니다. 한 달 비용이 $1,200에서 $4,500으로 급증했던 적이 있습니다.
스킬은 각 작업마다 예산을 설정합니다. max_cost_usd: 2.0처럼. 시스템은 이 예산 안에서 최선을 다합니다. “더 조사하고 싶지만 예산이 부족”하면, 현재까지의 정보로 리포트를 만듭니다.
놀라운 것은, 품질이 크게 떨어지지 않았다는 것입니다. 에이전트가 $47를 써서 만든 리포트와, 스킬이 $4를 써서 만든 리포트를 비교했을 때, 핵심 내용은 거의 같았습니다. 에이전트의 추가 $43는 대부분 “혹시 모르니까” 수집한 불필요한 데이터였습니다.
명확한 범위와 예산이, 오히려 집중력을 높였습니다.
버그의 변화
스킬 도입 전, 버그는 대개 “예상치 못한 동작”이었습니다. 에이전트가 이상한 데이터를 조회하거나, 잘못된 해석을 하거나, 무한 루프에 빠지는 식이었습니다. 디버깅이 어려웠습니다. “왜 에이전트가 그런 결정을 내렸는지” 추적하기 힘들었습니다.
스킬 도입 후, 버그는 “특정 단계의 실패”로 바뀌었습니다. “3단계에서 SQL 쿼리가 타임아웃”, “5단계에서 AI 생성 결과가 포맷에 맞지 않음” 같은 식입니다. 정확히 어디서 무엇이 잘못됐는지 알 수 있습니다.
더 중요한 것은, 버그를 재현할 수 있다는 것입니다. 같은 입력으로 스킬을 다시 실행하면, 같은 단계에서 같은 에러가 발생합니다. 에이전트는 그렇지 않았습니다. 같은 입력으로 다시 실행하면, 다른 경로로 가서 다른 에러가 나거나 성공하기도 했습니다.
재현 가능한 버그는 고칠 수 있는 버그입니다.
결론: 길들임의 의미
“에이전트를 길들인다”는 표현이 처음에는 부정적으로 들렸습니다. 마치 AI의 잠재력을 제한하는 것처럼 느껴졌습니다. “왜 똑똑한 AI를 바보로 만드는가?”
하지만 2개월간의 운영을 통해, 우리는 다르게 이해하게 되었습니다. 길들임은 제한이 아니라 집중입니다. 에이전트가 “무엇이든 할 수 있다”는 광대한 가능성 속에서 헤매는 대신, “이 일을 최고로 잘하라”는 명확한 목표에 집중하게 만드는 것입니다.
야생의 에너지를 없애는 것이 아니라, 그 에너지를 유용한 방향으로 채널링하는 것입니다. 말을 길들이면 느려지는 것이 아니라, 기수와 함께 더 멀리 갈 수 있는 것처럼.
스킬은 에이전트에게 레일을 제공합니다. 그 레일 위에서, 에이전트는 최고 속도로 달립니다. 레일 밖으로 벗어나 헤매는 대신, 목적지를 향해 일직선으로 갑니다.
운영 가능한 자율성. 이것이 우리가 찾은 해답입니다. 완전한 자율도 아니고, 완전한 통제도 아닌, 그 사이의 균형. 에이전트가 스스로 결정하되, 안전한 경계 안에서. 창의적이되, 예측 가능하게. 똑똑하되, 신뢰할 수 있게.
QC를 사람의 책임으로 남겨두지 않고, 구조의 책임으로 옮기는 방법. 품질 보증이 프롬프트가 아니라, Gateway와 Policy와 제약 조건에 내장되는 설계.
에이전트를 길들이는 것은, 똑똑함을 깎는 것이 아니라 신뢰를 쌓는 일이라는 것. 이것이 우리가 2개월간 배운 가장 중요한 교훈입니다.
당신의 에이전트도 때때로 너무 멀리 간다면, 스킬을 고려해보세요. 그것은 퇴보가 아니라, 진화의 시작입니다.
작성 일자: 2026-01-10
관련글
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
단상
스킬스로 에이전트를 길들이는 이유
에이전트는 똑똑한데, 가끔은 너무 멀리 갑니다.
한 번 더 해보겠다고 돌고, 근거를 더 찾겠다고 헤매고, 내가 기대한 범위를 넘어섭니다.
결과가 나쁘다기보다, QC가 어려워지는 쪽이 더 문제였습니다.
운영은 “좋은 답”보다 “예측 가능한 답”이 필요합니다.
매번 다른 길로 가는 에이전트는, 잘 될 때도 있지만 검증의 비용이 커집니다.
사람이 결국 마지막에 다 읽고, 다 확인하고, 다 막아야 합니다.
그 순간부터 자동화가 아니라 수동 검수의 확장이 됩니다.
그래서 우리는 대안을 선택했습니다.
에이전트를 더 똑똑하게 만들기보다, 일을 더 잘게 나눠 ‘레시피’로 고정하기로 했습니다.
스킬스(Skills)를 적용하기로 한 이유입니다.
스킬스는 “이 일을 이렇게 한다”는 정해진 흐름입니다.
입력과 출력, 권한과 예산, 실패 방식까지 미리 정의합니다.
에이전트가 마음대로 뛰기 전에, 스킬이라는 레일 위에서 달리게 합니다.
효과는 생각보다 즉각적이었습니다.
•컨텍스트는 ‘전부 로딩’에서 ‘필요한 것만 열기’로 바뀌고
•사용자는 커맨드를 외우지 않아도 자동 제안으로 시작하고
•비용과 시간은 Budget으로 한계를 두고
•파괴적 작업은 HITL 승인으로 멈춰 세웁니다.
여기서 중요한 건, 안전장치가 프롬프트 안에 있지 않다는 점입니다.
모델이 바뀌어도, 대화가 길어져도, 누락이 생겨도
Gateway의 Policy Gate가 마지막 선을 지켜줍니다.
결국 우리가 원하는 건 “에이전트의 자율성”이 아니라
운영 가능한 자율성입니다.
스킬스는 그 타협이 아니라, 현실적인 해답입니다.
QC를 사람의 책임으로 남겨두지 않고,
구조의 책임으로 옮기는 방법.
에이전트를 길들이는 건, 똑똑함을 깎는 게 아니라
신뢰를 쌓는 일이라는 걸 다시 배웠습니다.
#진화의시작
#버그잡기2개월
#품질보증
https://www.facebook.com/share/p/17d6EuHcUh/