Files
aio-01d4-stu/Lecture/74/02-STAGE2-RELEASE-GATE.md

31 KiB
Raw Permalink Blame History

2단계 · 릴리스와 배포 전 게이트

  • 교시: 3교시 강의 (11:0011:50) · 4교시 Lab 02 실습 5개 (12:0012:50)
  • 이 단계가 끝나면: 지시문과 모델을 릴리스로 묶어 밖에서 관리하고, 후보 릴리스를 운영을 건드리지 않고 시험하고, 게이트로 배포 여부를 숫자로 정할 수 있다. 게이트의 기준을 재서 정하고, 게이트가 약하면 나쁜 릴리스도 통과한다는 것을 알고 테스트를 강하게 고친다. 망가진 릴리스는 장애와 구분해 크게 실패하게 한다.
  • 숫자: 모두 강사 노트북 실측입니다 (65일차 n8n 2.42.6, OpenAI gpt-4.1-mini · gpt-5.4-nano, 10/27 정답 워크플로우를 학생 순서대로 1회). 출력 전부는 수업 뒤 공개되는 Last3Mon/74/answers/LAB-TEST-RESULT.md에 있습니다.
  • 실습 가리키기: "Lab 02 예제 1"은 4교시 Lab 02 문서의 그 절입니다.
flowchart LR
  A["1. 릴리스"] --> B["2. 설정을 밖에서 읽기"] --> C["3. 후보 고정"] --> D["4. 게이트"] --> E["5. 게이트는<br/>테스트만큼만"]
  E --> F["6. 기준선"] --> G["7. 작은 모델 판단"] --> H["8. 망가진 릴리스"] --> I["9. 요청문(프롬프트)으로"]

0. 이 시간 (50분)

시간 절 · 개요 하는 일
11:00~11:06 1 · 개요 15장 릴리스 = 지시문 + 모델, 한 릴리스에 한 가지, 불변 결과물
11:06~11:12 2 · 개요 16장 설정과 코드의 분리, 원격 설정 · 기능 플래그
11:12~11:17 3 · 개요 16장 후보 시험: 고정(pin) · 스테이징 · 그림자
11:17~11:24 4 · 개요 17장 품질 게이트, CI/CD, 평가 세트(golden set)
11:24~11:31 5 · 개요 18장 게이트는 테스트만큼만, 기대는 데이터에서
11:31~11:36 6 · 개요 19장 기준선, 흔들리는 테스트, SLO
11:36~11:40 7 · 개요 20장 모델 교체 판단: 품질 · 속도 · 비용
11:40~11:46 8 · 개요 21장 장애와 설정 오류, fail fast
11:46~11:50 9 · 10 · 확인 질문 요청문, 자주 하는 실수

4교시 Lab 02에서 이어서 하는 것

4교시 실습 (시각) 이 강의의 절 실습에서 하는 일 쓰는 파일 · 명령
시작 (12:00) — 릴리스 점검표 복사 cp templates/RELEASE-CHECKLIST-TEMPLATE.md my-work/RELEASE.md
Lab 02 예제 1 (12:00~12:12) 1 · 2 · 3 운영 릴리스를 데이터 API에서 읽고, 본문 release면 고정 my-work/helpdesk.json, 테스트 cases/lab02.json
Lab 02 예제 2 (12:12~12:20, 에이전트 없이) 4 · 5 약한 golden으로 네 릴리스 게이트, 1.2.0 답 읽기 gate cases/golden.json --set release=... × 4 → RELEASE.md 1절
Lab 02 예제 3 (12:20~12:27, 에이전트 없이) 6 · 7 1.0.0 · 2.0.0 시간, golden 3번 반복 bench my-helpdesk requests/faq-vpn.json -n 5 --set release=..., gate ... --repeat 3
Lab 02 과제 1 (12:27~12:40) 5 · 7 1.2.0을 막는 my-golden, 2.0.0 판단 cases/my-golden.json, gate × 4
Lab 02 과제 2 (12:40~12:50) 8 망가진 릴리스 2.1.0은 400, 장애는 기본 지시문 테스트 cases/lab02-challenge2.json, gate cases/my-golden.json --set release=2.1.0

1. 릴리스 = 지시문 + 모델 (+ 메모)

일반 설명

릴리스(release)는 "함께 내보내는 것의 묶음"에 붙인 이름입니다. 일반 소프트웨어에서 릴리스는 빌드된 결과물(artifact: 실행 파일, 컨테이너 이미지)에 버전 번호를 붙인 것입니다. 결과물은 한 번 만들면 바꾸지 않습니다(불변). 같은 버전 번호면 언제 꺼내도 같은 것이어야, 시험한 것과 배포한 것이 같다고 믿을 수 있습니다.

LLM 앱의 릴리스는 "흐름 + 지시문 + 모델 + 그 밖의 설정(temperature, 검색 개수 …)"의 묶음입니다. 오늘은 흐름(워크플로우)은 그대로 두고 지시문 버전 + 모델 이름을 묶어 릴리스로 부릅니다.

일반 소프트웨어 오늘의 릴리스
결과물 (이미지 · 실행 파일) {prompt_version, model, note}
버전 번호 1.0.0 · 1.1.0 · 2.0.0
릴리스 노트 note, CHANGELOG.md
결과물 저장소 (registry) 데이터 API /releases/helpdesk

한 릴리스에 한 가지 변화. 1단계에서 v2는 셋이 한꺼번에 바뀌어 원인을 알 수 없었습니다. 릴리스를 한 가지씩만 다르게 만들면, 두 릴리스를 비교했을 때 차이는 그 한 가지 때문입니다. 작고 잦은 릴리스가 크고 드문 릴리스보다 안전한 까닭입니다 (문제가 생겨도 원인이 작고, 되돌릴 것도 작음).

릴리스에 무엇을 넣을까. 오늘은 지시문 버전과 모델 이름 둘이지만, 실제로는 답에 영향을 주는 설정을 모두 묶습니다: temperature, 최대 길이, 검색할 문서 수, 색인 버전, 도구 목록. 묶음에 빠진 설정이 있으면, 그 설정이 바뀌었을 때 "릴리스는 같은데 답이 다르다"가 생깁니다. 반대로 너무 많이 묶으면 사소한 변경에도 새 릴리스를 만들어야 합니다. 기준은 **"이것이 바뀌면 다시 시험해야 하나"**입니다.

릴리스 노트. 각 릴리스에는 "무엇이 왜 바뀌었는지" 한두 줄을 남깁니다 (오늘 note). 몇 달 뒤 "2.0.0은 왜 만들었지?"에 답하는 것이 이 한 줄입니다. 좋은 릴리스 노트는 변경 · 이유 · 영향(누가 무엇을 다르게 받나) · 시험 결과를 담습니다.

오늘 실습에서

릴리스 지시문 모델 1.0.0과 다른 것
1.0.0 p1 gpt-4.1-mini (기준)
1.1.0 p2 gpt-4.1-mini 지시문만 (출처 번호)
1.2.0 p3 gpt-4.1-mini 지시문만 (한 문장 · 포털로)
2.0.0 p2 gpt-5.4-nano 1.1.0과 비교하면 모델만
2.1.0 p9 gpt-4.1-mini 저장소에 없는 지시문 (8절)

2. 설정을 밖에서 읽는다 (4교시 Lab 02 예제 1)

일반 설명

설정과 코드의 분리. Lab 01의 릴리스 정보는 워크플로우 안의 Set 노드였습니다. 1.1.0을 내보내려면 값을 고치고 push 해야 합니다. 이것은 "설정이 코드 안에 있는" 상태입니다. 값을 밖(데이터 API)에서 매번 읽게 하면 배포는 그 값을 바꾸는 일이 됩니다.

업계에서는 이것을 원격 설정 · 기능 플래그라고 부릅니다.

이름 하는 일 대표 도구
원격 설정 (remote config) 앱을 다시 배포하지 않고 값을 바꿈 Firebase Remote Config, AWS AppConfig
기능 플래그 (feature flag) 기능을 켜고 끄거나 일부에게만 켬 LaunchDarkly, Unleash, Flagsmith
모델 · 지시문 라우팅 요청마다 어느 모델 · 지시문을 쓸지 LLM 게이트웨이 (LiteLLM, Portkey 등)

이 도구들은 공통으로 ① 값을 한 곳에 두고 ② 앱이 짧은 간격으로(또는 요청마다) 읽고 ③ 바꾼 기록을 남기고 ④ 일부 사용자에게만 다른 값을 줄 수 있게 합니다 (④는 3단계 카나리).

고르는 규칙은 코드가. "본문에 release가 있으면 그것, 없으면 active"는 틀리면 안 되는 규칙입니다. 73일차 "틀리면 안 되는 값은 LLM이 정하지 않는다"처럼 Code 노드가 정합니다.

읽는 비용과 캐시. 요청마다 설정을 읽으면 바꾼 즉시 반영되지만, 설정 서버를 그만큼 자주 부릅니다. 그래서 많은 도구가 설정을 몇 초 ~ 몇 분 캐시합니다. 캐시가 길면 배포 · 되돌리기가 그만큼 늦게 반영됩니다. 오늘은 요청마다 읽어 "다음 요청부터 바로"를 확인합니다. 운영에서는 "되돌리기가 몇 초 안에 반영돼야 하나"로 캐시 시간을 정합니다.

설정이 바뀐 기록. 원격 설정 도구들은 값을 바꿀 때마다 누가 · 언제 · 무엇을 바꿨는지 남깁니다. 배포가 "값 하나"가 되면 배포 기록은 곧 설정 변경 기록입니다. 오늘 데이터 API는 지금 값만 들고 있고 기록은 남기지 않습니다 — 그래서 CHANGELOG.md에 사람이 적고, 3단계 과제 2에서 점검 기록을 데이터 API에 남깁니다.

오늘 실습에서

flowchart LR
  G["GET /releases/helpdesk"] --> P["Pick release (Code)<br/>pinned > active"] --> K{"Known release?"}
  K -->|예| GP["Get prompt<br/>prompt_version"] --> A{{"Answer<br/>model"}}
  K -->|아니오| R(["400 unknown release"])

Lab 02 예제 1 (10/27): 고치기 전(Lab 01 과제 2 상태) lab02 1/5 — 고정 케이스가 모두 1.0.0으로 답함. 고친 뒤 5/5, lab01-challenge 3/3 · lab01-challenge2 4/4 그대로.

3. 후보는 고정해서 시험한다

일반 설명

운영을 건드리지 않고 새 버전을 시험하는 방법들.

방법 뜻 좋은 점 비용
스테이징 (staging) 운영과 같은 모양의 별도 환경 운영과 완전히 분리 환경을 하나 더 유지
고정 (pin) 같은 운영 환경에서 요청이 버전을 지정 환경 하나로 충분, 바로 시험 지정하는 문을 아무에게나 열면 위험
그림자 (shadow) 실제 요청을 새 버전에도 보내고 답은 버리고 비교만 실제 질문으로 시험 요청마다 두 배 비용

오늘은 고정을 씁니다. 본문에 release: "1.1.0"을 넣은 요청만 1.1.0으로 답하고, 나머지 운영 요청은 계속 1.0.0입니다. flow.py test --set release=1.1.0이 테스트의 모든 요청에 이 칸을 더합니다.

고정의 문은 좁게. 누구나 release를 지정할 수 있으면, 아직 검증 안 된 릴리스(또는 망가진 릴리스)를 아무나 쓸 수 있습니다. 73일차 관점으로는 공격면이 하나 늘어난 것입니다. 실제 서비스에서는 이 칸을 운영자 · 테스트 계정만 쓸 수 있게 막습니다 (인증된 사용자의 권한으로 판단).

없는 버전은 거절한다. 모르는 릴리스를 지정했을 때 조용히 active로 답하면, 테스트는 엉뚱한 릴리스를 시험하고 PASS를 받습니다. 400 unknown release로 분명히 실패해야 테스트를 쓴 사람이 오타를 압니다 (8절과 같은 원칙).

환경을 나누는 까닭. 개발 · 스테이징 · 운영을 나누는 것은 "시험하다 운영을 망가뜨리지 않기" 위해서입니다. 하지만 환경이 늘면 환경끼리 설정이 어긋납니다 (스테이징에서는 됐는데 운영에서는 안 됨). 고정(pin)은 운영 환경 그대로 후보를 시험하므로 이 어긋남이 없습니다. 대신 후보가 운영의 데이터 · 외부 시스템을 실제로 건드립니다 — 오늘 헬프데스크는 읽기만 하니 괜찮지만, 접수 건을 만드는 워크플로우라면 시험 요청에 "시험용" 표시를 붙여 실제로 만들지 않게 해야 합니다.

후보 시험이 건드리는 것 오늘 조심할 때
읽기 (FAQ · 접수 건 조회) 예 괜찮음
쓰기 (접수 만들기 · 메일 보내기) 없음 시험 표시 · 가짜 대상 · 그림자로
비용 (LLM 호출) 게이트 한 번에 6~9번 반복 · 여러 릴리스면 곱해짐 (75일차)

오늘 실습에서

lab02 케이스 기대 결과 (10/27)
운영 중인 릴리스 1.0.0 · stable · p1 ✔
1.1.0 고정 1.1.0 · pinned · p2 · [FAQ-003] ✔
2.0.0 고정 gpt-5.4-nano ✔
없는 릴리스 9.9.9 400, known ✔
빈 질문 400 ✔

4. 게이트: 배포 전에 숫자로 (4교시 Lab 02 예제 2)

일반 설명

품질 게이트(quality gate). 소프트웨어 배포 파이프라인(CI/CD)에는 "여기를 지나야 다음 단계로"인 관문이 여러 개 있습니다: 빌드가 되나, 단위 테스트를 통과하나, 보안 검사, 성능 기준. 각 관문은 기준을 숫자로 정하고 PASS/FAIL을 냅니다. 사람의 "괜찮아 보이는데요"를 숫자로 바꾸는 장치입니다.

flowchart LR
  B["빌드"] --> U["단위 테스트"] --> S["보안 검사"] --> Q["품질 게이트<br/>평가 세트"] --> D["배포"]
  Q -->|FAIL| X(["멈춤"])

LLM 앱의 게이트는 평가 세트(evaluation set)로. 정해 둔 질문과 기대(판정 규칙)의 모음을 평가 세트, 또는 **골든 세트(golden set)**라고 부릅니다. 새 지시문 · 모델을 내보내기 전에 평가 세트를 돌려 기준을 넘는지 봅니다. promptfoo, DeepEval, Ragas, OpenAI Evals, LangSmith Evaluation 같은 도구가 이 일을 합니다. 도구가 달라도 구조는 같습니다: 질문 목록 + 판정 규칙 + 기준(통과율 · 점수 · 시간) + 결과 기록.

게이트 기준 뜻 오늘
통과율 케이스 중 통과한 비율 --min-pass 1.0 (모두)
처리시간 케이스 시간의 p95 --max-p95 3 (초)
종료 코드 자동화가 다음 단계를 정함 PASS 0 · FAIL 1
기록 누가 언제 무엇으로 게이트를 돌렸나 /gates/helpdesk (3단계 과제 2에서 씀)

게이트는 자동화의 문. 종료 코드가 0이면 다음 단계(배포)가 이어지고, 1이면 멈춥니다. 사람이 결과를 읽지 않아도 파이프라인이 멈춥니다. 그래서 게이트가 무엇을 확인하는지가 매우 중요합니다 (5절).

평가 세트를 어떻게 모으나. 처음에는 대표 질문 몇십 개로 시작하고, 운영하면서 실제로 문제가 됐던 질문을 더해 갑니다. 사고가 났던 질문을 평가 세트에 넣으면 같은 사고가 다시 나지 않는지 늘 확인할 수 있습니다 (회귀 테스트). 평가 세트는 한 번 만들고 끝나는 것이 아니라 서비스와 함께 자라는 자산입니다.

평가 세트에 넣을 것 예
자주 오는 질문 VPN · 비밀번호 · 프린터
꼭 지켜야 할 것 접수 상태 · 예정일을 정확히, 지어내지 않음
사고가 났던 질문 "포털만 보라"는 답이 나갔던 프린터 질문
경계 · 예외 빈 질문, 없는 접수 번호, 남의 건 (73일차)

오늘 실습에서

운영팀의 golden.json(6케이스, status 200 · route 자동 답변만 확인)으로 네 릴리스 (10/27):

릴리스 결과 p95
1.0.0 6/6 PASS 1.6초
1.1.0 6/6 PASS 1.1초
1.2.0 6/6 PASS 1.5초
2.0.0 6/6 PASS 2.0초

1.2.0의 프린터 답: "IT 포털에서 프린터 문제 해결 방법을 확인해 주세요." — HTTP 200, 자동 답변. 게이트는 PASS입니다.

5. 게이트는 테스트만큼만 막는다 (4교시 Lab 02 과제 1)

일반 설명

PASS의 뜻은 테스트가 정합니다. 게이트는 테스트를 돌릴 뿐입니다. 테스트가 "200이 왔나"만 보면 PASS는 "200이 왔다"는 뜻입니다. 나쁜 릴리스를 막지 못한 게이트는 게이트가 고장 난 것이 아니라 테스트가 약한 것입니다.

테스트 오라클 문제. 테스트를 쓰려면 "맞는 답이 무엇인지"를 알아야 합니다. 이것을 오라클(oracle)이라고 합니다. LLM 답은 정답 글자가 하나가 아니라서 오라클을 정하기 어렵습니다. 그래서 정답 전체 대신 판정 규칙을 씁니다.

판정 규칙 예 잡는 것
꼭 있어야 할 낱말 (contains) VPN 답에 "인증서" 해결 방법을 빼먹은 답, 떠넘기기
없어야 할 말 (not_contains) "포털을 확인" 특정 문구 (글자가 정확해야)
값 비교 (equals) route = 자동 답변 갈래가 틀림
형식 JSON · 길이 모양이 깨짐
LLM 채점 "이 답이 질문을 해결하나 1~5점" 글자로 못 잡는 품질 (79일차, 채점도 흔들림)

기대는 데이터에서, 사람이. 기대 낱말을 지금 답에서 베끼면, 지금 답이 틀린 것도 정답이 됩니다. 기대는 원천 데이터(FAQ의 해결 방법, 접수 건의 상태 · 예정일)를 보고 사람이 정합니다. 에이전트에게 테스트 파일을 쓰게 해도 되지만, 기대 낱말은 사람이 정해 요청문에 적습니다.

굿하트의 법칙. "숫자가 목표가 되면 그 숫자는 더 이상 좋은 숫자가 아니다." 게이트를 통과시키려고 테스트를 느슨하게 고치거나, 테스트에만 맞춘 지시문을 쓰면 게이트는 의미를 잃습니다. 그래서 오늘 규칙: 테스트를 통과시키려고 기대 결과를 바꾸지 않는다 (AGENTS.md 8절).

좋은 판정 규칙의 조건. ① 맞는 답이면 통과한다 (같은 뜻의 다른 표현도) ② 틀린 답이면 실패한다 ③ 같은 답이면 늘 같은 판정이다. ①이 약하면 정상 릴리스가 FAIL(지나치게 막기), ②가 약하면 나쁜 릴리스가 PASS(덜 막기), ③이 약하면 흔들리는 테스트. 73일차 보안 테스트의 "막을 것 · 막지 말 것"과 같은 균형입니다.

오늘 실습에서

강한 golden(my-golden, 9케이스: 해결 낱말 6 · 상태 · 예정일 · 떠넘기기 금지) (10/27):

릴리스 약한 golden 강한 golden p95
1.0.0 6/6 PASS 9/9 PASS 1.4초
1.1.0 6/6 PASS 9/9 PASS 1.7초
1.2.0 6/6 PASS 1/9 FAIL 1.3초
2.0.0 6/6 PASS 9/9 PASS 1.3초

1.2.0이 통과한 하나는 "포털로 떠넘기지 않기"(not_contains: "IT 포털을 확인" · "포털을 참고")입니다. 실제 답은 "IT 포털에서 … 확인해 주세요"라서 걸리지 않았습니다. 금지 문구는 글자가 정확해야 하고, 실제로 1.2.0을 막은 것은 **해결 낱말(contains)**이었습니다.

6. 기준선: 기준은 재서 정한다 (4교시 Lab 02 예제 3)

일반 설명

기준선(baseline). "3초 안"이라는 기준은 지금 운영 중인 버전이 보통 몇 초인지 알아야 의미가 있습니다. 성능 · 품질 기준은 지금을 재서 정하고, 흔들림만큼 여유를 둡니다.

기준을 정하는 법 예 특징
감으로 "3초면 되겠지" 근거 없음, 너무 느슨하거나 빡빡함
기준선 대비 "1.0.0 p95의 1.5배 이내" 지금보다 많이 나빠지는 것만 막음
서비스 목표(SLO)에서 "문의 95%는 5초 안에 답" 사용자에게 한 약속에서 내려옴 (78일차)
회귀 기준 "1.0.0보다 통과율이 떨어지면 FAIL" 새 버전이 나빠지지 않았는지

흔들리는 테스트는 게이트를 무너뜨립니다. 같은 릴리스로 돌려도 가끔 FAIL이 나는 테스트가 있으면, 사람들은 FAIL을 보고 "또 흔들렸네" 하고 다시 돌립니다. 그러다 진짜 FAIL도 무시하게 됩니다. 그래서 기준을 정할 때 같은 릴리스로 여러 번 돌려 흔들리지 않는지 먼저 봅니다.

재는 횟수. 다섯 번으로 잰 p95는 다섯 개 중 가장 느린 값입니다 (72일차 nearest-rank). 한 번 튄 값이 그대로 기준이 됩니다. 실제 기준을 정할 때는 수십 번 이상 재고, 다른 시간대에도 잽니다. 오늘은 수업 시간과 비용 때문에 다섯 번입니다.

흔들림을 줄이는 방법. 흔들리는 케이스를 찾으면 바로 지우지 않고 원인을 봅니다. 기대 낱말이 너무 좁으면(같은 뜻의 다른 말) 낱말을 더하고, 질문이 애매하면(두 FAQ에 걸침) 질문을 분명하게 하고, 정말 모델이 흔들리면 반복 기준("3번 중 3번")을 둡니다. 흔들리는 케이스를 그냥 지우면 평가 세트가 점점 쉬운 질문만 남습니다.

오늘 실습에서

VPN bench -n 5 (10/27, 같은 워크플로우에서 --set release만 바꿈):

릴리스 모델 p50 p95
1.0.0 gpt-4.1-mini 1.23초 1.42초
2.0.0 gpt-5.4-nano 1.12초 1.24초

1.0.0의 golden을 --repeat 3: 18/18, p95 1.5초. 흔들리지 않으므로 이 golden의 FAIL은 믿을 수 있습니다. 1.0.0 p95가 1.4~1.6초 근처이니 --max-p95 3은 약 두 배 여유입니다.

7. 작은 모델로 바꿔도 될까

일반 설명

모델 교체는 세 가지 저울. 더 작은 · 싼 모델로 바꾸는 결정은 품질 · 속도 · 비용을 함께 봅니다.

저울 재는 법 오늘
품질 같은 평가 세트의 통과율 강한 golden
속도 p50 · p95 bench
비용 요청당 토큰 × 단가 75일차

게이트를 통과했다 = 게이트가 보는 것은 괜찮다. 평가 세트에 없는 것(말투 · 길이 · 드문 질문 · 긴 대화 · 다른 언어)은 게이트가 보지 않습니다. 그래서 모델 교체는 바로 전원에게가 아니라 일부에게 먼저(카나리, 3단계) 내보내고 실제 사용을 봅니다.

공급자의 모델 일정도 판단에 들어갑니다. 1단계에서 본 것처럼 모델은 지원 종료됩니다. 지금 쓰는 모델의 종료 일정, 대체 모델로 옮기는 데 드는 시험 비용도 판단 재료입니다. 10/27 수업 준비 때 gpt-4.1-nano가 그랬습니다.

같은 값이면 더 싼 것. 품질 기준을 넘는 모델이 여럿이면 더 싸고 빠른 것을 고릅니다. 그런데 "기준을 넘는다"는 평가 세트 안의 이야기입니다. 작은 모델은 평가 세트에 없는 어려운 질문 · 긴 대화 · 드문 언어에서 먼저 무너지는 경우가 많습니다. 그래서 모델을 작게 바꿀 때는 평가 세트에 어려운 질문을 일부러 더해 두고, 카나리로 실제 질문을 봅니다.

섞어 쓰기. 모든 질문에 같은 모델을 쓸 필요는 없습니다. 쉬운 질문(FAQ 그대로)은 작은 모델, 어려운 질문(여러 문서 · 판단)은 큰 모델로 보내는 라우팅도 흔합니다 (75일차 비용). 이때도 릴리스는 "어떤 규칙으로 어느 모델에 보내나"까지 묶어 버전으로 둡니다.

오늘 실습에서

2.0.0 (gpt-5.4-nano): 강한 golden 9/9 PASS, p95 1.3초, VPN p50 1.12초 (1.0.0 1.23초). 게이트로는 "내보내도 됨". 판단 한 줄 예: "품질 · 속도는 게이트 기준 통과 → 카나리 10%로 먼저, 비용은 75일차에 잰 뒤 배포".

8. 망가진 릴리스는 크게 실패하게 (4교시 Lab 02 과제 2)

일반 설명

실패에는 두 종류가 있습니다.

장애 (outage) 설정 오류 (misconfiguration)
예 저장소 서버가 멈춤 · 느림 (500, 시간 초과) 릴리스가 없는 지시문을 가리킴 (404)
다시 하면 될 수도 있음 (잠깐의 문제) 절대 안 됨 (설정이 틀림)
맞는 대응 견딘다: 기본 지시문으로 계속 (graceful degradation) 크게 실패한다 (fail fast): 분명한 오류로 멈춤
HTTP로 보면 5xx, 응답 없음 4xx

견디는 장치가 오류를 숨깁니다. 1단계 과제 2에서 만든 기본 지시문은 저장소의 모든 실패를 받아 줍니다. 그래서 2.1.0(p9)처럼 설정이 틀린 릴리스도 기본 지시문(p1)으로 멀쩡하게 답합니다. 운영팀은 2.1.0의 새 지시문이 나간다고 믿지만, 실제로는 1.0.0의 지시문이 나갑니다. 게이트도 PASS를 줍니다(p1의 답이니까). **조용한 실패(silent failure)**가 가장 비싼 실패입니다. 늦게 발견되고, 발견했을 때는 원인이 멀리 있습니다.

fail fast. 다시 해도 안 될 오류는 가능한 한 일찍, 분명하게 실패해야 합니다. 그래야 게이트가 배포 전에 잡습니다. 컴파일 오류, 설정 파일 검사(schema validation), 시작할 때 필수 값 확인 같은 것이 모두 fail fast입니다.

응답 코드로 가릅니다. HTTP는 이미 둘을 나눠 줍니다: 4xx는 "요청이 틀렸다(다시 해도 안 됨)", 5xx는 "서버가 아프다(다시 하면 될 수도)". 72일차 재시도 원칙("4xx는 다시 하지 않는다")과 같은 구분을 여기서는 기본 지시문으로 갈지에 씁니다.

flowchart LR
  G["Get prompt"] --> C{"응답 코드"}
  C -->|200| R(["registry"])
  C -->|404 설정 오류| B(["400 release broken<br/>→ 게이트 FAIL"])
  C -->|500 장애| F(["fallback"])
  G -->|시간 초과| F

설정은 배포 전에 검사합니다. 2.1.0 같은 망가진 릴리스는 사실 릴리스를 올릴 때 막을 수도 있습니다: "릴리스가 가리키는 지시문 버전이 저장소에 있는가, 모델 이름이 공급자 목록에 있는가"를 확인하는 검사입니다. 설정 파일의 형식 검사(스키마 검증)와 같은 일입니다. 오늘은 워크플로우에서 404를 크게 실패하게 만들어 게이트가 잡게 했지만, 운영에서는 둘 다 둡니다 — 올릴 때 한 번, 쓸 때 한 번.

오늘 실습에서

lab02-challenge2 (10/27):

케이스 고치기 전 (예제 1 상태) 고친 뒤
망가진 릴리스 2.1.0 ✘ 200 (기본 지시문으로 답, 1.3초) ✔ 400 release broken · p9
평소 · 1.1.0 ✔ ✔ registry · [FAQ-003]
저장소 고장 · 1.1.0 ✔ fallback ✔ fallback
저장소 고장 · 없는 릴리스 9.9.9 ✔ 400 ✔ 400
운영(active 1.0.0) ✔ ✔
합계 4/5 5/5

그리고 같은 강한 golden으로 2.1.0을 게이트: 고치기 전 9/9 PASS (p95 1.5초, 위험!) → 고친 뒤 FAIL (모든 케이스 400). 게이트가 잡지 못하던 설정 오류를 워크플로우가 크게 실패하게 만들자 게이트가 잡았습니다.

9. 요청문(프롬프트)으로

요청문(프롬프트)이란. 실습에서 CLI 코딩 에이전트(codex · claude · agy)에게 보내는 글입니다. 에이전트는 이 글만 보고 n8n 워크플로우 JSON을 만들거나 고칩니다. 이 절은 이 단계에서 배운 것을 요청문(프롬프트)에 어떤 칸으로 적을지 실습 전에 정리하는 자리입니다. 실습 문서의 ▶ 빈칸 채우기 · 요청문(프롬프트) 뼈대가 이 칸을 실제 문장으로 만든 것이고, 빈칸 ①~④는 그 문장 속 정확한 값입니다.

칸 에이전트에게 알려 주는 것 빠지면 생기는 일
무엇을 만들거나 바꿀 기능 에이전트가 짐작해서 다른 것을 만듦
어디에 고칠 파일 · 노드 · 자리 엉뚱한 노드를 고치거나 새 파일을 만듦
어떤 모양으로 결과 JSON의 칸 이름 · 값 · 응답 코드 테스트가 보는 칸 이름과 달라 실패
그대로 · 하지 말 것 건드리면 안 되는 것 다른 곳까지 바뀌어 무엇 때문인지 모르게 됨
확인 통과해야 할 테스트 파일 "다 됐다"를 판단할 기준이 없음

67~71일차의 다섯 가지(① 파일 ② 입력 ③ 처리 ④ 결과 모양 ⑤ 기대 결과)와 같은 뜻입니다. 파일 · 입력이 "어디에", 처리가 "무엇을", 결과 모양이 "어떤 모양으로", 기대 결과가 "확인"입니다. 72일차부터는 이미 있는 워크플로우를 고치는 요청이 많아 "그대로"가 따로 한 칸이 됐습니다.

아래 표의 열은 이 단계에서 에이전트를 쓰는 실습, 줄은 위 다섯 칸입니다. 각 칸에 그 실습에서 채울 것을 적었습니다.

요청문에 Lab 02 예제 1 Lab 02 과제 1 Lab 02 과제 2
무엇을 릴리스를 밖에서 읽고 고정 강한 golden (테스트만) 404는 400, 장애는 기본 지시문
어디에 Release info 대신 Get release → Pick release → Known release? cases/my-golden.json Get prompt 응답 옵션, 다음 Code · If
어떤 모양 channel stable · pinned, 400 known contains 해결 낱말 · not_contains 400 {error: release broken, release, prompt_version}
그대로 기본 지시문 워크플로우 시간 초과 쪽 기본 지시문
확인 lab02 · lab01-challenge · lab01-challenge2 gate × 4 lab02-challenge2, gate my-golden 2.1.0 FAIL

예를 들어 Lab 02 과제 2 열을 위에서부터 이어 읽으면 요청문(프롬프트)이 됩니다 (화살표 오른쪽은 다섯 칸 중 어디인지):

지시문 저장소가 404 면 기본 지시문으로 넘어가지 말고 크게 실패하게,
장애(500 · 시간 초과)일 때만 기본 지시문으로 답하게 해 줘.                       ← 무엇을
Get prompt 의 응답 옵션을 바꾸고 그 다음에 Code · If 를 넣어서.                    ← 어디에
404 면 HTTP 400 {error: 'release broken', release, prompt_version}.               ← 어떤 모양으로
시간 초과 쪽 기본 지시문(Fallback prompt)은 그대로.                              ← 그대로
cases/lab02-challenge2.json 이 통과하고, gate my-golden --set release=2.1.0 은 FAIL 이어야 해.  ← 확인

과제 1은 테스트를 쓰는 요청입니다. 기대 낱말은 내가 데이터에서 정해 요청문에 적습니다. 에이전트가 지금 답을 보고 기대를 만들게 하지 않습니다.

10. 자주 하는 실수

실수 왜 문제인가 이렇게
한 릴리스에 지시문과 모델을 같이 바꿈 결과가 달라도 원인을 모름 한 릴리스에 한 가지
후보를 운영(active)으로 바꿔서 시험 시험하는 동안 직원 모두가 후보를 받음 고정(--set release=)으로
게이트 PASS면 끝 게이트가 보는 것만 괜찮다는 뜻 테스트가 무엇을 보는지 읽기, 카나리로
지금 답을 보고 기대를 만듦 지금 틀린 것도 정답이 됨 데이터에서 사람이
게이트가 FAIL이라 기대를 느슨하게 게이트가 의미를 잃음 답이 틀렸는지 먼저 확인, 같은 뜻이면 낱말을 더함
기준을 감으로 너무 느슨하거나 같은 릴리스도 FAIL 기준선을 재고, 흔들리지 않는지 반복
모든 실패를 기본값으로 받음 설정 오류가 숨어 게이트도 통과 4xx는 크게 실패, 5xx · 시간 초과만 견딤

확인 질문

  1. 릴리스 1.1.0과 2.0.0을 비교하면 무엇의 영향만 보이나요? 1.0.0과 2.0.0을 비교하면?
  2. 본문 release로 고정하는 문을 모든 직원에게 열어 두면 생길 수 있는 문제는?
  3. 약한 golden에서 1.2.0이 PASS였습니다. 게이트가 고장 난 것인가요?
  4. 1.2.0의 답 "IT 포털에서 … 확인해 주세요"를 not_contains: "포털을 참고"가 못 잡았습니다. 무엇으로 잡았고, 그것이 더 튼튼한 까닭은?
  5. 2.0.0이 강한 golden을 통과했습니다. 바로 전원에게 배포해도 되나요? 무엇을 더 봐야 하나요?
  6. 2.1.0은 고치기 전 강한 golden을 9/9로 통과했습니다. 왜 그랬고, 어떻게 고쳤나요? "장애"와 "설정 오류"를 가르는 기준은?

정리

  • 릴리스 = 지시문 + 모델, 한 릴리스에 한 가지 변화, 한 번 낸 릴리스는 바꾸지 않는다
  • 운영 릴리스는 밖에서 읽고, 후보는 고정으로 시험한다 (고정의 문은 좁게)
  • 게이트는 통과율 · p95로 PASS/FAIL — 그러나 테스트만큼만 막는다, 기대는 데이터에서 사람이
  • 기준은 기준선을 재서, 흔들리지 않는지 반복해서
  • 장애는 견디고, 설정 오류는 크게 실패하게 — 그래야 게이트가 잡는다