Genkit Agents 사용법: 대화형 AI 앱에서 세션·도구·스트리밍 묶기
대화형 AI 앱을 만들 때 가장 귀찮은 부분은 모델 호출 자체가 아니다. 메시지 히스토리, 세션 저장, 도구 호출 루프, 스트리밍, 중단된 작업 재개, 프론트엔드 프로토콜을 매번 다시 엮는 일이 더 오래 걸린다. Google의 Genkit Agents API는 이 반복 배관을 하나의 에이전트 인터페이스로 묶으려는 접근이다.
Genkit은 TypeScript, Go, Dart, Python을 지원하는 오픈소스 프레임워크다. 이번 Agents API는 특히 서버에서 에이전트를 정의하고, 같은 chat 또는 run API로 원샷 응답, 스트리밍, 멀티턴 대화, 도구 호출을 다루는 데 초점을 둔다. 현재 TypeScript와 Go에서 preview 상태이므로 프로덕션 도입 전 변경 가능성은 반드시 감안해야 한다.
핵심 키워드는 Genkit Agents, 대화형 AI 앱, AI 세션 관리, agentic full-stack app이다. 이 글은 “무엇이 나왔다”보다 “어떤 구조로 쓰면 반복 구현을 줄일 수 있는가”에 맞춘 실무 사용법이다.
대화형 AI 앱의 반복 배관
일반적인 챗봇 데모는 generate 호출 하나로 충분하다. 하지만 제품 기능이 되는 순간 요구사항이 늘어난다. 사용자는 이전 대화를 기억하길 원한다. 도구 호출 결과가 UI에 반영되어야 한다. 답변은 스트리밍으로 보여줘야 한다. 장바구니, 티켓, 문서 편집 상태처럼 앱 자체의 state도 함께 움직여야 한다.
이때 많은 팀이 자체 세션 포맷을 만들고, 메시지 배열을 DB에 저장하고, tool call을 별도 큐로 처리하고, 프론트엔드에는 임시 이벤트 프로토콜을 붙인다. 처음에는 빠르지만 기능이 늘어날수록 예외 처리가 쌓인다. “같은 대화를 다른 기기에서 이어가기”, “승인 전 상태로 되돌리기”, “도구 호출 중 실패한 턴 재시도” 같은 요구가 들어오면 구조가 흔들린다.
Genkit Agents API는 이 반복 배관을 agent라는 단위로 감싼다. 이름, 시스템 프롬프트, 모델, 도구, 상태, 세션 스토어를 한곳에서 정의하고, 서버 내부 실행과 HTTP endpoint 뒤 실행을 같은 개념으로 다루게 한다.
세션 소유권: 서버 관리와 클라이언트 관리
Genkit Agents에서 중요한 선택은 세션을 누가 소유하느냐다. 서버-managed 방식은 서버가 메시지, custom state, artifacts를 snapshot으로 저장한다. 클라이언트는 sessionId를 보내 이어간다. 이 방식은 로그인 사용자, 여러 기기, 팀 공유, 장기 대화, 규정 준수 로그가 필요한 서비스에 적합하다.
반대로 client-managed 방식은 서버가 전체 상태를 돌려주고, 클라이언트가 다음 턴에 다시 보낸다. 서버를 stateless하게 유지하고 싶거나, 앱이 이미 자체 저장소를 갖고 있거나, 짧은 세션만 필요한 경우에 유리하다. 다만 상태 크기가 커질수록 네트워크 비용과 보안 검토가 필요하다.
실무에서는 두 방식을 섞을 수도 있다. 예를 들어 민감한 사용자 식별 정보와 결제 상태는 서버에 두고, UI 임시 상태나 짧은 초안은 클라이언트가 들고 있게 할 수 있다. 중요한 건 “편해서”가 아니라 데이터 민감도, 세션 길이, 멀티 디바이스 요구, 장애 복구 기준으로 결정하는 것이다.
도구 호출은 권한 모델과 함께 설계해야 한다
에이전트가 도구를 호출하기 시작하면 단순 챗봇이 아니라 업무 실행 시스템이 된다. 날씨 조회나 문서 검색처럼 읽기 전용 도구는 위험이 낮다. 하지만 이메일 발송, DB 수정, 이슈 생성, 결제 처리, 파일 삭제처럼 외부 부작용이 있는 도구는 권한 모델이 필요하다.
Genkit에서 도구를 agent 정의에 붙일 때는 도구별 권한, 입력 검증, 감사 로그를 같이 설계해야 한다. 예를 들어 supportAgent가 고객 정보를 조회할 수는 있지만 환불 실행은 승인 없이는 못 하게 해야 한다. codingAgent가 PR 초안을 만들 수는 있지만 main 브랜치 push는 못 하게 해야 한다.
또한 도구 결과를 그대로 모델에게 넘길 때 prompt injection 위험이 생긴다. 외부 문서, 웹페이지, 고객 입력에는 “이전 지시를 무시하라” 같은 악성 문구가 들어갈 수 있다. 도구 출력은 신뢰 수준을 표시하고, 시스템 지시와 사용자 데이터의 경계를 분리해야 한다.
스트리밍과 UI 상태를 같이 생각하기
대화형 앱에서 스트리밍은 체감 성능을 크게 개선한다. 사용자는 전체 답변이 끝날 때까지 기다리지 않아도 된다. 하지만 스트리밍은 UI 설계를 더 어렵게 만든다. 텍스트 토큰, 도구 호출 시작, 도구 결과, 중간 경고, 최종 상태 업데이트를 구분해서 보여줘야 하기 때문이다.
좋은 UI는 “모델이 생각 중”이라는 추상 문구보다 실제 진행 단계를 보여준다. 예를 들어 “문서 검색 중”, “최근 주문 3건 확인”, “환불 정책 비교”, “응답 초안 작성”처럼 사용자가 이해할 수 있는 이벤트가 좋다. 실패 시에도 단순 에러가 아니라 어느 단계에서 실패했는지 보여줘야 재시도 버튼을 만들 수 있다.
Genkit Agents를 쓸 때도 프론트엔드 이벤트 계약을 명확히 해야 한다. token 이벤트, tool_start, tool_result, state_update, approval_required, final 같은 이벤트 이름을 정하고, UI 컴포넌트가 이를 안정적으로 처리하게 만든다. 프레임워크는 실행을 돕지만, 좋은 제품 경험은 이벤트 설계에서 나온다.
Preview API를 프로덕션에 넣는 기준
Genkit Agents API는 preview 상태다. Google도 minor version에서 breaking change가 생길 수 있다고 안내한다. 따라서 핵심 매출 흐름에 바로 넣기보다는 내부 도구, 실험 기능, 제한된 베타 사용자 기능부터 시작하는 편이 안전하다.
프로덕션에 넣어야 한다면 adapter layer를 둔다. 앱 전체가 Genkit의 특정 API 모양에 직접 의존하지 않게 하고, 내부 인터페이스를 하나 만든다. 예를 들어 runAssistantTurn, streamAssistantTurn, resumeSession 같은 사내 함수 뒤에 Genkit을 감춘다. 나중에 API가 바뀌어도 adapter만 고치면 된다.
또한 세션 데이터 스키마를 버전 관리해야 한다. 메시지 구조, custom state, artifacts, snapshot 포맷이 바뀔 수 있기 때문이다. 장기 보관 세션을 제공한다면 마이그레이션 전략이 필요하다. preview 기술을 쓰는 가장 좋은 방법은 “격리해서 빠르게 배우고, 핵심 경로는 보호하는 것”이다.
실행 체크리스트
- 대화 기능을 만들기 전에 세션 소유권을 서버/클라이언트/혼합 중 하나로 정한다.
- 도구마다 읽기·쓰기 권한과 승인 필요 여부를 문서화한다.
- 외부 도구 출력은 untrusted data로 보고 prompt injection 방어를 넣는다.
- 스트리밍 이벤트 이름과 UI 처리 규칙을 먼저 합의한다.
- preview API는 adapter layer 뒤에 숨긴다.
- snapshot, custom state, artifacts 스키마를 버전 관리한다.
- 내부 도구나 베타 기능으로 시작해 장애 패턴을 수집한다.
참고한 공개 자료
- Google Developers Blog, “Build agentic full-stack apps with Genkit” https://developers.googleblog.com/build-agentic-full-stack-apps-with-genkit/