예약된 AI 스크립트를 위한 인간 승인 프로세스
요약
GitHub Actions나 Lambda와 같은 예약된 AI 스크립트 실행 시, LLM의 응답을 기다리며 러너를 유휴 상태로 두지 않는 효율적인 워크플로 설계 방법을 제안합니다. 작업을 '제안(propose)'과 '실행(execute)' 단계로 분리하여 비용을 절감하고 CI 리소스를 최적화하는 것이 핵심입니다.
핵심 포인트
- 긴 차단형 작업 대신 제안과 실행 워크플로를 분리하여 CI 비용 절감
- 제안 단계에서 LLM 결과를 저장하고 즉시 종료하여 러너 유휴 방지
- 실행 단계에서 승인 여부를 확인한 후 실제 부수 효과(Side effect) 수행
- 보안을 위해 API 키는 Secret으로 관리하고 권한 범위를 최소화
매일 밤 실행되는 GitHub Actions 작업, Lambda cron, CI 트리거 생성기 등 예약된 AI 스크립트의 출력을 결정(decision)을 기다리며 러너(runner)를 몇 시간 동안 유휴 상태로 두지 않고 제어하는 방법입니다.
장시간 실행되는 에이전트에게는 없는 제약 사항
직접 소유한 서버의 cron 작업은 한 시간 동안 폴링 루프(polling loop)에서 대기할 여유가 있을 수 있습니다. 하지만 CI 예약 스크립트는 대개 그럴 수 없거나, 그래서는 안 됩니다. GitHub Actions는 분 단위로 비용을 청구하며, 대부분의 셀프 호스팅 러너(self-hosted runners)는 공유됩니다. 또한 GET /v1/actions/:id에서 몇 시간 동안 차단(block)되는 작업은 기술적으로 허용되더라도 CI 러너를 잘못 사용하는 사례입니다. 이는 LLM을 한 단계로 사용하는 작은 자동화 스크립트에서 끊임없이 나타납니다. 예를 들어, git 로그로부터 릴리스 노트(release notes) 초안을 작성하고 PR을 여는 야간 작업, 밤사이 접수된 지원 티켓을 요약하여 Slack에 게시하는 예약 스크립트, 변경 로그(changelog)를 생성하고 게시하는 워크플로 등이 있습니다.
해결책은 다른 승인 흐름을 만드는 것이 아니라, 하나의 긴 차단형(blocking) 작업 대신 스크립트를 두 개의 별도 예약 실행으로 분리하는 것입니다.
제안(propose) 워크플로와 실행(execute) 워크플로로 분리
제안 (propose) 워크플로는 LLM 생성을 수행하고, 작업을 Impri로 푸시한 뒤 즉시 종료합니다. 폴링(polling)이나 대기 시간은 없습니다.
# .github/workflows/nightly-release-notes-propose.yml
name: Propose nightly release notes
on:
...
실행 (execute) 워크플로는 자체적인 짧은 일정에 따라 실행되며, 그 사이에 승인된 작업이 있는지 확인한 후 실제 부수 효과(side effect, 즉 게시, 포스팅, 커밋 등)를 실행합니다.
# .github/workflows/execute-approved-actions.yml
name: Execute approved actions
on:
...
두 워크플로 모두 절대 차단(block)되지 않습니다. propose 작업은 작업 ID(action ID)를 영구적인 어딘가(레포지토리 파일, 작은 키-값 저장소(key-value store), GitHub Actions 아티팩트(artifact))에 저장하여, execute 작업이 무엇을 조회해야 하는지 알 수 있게 합니다.
// scripts/propose-release-notes.mjs
import { writeFile } from "node:fs/promises";
...
execute 작업은 해당 파일을 읽고, GET /v1/actions/:id를 한 번 호출합니다. 그 후 approved(승인됨) 상태라면 게시 단계를 실행하고, 여전히 pending(대기 중) 상태라면 아무것도 하지 않고 대기 중인 파일을 그대로 둡니다.
Secrets 및 scope (권한 범위)
IMPRI_API_KEY는 GitHub Actions 저장소 secret (비밀값)으로 저장해야 하며, 워크플로(workflow) 파일 자체에 절대 포함해서는 안 됩니다. 권한 범위(scope)는 actions로만 제한하십시오. 액션을 제안하고 확인하는 CI 스크립트가 admin이나 watch 권한을 가질 이유는 없습니다. 만약 제안(propose)과 실행(execute) 워크플로가 별도의 작업으로 실행된다면, 동일한 actions 범위의 키를 공유할 수 있습니다. 이를 더 세분화하여 분리할 실익은 없습니다.
사람이 아닌 스케줄을 위한 expires_in 설정
동기식 승인의 경우, 몇 분에서 몇 시간 정도의 짧은 만료 시간을 설정합니다. 스케줄링된 스크립트의 경우, 실행 간격에 주말이나 누군가 휴대폰을 확인하지 않을 하루 정도의 여유 시간을 더해 만료 시간을 산정하십시오.
| 스크립트 주기 | 권장 expires_in |
|---|---|
| 매시간 실행, 당일 검토 | 21600 (6시간) |
| ... |
만약 만료 시간이 너무 짧으면 금요일 저녁에 쌓인 백로그가 월요일이 되기 전 조용히 만료되어 버리고, 너무 길면 오래되어 더 이상 유효하지 않은 초안들이 누군가의 무심한 승인을 기다리며 방치됩니다. execute 작업에서 expired(만료됨) 상태는 rejected(거절됨)와 동일하게 취급하십시오. 이를 로그로 남기고, 해당 작업이 여전히 중요하다면 다음 예정된 실행 시 새로운 제안을 생성하도록 합니다.
이 방식이 대체할 수 없는 것
이 패턴은 단일 워크플로 실행 내에서 배포 게이팅(deploy gating)을 위해 이미 GitHub의 환경 보호 규칙(Environments protection rules)을 사용하고 있다면 이를 대체하는 것이 아닙니다. 승인과 작업이 동일한 CI 실행 내에서 이루어진다면 해당 규칙을 사용하십시오. 제안/실행 분리 방식은 스크립트가 스케줄에 따라 무인(unattended)으로 실행되며, 사람이 응답할 때까지 러너(runner)가 시간을 낭비하거나(또는 셀프 호스팅 러너가 차단된 상태로 대기하는 것을) 원치 않을 때 구체적으로 사용하십시오. 그리고 언제나 그렇듯, Impri는 결정권만 가질 뿐입니다. Impri는 릴리스 노트(release notes)를 생성하지 않으며, 무엇이
이 패턴의 단일 워크플로 차단(single-workflow blocking) 버전에 대해서는 gating a cron job을 참조하세요. 세 가지 호출을 연결하는 그 외의 모든 사항은 quickstart로 시작하거나 integrations를 살펴보시기 바랍니다.
AI 자동 생성 콘텐츠
본 콘텐츠는 Dev.to AI tag의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기