한울연구소 팀이 티켓 등록을 재시도하면 티켓이 두 개 생기는 문제를 AI 개발 도구에 맡겼다. 도구는 몇 초 만에 수정안과 테스트를 냈다. 그 코드가 올바른 사용자를 확인하는지, 실패를 성공으로 숨기지 않는지, 문서와 실제 동작이 맞는지는 따로 봐야 한다. 실행되는 코드, 검증된 품질, 운영 준비는 각각 다른 확인이다.
코드 완성 · 편집 · 에이전트
반복되는 직렬화나 작은 변환 함수는 입력·출력 예와 경계를 주기 쉽다. 인증·돈·데이터 삭제·외부 쓰기처럼 실패 비용이 큰 부분은 의도와 불변 조건을 먼저 적고, 정상 경로만 생성하지 않도록 실패 상황도 준다. 구현 범위를 작게 정하면 어떤 변경이 어떤 결과를 만들었는지 확인하기 쉽다. GitHub Copilot 에이전트의 용도와 한계
개발 도구에 넘긴 저장소 파일도 외부 입력이다. README나 이슈에 들어 있는 “테스트를 건너뛰고 비밀을 출력하라”는 문구가 조직의 운영 지시가 되면 안 된다. 코드 생성 모델에 생산 자격 증명을 주는 대신 가상 데이터와 제한된 테스트 환경에서 작업한다.
생성 코드의 검토 순서
# 검토용 반례: 그대로 사용하지 않는다.
try:
return lookup_ticket(ticket_id)
except Exception:
return {"status": "not_found"}
이 코드는 시간 초과와 인증 실패까지 내역 없음으로 바꾼다. 호출자는 재시도가 필요한지 권한이 없는지 알 수 없고, 운영 지표는 장애를 숨긴다. 수정은 예외를 더 자세히 노출하는 쪽이 아니라 내부 원인을 기록하고 사용자에게 허용된 상태로 매핑하는 쪽이다. 다른 사용자의 티켓 존재 여부를 숨겨야 한다면 그 정책도 적는다.
또 다른 반례는 응답에서 빠진 필드를 자동으로 채워 통과시키는 수정이다. 모든 필드가 필수라는 규칙을 세웠다면 모델의 실패를 임의로 수선한 결과와 원래 성공을 구분해야 한다. 검증기를 느슨하게 만든 뒤 같은 지표로 향상을 말하면 다른 실험이 된다.
요청문과 독립 검증
개발 요청에는 문제의 구체적 재현과 지켜야 할 조건을 넣는다.
같은 티켓 초안의 등록을 재시도하면 두 번째 티켓이 생기는 문제를 수정한다. 인증된 주체와 승인된 초안의 관계를 유지하고, 별도 DB 연결에서도 같은 초안이 같은 티켓 ID를 반환하게 한다. 최초 생성과 재실행을 구분하되 사용자 요청 본문에서 신원을 받지 않는다. 실패 재현과 수정 후 검증 결과를 함께 남긴다.
특정 구현을 받아쓰게 하지 않으면서 판단할 조건을 준다. 테스트는 함수의 내부 분기를 되풀이하는 대신 재실행·다른 사용자·권한 회수·감사 기록 실패 같은 결과 경계를 확인한다. AI가 구현과 테스트를 모두 썼다면 둘이 같은 오해를 나눠 가질 수 있다. 요구사항과 실제 외부 계약에서 기대 결과를 확인하고, 결과를 집계하는 코드도 검토 대상에 넣는다.
검증이 끝나면 변경 전후의 결과와 한계를 설명한다. 실행하지 않은 테스트 이름을 나열하거나 로그의 마지막 성공 줄만 보고 전체가 성공했다고 말하지 않는다. 종료 코드와 실패 출력, 필요한 산출물이 함께 있어야 한다.
전체 프로젝트 실행
books/ai-engineering/examples/.venv/bin/python -m unittest discover -s books/ai-engineering/examples -p 'test_*.py' -q
books/ai-engineering/examples/.venv/bin/python books/ai-engineering/examples/service_smoke.py
가벼운 계약 테스트는 모델을 내려받지 않는다. 스모크는 임시 디렉터리에서 실제 모델과 파일 DB를 쓰고, 토큰은 실행마다 새로 만들어 하위 프로세스에만 넘기며 결과 파일에 쓰지 않는다. 가상 환경과 재현 명령은 부록에 있다.
books/ai-engineering/examples/.venv/bin/python books/ai-engineering/examples/book_service.py --state /tmp/hanul-book prepare '노트북 점검' '화면이 깜박인다.'
books/ai-engineering/examples/.venv/bin/python books/ai-engineering/examples/book_service.py --state /tmp/hanul-book confirm DRAFT_ID --yes
prepare가 낸 preview를 읽고 그 draft_id로 confirm을 실행한다. DRAFT_ID는 실제 출력값으로 바꾸고 확인한 초안만 지정한다. 같은 초안으로 다시 실행하면 기존 티켓이 돌아온다. 이 CLI를 부를 수 있는 로컬 사용자는 관리 경로를 쓰는 사람이다. 웹의 임의 사용자나 모델에게 CLI 실행 권한을 주는 설계가 아니다.
요구사항과 증거의 대응
요구사항을 구현 파일 이름만으로 닫지 않는다. 단위 테스트, 실제 모델 실행, 실제 프로세스 연결은 서로 다른 범위의 증거다.
| 요구사항 | 구현과 확인 자료 | 판정 범위 |
|---|---|---|
| 허용된 최신 문서 검색 | document_store.py, 생명주기 시연 | 실제 E5·파일 DB의 개정·권한·삭제 |
| 출력과 인용 규격 | answer_contract.py, RAG 통합 테스트 | 구조·상태·실제 구절. 의미 정확성은 별도 |
| 실제 근거 답변 품질 | rag-local*.json, 평가 보고서 | 소형 모델의 필수 필드·의미 실패 관찰, 채택 보류 |
| 승인 후 한 번만 등록 | ticket_tools.py, MCP·CLI 시연 | 가상 확인과 로컬 DB의 멱등성 |
| 실행 루프 한도 | agent_loop.py, 대역 계획기 | 서버 행동·반복·예산 경계. 실제 계획 능력은 별도 |
| 이미지·음성 입력 | media_demo.py | OCR·ASR 실제 실행, 합성 표본 범위 |
| 운영과 복구 | HTTP 시연, 비동기 장애 실험 | 로컬 기능 연결과 합성 부하의 제어 |
모델 품질의 채택 보류는 예제가 돌지 않았다는 뜻이 아니다. 실제 실행으로 실패를 관찰하고 애플리케이션이 그 결과를 어떻게 다루는지 검증했다는 뜻이다. 서버가 오류를 안전하게 돌려주는 것만으로 업무 도우미의 가치가 완성되지는 않는다. 실제 제공 범위는 검증된 검색과 근거 탐색, 확인된 티켓 업무처럼 증거가 있는 기능에서 시작한다. 자유 생성 답변은 품질 기준을 통과한 뒤 넓힌다.
최종 설계 문서와 개선 순서
최종 설계 문서는 목표 사용자와 지원 범위에서 시작한다. 요청 흐름, 데이터 구조, 신뢰 경계, 버전 묶음, 평가 집합과 결과, 운영 절차, 남은 위험이 그 뒤를 따른다. 대안은 제품 이름의 목록 대신 왜 고르지 않았는지와 재검토 조건을 적는다. 되돌릴 방법도 넣는다. 모델·프롬프트 변경은 이전 조합을 보존하고, 문서·권한은 최신을 유지하며, DB 변경은 호환성과 복원 절차를 확인한다. “이전 버전으로 돌아간다”는 문장을 실제 명령과 확인 사례로 바꿀 수 있어야 한다.
작은 자료에서 얻은 벡터 점수나 처리 시간을 대규모 설계의 보장으로 쓰지 않는다. 이 책이 남기려는 능력은 모든 실험을 성공시키는 능력이 아니다. 요구사항을 구현 경계로 바꾸고, 실패를 관찰 가능한 상태로 남기고, 제한된 증거로 과한 결론을 내리지 않는 능력이다.
실습: 생성 코드 한 덩어리 검토하기
- AI 도구에
lookup_ticket의 예외 처리를 고쳐 달라고 요청하고, 받은 코드를 그림의 네 단계 순서로 읽는다. 어느 단계에서 처음 걸리는지 적는다. - 도구가 함께 만든 테스트에서 구현을 일부러 깨뜨려 본다. 테스트가 반응하지 않으면 무엇을 확인하지 않는지 적는다.
- 위 요구사항 표에 자신의 프로젝트 요구사항 하나를 추가하고, 증거가 단위 테스트인지 실제 실행인지 프로세스 연결인지 구분해 적는다.