๋ผ์ด๋ธ ์์ด์ ํธ๋ฅผ ์ํ ๊ตฌ์ฑ¶
RunConfig๋ ๋ผ์ด๋ธ ์ธ์
์ ๋์์ ๊ฒฐ์ ํ๋ ๊ณณ์
๋๋ค. ์์ด์ ํธ์ ์์ฑ ํค, ์์ฑ ์ ์ฌ ๋ฐฉ์, ํด ์ข
๋ฃ ํ๋จ ์์ , ์ ์งํ ๋ํ ํ์คํ ๋ฆฌ์ ์, ์ ์ฉํ ํ๋ ๋ฑ์ ์ค์ ํฉ๋๋ค. ์ด๋ฅผ Runner.run_live()์ ์ ๋ฌํ๋ฉด ํด๋น ์ธ์
์๋ง ์ ์ฉ๋ฉ๋๋ค. ๋์ผํ ์์ด์ ํธ๋ผ๋ ๋ ๋ช
์ ์ฌ์ฉ์๊ฐ ์์ ํ ๋ค๋ฅธ ๊ตฌ์ฑ์ผ๋ก ์คํํ ์ ์์ต๋๋ค.
RunConfig๋ ๋ผ์ด๋ธ ์ ์ฉ์ด ์๋๋๋ค. ๋ฐํ์ ๊ตฌ์ฑ์์๋ ์ ์ฒด ํด๋์ค์ run_async()์ ์ ์ฉ๋๋ ํ๋๋ฅผ ์ค๋ช
ํฉ๋๋ค. ๋ค์์ run_live()์์ ์ค์ํ ํ์ ์งํฉ๊ณผ ๋ผ์ด๋ธ ์ธ์
์๋ง ์กด์ฌํ๋ ์์ฑ ๊ด๋ จ ์ค์ ์
๋๋ค.
RunConfig ๋งค๊ฐ๋ณ์ ๋น ๋ฅธ ์ฐธ์กฐ¶
๋ค์ ํ๋ ๋ผ์ด๋ธ ์์ด์ ํธ์ ๊ฐ์ฅ ๊ด๋ จ์ฑ์ด ๋์ RunConfig ๋งค๊ฐ๋ณ์์ ๋ํ ๋น ๋ฅธ ์ฐธ์กฐ๋ฅผ ์ ๊ณตํฉ๋๋ค:
| ๋งค๊ฐ๋ณ์ | ํ์ | ๋ชฉ์ | ์ฐธ์กฐ |
|---|---|---|---|
| response_modalities | list[str] | ์ถ๋ ฅ ํ์. ๋ผ์ด๋ธ ์์ด์ ํธ๋ ๋ฐ๋์ AUDIO๋ฅผ ์ฌ์ฉํด์ผ ํจ โ ๋ผ์ด๋ธ ๋ชจ๋ธ์ TEXT๋ฅผ ์ง์ํ์ง ์์ |
์ธ๋ถ์ ๋ณด |
| streaming_mode | StreamingMode | run_async() ๊ฒฝ๋ก์์์ ์ฒญํฌ ๋๋ ๋จ์ผ ์ท ์ ๋ฌ; run_live()์์๋ ์ฝํ์ง ์์ |
์ธ๋ถ์ ๋ณด |
| session_resumption | SessionResumptionConfig | ์๋ ์ฌ์ฐ๊ฒฐ ํ์ฑํ | ์ธ๋ถ์ ๋ณด |
| context_window_compression | ContextWindowCompressionConfig | ๋ฌด์ ํ ์ธ์ ์ง์ ์๊ฐ ์ง์ | ์ธ๋ถ์ ๋ณด |
| history_config | HistoryConfig | ์ด์ ๋ํ ๊ธฐ๋ก์ด ๋ผ์ด๋ธ ์๋ฒ์ ์ฌ์๋๋ ๋ฐฉ์ ์ ์ด | ์ธ๋ถ์ ๋ณด |
| max_llm_calls | int | ์ธ์ ๋น ์ด LLM ํธ์ถ ์ ์ ํ | ์ธ๋ถ์ ๋ณด |
| save_live_blob | bool | ์ค๋์ค/๋น๋์ค ์คํธ๋ฆผ ์๊ตฌ ์ ์ฅ | ์ธ๋ถ์ ๋ณด |
| custom_metadata | dict[str, Any] | ํธ์ถ ์ด๋ฒคํธ์ ์ปค์คํ ๋ฉํ๋ฐ์ดํฐ ์ฒจ๋ถ | ์ธ๋ถ์ ๋ณด |
| speech_config | SpeechConfig | ์์ฑ ๋ฐ ์ธ์ด ๊ตฌ์ฑ | ์์ฑ ๋ฐ ์ธ์ด |
| input_audio_transcription | AudioTranscriptionConfig | ์ฌ์ฉ์ ์์ฑ ์ ์ฌ | ์ค๋์ค ์ ์ฌ |
| output_audio_transcription | AudioTranscriptionConfig | ๋ชจ๋ธ ์์ฑ ์ ์ฌ | ์ค๋์ค ์ ์ฌ |
| realtime_input_config | RealtimeInputConfig | VAD ๊ตฌ์ฑ | ์์ฑ ํ๋ ๊ฐ์ง |
| explicit_vad_signal | bool | ๋ชจ๋ธ๋ก๋ถํฐ ์์ฑ ํ๋ ์ด๋ฒคํธ ๋ฐฉ์ถ | ์ธ๋ถ์ ๋ณด |
| proactivity | ProactivityConfig | ๋ฅ๋์ ์ค๋์ค ํ์ฑํ (๋ชจ๋ธ๋ณ ์ง์) | ๋ฅ๋์ ๋ฐ ๊ฐ์ ์ ๋ํ |
| enable_affective_dialog | bool | ๊ฐ์ ์ ์ ํ์ฑํ (๋ชจ๋ธ๋ณ ์ง์) | ๋ฅ๋์ ๋ฐ ๊ฐ์ ์ ๋ํ |
| translation_config | TranslationConfig | ์ค์๊ฐ ์์ฑ ๋ ์์ฑ ๋ฒ์ญ (๋ฒ์ญ ๋ชจ๋ธ ์ ์ฉ) | ์ธ๋ถ์ ๋ณด |
| avatar_config | AvatarConfig | ์์ด์ ํธ๋ฅผ ์ ๋๋ฉ์ด์ ์๋ฐํ๋ก ๋ ๋๋ง | ์ธ๋ถ์ ๋ณด |
๊ตฌ์ฑ ์ต์
์ ๋ํ ์์ธํ ๋ด์ฉ์ Python API ์ฐธ์กฐ์ RunConfig๋ฅผ ์ฐธ๊ณ ํ์ธ์.
Import ๊ฒฝ๋ก:
์ ํ์์ ์ฐธ์กฐ๋ ๋ชจ๋ ๊ตฌ์ฑ ํ์
ํด๋์ค๋ google.genai.types์์ ๊ฐ์ ธ์ต๋๋ค:
from google.genai import types
from google.adk.agents.run_config import RunConfig, StreamingMode
# types ๋ชจ๋์ ํตํด ๊ตฌ์ฑ ํ์
์ ์ ๊ทผํฉ๋๋ค
run_config = RunConfig(
session_resumption=types.SessionResumptionConfig(),
context_window_compression=types.ContextWindowCompressionConfig(...),
speech_config=types.SpeechConfig(...),
# ๋ฑ๋ฑ
)
RunConfig ํด๋์ค ์์ฒด์ StreamingMode enum์ google.adk.agents.run_config์์ ๊ฐ์ ธ์ต๋๋ค.
์๋ต ๋ชจ๋ฌ๋ฆฌํฐ (Response modes)¶
response_modalities ์ค์ ์ ์ถ๋ ฅ ํ์์ ์ ์ดํ๋ฉฐ, ์ธ์
๋น ์ ํํ ํ๋์ ํ์์ ๊ฐ์ง๋๋ค. ๋ผ์ด๋ธ ์์ด์ ํธ์ ๊ฒฝ์ฐ ๊ฐ์ ํญ์ ["AUDIO"]์
๋๋ค. ADK๊ฐ ์ง์ํ๋ ๋ชจ๋ ๋ผ์ด๋ธ ๋ชจ๋ธ์ ๋ค๋ฅธ ๋ชจ๋ฌ๋ฆฌํฐ๋ฅผ ์ง์ํ์ง ์๊ธฐ ๋๋ฌธ์
๋๋ค. ๊ฐ์ ์ค์ ํ์ง ์์ผ๋ฉด ADK๊ฐ ์๋์ผ๋ก ์ฑ์์ฃผ๋ฏ๋ก ๋๋ถ๋ถ์ ๋ผ์ด๋ธ ์ ํ๋ฆฌ์ผ์ด์
์์๋ ์ด ํ๋๋ฅผ ์ง์ ๋ค๋ฃฐ ํ์๊ฐ ์์ต๋๋ค.
response_modalities=["TEXT"] ๋ง์ด๊ทธ๋ ์ด์
์๋ด
์ด์ ADK ์ํ๊ณผ ์ ๋ฐ ๊ณ๋จ์(half-cascade) ๋ชจ๋ธ์ ํ
์คํธ ์ ์ฉ ๋ผ์ด๋ธ ์ธ์
์ ํ์ฉํ์ต๋๋ค. ํ์ง๋ง ์ด๋ ๋ ์ด์ ์๋ํ์ง ์์ต๋๋ค. ["TEXT"]๋ก run_live()๋ฅผ ํธ์ถํ๋ฉด ์ค๋์ค๋ง ์์ฑํ๋ ์ต์ ๋ผ์ด๋ธ ๋ชจ๋ธ์์๋ ์คํจํฉ๋๋ค.
๋ผ์ด๋ธ ์์ด์ ํธ์์ ํ
์คํธ๋ฅผ ์ป์ผ๋ ค๋ฉด event.output_transcription์ ์ฝ์ผ์ธ์. ADK์์๋ ์ ์ฌ๊ฐ ๊ธฐ๋ณธ์ ์ผ๋ก ํ์ฑํ๋์ด ์์ผ๋ฏ๋ก response_modalities ๋ผ์ธ์ ์ญ์ ํ๊ธฐ๋ง ํด๋ ๋๋ถ๋ถ ํด๊ฒฐ๋ฉ๋๋ค.
ํ์ค Gemini ๋ชจ๋ธ์์ ์คํ๋๋ run_async() ๊ฒฝ๋ก์์๋ ["TEXT"]๊ฐ ์ฌ์ ํ ์ฌ๋ฐ๋ฅธ ์ค์ ์
๋๋ค. ์๋ฐฉํฅ ์คํธ๋ฆฌ๋ฐ ๋๋ SSE๋ฅผ ์ฐธ๊ณ ํ์ธ์.
์๋ต ๋ชจ๋ฌ๋ฆฌํฐ๋ ๋ชจ๋ธ์ ์ถ๋ ฅ์๋ง ์ํฅ์ ์ค๋๋ค. ๋ชจ๋ธ์ด ํด๋น ์ ๋ ฅ ๋ชจ๋ฌ๋ฆฌํฐ๋ฅผ ์ง์ํ๋ค๋ฉด ์ด์ ๊ด๊ณ์์ด ํญ์ ํ ์คํธ, ์์ฑ ๋๋ ๋น๋์ค ์ ๋ ฅ์ ๋ณด๋ผ ์ ์์ต๋๋ค.
์๋ฐฉํฅ ์คํธ๋ฆฌ๋ฐ ๋๋ SSE¶
ADK๋ ๋ ๊ฐ์ง ์๋ก ๋ค๋ฅธ ์๋ํฌ์ธํธ๋ฅผ ํตํด Gemini์ ์ ๊ทผํ ์ ์์ผ๋ฉฐ, ํธ์ถํ๋ Runner ๋ฉ์๋์ ๋ฐ๋ผ ์๋ํฌ์ธํธ๊ฐ ๊ฒฐ์ ๋ฉ๋๋ค:
runner.run_live(): ADK๋ Live API(live.connect()๋ฅผ ํตํ ์๋ฐฉํฅ ์คํธ๋ฆฌ๋ฐ ์๋ํฌ์ธํธ)์ ๋ํ WebSocket์ ์ฝ๋๋ค. ์ด ๊ฐ์ด๋์ ๋๋จธ์ง ๋ถ๋ถ์์ ๋ค๋ฃจ๋ ๋ด์ฉ์ด๋ฉฐ ์ค์๊ฐ ์ค๋์ค ๋ฐ ๋น๋์ค์ ํ์์ ์ ๋๋ค.runner.run_async(): ADK๋ ํ์ค Gemini API(generate_content_async()๋ฅผ ํตํ ๋จํญ/์คํธ๋ฆฌ๋ฐ ์๋ํฌ์ธํธ)์ HTTP๋ฅผ ์ฌ์ฉํฉ๋๋ค. ์ฒญํฌ ๋จ์๋ก ์๋ต์ ๋ค์ ์คํธ๋ฆฌ๋ฐํ๋ ค๋ฉดRunConfig.streaming_mode = StreamingMode.SSE๋ก ์ค์ ํ์ธ์.
๋ ๋ชจ๋ธ ์ธํธ๋ ๊ฑฐ์ ๊ฒน์น์ง ์์ต๋๋ค. gemini-flash-latest์ ๊ฐ์ ํ์ค Gemini ๋ชจ๋ธ์ ์๋ฐฉํฅ ์ฐ๊ฒฐ์ ์ ์งํ์ง ์์ผ๋ฉฐ, ์ง์ ๋ชจ๋ธ์ ๋ชจ๋ธ๋ค์ run_live()๋ก ๊ตฌ๋ํ๋๋ก ์ค๊ณ๋์์ผ๋ฏ๋ก ๋ชจ๋ธ ์ ํ์ ๊ณง Runner ๋ฉ์๋ ์ ํ์ ์ผ๋ถ์
๋๋ค.
Python: StreamingMode.BIDI๋ ADK๋ฅผ Live API๋ก ์ ํํ์ง ์์ต๋๋ค
Python์์ RunConfig.streaming_mode๋ run_async() ์ฝ๋ ๊ฒฝ๋ก์์๋ง ์ฝํ๋ฉฐ, ๋จ์ผ ์๋ฃ ์๋ต(StreamingMode.NONE, ๊ธฐ๋ณธ๊ฐ)๊ณผ ์ฒญํฌ ๋จ์ ์ ๋ฌ(StreamingMode.SSE) ์ฌ์ด๋ฅผ ์ ํํฉ๋๋ค. run_live() ๊ฒฝ๋ก๋ ์ด๋ฅผ ์ ํ ์ฝ์ง ์์ผ๋ฏ๋ก streaming_mode=StreamingMode.BIDI๋ฅผ ์ค์ ํด๋ ์๋ฌด๋ฐ ํจ๊ณผ๊ฐ ์์ผ๋ฉฐ ์กฐ์ฉํ ๋ฌด์๋ฉ๋๋ค. ์๋ฐฉํฅ ์คํธ๋ฆฌ๋ฐ์ ์ฌ์ฉํ๋ ค๋ฉด run_live()๋ฅผ ํธ์ถํด์ผ ํฉ๋๋ค. ADK ์์ฒด์ Python StreamingMode ๋
์คํธ๋ง์์๋ BIDI๋ "ํ์ค ์คํ ๊ฒฝ๋ก์์ ์ฌ์ฉ๋์ง ์์ผ๋ฉฐ", ์ค์ ์๋ฐฉํฅ ๋์์ "streaming_mode์ ์์กดํ์ง ์๋ ์์ ํ ๋ค๋ฅธ ์ฝ๋ ๊ฒฝ๋ก๋ฅผ ์ฌ์ฉํ๋ค"๊ณ ๋ช
์ํ๊ณ ์์ต๋๋ค.
Java๋ ๋ค๋ฆ
๋๋ค. ADK Java์ ํ๋ฆ์ StreamingMode.BIDI๋ฅผ ์ค์ ๋ก ์ฝ์ผ๋ฉฐ, Java ํต์คํํธ๋ runLive()์ ์ ๋ฌํ๋ RunConfig์ ์ด๋ฅผ ๋ช
์์ ์ผ๋ก ์ค์ ํฉ๋๋ค. ์ธ์ด ๊ฐ์ ์ค์ ์ ๊ทธ๋๋ก ๋ณต์ฌํ์ง ๋ง๊ณ ๊ฐ ์ธ์ด์ ํต์คํํธ๋ฅผ ๋ฐ๋ฅด์ธ์.
# Live API: streaming_mode๊ฐ ํ์ ์์ผ๋ฉฐ, run_live() ํธ์ถ ์์ฒด๊ฐ ์ด๋ฅผ ์ ํํจ
run_config = RunConfig(response_modalities=["AUDIO"])
async for event in runner.run_live(..., run_config=run_config):
...
์ด ์ ํ์ ADK๊ฐ Gemini์ ํต์ ํ๋ ๋ฐฉ์์๋ง ์ํฅ์ ์ค๋๋ค. ํด๋ผ์ด์ธํธ๋ฅผ ํฅํ ์ํคํ ์ฒ๋ ๋ ๋ฆฝ์ ์ ๋๋ค. ์ด๋ ๊ฒฝ๋ก์์๋ WebSocket ์๋ฒ, REST API ๋๋ SSE ์๋ํฌ์ธํธ๋ฅผ ๊ตฌ์ถํ ์ ์์ต๋๋ค.
run_async() ๋ฐ SSE ๊ฒฝ๋ก(streaming_mode ๊ฐ, ํ๋ก๊ทธ๋ ์๋ธ SSE ์คํธ๋ฆฌ๋ฐ, ์ธ์ด๋ณ ๊ตฌ์ฑ)์ ๋ํ ๋ด์ฉ์ ๋ฐํ์ ๊ตฌ์ฑ์ ์ฐธ๊ณ ํ์ธ์.
๊ธฐํ ์ ์ด ์ต์ ¶
ADK๋ ์ธ์ ๋์์ ์ ์ดํ๊ณ , ๋น์ฉ์ ๊ด๋ฆฌํ๋ฉฐ, ๋๋ฒ๊น ๋ฐ ์ปดํ๋ผ์ด์ธ์ค ๋ชฉ์ ์ผ๋ก ์ค๋์ค ๋ฐ์ดํฐ๋ฅผ ์ ์งํ๊ธฐ ์ํ ์ถ๊ฐ RunConfig ์ต์ ์ ์ ๊ณตํฉ๋๋ค.
run_config = RunConfig(
# ํธ์ถ๋น ์ด LLM ํธ์ถ ์ ํ
max_llm_calls=500, # ๊ธฐ๋ณธ๊ฐ: 500 (๋ฌดํ ๋ฃจํ ๋ฐฉ์ง)
# 0 ๋๋ ์์ = ๋ฌด์ ํ (์ฃผ์ํด์ ์ฌ์ฉ)
# ๋๋ฒ๊น
/์ปดํ๋ผ์ด์ธ์ค๋ฅผ ์ํ ์ค๋์ค/๋น๋์ค ์ํฐํฉํธ ์ ์ฅ
save_live_blob=True, # ๊ธฐ๋ณธ๊ฐ: False
# ์ด๋ฒคํธ์ ์ปค์คํ
๋ฉํ๋ฐ์ดํฐ ์ฒจ๋ถ
custom_metadata={"user_tier": "premium", "session_type": "support"}, # ๊ธฐ๋ณธ๊ฐ: None
)
max_llm_calls¶
max_llm_calls๋ ํธ์ถ ์ปจํ
์คํธ๋น LLM ํธ์ถ ์๋ฅผ ์ ํํ๋ฉฐ, ๋ฐํ์ ๊ตฌ์ฑ์ ์ ์ฒด ๋ด์ฉ์ด ์ค๋ช
๋์ด ์์ต๋๋ค.
์ด ์ค์ ์ run_live()์๋ ์ ์ฉ๋์ง ์์ต๋๋ค. ์ด ๋งค๊ฐ๋ณ์๋ run_async() ๊ฒฝ๋ก๋ง ๋ณดํธํ๋ฏ๋ก ๋ผ์ด๋ธ ์ธ์
์ ์ด๋ฅผ ํตํ ์๋ ๋น์ฉ ์ํ์ ์ป์ง ๋ชปํฉ๋๋ค. ์ธ์
์ง์ ์๊ฐ์ ์ ํํ๊ณ , ํด ์๋ฅผ ์ธ๋ฉฐ, ๋ชจ๋ธ ์ด๋ฒคํธ์ usage_metadata(๋ฉํ๋ฐ์ดํฐ)๋ฅผ ๋ชจ๋ํฐ๋งํ๊ณ , ๋ฃจํ ์์ ์ํท ๋ธ๋ ์ด์ปค๋ฅผ ๋์ด ์์ฒด์ ์ผ๋ก ์์ฐ์ ๊ด๋ฆฌํ์ธ์.
save_live_blob¶
save_live_blob=True๋ ์ธ์
์ ์ค๋์ค๋ฅผ ์ธ์
์๋น์ค์๋ ์ฐธ์กฐ๋ก, ์ํฐํฉํธ ์๋น์ค์๋ ํ์ผ๋ก ์ ์งํฉ๋๋ค. ์ด๋ฆ๊ณผ ๋ฌ๋ฆฌ ํ์ฌ๋ ์ค๋์ค๋ง ์ ์ง๋๋ฉฐ ๋น๋์ค๋ ์ ์ฅ๋์ง ์์ต๋๋ค.
์์ฑ ๋์์ ๋๋ฒ๊น ํ๊ฑฐ๋ ๊ท์ ๋์ ํ๊ฒฝ์ ๊ฐ์ฌ ์ถ์ ์ ์ํด ํ์ฑํํ์ธ์. ๊ทธ๋ ์ง ์์ ๊ฒฝ์ฐ์๋ ๋นํ์ฑํ ์ํ๋ก ๋๋ ๊ฒ์ด ์ข์ต๋๋ค. 16 kHz PCM ์ ๋ ฅ์ ๋ ์๋น์ค์ ๊ธฐ๋ก๋ ๋ ์ธ์ ๋น ๋ถ๋น ์ฝ 1.92 MB๋ฅผ ์ฐจ์งํ๋ฉฐ, ์์ฑ ์ํฌ๋ก๋์์๋ ์ฉ๋์ด ๋น ๋ฅด๊ฒ ๋์ ๋ฉ๋๋ค. ํ๋ก๋์ ํ๊ฒฝ์์ ํ์ํ ๊ฒฝ์ฐ ๋ชจ๋ ์ธ์ ๋์ ์ผ๋ถ ์ธ์ ๋ง ์ํ๋งํ๊ณ ์ํฐํฉํธ ์๋น์ค์ ๋ณด์กด ์ ์ฑ (retention policy)์ ์ค์ ํ์ธ์. ADK๋ ์ด๋ฅผ ์๋์ผ๋ก ๋ง๋ฃ์ํค์ง ์์ต๋๋ค.
save_live_audio๋ ์ง์ ์ค๋จ(deprecated)๋์์ต๋๋ค
ADK๋ save_live_audio=True๋ฅผ save_live_blob=True๋ก ์๋ ๋ง์ด๊ทธ๋ ์ด์
ํ๊ณ ๊ฒฝ๊ณ ๋ฅผ ํ์ํ์ง๋ง, ์ด ํธํ์ฑ ๊ณ์ธต์ ํฅํ ๋ฆด๋ฆฌ์ค์์ ์ ๊ฑฐ๋ ์์ ์
๋๋ค. save_live_blob์ผ๋ก ์
๋ฐ์ดํธํ์ธ์.
history_config¶
์ด๋ฏธ ๋ํ ๊ธฐ๋ก์ด ์๋ ์ธ์
์ ๋ํด ADK๊ฐ ์๋ก์ด Live API ์ฐ๊ฒฐ์ ์ด ๋ ํด๋น ๊ธฐ๋ก์ ์๋ฒ์ ์ฌ์(replay)ํฉ๋๋ค. ํด๋น ๊ธฐ๋ก์๋ ๋ชจ๋ธ ์์ฒด์ ๊ณผ๊ฑฐ ํด์ด ํฌํจ๋์ด ์์ผ๋ฏ๋ก ์๋ฒ์ ๋ค์ ์๋ตํ์ง ์๋๋ก ์๋ ค์ฃผ์ด์ผ ํฉ๋๋ค. ADK๊ฐ ์ด๋ฅผ ์๋์ผ๋ก ์ฒ๋ฆฌํฉ๋๋ค. ์ ์กํ ๊ธฐ๋ก์ด ์๊ณ ์ธ์
์ฌ๊ฐ ํธ๋ค์ด ํ์ฑํ๋์ง ์์ ๊ฒฝ์ฐ ์ฐ๊ฒฐํ๊ธฐ ์ ์ live_connect_config.history_config.initial_history_in_client_content = True๋ฅผ ์ค์ ํฉ๋๋ค.
from google.genai import types
# ADK๊ฐ ์ด๋ฅผ ์๋์ผ๋ก ์ค์ ํฉ๋๋ค. ๋ฐ๋ ๋์์ด ํ์ํ ๊ฒฝ์ฐ์๋ง ์ฌ์ ์ํ์ธ์.
run_config = RunConfig(
history_config=types.HistoryConfig(
initial_history_in_client_content=True,
),
)
์ค์ ์๋ฏธ:
- ์ผ๋ฐ์ ์ผ๋ก ์๋ฌด๊ฒ๋ ํ ํ์๊ฐ ์์ต๋๋ค. ADK๋ ๊ฐ์ ์ค์ ํ์ง ์์์ ๋๋ง ์ฑ์ ๋ฃ์ผ๋ฏ๋ก
RunConfig์ ๋ช ์๋history_config๊ฐ ํญ์ ์ฐ์ ์ ์ฉ๋ฉ๋๋ค. - ์ฌ์ฐ๊ฒฐ ์์๋ ๊ธฐ๋ก ์ ์ก์ ๊ฑด๋๋๋๋ค. ADK๊ฐ ์ธ์
์ฌ๊ฐ ํธ๋ค๋ก ์ฌ์ฐ๊ฒฐํ ๋ ์๋ฒ๋ ์ด๋ฏธ ํด๋น ์ธ์
์ ์ํ๋ฅผ ์ ์งํ๊ณ ์์ผ๋ฏ๋ก ADK๋ ๊ธฐ๋ก์ ๋ณด๋ด์ง ์์ผ๋ฉฐ
history_config๋ฅผ ๊ฑด๋๋ฆฌ์ง ์์ต๋๋ค. - ์๋ชป๋์์ ๋์ ์ฆ์: ๊ธฐ๋ก์ ์๋ฉํ๋ ๋์
initial_history_in_client_content=False๋ก ์ค์ ํ๋ฉด ๋ชจ๋ธ์ด ์ฌ์๋ ํด์ ์๋ตํ์ฌ ์ฐ๊ฒฐ ์์ ์ ์ค๋ณต๋ ๋ต๋ณ์ด ์์์ ธ ๋์ต๋๋ค.
custom_metadata¶
custom_metadata๋ ํธ์ถ ๋ด์ ๋ชจ๋ Event์ ์์์ JSON ์ง๋ ฌํ ๊ฐ๋ฅํ ๋์
๋๋ฆฌ๋ฅผ ์ฒจ๋ถํ๋ฉฐ, ๋ฐํ์ ๊ตฌ์ฑ์ ์ค๋ช
๋ ๋๋ก ๋ผ์ด๋ธ ์ธ์
์์๋ ๋์ผํ๊ฒ ์๋ํฉ๋๋ค.
run_config = RunConfig(
response_modalities=["AUDIO"],
custom_metadata={"user_tier": "premium", "session_type": "support"},
)
๋ผ์ด๋ธ ๊ณ ์ ์ ์ฐจ์ด์ ์ ์ค์ฝํ์
๋๋ค. ํ ๋ฒ์ run_live() ํธ์ถ์ด ํ๋์ ํธ์ถ(invocation)์ด๋ฏ๋ก, ๋ฉํ๋ฐ์ดํฐ๋ ๋จ์ผ ํด์ด ์๋๋ผ ์ ์ฒด ์คํธ๋ฆฌ๋ฐ ์ธ์
์ ๋ชจ๋ ์ด๋ฒคํธ์ ๊ฐ์ธ๋ฉ๋๋ค. event.custom_metadata๋ฅผ ํตํด ์ด๋ฅผ ๋ค์ ์ฝ์ ์ ์์ต๋๋ค.
custom_metadata์ ๋ฏผ๊ฐํ ๋ฐ์ดํฐ๋ฅผ ๋ฃ์ง ๋ง์ธ์
์ด ๋ฉํ๋ฐ์ดํฐ๋ฅผ ํฌํจํ๋ ๋ชจ๋ ์ด๋ฒคํธ๋ ์ธ์ ์๋น์ค์ ์ ์ฅ๋ฉ๋๋ค. ๊ฐ์ธ ์๋ณ ์ ๋ณด(PII), ์๊ฒฉ ์ฆ๋ช ๋ฐ ๊ธฐํ ๋ฏผ๊ฐํ ์ ๋ณด๋ ์ ์ธํ๊ณ ๋์์ด ์๋ ๊ฒฝ์ฐ ์ํธํํ์ธ์.
๊ธฐํ ๋ผ์ด๋ธ ๊ด๋ จ ํ๋¶
RunConfig์๋ run_live() ๊ฒฝ๋ก์์๋ง ์ ํจํ ๋ช ๊ฐ์ง ์ถ๊ฐ ํ๋๊ฐ ์์ต๋๋ค. ADK๋ ์ด๋ฅผ ๋ผ์ด๋ธ ์ฐ๊ฒฐ๋ก ๊ทธ๋๋ก ์ ๋ฌํ๋ฏ๋ก ์ ํํ ๋์์ ADK๊ฐ ์๋ Live API์ ์ํด ์ ์๋ฉ๋๋ค:
| ํ๋ | ํ์ | ์ค๋ช |
|---|---|---|
explicit_vad_signal |
bool |
๋ชจ๋ธ์ด ๋ช
์์ ์ธ ์์ฑ ํ๋ ์ ํธ๋ฅผ ๋ฐฉ์ถํ๋๋ก ์์ฒญํฉ๋๋ค. ADK๋ ์ฝํ
์ธ ๋ก๋ถํฐ ํด ๊ฒฝ๊ณ๋ฅผ ์ ์ถํ๋ ๋์ event.voice_activity๋ก ์ด๋ฅผ ํ์ํฉ๋๋ค |
translation_config |
types.TranslationConfig |
์ค์๊ฐ ์์ฑ ๋ ์์ฑ ๋ฒ์ญ์ ํ์ฑํํฉ๋๋ค. target_language_code(BCP-47) ๋ฐ echo_target_language๋ฅผ ๋ฐ์ต๋๋ค. gemini-3.5-live-translate-preview์ ๊ฐ์ ๋ฒ์ญ ๋ชจ๋ธ์์๋ง ์ง์๋๋ฉฐ, ์ง์ ๋ชจ๋ธ์ ์ผ๋ฐ ๋ชจ๋ธ์์๋ ์ง์๋์ง ์์ต๋๋ค |
avatar_config |
types.AvatarConfig |
์์ด์ ํธ๋ฅผ ์ ๋๋ฉ์ด์
์๋ฐํ๋ก ๋ ๋๋งํฉ๋๋ค. avatar_name(๋นํธ์ธ ์๋ฐํ) ๋๋ customized_avatar, ๊ทธ๋ฆฌ๊ณ audio_bitrate_bps / video_bitrate_bps๋ฅผ ๋ฐ์ต๋๋ค |
from google.genai import types
run_config = RunConfig(
response_modalities=["AUDIO"],
explicit_vad_signal=True,
)
๋ผ์ด๋ธ ์ ์ฉ์ ์๋์ง๋ง ๋ผ์ด๋ธ ์ธ์ ์์ ๋งค์ฐ ์ ์ฉํ ํ๋๊ฐ ํ๋ ๋ ์์ต๋๋ค:
model_input_context(list[types.Content]): ํ์ฌ ํธ์ถ์ ๋ํด์๋ง LLM ์์ฒญ์ ์ฃผ์ ๋๋ ์ผ์์ ์ธ ์ปจํ ์คํธ์ ๋๋ค.Runner๋ ์ด๋ฅผ ์ธ์ ์ ์ ์ฅํ์ง ์์ผ๋ฏ๋ก ๋ํ ๊ธฐ๋ก์ ์ค์ผ์ํค์ง ์๊ณ ํด๋ณ ๊ทธ๋ผ์ด๋ฉ ์ ๋ณด(์ฌ์ฉ์๊ฐ ๋ฐฉ๊ธ ์ฐ ๋ฌธ์, ๋ณด๊ณ ์๋ ํ์ด์ง ๋ฑ)๋ฅผ ๊น๋ํ๊ฒ ์ ๊ณตํ ์ ์์ต๋๋ค.
ํฉ์ฑ ํจ์ ํธ์ถ (support_cfc)¶
ํฉ์ฑ ํจ์ ํธ์ถ(Compositional Function Calling, CFC)์ ๋ผ์ด๋ธ ๊ธฐ๋ฅ์ด ์๋๋ผ run_async() / SSE ๊ธฐ๋ฅ์
๋๋ค. ํ์ฌ ๋ผ์ด๋ธ ๋ชจ๋ธ ์ค CFC ์๊ตฌ ์ฌํญ์ ์ถฉ์กฑํ๋ ๋ชจ๋ธ์ด ์์ผ๋ฏ๋ก ์ด๋ก ์์ผ๋ก๋ง ์ ์ฉ ๊ฐ๋ฅํฉ๋๋ค. support_cfc๋ SSE ๊ฒฝ๋ก์ ๋จ๊ฒจ๋๊ณ ๋ผ์ด๋ธ ์ธ์
์์๋ ํ์ค ํจ์ ํธ์ถ์ ์ฌ์ฉํ์ธ์(๋๊ตฌ ์ฐธ๊ณ ). ๋งค๊ฐ๋ณ์ ์์ฒด์ ๋ํด์๋ ๋ฐํ์ ๊ตฌ์ฑ์ ์ฐธ๊ณ ํ์ธ์.
์ค๋์ค ์ ์ฌ (Audio transcription)¶
Live API๋ ๋ํ ์์ธก์ ๋ชจ๋ ์ ์ฌํด์ฃผ๋ฏ๋ก ๋ณ๋์ ์์ฑ ํ
์คํธ ๋ณํ(STT) ์๋น์ค ์์ด๋ ์บก์
์ ํ์ํ๊ณ , ๋ํ๋ฅผ ๊ธฐ๋กํ๋ฉฐ, ์ ๊ทผ์ฑ์ ์ง์ํ ์ ์์ต๋๋ค. ADK์์๋ ์
๋ ฅ(์ฌ์ฉ์ ์์ฑ)๊ณผ ์ถ๋ ฅ(๋ชจ๋ธ ์์ฑ) ๋ชจ๋ ์ ์ฌ๊ฐ ๊ธฐ๋ณธ์ ์ผ๋ก ์ผ์ ธ ์์ต๋๋ค. ํน์ ๋ฐฉํฅ์ ๋๋ ค๋ฉด ํด๋น ํ๋๋ฅผ None์ผ๋ก ์ค์ ํ์ธ์.
from google.genai import types
from google.adk.agents.run_config import RunConfig
# ๊ธฐ๋ณธ์ ์ผ๋ก ํ์ฑํ๋์ด ์์ต๋๋ค. ๋ ๋ค AudioTranscriptionConfig()๋ก ์ค์ ํ๋ ๊ฒ๊ณผ ๋์ผํฉ๋๋ค.
run_config = RunConfig(response_modalities=["AUDIO"])
# ์ฌ์ฉ์ ์
๋ ฅ ์ ์ฌ๋ฅผ ๋๊ณ ๋ชจ๋ธ ์ถ๋ ฅ ์ ์ฌ๋ ์ ์งํฉ๋๋ค.
run_config = RunConfig(
response_modalities=["AUDIO"],
input_audio_transcription=None,
)
์ ์ฌ๋ event.content์ ๋ณ๋๋ก event.input_transcription ๋ฐ event.output_transcription์ types.Transcription ๊ฐ์ฒด๋ก ๋์ฐฉํฉ๋๋ค. ์ด๋ ์กฐ๊ฐ(fragment) ๋จ์๋ก ์คํธ๋ฆฌ๋ฐ๋ฉ๋๋ค. .text๋ ์ต์ ์กฐ๊ฐ์ ๋ด๊ณ ์๊ณ .finished๋ ํด๋น ํด์ ๋ง์ง๋ง ์กฐ๊ฐ์์ ํ์ํฉ๋๋ค. ์กฐ๊ฐ๋ค์ ์ด์ด ๋ถ์ฌ ์ ์ฒด ์ ์ฌ๋ฅผ ๊ตฌ์ฑํ ์ ์์ต๋๋ค.
async for event in runner.run_live(...):
if event.input_transcription and event.input_transcription.text:
update_caption(
event.input_transcription.text,
is_user=True,
is_final=event.input_transcription.finished,
)
if event.output_transcription and event.output_transcription.text:
update_caption(
event.output_transcription.text,
is_user=False,
is_final=event.output_transcription.finished,
)
์ด๋ฒคํธ ๊ตฌ์กฐ์ ๋ํด์๋ ์ ์ฌ ์ด๋ฒคํธ๋ฅผ ์ฐธ๊ณ ํ์ธ์.
๋ฉํฐ ์์ด์ ํธ ์ธ์ ์ ํญ์ ์ ์ฌ๋ฅผ ํ์ฑํํฉ๋๋ค
๋ฃจํธ ์์ด์ ํธ์ sub_agents๊ฐ ์๋ ๊ฒฝ์ฐ run_live()๋ ์ฌ์ฉ์๊ฐ None์ผ๋ก ์ค์ ํ๋๋ผ๋ ์
๋ ฅ ๋ฐ ์ถ๋ ฅ ์ ์ฌ๋ฅผ ๋ชจ๋ ํ์ฑํํฉ๋๋ค. ์์ด์ ํธ ์ธ๊ณ ์ ๋ค์ ์์ด์ ํธ์ ๋ํ ์ปจํ
์คํธ๋ฅผ ์ ๋ฌํ๊ธฐ ์ํด ํ
์คํธ ์ ์ฌ๊ฐ ํ์ํ๋ฏ๋ก ๋นํ์ฑํํ ์ ์์ต๋๋ค(runners.py).
์์ฑ ๋ฐ ์ธ์ด (Voice and language)¶
๋ชจ๋ธ์ ์์ฑ๊ณผ ์ธ์ด๋ฅผ ์ ํํ๋ ค๋ฉด speech_config๋ฅผ ์ค์ ํ์ธ์. ๋ ๊ณณ์์ ์ค์ ํ ์ ์์ต๋๋ค:
- ์์ด์ ํธ ์์ค:
speech_config๊ฐ ํฌํจ๋Gemini์ธ์คํด์ค๋ฅผ ์ ๋ฌํฉ๋๋ค. ๋ฉํฐ ์์ด์ ํธ ์ํฌํ๋ก์์ ๊ฐ ์์ด์ ํธ์ ๊ณ ์ ํ ์์ฑ์ ๋ถ์ฌํ ๋ ์ฌ์ฉํฉ๋๋ค. - ์ธ์
์์ค:
RunConfig.speech_config๋ฅผ ์ค์ ํฉ๋๋ค. ์ธ์ ์ ์ฒด์ ๊ฑธ์ณ ๋จ์ผ ์์ฑ์ ์ฌ์ฉํ ๋ ์ฌ์ฉํฉ๋๋ค.
๋ ๋ค ์ค์ ๋ ๊ฒฝ์ฐ ์์ด์ ํธ ์์ค์ ์์ฑ์ด ์ฐ์ ์ ์ฉ๋ฉ๋๋ค. ๋ ๋ค ์ค์ ๋์ง ์์ ๊ฒฝ์ฐ Live API๊ฐ ๊ธฐ๋ณธ ์์ฑ์ ์ ํํฉ๋๋ค.
from google.genai import types
from google.adk.agents import Agent
from google.adk.models.google_llm import Gemini
from google.adk.agents.run_config import RunConfig
# ์์ด์ ํธ ์์ค ์์ฑ (RunConfig๋ณด๋ค ์ฐ์ ์ ์ฉ๋จ).
agent = Agent(
model=Gemini(
model="gemini-live-2.5-flash-native-audio",
speech_config=types.SpeechConfig(
voice_config=types.VoiceConfig(
prebuilt_voice_config=types.PrebuiltVoiceConfig(voice_name="Puck")
),
language_code="en-US",
),
),
instruction="You are a helpful assistant.",
)
# ์ธ์
์์ค ๊ธฐ๋ณธ ์์ฑ (์์ฒด ์์ฑ์ด ์๋ ์์ด์ ํธ๊ฐ ์ฌ์ฉ).
run_config = RunConfig(
response_modalities=["AUDIO"],
speech_config=types.SpeechConfig(
voice_config=types.VoiceConfig(
prebuilt_voice_config=types.PrebuiltVoiceConfig(voice_name="Kore")
),
),
)
voice_name์ ์ฌ์ ์ ์๋ ์์ฑ์ ์ ํํฉ๋๋ค. ๋ผ์ด๋ธ ๋ชจ๋ธ์ 8๊ฐ์ง ์์ฑ(Puck, Charon, Kore, Fenrir, Aoede, Leda, Orus, Zephyr)๊ณผ ํ์ฅ๋ Text-to-Speech ์์ฑ ๋ชฉ๋ก์ ์ง์ํฉ๋๋ค. ํ์ฌ ๋ชฉ๋ก๊ณผ ๋ฐฑ์๋๋ณ ๊ฐ์ฉ์ฑ์ Gemini Live API ์์ฑ ๋ฌธ์๋ฅผ ํ์ธํ์ธ์. ์ง์๋์ง ์๋ ์์ฑ์ ์ฐ๊ฒฐ ์ ์ค๋ฅ๋ฅผ ๋ฐํํฉ๋๋ค.
language_code(์: en-US, ko-KR, ja-JP)๋ ์ธ์ด์ ์ต์์ ์ค์ ํฉ๋๋ค. ๋ผ์ด๋ธ ๋ชจ๋ธ์ ๋ํ ๋ด์ฉ์์ ์ธ์ด๋ฅผ ์์ฒด์ ์ผ๋ก ์ ์ถํ๋ ๊ฒฝ์ฐ๊ฐ ๋ง์ ์ด ์ค์ ์ ๋ฌด์ํ ์๋ ์์ต๋๋ค.
์์ฑ ํ๋ ๊ฐ์ง (VAD)¶
VAD๋ ์ฌ์ฉ์๊ฐ ๋ง์ ์์ํ๊ณ ๋ฉ์ถ๋ ์์ ์ ๊ฐ์งํ์ฌ ๋ชจ๋ธ์ด ๋ผ์ด๋ค๊ธฐ ์ฒ๋ฆฌ๋ฅผ ํฌํจํด ์์ฐ์ค๋ฝ๊ฒ ํด์ ์ฃผ๊ณ ๋ฐ์ ์ ์๋๋ก ํฉ๋๋ค. ๋ชจ๋ ๋ผ์ด๋ธ ๋ชจ๋ธ์์ ๊ธฐ๋ณธ์ ์ผ๋ก ์ผ์ ธ ์์ผ๋ฉฐ, ๋๋ถ๋ถ์ ์ ํ๋ฆฌ์ผ์ด์ ์์๋ ๋ณ๋ ๊ตฌ์ฑ์ด ํ์ํ์ง ์์ต๋๋ค.
Push-to-talk, ํด๋ผ์ด์ธํธ ์ธก VAD ๋๋ ์ฌ์ฉ์๊ฐ ๋ฐ์ธ ์๋ฃ๋ฅผ ์ง์ ์ ํธํ๋ UX ๋ฑ ์ ํ๋ฆฌ์ผ์ด์
์ด ํด ๊ฒฝ๊ณ๋ฅผ ์์ฒด์ ์ผ๋ก ๊ฒฐ์ ํ๋ ๊ฒฝ์ฐ ์๋ VAD๋ฅผ ๋นํ์ฑํํ์ธ์. ๋นํ์ฑํํ ๊ฒฝ์ฐ send_activity_start() / send_activity_end()๋ฅผ ์ฌ์ฉํ์ฌ ์๋์ผ๋ก ActivityStart/ActivityEnd ์ ํธ๋ฅผ ๋ณด๋ด์ผ ํ๋ฉฐ, ํด๋ผ์ด์ธํธ๋ ์์ฒด ํด ์ ํธ๋ฅผ ์๋ฒ์ ํด๋น ํธ์ถ๋ก ๋ณํํด์ผ ํฉ๋๋ค.
from google.genai import types
from google.adk.agents.run_config import RunConfig
run_config = RunConfig(
response_modalities=["AUDIO"],
realtime_input_config=types.RealtimeInputConfig(
automatic_activity_detection=types.AutomaticActivityDetection(disabled=True)
),
)
์์ฒด VAD๋ฅผ ์คํํ๋ ํด๋ผ์ด์ธํธ๋ ํด๋น ์ ํธ๋ฅผ ์๋ฒ๋ก ๋ณด๋ด๊ณ , ์๋ฒ๋ send_activity_start() / send_activity_end()๋ก ์ด๋ฅผ ์ ๋ฌํฉ๋๋ค. ํด๋ผ์ด์ธํธ ์ฐ๊ฒฐ์ ์ฐธ๊ณ ํ์ธ์.
๋ฅ๋์ ๋ฐ ๊ฐ์ ์ ๋ํ (Proactivity and affective dialog)¶
์ผ๋ถ ๋ผ์ด๋ธ ๋ชจ๋ธ์ ๊ธฐ๋ณธ์ ์ผ๋ก ๊บผ์ ธ ์๋ ๋ ๊ฐ์ง ๋ํ ๊ธฐ๋ฅ์ ์ ๊ณตํฉ๋๋ค:
- ๋ฅ๋์ ์ค๋์ค (
proactivity): ๋ชจ๋ธ์ด ์๋ตํ ์์ ์ ์ค์ค๋ก ๊ฒฐ์ ํ๊ณ , ์์ฒญํ์ง ์์๋ ์ ์์ ํ๊ฑฐ๋, ๊ด๋ จ ์๋ ์ ๋ ฅ์ ๋ฌด์ํ ์ ์์ต๋๋ค. - ๊ฐ์ ์ ๋ํ (
enable_affective_dialog): ๋ชจ๋ธ์ด ์ฌ์ฉ์์ ์ด์กฐ์์ ๊ฐ์ ์ ๊ฐ์งํ๊ณ ๊ทธ์ ๋ง๊ฒ ์๋ต์ ์กฐ์ ํ ์ ์์ต๋๋ค.
from google.genai import types
from google.adk.agents.run_config import RunConfig
run_config = RunConfig(
response_modalities=["AUDIO"],
proactivity=types.ProactivityConfig(proactive_audio=True),
enable_affective_dialog=True,
)
๋ ๋์ ๋ชจ๋ ํ๋ฅ ์ ์ด๋ฉฐ ์๋ต์ ์์ธก ๊ฐ๋ฅ์ฑ์ ๋ฎ์ถ๋ฏ๋ก ๊ฒฉ์ ์๋ ์ปจํ ์คํธ, ๋์ ์ ํ๋๊ฐ ์๊ตฌ๋๋ ํ๊ฒฝ ๋ฐ ๋๋ฒ๊น ์ค์๋ ๊บผ๋๋ ๊ฒ์ด ์ข์ต๋๋ค.
๋ ์ค์ ๋ชจ๋ ๋ชจ๋ธ์ ๋ฐ๋ผ ๋ฌ๋ผ์ง๋๋ค. Gemini 2.5 Flash Live๋ ์ด๋ฅผ ์ง์ํ์ง๋ง Gemini 3.1 Flash Live๋ ์ง์ํ์ง ์์ผ๋ฉฐ, ์ด๋ฅผ ์ค์ ํ ์ฑ๋ก ๋๋ ๊ฒ์ด 3.1๋ก ์ ํํ ๋ ๊ฐ์ฅ ํํ ์คํจ ์์ธ์ ๋๋ค. ๋ชจ๋ธ๋ณ ๊ธฐ๋ฅ ์ง์์ ์ฐธ๊ณ ํ์ธ์.