Genkit Agents 설계법: 멀티턴 상태와 human approval을 제품 코드에 넣는 방법
요약: Google Genkit Agents API는 대화형 AI 기능에서 반복적으로 필요한 메시지 히스토리, 도구 루프, 스트리밍, 상태 저장, HTTP 엔드포인트, 클라이언트 프로토콜을 하나의 에이전트 인터페이스로 묶습니다. 아직 TypeScript와 Go에서 preview 단계라 신중해야 하지만, 실무 개발자에게는 “agentic app의 뼈대”를 어떻게 나눌지 보여주는 좋은 참고 설계입니다.
왜 단일 generate 호출로는 부족한가
처음 AI 기능을 만들 때는 generate() 한 번으로 충분해 보입니다. 사용자가 질문하면 모델이 답합니다. 하지만 제품이 조금만 커져도 바로 문제가 생깁니다. 이전 대화를 기억해야 하고, 도구를 호출해야 하고, 사용자가 승인해야 하는 작업을 멈춰야 하고, 긴 작업은 탭을 닫아도 계속 돌아야 합니다.
예를 들어 고객지원 copilot을 생각해 봅시다. 사용자는 “지난번 환불 건 이어서 처리해줘”라고 말합니다. 에이전트는 티켓 기록을 불러오고, 주문 상태를 확인하고, 환불 가능 여부를 판단하고, 실제 환불 실행 전에는 사람 승인을 받아야 합니다. 이건 단순 텍스트 생성이 아니라 상태가 있는 워크플로우입니다.
Genkit Agents API가 겨냥하는 지점이 바로 여기입니다. 에이전트를 서버에서 정의하고, 동일한 chat 인터페이스로 로컬 실행, HTTP 엔드포인트 실행, 브라우저 원격 호출을 처리합니다. 발표에 따르면 Agents API는 TypeScript와 Go에서 preview이며 minor version에서 breaking change가 있을 수 있습니다. 따라서 바로 핵심 결제 시스템에 넣기보다는 내부 도구나 베타 기능부터 검증하는 게 맞습니다.
서버 관리 상태와 클라이언트 관리 상태를 나누는 기준
Genkit Agents의 중요한 설계 포인트는 상태 소유권입니다. 서버 관리 상태를 쓰면 서버가 메시지, custom state, artifact를 snapshot으로 저장하고 클라이언트는 session ID로 이어갑니다. Firestore session store 같은 저장소를 붙이면 여러 서버 인스턴스가 같은 대화를 이어받을 수 있습니다.
서버 관리 상태는 고객지원, 업무 자동화, 장기 리서치, 문서 작성처럼 continuity가 중요한 기능에 적합합니다. 사용자가 브라우저를 닫아도 작업이 이어져야 하고, 관리자나 다른 사용자가 같은 세션을 볼 수 있어야 하며, 감사 로그가 필요한 경우입니다.
반대로 클라이언트 관리 상태는 서버를 최대한 stateless하게 유지하고 싶을 때 맞습니다. 예를 들어 간단한 채팅 위젯, 로컬 문서 보조, 로그인 없는 데모에서는 클라이언트가 상태를 들고 다음 턴에 다시 보내는 방식이 단순합니다. 다만 이 방식은 대화가 길어질수록 payload가 커지고, 신뢰할 수 없는 클라이언트 입력을 검증해야 하는 부담이 생깁니다.
실무에서는 섞어 쓰는 경우가 많습니다. 민감한 업무 상태와 tool 결과는 서버에 저장하고, UI 필터나 임시 입력 상태는 클라이언트가 가집니다. 모든 것을 서버에 넣으면 비용과 개인정보 범위가 커지고, 모든 것을 클라이언트에 두면 무결성과 재개성이 약해집니다.
custom state와 artifact를 분리해야 하는 이유
에이전트 제품에서 흔한 실수는 모든 결과를 메시지 히스토리에 넣는 것입니다. 그러면 모델이 보기에는 편할 수 있지만, UI와 운영은 지저분해집니다. 작업 상태, 선택된 엔티티, 생성된 파일, 승인 대기 중인 액션이 모두 텍스트 메시지 안에 섞입니다.
Genkit은 custom state와 artifact를 구분합니다. custom state는 워크플로우 상태, task list, 선택된 고객, 현재 단계 같은 구조화된 애플리케이션 데이터입니다. artifact는 보고서, 패치, 일정표, 다운로드 가능한 파일처럼 사용자가 검사하거나 버전 관리할 수 있는 산출물입니다.
이 구분은 UI 품질에 직접 영향을 줍니다. 예를 들어 여행 계획 에이전트라면 custom state에는 날짜, 도시, 예산, 확정 여부가 들어가고 artifact에는 최종 일정표가 들어갑니다. 코드 에이전트라면 custom state에는 변경 대상 파일과 테스트 상태가 들어가고 artifact에는 patch diff가 들어갑니다. 이렇게 나누면 스트리밍 중에도 UI가 “지금 무엇을 하고 있는지”를 안정적으로 보여줄 수 있습니다.
human approval은 기능이 아니라 안전 경계다
Genkit Agents에서 특히 눈에 띄는 부분은 interruptible tool입니다. 도구가 위험한 입력을 만나면 실행을 멈추고 client에게 승인, 거절, 추가 값을 요청할 수 있습니다. 예시처럼 shell command 실행, 결제, 배포, 이메일 발송, 데이터 삭제 같은 작업은 모델이 자동으로 처리하면 안 됩니다.
중요한 것은 human approval을 버튼 하나로 끝내지 않는 것입니다. 사용자가 승인해야 할 정보가 충분히 보여야 합니다. 어떤 도구가 실행되는지, 어떤 입력으로 실행되는지, 예상 영향은 무엇인지, 되돌릴 수 있는지, 거절하면 어떤 상태가 되는지를 보여줘야 합니다.
승인 payload도 검증해야 합니다. 발표 내용에 따르면 runtime은 resume payload를 session history against 검증해 forged input으로 도구가 속지 않게 합니다. 제품 코드에서도 같은 원칙을 가져가야 합니다. 클라이언트가 “승인됨”이라고 보냈다는 이유만으로 실행하지 말고, 서버에 저장된 pending action ID와 원본 입력을 기준으로 실행해야 합니다.
긴 작업을 request lifetime 밖으로 빼는 방법
에이전트 작업은 종종 HTTP 요청보다 오래 걸립니다. 보고서 작성, 대규모 코드 분석, 시장 조사, 문서 변환은 30초 안에 끝나지 않을 수 있습니다. 사용자가 탭을 닫아도 작업이 계속되어야 하는 기능이라면 detach 모델이 필요합니다.
Genkit은 server-managed state에서 turn을 detach하고 snapshot ID로 나중에 재연결하는 패턴을 보여줍니다. 실무로 옮기면 작업 ID, pending snapshot, 진행률 스트림, abort API가 필요합니다. UI는 “생성 중” spinner만 보여주는 것이 아니라 현재 단계, 마지막 업데이트 시각, 중단 버튼, 재연결 상태를 보여줘야 합니다.
이 구조는 운영에도 좋습니다. 작업이 길어질수록 timeout, 중복 실행, 재시도 폭주, 사용자 이탈 문제가 생깁니다. detach 구조를 쓰면 작업 큐, 워커, 상태 저장소, 알림을 붙이기 쉽습니다. 단순 API handler 안에서 모든 에이전트 루프를 돌리는 방식보다 훨씬 안정적입니다.
도입할 때 피해야 할 함정
첫 번째 함정은 preview API를 제품 핵심 경로에 바로 넣는 것입니다. Genkit Agents API는 preview라 minor version breaking change 가능성이 있습니다. 내부 도구, 베타 기능, feature flag 뒤에서 검증하는 편이 안전합니다.
두 번째 함정은 agent abstraction이 모든 도메인 로직을 대신한다고 믿는 것입니다. 주문 취소, 환불, 배포, 권한 변경 같은 핵심 로직은 여전히 명시적 서비스 코드에 있어야 합니다. 에이전트는 의사결정과 도구 호출을 조율할 뿐, 비즈니스 규칙의 단일 출처가 되면 안 됩니다.
세 번째 함정은 대화 히스토리를 무한 저장하는 것입니다. 메시지, custom state, artifact에는 개인정보가 들어갈 수 있습니다. 보관 기간, 삭제 정책, 접근 권한, 감사 로그를 정해야 합니다.
실행 체크리스트
- 단일 generate 호출로 충분한 기능과 상태ful agent가 필요한 기능을 먼저 구분한다.
- 서버 관리 상태는 장기 작업, 감사 로그, 다중 기기 재개가 필요한 곳에 쓴다.
- 클라이언트 관리 상태는 가벼운 데모나 stateless 서버 요구가 강한 기능에만 쓴다.
- 메시지 히스토리에 모든 것을 넣지 말고 custom state와 artifact를 분리한다.
- 결제, 배포, 파일 수정, 외부 메시지 발송은 interruptible tool과 human approval을 기본값으로 둔다.
- 승인 실행은 클라이언트 payload가 아니라 서버의 pending action 원본 기준으로 검증한다.
- 30초 이상 걸릴 수 있는 작업은 detach, snapshot, abort, reconnect 흐름을 설계한다.
- preview API는 feature flag 뒤에서 시작하고 버전 변경 테스트를 자동화한다.