gemini-robotics-er-2-streaming-preview 모델 엔드포인트는 Live API와 통합되는 전용 스트리밍 엔드포인트를 노출하여 애플리케이션과 로봇 간의 실시간 양방향 상호작용을 지원합니다. 따라서 빠른 피드백 루프와 환경에 대한 반응형 응답이 필요한 에이전트에 적합합니다.
사용 사례
- 다중 로봇 조정: 공유 세션을 통해 작업 상태를 전달하고 하위 작업을 위임하는 여러 로봇입니다.
- 지속적 모니터링: 장면을 관찰하고 컨테이너가 채우기 수준에 도달하는 등 특정 이벤트가 발생하면 작업을 트리거하는 로봇
- 창고 및 물류: 상품을 시각적으로 확인하고, 포장 진행 상황을 추적하고, 오류를 복구하는 출고 및 포장 에이전트
기술 사양
다음 표에는 Live API의 기술 사양이 나와 있습니다.
| 카테고리 | 세부정보 |
|---|---|
| 입력 모달리티 | 오디오 (원시 16비트 PCM 오디오, 16kHz, 리틀 엔디안), 이미지 (JPEG <= 1FPS), 텍스트 |
| 출력 모달리티 | 텍스트 |
| 프로토콜 | 스테이트풀 WebSocket 연결(WSS) |
에이전트형 설정 빌드
Live API를 기반으로 빌드된 모든 로봇 에이전트는 다음 세 단계를 따릅니다.
- 로봇 기능을 도구로 선언합니다. 로봇이 실행할 수 있는 각 작업(탐색, 잡기, 말하기)은 이름, 설명, 매개변수 스키마가 있는 함수 선언이 됩니다. 물리적 작업은
"behavior": "BLOCKING"를 사용해야 하므로 모델은 로봇이 완료될 때까지 기다린 후 다음 단계를 선택합니다. - 멀티모달 입력을 지속적인 세션으로 스트리밍합니다.
live.connect세션을 열고 작업이 종료될 때까지 열어 둡니다. 로봇 센서에서 도착하는 대로 동영상 프레임, 오디오 또는 텍스트를 전송합니다. - 수신 루프에서 도구 호출 처리 모델이 작업을 선택할 때마다
tool_call메시지를 전송합니다. 수신 루프는 로봇 SDK에 대해 함수를 실행하고tool_response를 다시 보냅니다. 세션은 열린 상태로 유지되고 모델은 결과를 기반으로 다음 작업을 선택합니다.
다음 섹션에서는 이러한 단계를 세 가지 일반적인 패턴(기준 에이전트 루프, 하트비트를 사용한 사전 대응적 장면 모니터링, TTS를 도구로 사용한 음성 라우팅)에 적용하는 방법을 보여줍니다.
함수 호출을 통해 로봇 오케스트레이션
다음 예에서는 세 단계를 모두 하나의 Python 스크립트로 연결합니다.
1단계(도구 정의)에서는 로봇 기능을 함수 선언으로 선언합니다. navigate 함수는 "behavior": "BLOCKING"를 사용하므로 모델은 로봇이 경유지에 도달할 때까지 기다린 후 다른 도구를 호출합니다.
동일한 목록에 함수 선언을 더 추가하여 로봇 기능을 추가로 노출합니다.
2단계 - 입력 도우미 - 세션으로 다양한 모달리티 입력을 스트리밍하는 세 가지 함수를 보여줍니다. send_text는 명령어, send_image는 선택적 텍스트 프롬프트가 있는 카메라 프레임, send_audio는 마이크의 원시 PCM 오디오입니다.
3단계인 수신 루프는 동시에 실행되며 두 가지 메시지 유형을 처리합니다. server_content 메시지 (모델의 텍스트 출력)와 tool_call 메시지(모델이 로봇 작업을 요청함)입니다. 도구 호출이 도착하면 루프는 실제 로봇 SDK로 대체하는 스텁인 execute_tool를 호출한 다음 모델이 다음 작업을 선택할 수 있도록 tool_response를 다시 전송합니다.
import asyncio
from google import genai
from google.genai import types
MODEL = "gemini-robotics-er-2-streaming-preview"
# ── Tool definitions ─────────────────────────────────────────────────────────
tools = [
{
"function_declarations": [
{
"name": "navigate",
"description": "Navigate the robot to a named waypoint.",
"behavior": "BLOCKING",
"parameters": {
"type": "OBJECT",
"properties": {"name": {"type": "STRING"}},
"required": ["name"],
},
},
# Add more function definitions here
]
}
]
# ── Stub tool executor (replace with real robot SDK calls) ───────────────────
def execute_tool(name: str, args: dict) -> dict:
print(f" [Tool] {name}({args})")
return {"status": "success"}
# ── Input helpers ────────────────────────────────────────────────────────────
def send_text(session, text: str):
"""Send a text turn."""
return session.send_client_content(
turns=types.Content(role="user", parts=[types.Part(text=text)]),
turn_complete=True,
)
def send_image(session, image_bytes: bytes, prompt: str = ""):
"""Send a JPEG image with an optional text prompt."""
parts = [
types.Part(
inline_data=types.Blob(data=image_bytes, mime_type="image/jpeg")
)
]
if prompt:
parts.append(types.Part(text=prompt))
return session.send_client_content(
turns=types.Content(role="user", parts=parts),
turn_complete=True,
)
def send_audio(session, audio_chunk: bytes):
"""Stream a chunk of raw PCM audio (16-bit, 16 kHz, mono)."""
return session.send_realtime_input(
media=types.Blob(data=audio_chunk, mime_type="audio/pcm;rate=16000")
)
# ── Receive loop ─────────────────────────────────────────────────────────────
async def receive_loop(session):
"""Print model text and handle tool calls until the session ends."""
async for message in session.receive():
if message.server_content:
sc = message.server_content
if sc.model_turn and sc.model_turn.parts:
for part in sc.model_turn.parts:
if part.text:
print(f"Model: {part.text}", end="", flush=True)
if sc.turn_complete:
print("\n[Turn Complete]")
elif message.tool_call:
responses = []
for call in message.tool_call.function_calls:
print(f"\n[Tool Call] {call.name}({call.args})")
result = execute_tool(call.name, call.args)
responses.append(
types.FunctionResponse(
name=call.name,
response=result,
id=call.id,
)
)
await session.send_tool_response(function_responses=responses)
# ── Main ─────────────────────────────────────────────────────────────────────
async def main():
client = genai.Client(api_key=os.environ["GEMINI_API_KEY"])
config = types.LiveConnectConfig(
response_modalities=["TEXT"],
tools=tools,
system_instruction=types.Content(
parts=[types.Part(text="You are a robot controller. Use tools to execute commands.")]
),
)
async with client.aio.live.connect(model=MODEL, config=config) as session:
recv_task = asyncio.create_task(receive_loop(session))
# Connect robot perception callbacks and user inputs to the helpers above.
recv_task.cancel()
asyncio.run(main())
수신 루프는 각 도구 응답 후에도 활성 상태로 유지됩니다. 모델은 사용자가 전체 작업 시퀀스를 미리 인코딩하지 않아도 장기 계획을 구성하고 수정합니다.
능동적 시공간 추론
Live API는 동영상을 스트리밍하지만 동영상 프레임만으로는 새로운 추론 턴이 트리거되지 않습니다. 모델 응답을 트리거하려면 동영상 프레임에 텍스트 또는 오디오 프롬프트가 함께 제공되어야 합니다. 자세한 내용은 Live API 기능을 참고하세요.
사전 대응 추론을 사용 설정하려면 하트비트를 구현하세요. 모델이 장면을 검사하고 명시적인 결정을 내리도록 강제하는 짧은 텍스트 프롬프트가 뒤따르는 최신 카메라 프레임을 주기적으로 전송합니다. 동영상 입력은 초당 1프레임으로 속도 제한됩니다.
하트비트 구현
하트비트 코루틴은 동일한 세션에서 별도의 asyncio 작업으로 실행됩니다.
각 턴이 완료되기를 기다리는 동안 (er_turn_done) 비행 중 추론이 중단되지 않도록 1Hz 케이던스 (동영상 입력 속도 제한과 일치)를 기회주의적으로 타겟팅합니다.
async def heartbeat(session, camera, er_turn_done: asyncio.Event):
TARGET_INTERVAL_SEC = 1.0
while True:
start_time = asyncio.get_running_loop().time()
frame = await camera.latest_jpeg()
await session.send_realtime_input(
video=types.Blob(data=frame, mime_type="image/jpeg")
)
await session.send_realtime_input(
text=(
"[HEARTBEAT] If no task is active, call 'ack' and wait for user"
" input. If a task is active: observe the scene. If the current"
" step is progressing correctly, call 'ack'. If the current step"
" is complete, call 'run_instruction' with the next step. If the"
" overall goal is achieved, call 'reset' and inform the user."
)
)
# Wait for the model to finish responding before sending the next heartbeat
await er_turn_done.wait()
er_turn_done.clear()
# Sleep only the remaining time to maintain ~1 Hz cadence
elapsed = asyncio.get_running_loop().time() - start_time
remaining = TARGET_INTERVAL_SEC - elapsed
if remaining > 0:
await asyncio.sleep(remaining)
수신 루프 업데이트
모델이 차례를 완료한 시점을 알리려면 receive_loop를 업데이트하여 er_turn_done을 설정하세요.
# In receive_loop: signal when the model finishes its turn
if sc.turn_complete:
er_turn_done.set()
외부 TTS를 통한 오디오 출력
Gemini Robotics ER 2는 텍스트를 반환합니다. 애플리케이션은 삽입된 콜백을 통해 완료된 대답을 별도의 TTS 제공업체 (예: Gemini TTS)로 라우팅합니다. 이렇게 하면 음성 지연 시간, 음성 선택, 중단 동작을 제어할 수 있으며 에이전트 로직을 변경하지 않고도 TTS 백엔드를 전환할 수 있습니다.
모델이 '말하기'를 '팔 움직이기'와 동일하게 취급하도록 TTS를 도구로 선언할 수도 있습니다. 첫 번째 섹션의 tools 목록에 다음 함수 선언을 추가합니다.
TOOLS = [
{
"name": "send_message",
"description": (
"Speak a message aloud via TTS, then deliver it to the"
" specified target. Use target='user' to speak directly"
" to the user, or a peer agent name (e.g., 'duo') to"
" communicate with another robot."
),
"parameters": {
"type": "object",
"properties": {
"target": {
"type": "string",
"description": "Recipient: 'user' or a peer agent name.",
},
"message": {
"type": "string",
"description": "The message to speak and deliver.",
},
},
"required": ["target", "message"],
},
},
]
TTS를 함수 선언으로 래핑하면 모델이 다른 로봇 작업과 동일한 도구 호출 경로를 통해 음성을 처리합니다. 애플리케이션은 삽입된 콜백으로 호출을 처리합니다.
GitHub의 예
Spot 로봇 간식 가져오기 데모 및 Tinybot 팬틸트 Hello World를 비롯한 전체 작업 예시는 Robotics Live API 예시를 참고하세요.
다음 단계
- 동영상 이해: 장면 찾기 및 진행 상황 분류
- 작업 오케스트레이션: 스트리밍이 없는 장기적 작업입니다.
- Live API 개요: 전체 Live API 문서입니다.