에이전트는 긴 프롬프트가 아니라 실행 구조다
앞 장까지 우리는 Gemini를 호출하고, 출력을 구조화하고, 도구를 연결하고, RAG와 평가 체계를 만드는 방법을 살펴보았다. 이제 남은 질문은 하나다. 이 조각들을 어떻게 하나의 '일하는 시스템'으로 묶을 것인가?
많은 사람이 에이전트를 처음 만들 때 시스템 프롬프트를 길게 쓰는 방식으로 시작한다. '너는 유능한 비서다. 사용자의 목표를 이해하고 필요한 도구를 사용하며…' 같은 문장을 계속 덧붙인다. 하지만 프롬프트가 길어진다고 에이전트가 안정적으로 일하는 것은 아니다. 에이전트는 말재주가 좋은 챗봇이 아니라, 목표를 향해 상태를 갱신하고 도구를 호출하며 실패를 관찰하고 필요할 때 사람의 승인을 받는 실행 구조다.
Gemini Enterprise Agent Platform이 에이전트 생명주기를 Build, Scale, Govern, Optimize라는 축으로 나누는 이유도 여기에 있다. 에이전트는 모델 하나를 고르는 문제가 아니라 개발, 배포, 세션, 메모리, 권한, 관측 가능성, 평가까지 이어지는 시스템 설계 문제다.1
에이전트를 설계할 때는 먼저 다음 다섯 가지를 분리해서 생각해야 한다.
| 구성요소 | 질문 | Gemini 기반 구현 예 |
|---|---|---|
| 목표 | 무엇을 끝내야 하는가 | 환불 가능 여부 판단, 보고서 초안 작성, 장애 원인 분석 |
| 상태 | 지금까지 무엇을 알고 있는가 | 세션 기록, RAG 검색 결과, 사용자 프로필, 진행 단계 |
| 도구 | 무엇을 실제로 할 수 있는가 | Google Search, 사내 API, 티켓 시스템, 코드 실행, 파일 검색 |
| 승인 | 어디서 멈추고 사람에게 물어야 하는가 | 결제 취소, 메일 발송, 고객 데이터 수정, 외부 제출 |
| 관측 | 무엇을 기록해야 나중에 고칠 수 있는가 | 도구 호출, 지연시간, 토큰, 실패 사유, 승인자, 최종 결과 |
이 다섯 가지가 분리되지 않으면 에이전트는 곧 블랙박스가 된다. 사용자는 '알아서 처리해 줘'라고 말하고, 모델은 무언가를 시도하며, 개발자는 나중에 왜 그런 판단을 했는지 추적하지 못한다. 반대로 목표, 상태, 도구, 승인, 관측이 분리되어 있으면 에이전트는 소프트웨어처럼 테스트하고 운영할 수 있다.

대표 에이전트 패턴 여섯 가지
에이전트 설계에는 유행어가 많다. 오토노머스 에이전트, 멀티 에이전트, 리플렉션, 플래너, 워커, 크리틱, RAG 에이전트 같은 이름이 쏟아진다. 하지만 실무에서 자주 쓰이는 패턴은 생각보다 단순하다.
| 패턴 | 핵심 아이디어 | 적합한 상황 | 주의할 점 |
|---|---|---|---|
| 순차 실행 | 정해진 단계대로 실행한다 | 보고서 생성, 문서 검수, 데이터 정리 | 예외 흐름이 많으면 경직된다 |
| 오케스트레이터 | 중앙 에이전트가 하위 작업을 배분한다 | 고객지원, 리서치, 업무 자동화 | 중앙 에이전트의 판단 실패가 전체 실패로 이어진다 |
| 리플렉션 | 초안 작성 후 스스로 검토하고 수정한다 | 글쓰기, 코드 리뷰, 정책 검토 | 반복 횟수와 비용을 제한해야 한다 |
| 투표·앙상블 | 여러 답을 만들고 비교한다 | 고위험 분류, 법무 검토, 평가 데이터 생성 | 지연시간과 비용이 커진다 |
| Human-in-the-loop | 중요한 순간에 사람 승인을 받는다 | 결제, 환불, 계약, 고객 통지 | 승인 UI와 감사 로그가 필요하다 |
| RAG 에이전트 | 검색, 읽기, 추론, 답변을 반복한다 | 사내 지식검색, 기술지원, 조사 업무 | 검색 품질이 낮으면 추론도 흔들린다 |
순차 실행 패턴은 가장 안전한 출발점이다. 예를 들어 회의록 요약 에이전트는 녹취록을 읽고, 안건을 분리하고, 결정 사항을 추출하고, 담당자를 찾고, 후속 메일 초안을 만든다. 여기서는 모델이 매번 다음 행동을 자유롭게 고를 필요가 없다. 단계를 고정해도 충분하다.
오케스트레이터 패턴은 일이 복잡해질 때 등장한다. 고객지원 에이전트를 생각해 보자. 사용자가 '지난주에 산 노트북이 켜지지 않아요'라고 말하면 먼저 제품과 주문을 확인해야 한다. 그다음 보증 기간을 확인하고, 기술지원 절차를 안내하거나 교환 접수 초안을 만들어야 한다. 이때 중앙 에이전트는 문제를 분류하고 주문 조회 도구, 매뉴얼 검색 도구, 환불 정책 도구, 상담원 연결 도구 중 무엇을 쓸지 결정한다.
리플렉션 패턴은 모델에게 '다시 생각해'라고 막연히 말하는 것이 아니다. 초안 생성자와 검토자의 역할을 분리하는 것이다. 예를 들어 첫 번째 호출은 답변 초안을 만들고, 두 번째 호출은 정책 위반, 근거 부족, 과도한 확신, 누락된 조건을 검사한다. 세 번째 호출은 검토 결과를 반영해 최종 답변을 만든다. 이 패턴은 품질을 높일 수 있지만 무한 반복의 유혹이 있다. 따라서 최대 반복 횟수, 중단 조건, 비용 한도를 반드시 둔다.
투표 패턴은 하나의 모델 또는 여러 모델이 후보 답변을 만들고, 별도의 평가자가 가장 나은 답을 고르는 방식이다. 고위험 분류나 테스트 데이터 생성에는 유용하지만, 고객이 기다리는 실시간 서비스에는 부담이 될 수 있다. 투표를 쓸 때는 모든 요청에 적용하기보다 불확실성이 높은 요청에만 적용하는 편이 낫다.
Human-in-the-loop는 에이전트 설계에서 가장 현실적인 안전장치다. 에이전트가 고객에게 사과 메일을 쓰는 것은 괜찮지만 실제 발송은 사람이 승인해야 할 수 있다. 에이전트가 환불 가능성을 판단하는 것은 괜찮지만 실제 환불 실행은 금액과 고객 등급에 따라 승인 단계를 거쳐야 한다. 좋은 에이전트는 사람을 배제하지 않는다. 오히려 사람이 개입해야 하는 지점을 정확히 드러낸다.
RAG 에이전트는 앞 장에서 만든 RAG 시스템에 실행 루프를 얹은 형태다. 사용자의 질문을 바로 검색 쿼리로 바꾸지 않고, 먼저 질문을 분해한다. 필요한 문서를 검색하고, 근거가 부족하면 다시 검색하고, 충돌하는 근거가 있으면 사용자에게 확인 질문을 한다. 이 패턴은 사내 지식검색, 법무 검토, 기술지원처럼 근거가 중요한 업무에 특히 잘 맞는다.
ADK로 에이전트의 뼈대 만들기
Agent Development Kit, 즉 ADK는 Gemini 기반 에이전트를 코드 중심으로 만들기 위한 프레임워크다. Agent Studio가 빠른 프로토타이핑과 로우코드 실험에 가깝다면, ADK는 개발자가 도구, 상태, 실행 경계, 멀티 에이전트 흐름을 세밀하게 통제하는 쪽에 가깝다.
공식 ADK 튜토리얼의 기본 흐름은 단순하다. 가상 환경을 만들고 google-adk를 설치한 다음, agent.py에 root_agent를 정의하고, adk web으로 개발 UI를 실행한다. Agent Platform을 사용할 때는 .env에 Google Cloud 프로젝트, 위치, Vertex AI 사용 여부를 설정한다.2
python -m venv .venv
source .venv/bin/activate
pip install google-adk가장 작은 단일 에이전트는 다음과 같은 모양이다.
from google.adk.agents import Agent
from google.adk.tools import google_search
root_agent = Agent(
name='support_research_agent',
model='gemini-2.5-flash',
description='고객 문의를 조사하고 근거 있는 답변 초안을 만드는 에이전트',
instruction='''
당신은 고객지원 리서치 에이전트다.
먼저 사용자의 문제를 한 문장으로 정리한다.
최신 정보가 필요한 경우에만 검색 도구를 사용한다.
확실하지 않은 내용은 단정하지 말고 확인 질문을 제안한다.
환불, 계정 변경, 메일 발송 같은 실제 조치는 수행하지 않는다.
''',
tools=[google_search],
)이 예제에서 중요한 부분은 모델명이 아니라 실행 경계다. 에이전트에게 검색은 허용했지만 환불 실행이나 고객 정보 수정은 허용하지 않았다. 초보자는 도구를 많이 붙이면 에이전트가 똑똑해진다고 생각한다. 실제 운영에서는 반대다. 도구는 적을수록 좋고, 강력한 도구일수록 더 좁은 권한으로 감싸야 한다.
예를 들어 환불 업무를 처리한다고 해서 에이전트에게 곧바로 refund_payment 같은 도구를 주면 위험하다. 먼저 읽기 도구와 초안 도구를 분리한다.
def lookup_order(order_id: str) -> dict:
'''주문 상태와 결제 정보를 읽기 전용으로 조회한다.'''
return order_store.read_order(order_id)
def create_refund_draft(order_id: str, amount: float, reason: str) -> dict:
'''실제 환불을 실행하지 않고 승인용 초안을 만든다.'''
if amount > 100000:
return {
'status': 'needs_manager_approval',
'reason': 'amount_exceeds_limit',
'order_id': order_id,
'amount': amount,
}
return {
'status': 'draft_created',
'order_id': order_id,
'amount': amount,
'reason': reason,
}이 구조에서는 모델이 잘못 판단해도 실제 결제 시스템은 움직이지 않는다. 에이전트가 만드는 것은 실행 결과가 아니라 승인 가능한 초안이다. 실제 환불 실행은 별도의 백오피스 UI, 승인 워크플로, 감사 로그를 통과해야 한다.
전문 에이전트 협업 구조도 같은 원리로 설계한다. 하나의 거대한 에이전트에게 모든 도구를 주는 대신 역할을 나눈다.
triage_agent = Agent(
name='triage_agent',
model='gemini-2.5-flash',
instruction='문의 유형을 배송, 환불, 기술지원, 기타로 분류한다.',
)
policy_agent = Agent(
name='policy_agent',
model='gemini-2.5-flash',
instruction='사내 정책 문서를 근거로 가능한 조치만 제안한다.',
tools=[search_policy_docs],
)
refund_agent = Agent(
name='refund_agent',
model='gemini-2.5-flash',
instruction='환불 가능성을 검토하고 승인용 초안만 만든다.',
tools=[lookup_order, create_refund_draft],
)
root_agent = Agent(
name='support_orchestrator',
model='gemini-2.5-pro',
instruction='사용자 목표를 파악하고 적절한 전문 에이전트에 작업을 배분한다.',
sub_agents=[triage_agent, policy_agent, refund_agent],
)위 코드는 구조를 설명하기 위한 축약 예시다. 실제 ADK 버전별 클래스명과 세부 옵션은 출간 시점의 문서를 확인해야 한다. 그러나 원칙은 변하지 않는다. 오케스트레이터는 업무를 나누고, 전문 에이전트는 좁은 범위의 도구만 사용하며, 실제 변경 작업은 승인 경계를 통과해야 한다.
상태와 메모리: 모든 것을 컨텍스트에 넣지 않는다
에이전트가 실패하는 흔한 이유 중 하나는 모든 정보를 프롬프트에 밀어 넣는 것이다. 대화 기록, 검색 결과, 사용자 프로필, 도구 실행 결과, 시스템 규칙을 계속 누적하면 컨텍스트는 금방 비대해진다. 비용이 늘고 지연시간이 길어지며, 오래된 정보와 최신 정보가 충돌한다.
상태는 성격에 따라 나눠야 한다. 현재 대화에서만 필요한 단기 상태는 세션에 둔다. 사용자 선호나 장기 업무 이력처럼 다음 세션에서도 의미가 있는 정보는 장기 메모리나 별도 데이터베이스에 둔다. 문서 근거는 RAG 검색으로 필요할 때 가져온다. 승인 여부와 실행 결과는 감사 로그로 남긴다.
에이전트 상태를 설계할 때는 다음 원칙이 유용하다.
모델에게 필요한 정보와 애플리케이션에 필요한 정보를 구분한다.
원문 전체보다 요약, 식별자, 근거 링크를 우선 저장한다.
도구 실행 결과는 재현 가능하도록 요청 ID와 함께 남긴다.
사용자가 철회하거나 수정할 수 있는 개인정보는 장기 메모리에 무심코 저장하지 않는다.
예를 들어 고객지원 에이전트의 세션 상태에는 '현재 문의는 환불 요청이며 주문 ID는 O-1234, 정책 문서 P-9를 근거로 교환 우선 안내가 필요하다' 정도만 있어도 충분하다. 전체 주문 JSON과 모든 채팅 기록을 매 호출마다 넣을 필요는 없다.
관측 가능성: 에이전트를 디버그 가능한 소프트웨어로 만들기
단발성 LLM 호출은 입력과 출력만 저장해도 어느 정도 디버그할 수 있다. 에이전트는 다르다. 중간에 검색을 하고, API를 호출하고, 파일을 읽고, 사용자에게 되묻고, 다시 판단한다. 따라서 최종 답변만 보면 왜 그런 결과가 나왔는지 알 수 없다.
Agent Platform은 에이전트 실행 경로, 로그, 지연시간, 토큰 사용량, 토폴로지 같은 관측 가능성 기능을 강조한다.1 자체 애플리케이션을 만들 때도 같은 수준의 로그 설계를 해야 한다. 최소한 다음 항목은 남겨야 한다.
session_id,user_id_hash,request_id모델명, 프롬프트 버전, 도구 목록 버전
도구 호출 이름, 인자 요약, 민감정보 마스킹 여부
도구 호출 시작·종료 시각, 성공 여부, 오류 코드
모델 입력·출력 토큰, 전체 지연시간, 재시도 횟수
승인 요청 여부, 승인자, 승인 또는 거절 사유
최종 응답의 근거 문서, RAG 검색 ID, 안전 필터 결과
여기서 핵심은 '기록하되 과도하게 저장하지 않는 것'이다. 프롬프트와 응답 전체를 저장하면 디버깅은 쉬워지지만 개인정보와 영업비밀 위험이 커진다. 운영 로그에는 원문 대신 해시, 요약, 식별자, 마스킹된 인자를 저장하고, 원문 보관이 필요하면 별도 보안 저장소와 보존 기간 정책을 둔다.
Live API로 실시간 음성·영상 인터페이스 만들기
지금까지의 에이전트는 사용자가 입력하고, 서버가 처리하고, 결과를 반환하는 요청-응답 구조에 가깝다. 하지만 음성 상담, 현장 지원, 쇼핑 도우미, 교육 튜터처럼 자연스러운 인터페이스가 필요한 경우에는 이 구조가 답답하다. 사용자는 말하는 중간에 정정하고 싶고, 카메라로 물체를 보여 주고 싶고, 에이전트가 말하는 도중 끼어들고 싶다.
Gemini Live API는 이런 상황을 위해 만들어진 저지연 실시간 음성·영상 상호작용 API다. 오디오, 이미지·동영상, 텍스트의 연속 스트림을 처리하고, 텍스트 또는 오디오로 응답한다. Agent Platform 문서 기준으로 Live API는 스테이트풀 WebSocket 연결을 사용하며, 입력 오디오는 원시 16비트 PCM 16kHz, 출력 오디오는 원시 16비트 PCM 24kHz를 사용한다. 이미지·동영상 입력은 JPEG 프레임 형태로 다룰 수 있다.3
실시간 인터페이스에서 가장 중요한 변화는 '턴'의 개념이다. 일반 채팅에서는 사용자가 메시지를 보내면 모델이 답하고, 그다음 사용자가 다시 메시지를 보낸다. Live API에서는 사용자가 말하는 중간에 시각 정보를 보낼 수 있고, 모델이 답하는 중간에 사용자가 끼어들 수 있다. 공식 ADK Live API Toolkit은 이런 양방향 스트리밍을 위해 WebSocket 연결 수명주기, 메시지 라우팅, 세션 상태, 도구 실행을 추상화한다.4
전형적인 구조는 다음과 같다.
브라우저 마이크·카메라
-> FastAPI WebSocket 엔드포인트
-> LiveRequestQueue
-> ADK Runner와 Live API 세션
-> run_live 이벤트 스트림
-> 브라우저 오디오 재생·자막 표시실제 애플리케이션에서는 업스트림과 다운스트림을 분리한다. 업스트림은 브라우저에서 들어오는 오디오, 텍스트, 이미지 프레임을 Live API 쪽으로 보낸다. 다운스트림은 모델의 텍스트, 오디오, 전사, 도구 호출 이벤트를 받아 브라우저에 전달한다.
@app.websocket('/live')
async def live_endpoint(ws):
await ws.accept()
async def upstream():
async for msg in browser_messages(ws):
if msg.kind == 'audio':
await live_queue.send_realtime(msg.blob)
elif msg.kind == 'image':
await live_queue.send_realtime(msg.blob)
elif msg.kind == 'text':
await live_queue.send_text(msg.text)
async def downstream():
async for event in runner.run_live(
agent=root_agent,
session=session,
live_request_queue=live_queue,
run_config=run_config,
):
await ws.send_json(serialize_event(event))
await asyncio.gather(upstream(), downstream())이 코드는 전체 구조를 보여 주기 위한 축약 예시다. 실제 메서드명과 이벤트 타입은 사용 중인 ADK 버전에 맞춰 확인해야 한다. 중요한 것은 WebSocket 하나 안에서 입력과 출력이 동시에 흐른다는 점이다. 이 구조를 제대로 만들지 않으면 사용자가 말하는 동안 모델 응답이 끊기거나, 모델이 말하는 동안 사용자의 끼어들기가 반영되지 않는다.
실시간 음성 에이전트에서는 UI도 모델만큼 중요하다. 사용자가 지금 녹음 중인지, 에이전트가 듣고 있는지, 말하는 중인지, 끼어들기가 가능한지 알아야 한다. 브라우저는 오디오 청크를 너무 크게 보내면 지연이 커지고, 너무 작게 보내면 네트워크 오버헤드가 커진다. 서버는 연결 끊김, 재연결, 세션 만료, 마이크 권한 거부를 모두 처리해야 한다.
끼어들기 처리는 특히 중요하다. 사람이 대화할 때는 상대가 설명하는 중간에 '잠깐만요, 그게 아니고요'라고 말할 수 있다. 실시간 에이전트도 이 경험을 제공해야 한다. 사용자가 말하기 시작하면 클라이언트는 현재 재생 중인 모델 오디오를 중단하고, 서버는 새 입력을 Live API 세션에 전달한다. 모델 응답을 끝까지 재생해야만 다음 입력을 받는 구조라면 그것은 실시간 대화가 아니라 음성으로 포장한 챗봇에 가깝다.
실시간 에이전트에 도구를 붙일 때의 함정
Live API와 도구 호출을 결합하면 매우 강력한 경험이 가능하다. 사용자가 카메라로 고장 난 커피머신을 보여 주며 '여기서 물이 새요'라고 말하면, 에이전트는 제품 모델을 추정하고, 매뉴얼을 검색하고, 수리 절차를 안내할 수 있다. 쇼핑몰에서는 사용자가 옷을 보여 주며 '이 바지에 어울리는 신발을 찾아 줘'라고 말할 수 있다. 교육 서비스에서는 학생이 문제 풀이 과정을 말로 설명하면 에이전트가 중간 오류를 짚어 줄 수 있다.
하지만 실시간 도구 호출은 일반 도구 호출보다 위험하다. 사용자가 음성으로 말한 내용은 인식 오류가 있을 수 있고, 영상 프레임은 불완전할 수 있으며, 대화는 빠르게 흘러간다. 따라서 실시간 에이전트의 도구는 기본적으로 읽기 전용으로 시작하는 것이 좋다. 검색, 조회, 추천, 초안 생성은 허용하되 결제, 삭제, 발송, 예약 확정은 명시적 확인을 거치게 한다.
실시간 에이전트의 확인 문장은 짧고 구체적이어야 한다. '진행할까요?'보다 '파란색 270 사이즈 상품을 장바구니에 담을까요?'가 안전하다. 사용자가 '네'라고 답했을 때 무엇에 동의했는지 로그로 남길 수 있어야 한다. 음성 인터페이스에서는 사용자의 동의가 모호해지기 쉬우므로, 중요한 작업은 화면 확인 버튼과 함께 쓰는 것이 좋다.
Gemini CLI: 에이전트를 만드는 에이전트
Gemini CLI는 터미널에서 동작하는 개발자용 에이전트다. 파일을 읽고, 코드를 수정하고, 명령을 실행하고, MCP 서버를 통해 외부 도구와 연결할 수 있다. ADK와 함께 쓰면 에이전트 개발 자체를 빠르게 반복하는 도구가 된다.
Google 개발자 블로그는 ADK 저장소의 llms-full.txt를 Gemini CLI에 제공해 ADK 프레임워크의 구조와 권장사항을 컨텍스트로 삼는 흐름을 소개한다. 개발자는 먼저 에이전트의 목적을 설명하고, CLI와 함께 계획을 세운 다음, 그 계획을 ADK 코드로 바꾸고, 테스트하며, 다시 개선한다.5
이 방식의 장점은 속도다. 예를 들어 GitHub 이슈 라벨링 에이전트를 만든다고 하자. 사람이 먼저 '이슈를 읽고, 가능한 라벨을 조회하고, 가장 적절한 라벨을 고르고, 적용 전 확인한다'는 흐름을 정한다. Gemini CLI는 이 흐름을 바탕으로 ADK 에이전트, GitHub 도구 함수, 실행 스크립트, 테스트 코드를 초안으로 만들 수 있다.
하지만 Gemini CLI를 '마음대로 코드를 바꾸는 자동 개발자'로 두면 안 된다. 프로젝트 루트에 GEMINI.md를 두고 규칙을 명확히 적어야 한다.
# Agent development rules
- ADK 에이전트 도구는 읽기 도구와 쓰기 도구를 분리한다.
- 결제, 삭제, 발송, 권한 변경 도구는 기본적으로 만들지 않는다.
- 실제 외부 API를 호출하는 테스트는 mock으로 대체한다.
- 모든 도구 함수는 입력 검증과 타임아웃을 포함한다.
- 새 에이전트에는 실행 로그와 평가 케이스를 함께 추가한다.터미널 에이전트는 개발 생산성을 크게 높일 수 있지만, 파일 시스템과 셸에 접근한다는 점에서 위험도 크다. 첫 실행은 반드시 실험용 저장소에서 하고, 도구 실행 확인 프롬프트를 무심코 모두 허용하지 않는다. 에이전트가 제안한 코드는 테스트와 코드 리뷰를 통과해야 하며, 보안 관련 변경은 사람이 직접 확인해야 한다.
MCP: 도구 연결의 공용 어댑터
MCP, 즉 Model Context Protocol은 모델과 외부 도구·리소스가 통신하는 방식을 표준화하려는 프로토콜이다. Gemini CLI에서 MCP 서버는 도구 목록을 노출하고, 모델이 특정 도구를 호출하면 구조화된 결과를 반환한다. Gemini CLI 문서에 따르면 MCP 서버는 도구 발견, 도구 실행, 리소스 접근을 제공하며 Stdio, SSE, Streamable HTTP 같은 전송 방식을 지원한다.6
간단한 설정은 다음과 같은 형태다.
{
"mcp": {
"allowed": ["trusted-docs"]
},
"mcpServers": {
"trusted-docs": {
"command": "npx",
"args": ["-y", "@modelcontextprotocol/server-filesystem", "./docs"],
"timeout": 30000,
"trust": false
}
}
}여기서 trust: false가 중요하다. 신뢰 모드를 켜면 도구 호출 확인을 우회할 수 있는데, 실험 단계에서는 편하더라도 운영 보안 관점에서는 위험하다. MCP 서버는 사실상 에이전트에게 새로운 손과 발을 달아 주는 장치다. 파일을 읽는 MCP와 데이터베이스를 수정하는 MCP의 위험도는 완전히 다르다.
MCP 서버를 붙일 때는 최소 권한 원칙을 적용한다. 읽기 전용 토큰을 먼저 쓰고, 쓰기 권한은 별도 서버로 분리한다. includeTools와 excludeTools 같은 허용·차단 목록을 활용해 모델이 볼 수 있는 도구를 줄인다. 환경 변수에 있는 비밀값은 하드코딩하지 말고, 필요한 서버에만 명시적으로 전달한다. 원격 MCP 서버는 OAuth, 네트워크 경계, 감사 로그를 함께 설계해야 한다.
MCP의 가장 좋은 사용처는 내부 개발 도구와 지식 도구다. 예를 들어 사내 API 문서를 읽는 MCP, 배포 상태를 조회하는 MCP, 이슈 트래커에서 티켓을 검색하는 MCP, 데이터 카탈로그를 조회하는 MCP는 개발자 에이전트의 생산성을 크게 높인다. 반면 운영 데이터 삭제, 대량 메일 발송, 결제 실행처럼 되돌리기 어려운 도구는 MCP로 노출하기 전에 승인 워크플로를 먼저 만들어야 한다.
A2A와 Interactions API는 어디에 두어야 할까
에이전트가 많아지면 다음 질문이 생긴다. 에이전트끼리는 어떻게 대화해야 할까? MCP가 모델과 도구 사이의 연결에 가깝다면, A2A는 에이전트와 에이전트 사이의 상호운용성 방향으로 이해할 수 있다. 예를 들어 고객지원 에이전트가 재고 에이전트, 배송 에이전트, 청구 에이전트에게 작업을 위임하는 구조를 생각할 수 있다.
다만 2026년 5월 기준 이 책의 본문 구현 축은 여전히 안정적인 generateContent, ADK, Agent Platform, Live API의 검증된 흐름에 둔다. Interactions API는 모델과 에이전트를 하나의 인터페이스로 다루고, 서버 측 상태와 배경 실행, 원격 MCP 도구 같은 에이전트형 기능을 제공하는 방향으로 소개되었지만, 공개 베타이며 변경 가능성이 있다. Google도 표준 프로덕션 워크로드에는 generateContent가 기본 경로로 계속 유지된다고 설명한다.7
따라서 실무 설계에서는 어댑터 계층을 둔다. 비즈니스 로직이 특정 API 스키마에 직접 묶이지 않게 하고, 모델 호출, 도구 호출, 상태 저장, 이벤트 스트림을 내부 인터페이스로 감싼다. 나중에 Interactions API나 A2A 흐름을 도입하더라도 에이전트 전체를 다시 쓰지 않도록 하기 위해서다.
실전 설계: 현장 지원 에이전트
이제 하나의 예로 묶어 보자. 제조 장비를 다루는 회사가 현장 기술자를 위한 실시간 지원 에이전트를 만든다고 가정하자. 기술자는 스마트폰 카메라로 장비를 보여 주며 '압축기 쪽에서 이상한 소리가 납니다. 어떤 절차로 확인해야 하나요?'라고 묻는다.
첫 번째 버전은 단순해야 한다. Live API로 음성과 영상을 받고, ADK 에이전트는 사용자의 발화를 전사해 문제 유형을 분류한다. RAG 도구는 장비 매뉴얼과 점검 절차에서 관련 문서를 검색한다. 에이전트는 '전원을 차단했는지 확인하세요', '오른쪽 하단 밸브의 모델명을 보여 주세요'처럼 단계별로 안내한다. 이때 에이전트가 할 수 있는 일은 조회와 안내뿐이다.
두 번째 버전에서는 티켓 초안 생성 도구를 붙인다. 기술자가 '이 내용으로 정비 요청을 남겨 줘'라고 말하면 에이전트는 장비 ID, 증상, 점검 단계, 첨부 이미지, 추천 조치를 정리해 티켓 초안을 만든다. 하지만 티켓 제출 버튼은 사람이 누른다. 긴급 출동 요청이나 부품 주문은 별도 승인 단계로 둔다.
세 번째 버전에서는 관측 가능성과 평가를 강화한다. 어떤 매뉴얼 문서가 검색되었는지, 영상 입력이 어떤 시점에 들어왔는지, 에이전트가 어떤 확인 질문을 했는지, 기술자가 어느 단계에서 중단했는지 기록한다. 평가 데이터셋에는 정상 안내 사례뿐 아니라 흐릿한 영상, 잘못된 장비명, 소음이 큰 환경, 사용자의 중간 정정 같은 실패 조건을 넣는다.
이 예제에서 모델은 중요하지만 전부가 아니다. 제품 경험을 좌우하는 것은 실시간 스트리밍 품질, 검색 문서의 정확도, 도구 권한, 승인 흐름, 로그와 평가 체계다. 이것이 에이전트 설계가 프롬프트 엔지니어링보다 넓은 이유다.
운영 전 체크리스트
에이전트를 배포하기 전에는 다음 질문에 답할 수 있어야 한다.
이 에이전트의 성공 조건은 무엇이며, 실패 조건은 무엇인가?
모델이 사용할 수 있는 도구와 사용할 수 없는 도구는 명확히 분리되어 있는가?
읽기 도구, 초안 도구, 실제 실행 도구의 권한이 분리되어 있는가?
결제, 삭제, 발송, 권한 변경 같은 고위험 행동에는 승인 단계가 있는가?
세션 상태와 장기 메모리에 개인정보가 과도하게 저장되지 않는가?
도구 호출, 승인, 오류, 토큰, 지연시간을 추적할 수 있는가?
RAG 근거와 최종 답변을 연결해 나중에 검토할 수 있는가?
실시간 음성·영상 인터페이스에서 끼어들기, 재연결, 권한 거부를 처리하는가?
Gemini CLI와 MCP 서버에 최소 권한과 확인 프롬프트가 적용되어 있는가?
preview 기능에 의존한 부분이 별도 어댑터와 대체 경로를 갖고 있는가?
에이전트는 마법처럼 혼자 일하는 존재가 아니다. 잘 설계된 에이전트는 좋은 신입 직원과 비슷하다. 목표를 이해하고, 필요한 자료를 찾고, 할 수 있는 일과 할 수 없는 일을 구분하고, 위험한 결정은 상급자에게 묻고, 자신이 한 일을 기록한다. Gemini 기반 에이전트 설계의 핵심도 여기에 있다. 모델에게 더 많은 자유를 주는 것이 아니라, 안전하게 일할 수 있는 구조를 만들어 주는 것이다.
참고
1Gemini Enterprise Agent Platform Agents overview
2ADK를 사용하여 Gemini Live API 시작하기
5ADK 및 Gemini CLI로 에이전트의 바이브 빌딩 흐름 간소화
6MCP servers with the Gemini CLI
7Interactions API: A unified foundation for models and agents
이 노트는 보도·비평·교육·연구 목적으로 작성되었습니다. 저작권 문의가 있으시면 에서 알려주세요.