์ฝ˜ํ…์ธ ๋กœ ์ด๋™

ํ”Œ๋Ÿฌ๊ทธ์ธ

ADK์—์„œ ์ง€์›Python v1.7.0TypeScript v0.2.5Go v0.4.0Java v0.3.0Kotlin v0.7.0

ADK(Agent Development Kit)์˜ ํ”Œ๋Ÿฌ๊ทธ์ธ์€ ์ฝœ๋ฐฑ ํ›„ํฌ๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์—์ด์ „ํŠธ ์›Œํฌํ”Œ๋กœ ์ˆ˜๋ช… ์ฃผ๊ธฐ์˜ ๋‹ค์–‘ํ•œ ๋‹จ๊ณ„์—์„œ ์‹คํ–‰๋  ์ˆ˜ ์žˆ๋Š” ์‚ฌ์šฉ์ž ์ง€์ • ์ฝ”๋“œ ๋ชจ๋“ˆ์ž…๋‹ˆ๋‹ค. ํ”Œ๋Ÿฌ๊ทธ์ธ์€ ์—์ด์ „ํŠธ ์›Œํฌํ”Œ๋กœ ์ „๋ฐ˜์— ๊ฑธ์ณ ์ ์šฉ๋˜๋Š” ๊ธฐ๋Šฅ์— ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค. ํ”Œ๋Ÿฌ๊ทธ์ธ์˜ ์ผ๋ฐ˜์ ์ธ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์€ ๋‹ค์Œ๊ณผ ๊ฐ™์Šต๋‹ˆ๋‹ค.

  • ๋กœ๊น… ๋ฐ ์ถ”์ : ๋””๋ฒ„๊น… ๋ฐ ์„ฑ๋Šฅ ๋ถ„์„์„ ์œ„ํ•ด ์—์ด์ „ํŠธ, ๋„๊ตฌ ๋ฐ ์ƒ์„ฑํ˜• AI ๋ชจ๋ธ ํ™œ๋™์— ๋Œ€ํ•œ ์ž์„ธํ•œ ๋กœ๊ทธ๋ฅผ ์ƒ์„ฑํ•ฉ๋‹ˆ๋‹ค.
  • ์ •์ฑ… ์ ์šฉ: ํŠน์ • ๋„๊ตฌ๋ฅผ ์‚ฌ์šฉํ•  ๊ถŒํ•œ์ด ์žˆ๋Š”์ง€ ํ™•์ธํ•˜๊ณ  ๊ถŒํ•œ์ด ์—†๋Š” ๊ฒฝ์šฐ ์‹คํ–‰์„ ๋ฐฉ์ง€ํ•˜๋Š” ํ•จ์ˆ˜์™€ ๊ฐ™์€ ๋ณด์•ˆ ๋ณดํ˜ธ ์žฅ์น˜๋ฅผ ๊ตฌํ˜„ํ•ฉ๋‹ˆ๋‹ค.
  • ๋ชจ๋‹ˆํ„ฐ๋ง ๋ฐ ์ธก์ • ํ•ญ๋ชฉ: Prometheus ๋˜๋Š” Google Cloud Observability (์ด์ „ Stackdriver)์™€ ๊ฐ™์€ ๋ชจ๋‹ˆํ„ฐ๋ง ์‹œ์Šคํ…œ์— ํ† ํฐ ์‚ฌ์šฉ๋Ÿ‰, ์‹คํ–‰ ์‹œ๊ฐ„ ๋ฐ ํ˜ธ์ถœ ํšŸ์ˆ˜์— ๋Œ€ํ•œ ์ธก์ • ํ•ญ๋ชฉ์„ ์ˆ˜์ง‘ํ•˜๊ณ  ๋‚ด๋ณด๋ƒ…๋‹ˆ๋‹ค.
  • ์‘๋‹ต ์บ์‹ฑ: ์š”์ฒญ์ด ์ด์ „์— ์ด๋ฃจ์–ด์กŒ๋Š”์ง€ ํ™•์ธํ•˜์—ฌ ๋น„์šฉ์ด ๋งŽ์ด ๋“ค๊ฑฐ๋‚˜ ์‹œ๊ฐ„์ด ์˜ค๋ž˜ ๊ฑธ๋ฆฌ๋Š” AI ๋ชจ๋ธ ๋˜๋Š” ๋„๊ตฌ ํ˜ธ์ถœ์„ ๊ฑด๋„ˆ๋›ฐ๊ณ  ์บ์‹œ๋œ ์‘๋‹ต์„ ๋ฐ˜ํ™˜ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
  • ์š”์ฒญ ๋˜๋Š” ์‘๋‹ต ์ˆ˜์ •: AI ๋ชจ๋ธ ํ”„๋กฌํ”„ํŠธ์— ์ •๋ณด๋ฅผ ๋™์ ์œผ๋กœ ์ถ”๊ฐ€ํ•˜๊ฑฐ๋‚˜ ๋„๊ตฌ ์ถœ๋ ฅ ์‘๋‹ต์„ ํ‘œ์ค€ํ™”ํ•ฉ๋‹ˆ๋‹ค.

ํŒ: ์•ˆ์ „ ๊ธฐ๋Šฅ์—๋Š” ํ”Œ๋Ÿฌ๊ทธ์ธ ์‚ฌ์šฉ

๋ณด์•ˆ ๊ฐ€๋“œ๋ ˆ์ผ๊ณผ ์ •์ฑ…์„ ๊ตฌํ˜„ํ•  ๋•Œ๋Š” ์ฝœ๋ฐฑ๋ณด๋‹ค ๋ชจ๋“ˆ์„ฑ๊ณผ ์œ ์—ฐ์„ฑ์ด ๋†’์€ ADK ํ”Œ๋Ÿฌ๊ทธ์ธ์„ ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ์ด ์ข‹์Šต๋‹ˆ๋‹ค. ์ž์„ธํ•œ ๋‚ด์šฉ์€ ๋ณด์•ˆ ๊ฐ€๋“œ๋ ˆ์ผ์„ ์œ„ํ•œ ์ฝœ๋ฐฑ ๋ฐ ํ”Œ๋Ÿฌ๊ทธ์ธ์„ ์ฐธ๊ณ ํ•˜์„ธ์š”.

ํŒ: ADK ํ†ตํ•ฉ

ADK์šฉ ์‚ฌ์ „ ๊ตฌ์ถ• ํ”Œ๋Ÿฌ๊ทธ์ธ ๋ฐ ๊ธฐํƒ€ ํ†ตํ•ฉ ๋ชฉ๋ก์€ ๋„๊ตฌ ๋ฐ ํ†ตํ•ฉ์„ ์ฐธ๊ณ ํ•˜์„ธ์š”.

ํ”Œ๋Ÿฌ๊ทธ์ธ์€ ์–ด๋–ป๊ฒŒ ์ž‘๋™ํ•ฉ๋‹ˆ๊นŒ?

ADK ํ”Œ๋Ÿฌ๊ทธ์ธ์€ BasePlugin ํด๋ž˜์Šค๋ฅผ ํ™•์žฅํ•˜๋ฉฐ ์—์ด์ „ํŠธ ์ˆ˜๋ช… ์ฃผ๊ธฐ์—์„œ ํ”Œ๋Ÿฌ๊ทธ์ธ์ด ์‹คํ–‰๋˜์–ด์•ผ ํ•˜๋Š” ์œ„์น˜๋ฅผ ๋‚˜ํƒ€๋‚ด๋Š” ํ•˜๋‚˜ ์ด์ƒ์˜ callback ๋ฉ”์„œ๋“œ๋ฅผ ํฌํ•จํ•ฉ๋‹ˆ๋‹ค. ์—์ด์ „ํŠธ์˜ Runner ํด๋ž˜์Šค(Python์˜ ๊ฒฝ์šฐ App ๊ฐ์ฒด)์— ํ”Œ๋Ÿฌ๊ทธ์ธ์„ ๋“ฑ๋กํ•˜์—ฌ ์—์ด์ „ํŠธ์— ํ†ตํ•ฉํ•ฉ๋‹ˆ๋‹ค. ์—์ด์ „ํŠธ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์—์„œ ํ”Œ๋Ÿฌ๊ทธ์ธ์„ ํŠธ๋ฆฌ๊ฑฐํ•  ์ˆ˜ ์žˆ๋Š” ๋ฐฉ๋ฒ•๊ณผ ์œ„์น˜์— ๋Œ€ํ•œ ์ž์„ธํ•œ ๋‚ด์šฉ์€ ํ”Œ๋Ÿฌ๊ทธ์ธ ์ฝœ๋ฐฑ ํ›„ํฌ๋ฅผ ์ฐธ๊ณ ํ•˜์„ธ์š”.

ํ”Œ๋Ÿฌ๊ทธ์ธ ๊ธฐ๋Šฅ์€ ADK์˜ ํ™•์žฅ ๊ฐ€๋Šฅํ•œ ์•„ํ‚คํ…์ฒ˜์˜ ํ•ต์‹ฌ ์„ค๊ณ„ ์š”์†Œ์ธ ์ฝœ๋ฐฑ์„ ๊ธฐ๋ฐ˜์œผ๋กœ ํ•ฉ๋‹ˆ๋‹ค. ์ผ๋ฐ˜์ ์ธ ์—์ด์ „ํŠธ ์ฝœ๋ฐฑ์€ ํŠน์ • ์ž‘์—…์„ ์œ„ํ•ด ๋‹จ์ผ ์—์ด์ „ํŠธ, ๋‹จ์ผ ๋„๊ตฌ์— ๊ตฌ์„ฑ๋˜๋Š” ๋ฐ˜๋ฉด, ํ”Œ๋Ÿฌ๊ทธ์ธ์€ Runner(Python์˜ ๊ฒฝ์šฐ App)์— ํ•œ ๋ฒˆ ๋“ฑ๋ก๋˜๋ฉฐ ํ•ด๋‹น ์ฝœ๋ฐฑ์€ ํ•ด๋‹น ๋Ÿฌ๋„ˆ๊ฐ€ ๊ด€๋ฆฌํ•˜๋Š” ๋ชจ๋“  ์—์ด์ „ํŠธ, ๋„๊ตฌ ๋ฐ LLM ํ˜ธ์ถœ์— ์ „์—ญ์ ์œผ๋กœ ์ ์šฉ๋ฉ๋‹ˆ๋‹ค. ํ”Œ๋Ÿฌ๊ทธ์ธ์„ ์‚ฌ์šฉํ•˜๋ฉด ๊ด€๋ จ ์ฝœ๋ฐฑ ํ•จ์ˆ˜๋ฅผ ํ•จ๊ป˜ ๋ฌถ์–ด ์›Œํฌํ”Œ๋กœ ์ „์ฒด์—์„œ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๋”ฐ๋ผ์„œ ํ”Œ๋Ÿฌ๊ทธ์ธ์€ ์ „์ฒด ์—์ด์ „ํŠธ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์— ๊ฑธ์ณ ์žˆ๋Š” ๊ธฐ๋Šฅ์„ ๊ตฌํ˜„ํ•˜๋Š” ๋ฐ ์ด์ƒ์ ์ธ ์†”๋ฃจ์…˜์ž…๋‹ˆ๋‹ค.

์‚ฌ์ „ ๋นŒ๋“œ๋œ ํ”Œ๋Ÿฌ๊ทธ์ธ

ADK์—๋Š” ์—์ด์ „ํŠธ ์›Œํฌํ”Œ๋กœ์— ์ฆ‰์‹œ ์ถ”๊ฐ€ํ•  ์ˆ˜ ์žˆ๋Š” ์—ฌ๋Ÿฌ ํ”Œ๋Ÿฌ๊ทธ์ธ์ด ํฌํ•จ๋˜์–ด ์žˆ์Šต๋‹ˆ๋‹ค.

  • ๋ฐ˜์˜ ๋ฐ ์žฌ์‹œ๋„ ๋„๊ตฌ: ๋„๊ตฌ ์‹คํŒจ๋ฅผ ์ถ”์ ํ•˜๊ณ  ๋„๊ตฌ ์š”์ฒญ์„ ์ง€๋Šฅ์ ์œผ๋กœ ์žฌ์‹œ๋„ํ•ฉ๋‹ˆ๋‹ค.
  • BigQuery ๋ถ„์„: BigQuery๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์—์ด์ „ํŠธ ๋กœ๊น… ๋ฐ ๋ถ„์„์„ ํ™œ์„ฑํ™”ํ•ฉ๋‹ˆ๋‹ค.
  • Model Armor: Google Cloud Model Armor ํ…œํ”Œ๋ฆฟ์„ ๊ธฐ๋ฐ˜์œผ๋กœ ์‚ฌ์šฉ์ž ์ž…๋ ฅ ๋ฐ ๋ชจ๋ธ ์ถœ๋ ฅ์„ ํ•„ํ„ฐ๋งํ•ฉ๋‹ˆ๋‹ค.
  • ์ปจํ…์ŠคํŠธ ํ•„ํ„ฐ: ์ƒ์„ฑํ˜• AI ์ปจํ…์ŠคํŠธ๋ฅผ ํ•„ํ„ฐ๋งํ•˜์—ฌ ํฌ๊ธฐ๋ฅผ ์ค„์ž…๋‹ˆ๋‹ค.
  • ์ „์—ญ ์ง€์นจ: ์•ฑ ์ˆ˜์ค€์—์„œ ์ „์—ญ ์ง€์นจ ๊ธฐ๋Šฅ์„ ์ œ๊ณตํ•˜๋Š” ํ”Œ๋Ÿฌ๊ทธ์ธ์ž…๋‹ˆ๋‹ค.
  • ์•„ํ‹ฐํŒฉํŠธ๋กœ ํŒŒ์ผ ์ €์žฅ: ์‚ฌ์šฉ์ž ๋ฉ”์‹œ์ง€์— ํฌํ•จ๋œ ํŒŒ์ผ์„ ์•„ํ‹ฐํŒฉํŠธ๋กœ ์ €์žฅํ•ฉ๋‹ˆ๋‹ค.
  • ์ž๋™ ํŠธ๋ ˆ์ด์‹ฑ(Auto Tracing): ์—์ด์ „ํŠธ ์ž์ฒด ํŒจํ‚ค์ง€์˜ ํ•จ์ˆ˜๋ฅผ OpenTelemetry ์ŠคํŒฌ์œผ๋กœ ๋ž˜ํ•‘ํ•ฉ๋‹ˆ๋‹ค.
  • ๋ฉ€ํ‹ฐ๋ชจ๋‹ฌ ๋„๊ตฌ ๊ฒฐ๊ณผ(Multimodal Tool Results): ํ•จ์ˆ˜ ๋„๊ตฌ๊ฐ€ ์ฝ˜ํ…์ธ  ํŒŒํŠธ๋ฅผ ๋ชจ๋ธ์— ์ง์ ‘ ๋ฐ˜ํ™˜ํ•  ์ˆ˜ ์žˆ๋„๋ก ํ•ฉ๋‹ˆ๋‹ค.
  • ๋กœ๊น…: ๊ฐ ์—์ด์ „ํŠธ ์›Œํฌํ”Œ๋กœ ์ฝœ๋ฐฑ ์ง€์ ์—์„œ ์ค‘์š”ํ•œ ์ •๋ณด๋ฅผ ๋กœ๊น…ํ•ฉ๋‹ˆ๋‹ค.
  • ๋””๋ฒ„๊ทธ ๋กœ๊น…(Debug Logging): ๊ฐ ํ˜ธ์ถœ์— ๋Œ€ํ•œ ์ „์ฒด ๋””๋ฒ„๊ทธ ์ •๋ณด๋ฅผ YAML ํŒŒ์ผ๋กœ ์บก์ฒ˜ํ•ฉ๋‹ˆ๋‹ค.
  • ๋ชจ๋ธ ๋ฐ˜์˜ ๋ฐ ์žฌ์‹œ๋„(Reflect and Retry Model): ์ž˜๋ชป๋œ ํ˜•์‹์˜ ํ•จ์ˆ˜ ํ˜ธ์ถœ๊ณผ ๊ฐ™์ด ์‘๋‹ต์ด ์˜ค๋ฅ˜๋กœ ๋๋‚  ๋•Œ ๋ชจ๋ธ์— ๋‹ค์‹œ ์‹œ๋„ํ•˜๋„๋ก ์š”์ฒญํ•ฉ๋‹ˆ๋‹ค.
  • ๋„๊ตฌ ํ˜ธ์ถœ ๋ฌด๊ฒฐ์„ฑ(Tool Call Integrity): ์„ธ์…˜์— ์ €์žฅ๋œ ๊ฐ ํ•จ์ˆ˜ ํ˜ธ์ถœ์— ๋น„๋ฐ€ ํ‚ค๋กœ ์„œ๋ช…ํ•˜๊ณ  ํ˜ธ์ถœ์˜ ์„œ๋ช…์ด ๊ฒ€์ฆ๋œ ๊ฒฝ์šฐ์—๋งŒ ๋„๊ตฌ๋ฅผ ์‹คํ–‰ํ•ฉ๋‹ˆ๋‹ค. ์›Œํฌํ”Œ๋กœ ๋…ธ๋“œ๋กœ ์‹คํ–‰๋˜๋Š” ๋„๊ตฌ๋Š” ํ™•์ธํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.

์—์ด์ „ํŠธ๋ฅผ ์œ„ํ•œ ์ถ”๊ฐ€ ๊ธฐ๋ณธ ํ”Œ๋Ÿฌ๊ทธ์ธ ๋ฐ ์„œ๋“œํŒŒํ‹ฐ ํ”Œ๋Ÿฌ๊ทธ์ธ์€ ADK ํ†ตํ•ฉ ํŽ˜์ด์ง€๋ฅผ ํ™•์ธํ•˜์„ธ์š”.

ํ”Œ๋Ÿฌ๊ทธ์ธ ์ •์˜ ๋ฐ ๋“ฑ๋ก

์ด ์„น์…˜์—์„œ๋Š” ํ”Œ๋Ÿฌ๊ทธ์ธ ํด๋ž˜์Šค๋ฅผ ์ •์˜ํ•˜๊ณ  ์—์ด์ „ํŠธ ์›Œํฌํ”Œ๋กœ์˜ ์ผ๋ถ€๋กœ ๋“ฑ๋กํ•˜๋Š” ๋ฐฉ๋ฒ•์„ ์„ค๋ช…ํ•ฉ๋‹ˆ๋‹ค. ์™„์ „ํ•œ ์ฝ”๋“œ ์˜ˆ์‹œ๋Š” ์ €์žฅ์†Œ์˜ ํ”Œ๋Ÿฌ๊ทธ์ธ ๊ธฐ๋ณธ์„ ์ฐธ๊ณ ํ•˜์„ธ์š”.

ํ”Œ๋Ÿฌ๊ทธ์ธ ํด๋ž˜์Šค ์ƒ์„ฑ

BasePlugin ํด๋ž˜์Šค๋ฅผ ํ™•์žฅํ•˜๊ณ  ๋‹ค์Œ ์ฝ”๋“œ ์˜ˆ์‹œ์— ํ‘œ์‹œ๋œ ๋Œ€๋กœ ํ•˜๋‚˜ ์ด์ƒ์˜ callback ๋ฉ”์„œ๋“œ๋ฅผ ์ถ”๊ฐ€ํ•˜์—ฌ ์‹œ์ž‘ํ•ฉ๋‹ˆ๋‹ค.

count_plugin.py
from google.adk.agents.base_agent import BaseAgent
from google.adk.agents.callback_context import CallbackContext
from google.adk.models.llm_request import LlmRequest
from google.adk.plugins.base_plugin import BasePlugin

class CountInvocationPlugin(BasePlugin):
  """์—์ด์ „ํŠธ ๋ฐ ๋„๊ตฌ ํ˜ธ์ถœ ํšŸ์ˆ˜๋ฅผ ๊ณ„์‚ฐํ•˜๋Š” ์‚ฌ์šฉ์ž ์ง€์ • ํ”Œ๋Ÿฌ๊ทธ์ธ์ž…๋‹ˆ๋‹ค."""

  def __init__(self) -> None:
    """์นด์šดํ„ฐ๋กœ ํ”Œ๋Ÿฌ๊ทธ์ธ์„ ์ดˆ๊ธฐํ™”ํ•ฉ๋‹ˆ๋‹ค."""
    super().__init__(name="count_invocation")
    self.agent_count: int = 0
    self.tool_count: int = 0
    self.llm_request_count: int = 0

  async def before_agent_callback(
      self, *, agent: BaseAgent, callback_context: CallbackContext
  ) -> None:
    """์—์ด์ „ํŠธ ์‹คํ–‰ ํšŸ์ˆ˜๋ฅผ ๊ณ„์‚ฐํ•ฉ๋‹ˆ๋‹ค."""
    self.agent_count += 1
    print(f"[Plugin] Agent run count: {self.agent_count}")

  async def before_model_callback(
      self, *, callback_context: CallbackContext, llm_request: LlmRequest
  ) -> None:
    """LLM ์š”์ฒญ ํšŸ์ˆ˜๋ฅผ ๊ณ„์‚ฐํ•ฉ๋‹ˆ๋‹ค."""
    self.llm_request_count += 1
    print(f"[Plugin] LLM request count: {self.llm_request_count}")
count_plugin.ts
import { BaseAgent, BasePlugin, Context } from "@google/adk";
import type { LlmRequest, LlmResponse } from "@google/adk";
import type { Content } from "@google/genai";

export class CountInvocationPlugin extends BasePlugin {
    public agentCount = 0;
    public toolCount = 0;
    public llmRequestCount = 0;

    constructor() {
        super("count_invocation");
    }

    async beforeAgentCallback(
        agent: BaseAgent,
        context: Context
    ): Promise<Content | undefined> {
        this.agentCount++;
        console.log(`[Plugin] Agent run count: ${this.agentCount}`);
        return undefined;
    }

    async beforeModelCallback(
        context: Context,
        llmRequest: LlmRequest
    ): Promise<LlmResponse | undefined> {
        this.llmRequestCount++;
        console.log(`[Plugin] LLM request count: ${this.llmRequestCount}`);
        return undefined;
    }
}
CountInvocationPlugin.java
import com.google.adk.agents.BaseAgent;
import com.google.adk.agents.CallbackContext;
import com.google.adk.models.LlmRequest;
import com.google.adk.models.LlmResponse;
import com.google.adk.plugins.BasePlugin;
import com.google.genai.types.Content;
import io.reactivex.rxjava3.core.Maybe;

public class CountInvocationPlugin extends BasePlugin {
  public int agentCount = 0;
  public int toolCount = 0;
  public int llmRequestCount = 0;

  public CountInvocationPlugin() {
    super("count_invocation");
  }

  @Override
  public Maybe<Content> beforeAgentCallback(BaseAgent agent, CallbackContext callbackContext) {
    agentCount++;
    System.out.println("[Plugin] Agent run count: " + agentCount);
    return Maybe.empty();
  }

  @Override
  public Maybe<LlmResponse> beforeModelCallback(
      CallbackContext callbackContext, LlmRequest.Builder llmRequest) {
    llmRequestCount++;
    System.out.println("[Plugin] LLM request count: " + llmRequestCount);
    return Maybe.empty();
  }
}
/** A custom plugin that counts agent runs and LLM requests. */
class CountInvocationPlugin : Plugin {
    override val name = "count_invocation"

    var agentCount = 0
        private set

    var llmRequestCount = 0
        private set

    // Plugin declares one abstract member, `name`; every callback has a default,
    // so a plugin only overrides the ones it cares about.
    override suspend fun beforeAgent(
        context: CallbackContext,
    ): CallbackChoice<EventActions, Content> {
        agentCount++
        println("[Plugin] Agent run count: $agentCount")
        return CallbackChoice.Continue(EventActions())
    }

    override suspend fun beforeModel(
        context: CallbackContext,
        request: LlmRequest,
    ): CallbackChoice<LlmRequest, LlmResponse> {
        llmRequestCount++
        println("[Plugin] LLM request count: $llmRequestCount")
        return CallbackChoice.Continue(request)
    }
}

์ด ์˜ˆ์‹œ ์ฝ”๋“œ๋Š” ์—์ด์ „ํŠธ ์ˆ˜๋ช… ์ฃผ๊ธฐ ๋™์•ˆ ์ด๋Ÿฌํ•œ ์ž‘์—…์˜ ์‹คํ–‰ ํšŸ์ˆ˜๋ฅผ ๊ณ„์‚ฐํ•˜๊ธฐ ์œ„ํ•ด before_agent_callback ๋ฐ before_model_callback์— ๋Œ€ํ•œ ์ฝœ๋ฐฑ์„ ๊ตฌํ˜„ํ•ฉ๋‹ˆ๋‹ค.

ํ”Œ๋Ÿฌ๊ทธ์ธ ํด๋ž˜์Šค ๋“ฑ๋ก

plugins ๋งค๊ฐœ๋ณ€์ˆ˜๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์—์ด์ „ํŠธ ์ดˆ๊ธฐํ™” ์‹œ Runner ํด๋ž˜์Šค(Python์˜ ๊ฒฝ์šฐ App ๊ฐ์ฒด)์˜ ์ผ๋ถ€๋กœ ํ”Œ๋Ÿฌ๊ทธ์ธ ํด๋ž˜์Šค๋ฅผ ๋“ฑ๋กํ•˜์—ฌ ํ†ตํ•ฉํ•ฉ๋‹ˆ๋‹ค. ์ด ๋งค๊ฐœ๋ณ€์ˆ˜๋กœ ์—ฌ๋Ÿฌ ํ”Œ๋Ÿฌ๊ทธ์ธ์„ ์ง€์ •ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๋‹ค์Œ ์ฝ”๋“œ ์˜ˆ์‹œ๋Š” ์ด์ „ ์„น์…˜์—์„œ ์ •์˜๋œ CountInvocationPlugin ํ”Œ๋Ÿฌ๊ทธ์ธ์„ ๊ฐ„๋‹จํ•œ ADK ์—์ด์ „ํŠธ์— ๋“ฑ๋กํ•˜๋Š” ๋ฐฉ๋ฒ•์„ ๋ณด์—ฌ์ค๋‹ˆ๋‹ค.

Python: Runner(plugins=...) ๋Œ€์‹  App(plugins=...) ์‚ฌ์šฉ

Python์—์„œ Runner ๋ฐ InMemoryRunner์˜ plugins ๋งค๊ฐœ๋ณ€์ˆ˜๋Š” ์ง€์› ์ค‘๋‹จ๋˜์—ˆ์œผ๋ฉฐ DeprecationWarning์„ ๋ฐœ์ƒ์‹œํ‚ต๋‹ˆ๋‹ค. ๋Œ€์‹  App์— plugins๋ฅผ ์„ค์ •ํ•˜๊ณ  ํ•ด๋‹น App์„ InMemoryRunner(app=app)์œผ๋กœ ๋Ÿฌ๋„ˆ์— ์ „๋‹ฌํ•˜์„ธ์š”. plugins์™€ app์„ ๋ชจ๋‘ ์ „๋‹ฌํ•˜๋ฉด ValueError๊ฐ€ ๋ฐœ์ƒํ•ฉ๋‹ˆ๋‹ค.

from google.adk.runners import InMemoryRunner
from google.adk import Agent
from google.adk.apps import App
from google.adk.tools.tool_context import ToolContext
from google.genai import types
import asyncio

# ํ”Œ๋Ÿฌ๊ทธ์ธ ๊ฐ€์ ธ์˜ค๊ธฐ.
from .count_plugin import CountInvocationPlugin

async def hello_world(tool_context: ToolContext, query: str):
  print(f'Hello world: query is [{query}]')

root_agent = Agent(
    model='gemini-flash-latest',
    name='hello_world',
    description='์‚ฌ์šฉ์ž ์ฟผ๋ฆฌ๋กœ ํ—ฌ๋กœ์›”๋“œ๋ฅผ ์ถœ๋ ฅํ•ฉ๋‹ˆ๋‹ค.',
    instruction="""ํ—ฌ๋กœ์›”๋“œ ๋„๊ตฌ๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ํ—ฌ๋กœ์›”๋“œ์™€ ์‚ฌ์šฉ์ž ์ฟผ๋ฆฌ๋ฅผ ์ถœ๋ ฅํ•˜์‹ญ์‹œ์˜ค.
    """,
    tools=[hello_world],
)

app = App(
    name='test_app_with_plugin',
    root_agent=root_agent,

    # ์—ฌ๊ธฐ์— ํ”Œ๋Ÿฌ๊ทธ์ธ์„ ์ถ”๊ฐ€ํ•ฉ๋‹ˆ๋‹ค. ์—ฌ๋Ÿฌ ํ”Œ๋Ÿฌ๊ทธ์ธ์„ ์ถ”๊ฐ€ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
    plugins=[CountInvocationPlugin()],
)

async def main():
  """์—์ด์ „ํŠธ์˜ ๋ฉ”์ธ ์ง„์ž…์ ์ž…๋‹ˆ๋‹ค."""
  prompt = 'hello world'
  runner = InMemoryRunner(app=app)

  # ๋‚˜๋จธ์ง€๋Š” ์ผ๋ฐ˜ ADK ๋Ÿฌ๋„ˆ๋ฅผ ์‹œ์ž‘ํ•˜๋Š” ๊ฒƒ๊ณผ ๋™์ผํ•ฉ๋‹ˆ๋‹ค.
  session = await runner.session_service.create_session(
      user_id='user',
      app_name='test_app_with_plugin',
  )

  async for event in runner.run_async(
      user_id='user',
      session_id=session.id,
      new_message=types.Content(
        role='user', parts=[types.Part.from_text(text=prompt)]
      )
  ):
    print(f'** {event.author}๋กœ๋ถ€ํ„ฐ ์ด๋ฒคํŠธ๋ฅผ ๋ฐ›์•˜์Šต๋‹ˆ๋‹ค.')

if __name__ == "__main__":
  asyncio.run(main())
import { InMemoryRunner, LlmAgent, FunctionTool } from "@google/adk";
import { z } from "zod";
import { CountInvocationPlugin } from "./count_plugin.ts";

const HelloWorldInput = z.object({
    query: z.string().describe("The query string to print."),
});

async function helloWorld({ query }: z.infer<typeof HelloWorldInput>): Promise<{ result: string }> {
    const output = `Hello world: query is [${query}]`;
    console.log(output);
    return { result: output };
}

const helloWorldTool = new FunctionTool({
    name: "hello_world",
    description: "Prints hello world with user query.",
    parameters: HelloWorldInput,
    execute: helloWorld,
});

const rootAgent = new LlmAgent({
    model: "gemini-flash-latest",
    name: "hello_world",
    description: "Prints hello world with user query.",
    instruction: `Use hello_world tool to print hello world and user query.`,
    tools: [helloWorldTool],
});

async function main(): Promise<void> {
    const prompt = "hello world";
    const runner = new InMemoryRunner({
        agent: rootAgent,
        appName: "test_app_with_plugin",
        plugins: [new CountInvocationPlugin()],
    });

    const session = await runner.sessionService.createSession({
        appName: "test_app_with_plugin",
        userId: "user",
    });

    for await (const event of runner.runAsync({
        userId: "user",
        sessionId: session.id,
        newMessage: { role: "user", parts: [{ text: prompt }] },
    })) {
        console.log(`** Got event from ${event.author}`);
    }
}

main();
import com.google.adk.agents.LlmAgent;
import com.google.adk.runner.InMemoryRunner;
import com.google.adk.sessions.Session;
import com.google.adk.tools.Annotations.Schema;
import com.google.adk.tools.FunctionTool;
import com.google.genai.types.Content;
import com.google.genai.types.Part;
import java.util.Collections;
import java.util.List;
import java.util.Map;

public class Main {
  public static class HelloTool {
    @Schema(name = "hello_world", description = "Prints hello world with user query.")
    public static Map<String, Object> helloWorld(
        @Schema(name = "query", description = "The query string to print.") String query) {
      String output = "Hello world: query is [" + query + "]";
      System.out.println(output);
      return Map.of("result", output);
    }
  }

  public static void main(String[] args) {
    LlmAgent rootAgent = LlmAgent.builder()
        .model("gemini-flash-latest")
        .name("hello_world")
        .description("Prints hello world with user query.")
        .instruction("Use hello_world tool to print hello world and user query.")
        .tools(FunctionTool.create(HelloTool.class, "helloWorld"))
        .build();

    InMemoryRunner runner = new InMemoryRunner(
        rootAgent,
        "test_app_with_plugin",
        Collections.singletonList(new CountInvocationPlugin())
    );

    Session session = runner.sessionService().createSession(
        "test_app_with_plugin",
        "user"
    ).blockingGet();

    Content newContent = Content.builder()
        .role("user")
        .parts(List.of(Part.builder().text("hello world").build()))
        .build();

    runner.runAsync("user", session.id(), newContent).blockingForEach(event -> {
      if (event.author() != null) {
        System.out.println("** Got event from " + event.author());
      }
    });
  }
}
val rootAgent =
    LlmAgent(
        name = "hello_world",
        model = Gemini(name = "gemini-flash-latest"),
        instruction = Instruction("Greet the user."),
    )

// Since adk-kotlin 0.7.0 the agent-based InMemoryRunner constructor accepts
// plugins directly; before that they had to be set on an App.
val runner =
    InMemoryRunner(
        agent = rootAgent,
        appName = "test_app_with_plugin",
        plugins = listOf(CountInvocationPlugin()),
    )

Python์—์„œ๋Š” --extra_plugins ์˜ต์…˜์„ ์‚ฌ์šฉํ•˜์—ฌ adk web ๋˜๋Š” adk api_server์— ๊ฐ€์ ธ์˜ค๊ธฐ ๊ฒฝ๋กœ(import path)๋ฅผ ์ „๋‹ฌํ•จ์œผ๋กœ์จ ์—์ด์ „ํŠธ ์ฝ”๋“œ๋ฅผ ๋ณ€๊ฒฝํ•˜์ง€ ์•Š๊ณ ๋„ ํ”Œ๋Ÿฌ๊ทธ์ธ์„ ๋กœ๋“œํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ADK๋Š” App์—์„œ ๋“ฑ๋กํ•œ ํ”Œ๋Ÿฌ๊ทธ์ธ ๋’ค์— ์ด๋ฅผ ์ถ”๊ฐ€ํ•ฉ๋‹ˆ๋‹ค. ์ƒ์„ฑ์ž๊ฐ€ name ์ธ์ˆ˜๋ฅผ ํ—ˆ์šฉํ•˜๋Š” ๊ฒฝ์šฐ์—๋งŒ ํด๋ž˜์Šค๋ฅผ ์ „๋‹ฌํ•˜๊ณ , ๊ทธ๋ ‡์ง€ ์•Š์œผ๋ฉด ๋ชจ๋“ˆ ์ˆ˜์ค€์—์„œ ์ •์˜๋œ ํ”Œ๋Ÿฌ๊ทธ์ธ ์ธ์Šคํ„ด์Šค๋ฅผ ์ „๋‹ฌํ•˜์„ธ์š”. ๋‘˜ ์ด์ƒ์˜ ํ”Œ๋Ÿฌ๊ทธ์ธ์„ ๋กœ๋“œํ•˜๋ ค๋ฉด ์˜ต์…˜์„ ๋ฐ˜๋ณตํ•˜์—ฌ ์‚ฌ์šฉํ•˜์„ธ์š”.

adk web --extra_plugins=google.adk.plugins.LoggingPlugin /path/to/agents

ํ”Œ๋Ÿฌ๊ทธ์ธ์œผ๋กœ ์—์ด์ „ํŠธ ์‹คํ–‰

ํ‰์†Œ์ฒ˜๋Ÿผ ํ”Œ๋Ÿฌ๊ทธ์ธ์„ ์‹คํ–‰ํ•ฉ๋‹ˆ๋‹ค. ๋‹ค์Œ์€ ๋ช…๋ น์ค„์„ ์‹คํ–‰ํ•˜๋Š” ๋ฐฉ๋ฒ•์„ ๋ณด์—ฌ์ค๋‹ˆ๋‹ค.

python3 -m path.to.main

Java

./mvnw -q clean compile exec:java -Dexec.mainClass="com.example.Main"

ํ”Œ๋Ÿฌ๊ทธ์ธ์€ ADK ์›น ์ธํ„ฐํŽ˜์ด์Šค์—์„œ ์ง€์›๋˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ADK ์›Œํฌํ”Œ๋กœ์—์„œ ํ”Œ๋Ÿฌ๊ทธ์ธ์„ ์‚ฌ์šฉํ•˜๋Š” ๊ฒฝ์šฐ ์›น ์ธํ„ฐํŽ˜์ด์Šค ์—†์ด ์›Œํฌํ”Œ๋กœ๋ฅผ ์‹คํ–‰ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

์ด์ „์— ์„ค๋ช…ํ•œ ์—์ด์ „ํŠธ์˜ ์ถœ๋ ฅ์€ ๋‹ค์Œ๊ณผ ๋น„์Šทํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

[Plugin] Agent run count: 1
[Plugin] LLM request count: 1
** Got event from hello_world
Hello world: query is [hello world]
** Got event from hello_world
[Plugin] LLM request count: 2
** Got event from hello_world

ADK ์—์ด์ „ํŠธ ์‹คํ–‰์— ๋Œ€ํ•œ ์ž์„ธํ•œ ๋‚ด์šฉ์€ ์—์ด์ „ํŠธ ๋Ÿฐํƒ€์ž„ ๊ฐ€์ด๋“œ๋ฅผ ์ฐธ๊ณ ํ•˜์„ธ์š”.

ํ”Œ๋Ÿฌ๊ทธ์ธ์œผ๋กœ ์›Œํฌํ”Œ๋กœ ๋นŒ๋“œ

ํ”Œ๋Ÿฌ๊ทธ์ธ ์ฝœ๋ฐฑ ํ›„ํฌ๋Š” ์—์ด์ „ํŠธ์˜ ์‹คํ–‰ ์ˆ˜๋ช… ์ฃผ๊ธฐ๋ฅผ ๊ฐ€๋กœ์ฑ„๊ณ , ์ˆ˜์ •ํ•˜๊ณ , ์‹ฌ์ง€์–ด ์ œ์–ดํ•˜๋Š” โ€‹โ€‹๋…ผ๋ฆฌ๋ฅผ ๊ตฌํ˜„ํ•˜๊ธฐ ์œ„ํ•œ ๋ฉ”์ปค๋‹ˆ์ฆ˜์ž…๋‹ˆ๋‹ค. ๊ฐ ํ›„ํฌ๋Š” ํ”Œ๋Ÿฌ๊ทธ์ธ ํด๋ž˜์Šค์—์„œ ๊ตฌํ˜„ํ•  ์ˆ˜ ์žˆ๋Š” ํŠน์ • ๋ฉ”์„œ๋“œ๋กœ, ์ค‘์š”ํ•œ ์ˆœ๊ฐ„์— ์ฝ”๋“œ๋ฅผ ์‹คํ–‰ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ํ›„ํฌ์˜ ๋ฐ˜ํ™˜ ๊ฐ’์— ๋”ฐ๋ผ ๋‘ ๊ฐ€์ง€ ์ž‘๋™ ๋ชจ๋“œ ์ค‘์—์„œ ์„ ํƒํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

  • ๊ด€์ฐฐ: ๋ฐ˜ํ™˜ ๊ฐ’์ด ์—†๋Š” ํ›„ํฌ(None)๋ฅผ ๊ตฌํ˜„ํ•ฉ๋‹ˆ๋‹ค. ์ด ์ ‘๊ทผ ๋ฐฉ์‹์€ ๋กœ๊น… ๋˜๋Š” ์ธก์ • ํ•ญ๋ชฉ ์ˆ˜์ง‘๊ณผ ๊ฐ™์€ ์ž‘์—…์— ์‚ฌ์šฉ๋˜๋ฉฐ, ์—์ด์ „ํŠธ ์›Œํฌํ”Œ๋กœ๊ฐ€ ์ค‘๋‹จ ์—†์ด ๋‹ค์Œ ๋‹จ๊ณ„๋กœ ์ง„ํ–‰๋˜๋„๋ก ํ•ฉ๋‹ˆ๋‹ค. ์˜ˆ๋ฅผ ๋“ค์–ด, ํ”Œ๋Ÿฌ๊ทธ์ธ์˜ after_tool_callback์„ ์‚ฌ์šฉํ•˜์—ฌ ๋””๋ฒ„๊น…์„ ์œ„ํ•ด ๋ชจ๋“  ๋„๊ตฌ์˜ ๊ฒฐ๊ณผ๋ฅผ ๋กœ๊น…ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
  • ๊ฐœ์ž…: ํ›„ํฌ๋ฅผ ๊ตฌํ˜„ํ•˜๊ณ  ๊ฐ’์„ ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค. ์ด ์ ‘๊ทผ ๋ฐฉ์‹์€ ์›Œํฌํ”Œ๋กœ๋ฅผ ๋‹จ๋ฝ์‹œํ‚ต๋‹ˆ๋‹ค. Runner๋Š” ์ฒ˜๋ฆฌ๋ฅผ ์ค‘๋‹จํ•˜๊ณ , ํ›„์† ํ”Œ๋Ÿฌ๊ทธ์ธ๊ณผ ๋ชจ๋ธ ํ˜ธ์ถœ๊ณผ ๊ฐ™์€ ์›๋ž˜ ์˜๋„๋œ ์ž‘์—…์„ ๊ฑด๋„ˆ๋›ฐ๊ณ , ํ”Œ๋Ÿฌ๊ทธ์ธ ์ฝœ๋ฐฑ์˜ ๋ฐ˜ํ™˜ ๊ฐ’์„ ๊ฒฐ๊ณผ๋กœ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค. ์ผ๋ฐ˜์ ์ธ ์‚ฌ์šฉ ์‚ฌ๋ก€๋Š” ์บ์‹œ๋œ LlmResponse๋ฅผ ๋ฐ˜ํ™˜ํ•˜๋„๋ก before_model_callback์„ ๊ตฌํ˜„ํ•˜์—ฌ ์ค‘๋ณต๋˜๊ณ  ๋น„์šฉ์ด ๋งŽ์ด ๋“œ๋Š” API ํ˜ธ์ถœ์„ ๋ฐฉ์ง€ํ•˜๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค.
  • ์ˆ˜์ •: ํ›„ํฌ๋ฅผ ๊ตฌํ˜„ํ•˜๊ณ  ์ปจํ…์ŠคํŠธ ๊ฐ์ฒด๋ฅผ ์ˆ˜์ •ํ•ฉ๋‹ˆ๋‹ค. ์ด ์ ‘๊ทผ ๋ฐฉ์‹์€ ํ•ด๋‹น ๋ชจ๋“ˆ์˜ ์‹คํ–‰์„ ๋ฐฉํ•ดํ•˜์ง€ ์•Š๊ณ  ์‹คํ–‰๋  ๋ชจ๋“ˆ์˜ ์ปจํ…์ŠคํŠธ ๋ฐ์ดํ„ฐ๋ฅผ ์ˆ˜์ •ํ•  ์ˆ˜ ์žˆ๋„๋ก ํ•ฉ๋‹ˆ๋‹ค. ์˜ˆ๋ฅผ ๋“ค์–ด, ๋ชจ๋ธ ๊ฐ์ฒด ์‹คํ–‰์„ ์œ„ํ•œ ์ถ”๊ฐ€ ํ‘œ์ค€ํ™”๋œ ํ”„๋กฌํ”„ํŠธ ํ…์ŠคํŠธ๋ฅผ ์ถ”๊ฐ€ํ•˜๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค.

์ฃผ์˜: ํ”Œ๋Ÿฌ๊ทธ์ธ ์ฝœ๋ฐฑ ํ•จ์ˆ˜๋Š” ๊ฐ์ฒด ์ˆ˜์ค€์—์„œ ๊ตฌํ˜„๋œ ์ฝœ๋ฐฑ๋ณด๋‹ค ์šฐ์„ ํ•ฉ๋‹ˆ๋‹ค. ์ด ๋™์ž‘์€ ํ”Œ๋Ÿฌ๊ทธ์ธ ์ˆ˜์ค€ ์ฝœ๋ฐฑ ์ฝ”๋“œ๊ฐ€ ์—์ด์ „ํŠธ, ๋ชจ๋ธ ๋˜๋Š” ๋„๊ตฌ ๊ฐ์ฒด ์ฝœ๋ฐฑ์ด ์‹คํ–‰๋˜๊ธฐ ์ „์— ์‹คํ–‰๋จ์„ ์˜๋ฏธํ•ฉ๋‹ˆ๋‹ค. ๋˜ํ•œ ํ”Œ๋Ÿฌ๊ทธ์ธ ์ˆ˜์ค€ ์—์ด์ „ํŠธ ์ฝœ๋ฐฑ์ด ๋น„์–ด ์žˆ์ง€ ์•Š์€(None์ด ์•„๋‹Œ) ์‘๋‹ต์„ ๋ฐ˜ํ™˜ํ•˜๋ฉด ์—์ด์ „ํŠธ, ๋ชจ๋ธ ๋˜๋Š” ๋„๊ตฌ ์ˆ˜์ค€ ์ฝœ๋ฐฑ์€ ์‹คํ–‰๋˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค (๊ฑด๋„ˆ๋œ€).

Python์—์„œ ๋‘˜ ์ด์ƒ์˜ ํ”Œ๋Ÿฌ๊ทธ์ธ์„ ๋“ฑ๋กํ•  ๋•Œ๋Š” ๋‹ค์Œ ๋™์ž‘์— ์œ ์˜ํ•˜์„ธ์š”.

  • ์ˆœ์„œ: ADK๋Š” plugins ๋ชฉ๋ก์— ํ”Œ๋Ÿฌ๊ทธ์ธ์ด ๋‚˜ํƒ€๋‚˜๋Š” ์ˆœ์„œ๋Œ€๋กœ ๊ฐ ์ฝœ๋ฐฑ์„ ์‹คํ–‰ํ•˜๋ฉฐ, None ์ด์™ธ์˜ ๊ฐ’์„ ๋ฐ˜ํ™˜ํ•˜๋Š” ์ฒซ ๋ฒˆ์งธ ํ”Œ๋Ÿฌ๊ทธ์ธ์—์„œ ๋ฉˆ์ถฅ๋‹ˆ๋‹ค.
  • ์ด๋ฆ„: ๊ฐ ํ”Œ๋Ÿฌ๊ทธ์ธ์—๋Š” ๊ณ ์œ ํ•œ name์ด ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค. ์ด๋ฆ„์ด ๊ฐ™์€ ๋‘ ํ”Œ๋Ÿฌ๊ทธ์ธ์ด ์žˆ์œผ๋ฉด Runner๊ฐ€ ์ƒ์„ฑ๋  ๋•Œ ValueError๊ฐ€ ๋ฐœ์ƒํ•˜๋ฏ€๋กœ, ๋™์ผํ•œ ํด๋ž˜์Šค์˜ ๊ฐ ์ธ์Šคํ„ด์Šค์— ๊ณ ์œ ํ•œ ์ด๋ฆ„์„ ์ง€์ •ํ•˜์„ธ์š”.
  • ์˜ˆ์™ธ: ํ”Œ๋Ÿฌ๊ทธ์ธ ์ฝœ๋ฐฑ์—์„œ ๋ฐœ์ƒํ•œ ์˜ˆ์™ธ๋Š” ์›๋ž˜ ์˜ˆ์™ธ๋ฅผ __cause__๋กœ ๊ฐ€์ง€๋Š” RuntimeError๋กœ ์ฝ”๋“œ์— ๋„๋‹ฌํ•ฉ๋‹ˆ๋‹ค. on_agent_error_callback ๋ฐ on_run_error_callback ํ›„ํฌ๋Š” ๋‹ค๋ฅด๊ฒŒ ์ž‘๋™ํ•ฉ๋‹ˆ๋‹ค. ADK๋Š” ๋ชจ๋“  ํ”Œ๋Ÿฌ๊ทธ์ธ์—์„œ ์ด๋ฅผ ์‹คํ–‰ํ•˜๋ฉฐ, ๋‚ด๋ถ€์—์„œ ๋ฐœ์ƒํ•œ ์˜ˆ์™ธ๋Š” ๋‹ค์‹œ ๋ฐœ์ƒ์‹œํ‚ค๋Š” ๋Œ€์‹  ๋กœ๊ทธ์— ๊ธฐ๋กํ•ฉ๋‹ˆ๋‹ค.

ํ”Œ๋Ÿฌ๊ทธ์ธ ์„ค๊ณ„๋Š” ์ฝ”๋“œ ์‹คํ–‰ ๊ณ„์ธต ๊ตฌ์กฐ๋ฅผ ์„ค์ •ํ•˜๊ณ  ์ „์—ญ ๊ด€์‹ฌ์‚ฌ๋ฅผ ๋กœ์ปฌ ์—์ด์ „ํŠธ ๋…ผ๋ฆฌ์—์„œ ๋ถ„๋ฆฌํ•ฉ๋‹ˆ๋‹ค. ํ”Œ๋Ÿฌ๊ทธ์ธ์€ PerformanceMonitoringPlugin๊ณผ ๊ฐ™์ด ๋นŒ๋“œํ•˜๋Š” ์ƒํƒœ ์ €์žฅ ๋ชจ๋“ˆ์ธ ๋ฐ˜๋ฉด, ์ฝœ๋ฐฑ ํ›„ํฌ๋Š” ํ•ด๋‹น ๋ชจ๋“ˆ ๋‚ด์—์„œ ์‹คํ–‰๋˜๋Š” ํŠน์ • ํ•จ์ˆ˜์ž…๋‹ˆ๋‹ค. ์ด ์•„ํ‚คํ…์ฒ˜๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™์€ ์ค‘์š”ํ•œ ๋ฐฉ์‹์œผ๋กœ ํ‘œ์ค€ ์—์ด์ „ํŠธ ์ฝœ๋ฐฑ๊ณผ ๊ทผ๋ณธ์ ์œผ๋กœ ๋‹ค๋ฆ…๋‹ˆ๋‹ค.

  • ๋ฒ”์œ„: ํ”Œ๋Ÿฌ๊ทธ์ธ ํ›„ํฌ๋Š” ์ „์—ญ์ ์ž…๋‹ˆ๋‹ค. Runner(Python์˜ ๊ฒฝ์šฐ App)์— ํ”Œ๋Ÿฌ๊ทธ์ธ์„ ํ•œ ๋ฒˆ ๋“ฑ๋กํ•˜๋ฉด ํ•ด๋‹น ํ›„ํฌ๋Š” ๊ด€๋ฆฌํ•˜๋Š” ๋ชจ๋“  ์—์ด์ „ํŠธ, ๋ชจ๋ธ ๋ฐ ๋„๊ตฌ์— ๋ณดํŽธ์ ์œผ๋กœ ์ ์šฉ๋ฉ๋‹ˆ๋‹ค. ๋Œ€์กฐ์ ์œผ๋กœ ์—์ด์ „ํŠธ ์ฝœ๋ฐฑ์€ ๋กœ์ปฌ์ด๋ฉฐ ํŠน์ • ์—์ด์ „ํŠธ ์ธ์Šคํ„ด์Šค์— ๊ฐœ๋ณ„์ ์œผ๋กœ ๊ตฌ์„ฑ๋ฉ๋‹ˆ๋‹ค.
  • ์‹คํ–‰ ์ˆœ์„œ: ํ”Œ๋Ÿฌ๊ทธ์ธ์ด ์šฐ์„ ํ•ฉ๋‹ˆ๋‹ค. ์ฃผ์–ด์ง„ ์ด๋ฒคํŠธ์— ๋Œ€ํ•ด ํ”Œ๋Ÿฌ๊ทธ์ธ ํ›„ํฌ๋Š” ํ•ญ์ƒ ํ•ด๋‹น ์—์ด์ „ํŠธ ์ฝœ๋ฐฑ๋ณด๋‹ค ๋จผ์ € ์‹คํ–‰๋ฉ๋‹ˆ๋‹ค. ์ด ์‹œ์Šคํ…œ ๋™์ž‘์€ ํ”Œ๋Ÿฌ๊ทธ์ธ์„ ๋ณด์•ˆ ์ •์ฑ…, ๋ฒ”์šฉ ์บ์‹ฑ ๋ฐ ์ „์ฒด ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์— ๊ฑธ์นœ ์ผ๊ด€๋œ ๋กœ๊น…๊ณผ ๊ฐ™์€ ๊ต์ฐจ ์ ˆ๋‹จ ๊ธฐ๋Šฅ์„ ๊ตฌํ˜„ํ•˜๊ธฐ ์œ„ํ•œ ์˜ฌ๋ฐ”๋ฅธ ์•„ํ‚คํ…์ฒ˜ ์„ ํƒ์œผ๋กœ ๋งŒ๋“ญ๋‹ˆ๋‹ค.

์—์ด์ „ํŠธ ์ฝœ๋ฐฑ ๋ฐ ํ”Œ๋Ÿฌ๊ทธ์ธ

์ด์ „ ์„น์…˜์—์„œ ์–ธ๊ธ‰ํ–ˆ๋“ฏ์ด ํ”Œ๋Ÿฌ๊ทธ์ธ๊ณผ ์—์ด์ „ํŠธ ์ฝœ๋ฐฑ ์‚ฌ์ด์—๋Š” ๊ธฐ๋Šฅ์  ์œ ์‚ฌ์„ฑ์ด ์žˆ์Šต๋‹ˆ๋‹ค. ๋‹ค์Œ ํ‘œ๋Š” ํ”Œ๋Ÿฌ๊ทธ์ธ๊ณผ ์—์ด์ „ํŠธ ์ฝœ๋ฐฑ์˜ ์ฐจ์ด์ ์„ ๋” ์ž์„ธํžˆ ๋น„๊ตํ•ฉ๋‹ˆ๋‹ค.

ํ”Œ๋Ÿฌ๊ทธ์ธ ์—์ด์ „ํŠธ ์ฝœ๋ฐฑ
๋ฒ”์œ„ ์ „์—ญ: Runner์˜ ๋ชจ๋“  ์—์ด์ „ํŠธ/๋„๊ตฌ/LLM์— ์ ์šฉ๋ฉ๋‹ˆ๋‹ค. ๋กœ์ปฌ: ๊ตฌ์„ฑ๋œ ํŠน์ • ์—์ด์ „ํŠธ ์ธ์Šคํ„ด์Šค์—๋งŒ ์ ์šฉ๋ฉ๋‹ˆ๋‹ค.
์ฃผ์š” ์‚ฌ์šฉ ์‚ฌ๋ก€ ์ˆ˜ํ‰์  ๊ธฐ๋Šฅ: ๋กœ๊น…, ์ •์ฑ…, ๋ชจ๋‹ˆํ„ฐ๋ง, ์ „์—ญ ์บ์‹ฑ. ํŠน์ • ์—์ด์ „ํŠธ ๋กœ์ง: ๋‹จ์ผ ์—์ด์ „ํŠธ์˜ ๋™์ž‘ ๋˜๋Š” ์ƒํƒœ ์ˆ˜์ •.
๊ตฌ์„ฑ Runner(Python์˜ ๊ฒฝ์šฐ App)์—์„œ ํ•œ ๋ฒˆ ๊ตฌ์„ฑํ•ฉ๋‹ˆ๋‹ค. ๊ฐ BaseAgent ์ธ์Šคํ„ด์Šค์—์„œ ๊ฐœ๋ณ„์ ์œผ๋กœ ๊ตฌ์„ฑํ•ฉ๋‹ˆ๋‹ค.
์‹คํ–‰ ์ˆœ์„œ ํ”Œ๋Ÿฌ๊ทธ์ธ ์ฝœ๋ฐฑ์€ ์—์ด์ „ํŠธ ์ฝœ๋ฐฑ ์ „์— ์‹คํ–‰๋ฉ๋‹ˆ๋‹ค. ์—์ด์ „ํŠธ ์ฝœ๋ฐฑ์€ ํ”Œ๋Ÿฌ๊ทธ์ธ ์ฝœ๋ฐฑ ํ›„์— ์‹คํ–‰๋ฉ๋‹ˆ๋‹ค.

ํ”Œ๋Ÿฌ๊ทธ์ธ ์ฝœ๋ฐฑ ํ›„ํฌ

ํ”Œ๋Ÿฌ๊ทธ์ธ ํด๋ž˜์Šค์—์„œ ์ •์˜ํ•  ์ฝœ๋ฐฑ ํ•จ์ˆ˜๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ํ”Œ๋Ÿฌ๊ทธ์ธ์ด ํ˜ธ์ถœ๋˜๋Š” ์‹œ๊ธฐ๋ฅผ ์ •์˜ํ•ฉ๋‹ˆ๋‹ค. ์ฝœ๋ฐฑ์€ ์‚ฌ์šฉ์ž ๋ฉ”์‹œ์ง€๊ฐ€ ์ˆ˜์‹ ๋  ๋•Œ, Runner, Agent, Model ๋˜๋Š” Tool์ด ํ˜ธ์ถœ๋˜๊ธฐ ์ „ํ›„, Events์— ๋Œ€ํ•ด, ๊ทธ๋ฆฌ๊ณ  Model ๋˜๋Š” Tool ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ•  ๋•Œ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. Python์—์„œ๋Š” Agent๊ฐ€ ์˜ˆ์™ธ๋ฅผ ๋ฐœ์ƒ์‹œํ‚ฌ ๋•Œ์™€ ์‹คํ–‰ ์ž์ฒด๊ฐ€ ์‹คํŒจํ•  ๋•Œ์—๋„ ์˜ค๋ฅ˜ ์ฝœ๋ฐฑ์ด ์‹คํ–‰๋ฉ๋‹ˆ๋‹ค. ์ด๋Ÿฌํ•œ ์ฝœ๋ฐฑ์€ ์—์ด์ „ํŠธ, ๋ชจ๋ธ ๋ฐ ๋„๊ตฌ ํด๋ž˜์Šค ๋‚ด์— ์ •์˜๋œ ๋ชจ๋“  ์ฝœ๋ฐฑ์„ ํฌํ•จํ•˜๋ฉฐ ์šฐ์„ ํ•ฉ๋‹ˆ๋‹ค.

๋‹ค์Œ ๋‹ค์ด์–ด๊ทธ๋žจ์€ ์—์ด์ „ํŠธ ์›Œํฌํ”Œ๋กœ ์ค‘์— ํ”Œ๋Ÿฌ๊ทธ์ธ ๊ธฐ๋Šฅ์„ ์—ฐ๊ฒฐํ•˜๊ณ  ์‹คํ–‰ํ•  ์ˆ˜ ์žˆ๋Š” ์ฝœ๋ฐฑ ์ง€์ ์„ ๋ณด์—ฌ์ค๋‹ˆ๋‹ค.

ADK ํ”Œ๋Ÿฌ๊ทธ์ธ ์ฝœ๋ฐฑ ํ›„ํฌ ๊ทธ๋ฆผ 1. ํ”Œ๋Ÿฌ๊ทธ์ธ ์ฝœ๋ฐฑ ํ›„ํฌ ์œ„์น˜๋ฅผ ํฌํ•จํ•œ ADK ์—์ด์ „ํŠธ ์›Œํฌํ”Œ๋กœ ๋‹ค์ด์–ด๊ทธ๋žจ.

๋‹ค์Œ ์„น์…˜์—์„œ๋Š” ํ”Œ๋Ÿฌ๊ทธ์ธ์— ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋Š” ์ฝœ๋ฐฑ ํ›„ํฌ์— ๋Œ€ํ•ด ๋” ์ž์„ธํžˆ ์„ค๋ช…ํ•ฉ๋‹ˆ๋‹ค.

์‚ฌ์šฉ์ž ๋ฉ”์‹œ์ง€ ์ฝœ๋ฐฑ

์‚ฌ์šฉ์ž ๋ฉ”์‹œ์ง€ ์ฝœ๋ฐฑ(on_user_message_callback)์€ ์‚ฌ์šฉ์ž๊ฐ€ ๋ฉ”์‹œ์ง€๋ฅผ ๋ณด๋‚ผ ๋•Œ ๋ฐœ์ƒํ•ฉ๋‹ˆ๋‹ค. on_user_message_callback์€ ์‹คํ–‰๋˜๋Š” ์ฒซ ๋ฒˆ์งธ ํ›„ํฌ๋กœ, ์ดˆ๊ธฐ ์ž…๋ ฅ์„ ๊ฒ€์‚ฌํ•˜๊ฑฐ๋‚˜ ์ˆ˜์ •ํ•  ๊ธฐํšŒ๋ฅผ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค.

  • ์‹คํ–‰ ์‹œ์ : runner.run() ์งํ›„, ๋‹ค๋ฅธ ์ฒ˜๋ฆฌ ์ด์ „์— ๋ฐœ์ƒํ•ฉ๋‹ˆ๋‹ค.
  • ๋ชฉ์ : ์‚ฌ์šฉ์ž์˜ ์›์‹œ ์ž…๋ ฅ์„ ๊ฒ€์‚ฌํ•˜๊ฑฐ๋‚˜ ์ˆ˜์ •ํ•˜๋Š” ์ฒซ ๋ฒˆ์งธ ๊ธฐํšŒ์ž…๋‹ˆ๋‹ค.
  • ํ๋ฆ„ ์ œ์–ด: ์‚ฌ์šฉ์ž ์›๋ณธ ๋ฉ”์‹œ์ง€๋ฅผ ๋Œ€์ฒดํ•  types.Content ๊ฐ์ฒด๋ฅผ ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค.

๋‹ค์Œ ์ฝ”๋“œ ์˜ˆ์‹œ๋Š” ์ด ์ฝœ๋ฐฑ์˜ ๊ธฐ๋ณธ ๊ตฌ๋ฌธ์„ ๋ณด์—ฌ์ค๋‹ˆ๋‹ค.

async def on_user_message_callback(
    self,
    *,
    invocation_context: InvocationContext,
    user_message: types.Content,
) -> Optional[types.Content]:
        ...
    ```

=== "TypeScript"

    ```typescript
    async onUserMessageCallback(
        invocationContext: InvocationContext,
        user_message: Content
    ): Promise<Content | undefined> {
      // ์—ฌ๊ธฐ์— ๊ตฌํ˜„์„ ์ž‘์„ฑํ•ฉ๋‹ˆ๋‹ค
    }
    ```

```java
@Override
public Maybe<Content> onUserMessageCallback(
  InvocationContext invocationContext, Content userMessage) {
  return Maybe.empty();
}

๋Ÿฌ๋„ˆ ์‹œ์ž‘ ์ฝœ๋ฐฑ

๋Ÿฌ๋„ˆ ์‹œ์ž‘ ์ฝœ๋ฐฑ(before_run_callback)์€ Runner ๊ฐ์ฒด๊ฐ€ ์ž ์žฌ์ ์œผ๋กœ ์ˆ˜์ •๋œ ์‚ฌ์šฉ์ž ๋ฉ”์‹œ์ง€๋ฅผ ๋ฐ›์•„ ์‹คํ–‰์„ ์ค€๋น„ํ•  ๋•Œ ๋ฐœ์ƒํ•ฉ๋‹ˆ๋‹ค. before_run_callback์€ ์—ฌ๊ธฐ์„œ ์‹คํ–‰๋˜์–ด ์—์ด์ „ํŠธ ๋กœ์ง์ด ์‹œ์ž‘๋˜๊ธฐ ์ „์— ์ „์—ญ ์„ค์ •์„ ํ—ˆ์šฉํ•ฉ๋‹ˆ๋‹ค.

  • ์‹คํ–‰ ์‹œ์ : ์‚ฌ์šฉ์ž ๋ฉ”์‹œ์ง€๊ฐ€ ์ฒ˜๋ฆฌ๋œ ํ›„ ์—์ด์ „ํŠธ ์‹คํ–‰์ด ์‹œ์ž‘๋˜๊ธฐ ์ „์— ๋ฐœ์ƒํ•ฉ๋‹ˆ๋‹ค.
  • ๋ชฉ์ : ํ˜ธ์ถœ์ด ์‹คํ–‰๋˜๊ธฐ ์ „์˜ ์ „์—ญ ์„ค์ • ๋˜๋Š” ์ดˆ๊ธฐํ™”์ž…๋‹ˆ๋‹ค.
  • ํ๋ฆ„ ์ œ์–ด: types.Content ๊ฐ์ฒด๋ฅผ ๋ฐ˜ํ™˜ํ•˜์—ฌ ์‹คํ–‰์„ ์ค‘์ง€ํ•ฉ๋‹ˆ๋‹ค. Runner๊ฐ€ ์กฐ๊ธฐ์— ์ข…๋ฃŒ๋˜๊ณ  ํ•ด๋‹น ์ฝ˜ํ…์ธ ๋ฅผ ๊ฒฐ๊ณผ๋กœ ํ•˜์—ฌ ์‹คํ–‰์„ ๋งˆ์นฉ๋‹ˆ๋‹ค. Python์—์„œ๋Š” ์ด ์กฐ๊ธฐ ์ข…๋ฃŒ๊ฐ€ LlmAgent ๋˜๋Š” Workflow๋ฅผ ํฌํ•จํ•œ ๋ชจ๋“  ๋ฃจํŠธ ์—์ด์ „ํŠธ์— ์ ์šฉ๋ฉ๋‹ˆ๋‹ค. ์ •์ƒ์ ์œผ๋กœ ์ง„ํ–‰ํ•˜๋ ค๋ฉด None์„ ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค.

๋‹ค์Œ ์ฝ”๋“œ ์˜ˆ์‹œ๋Š” ์ด ์ฝœ๋ฐฑ์˜ ๊ธฐ๋ณธ ๊ตฌ๋ฌธ์„ ๋ณด์—ฌ์ค๋‹ˆ๋‹ค.

async def before_run_callback(
    self, *, invocation_context: InvocationContext
) -> Optional[types.Content]:
        ...
    ```

=== "TypeScript"

    ```typescript
    async beforeRunCallback(invocationContext: InvocationContext): Promise<Content | undefined> {
      // ์—ฌ๊ธฐ์— ๊ตฌํ˜„์„ ์ž‘์„ฑํ•ฉ๋‹ˆ๋‹ค
    }
    ```

```java
@Override
public Maybe<Content> beforeRunCallback(InvocationContext invocationContext) {
  return Maybe.empty();
}

์—์ด์ „ํŠธ ์‹คํ–‰ ์ฝœ๋ฐฑ

์—์ด์ „ํŠธ ์‹คํ–‰ ์ฝœ๋ฐฑ(before_agent, after_agent)์€ Runner ๊ฐ์ฒด๊ฐ€ ์—์ด์ „ํŠธ๋ฅผ ํ˜ธ์ถœํ•  ๋•Œ ๋ฐœ์ƒํ•ฉ๋‹ˆ๋‹ค. before_agent_callback์€ ์—์ด์ „ํŠธ์˜ ์ฃผ์š” ์ž‘์—…์ด ์‹œ์ž‘๋˜๊ธฐ ์ง์ „์— ์‹คํ–‰๋ฉ๋‹ˆ๋‹ค. ์ฃผ์š” ์ž‘์—…์€ ๋ชจ๋ธ ๋˜๋Š” ๋„๊ตฌ ํ˜ธ์ถœ์„ ํฌํ•จํ•  ์ˆ˜ ์žˆ๋Š” ์š”์ฒญ ์ฒ˜๋ฆฌ์˜ ์ „์ฒด ์—์ด์ „ํŠธ ํ”„๋กœ์„ธ์Šค๋ฅผ ํฌ๊ด„ํ•ฉ๋‹ˆ๋‹ค. ์—์ด์ „ํŠธ๊ฐ€ ๋ชจ๋“  ๋‹จ๊ณ„๋ฅผ ์™„๋ฃŒํ•˜๊ณ  ๊ฒฐ๊ณผ๋ฅผ ์ค€๋น„ํ•œ ํ›„ after_agent_callback์ด ์‹คํ–‰๋ฉ๋‹ˆ๋‹ค. Python์—์„œ๋Š” ์—์ด์ „ํŠธ ์‹คํ–‰์—์„œ ์˜ˆ์™ธ๊ฐ€ ๋ฐœ์ƒํ•˜๋ฉด after_agent_callback ๋Œ€์‹  on_agent_error_callback(*, agent, callback_context, error)์ด ์‹คํ–‰๋ฉ๋‹ˆ๋‹ค. ์ด ์ฝœ๋ฐฑ์€ ์‹คํŒจ๋งŒ ๊ด€์ฐฐํ•˜๋ฉฐ ๋ฐ˜ํ™˜ ๊ฐ’์€ ๋ฌด์‹œ๋˜๊ณ  ์›๋ž˜ ์˜ˆ์™ธ๊ฐ€ ๊ทธ๋Œ€๋กœ ๋ฐœ์ƒํ•ฉ๋‹ˆ๋‹ค.

์ฃผ์˜: ์ด๋Ÿฌํ•œ ์ฝœ๋ฐฑ์„ ๊ตฌํ˜„ํ•˜๋Š” ํ”Œ๋Ÿฌ๊ทธ์ธ์€ ์—์ด์ „ํŠธ ์ˆ˜์ค€ ์ฝœ๋ฐฑ์ด ์‹คํ–‰๋˜๊ธฐ ์ „์— ์‹คํ–‰๋ฉ๋‹ˆ๋‹ค. ๋˜ํ•œ ํ”Œ๋Ÿฌ๊ทธ์ธ ์ˆ˜์ค€ ์—์ด์ „ํŠธ ์ฝœ๋ฐฑ์ด None ๋˜๋Š” null ์‘๋‹ต ์ด์™ธ์˜ ๋‹ค๋ฅธ ๊ฐ’์„ ๋ฐ˜ํ™˜ํ•˜๋ฉด ์—์ด์ „ํŠธ ์ˆ˜์ค€ ์ฝœ๋ฐฑ์€ ์‹คํ–‰๋˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค (๊ฑด๋„ˆ๋œ€).

์—์ด์ „ํŠธ ๊ฐ์ฒด์˜ ์ผ๋ถ€๋กœ ์ •์˜๋œ ์—์ด์ „ํŠธ ์ฝœ๋ฐฑ์— ๋Œ€ํ•œ ์ž์„ธํ•œ ๋‚ด์šฉ์€ ์ฝœ๋ฐฑ ์œ ํ˜•์„ ์ฐธ๊ณ ํ•˜์„ธ์š”.

๋ชจ๋ธ ์ฝœ๋ฐฑ

๋ชจ๋ธ ์ฝœ๋ฐฑ **(before_model, after_model, on_model_error)**์€ ๋ชจ๋ธ ๊ฐ์ฒด๊ฐ€ ์‹คํ–‰๋˜๊ธฐ ์ „ํ›„ ๋˜๋Š” ๋ชจ๋ธ ํ˜ธ์ถœ์ด ์‹คํŒจํ•  ๋•Œ ๋‹ค์Œ๊ณผ ๊ฐ™์ด ๋ฐœ์ƒํ•ฉ๋‹ˆ๋‹ค.

  • ์—์ด์ „ํŠธ๊ฐ€ AI ๋ชจ๋ธ์„ ํ˜ธ์ถœํ•ด์•ผ ํ•˜๋Š” ๊ฒฝ์šฐ before_model_callback์ด ๋จผ์ € ์‹คํ–‰๋ฉ๋‹ˆ๋‹ค.
  • ๋ชจ๋ธ ํ˜ธ์ถœ์ด ์„ฑ๊ณตํ•˜๋ฉด after_model_callback์ด ๋‹ค์Œ์œผ๋กœ ์‹คํ–‰๋ฉ๋‹ˆ๋‹ค.
  • ๋ชจ๋ธ ํ˜ธ์ถœ์ด ์˜ˆ์™ธ์™€ ํ•จ๊ป˜ ์‹คํŒจํ•˜๋ฉด on_model_error_callback์ด ๋Œ€์‹  ํŠธ๋ฆฌ๊ฑฐ๋˜์–ด ์ •์ƒ์ ์ธ ๋ณต๊ตฌ๋ฅผ ํ—ˆ์šฉํ•ฉ๋‹ˆ๋‹ค.

์ฃผ์˜: before_model ๋ฐ after_model ์ฝœ๋ฐฑ ๋ฉ”์„œ๋“œ๋ฅผ ๊ตฌํ˜„ํ•˜๋Š” ํ”Œ๋Ÿฌ๊ทธ์ธ์€ ๋ชจ๋ธ ์ˆ˜์ค€ ์ฝœ๋ฐฑ์ด ์‹คํ–‰๋˜๊ธฐ ์ „์— ์‹คํ–‰๋ฉ๋‹ˆ๋‹ค. ๋˜ํ•œ ํ”Œ๋Ÿฌ๊ทธ์ธ ์ˆ˜์ค€ ๋ชจ๋ธ ์ฝœ๋ฐฑ์ด None ๋˜๋Š” null ์‘๋‹ต ์ด์™ธ์˜ ๋‹ค๋ฅธ ๊ฐ’์„ ๋ฐ˜ํ™˜ํ•˜๋ฉด ๋ชจ๋ธ ์ˆ˜์ค€ ์ฝœ๋ฐฑ์€ ์‹คํ–‰๋˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค (๊ฑด๋„ˆ๋œ€).

๋ชจ๋ธ ์˜ค๋ฅ˜ ์‹œ ์ฝœ๋ฐฑ ์„ธ๋ถ€ ์ •๋ณด

๋ชจ๋ธ ๊ฐ์ฒด์— ๋Œ€ํ•œ ์˜ค๋ฅ˜ ์‹œ ์ฝœ๋ฐฑ์€ ๋‹ค์Œ๊ณผ ๊ฐ™์ด ์ž‘๋™ํ•ฉ๋‹ˆ๋‹ค.

  • ์‹คํ–‰ ์‹œ์ : ๋ชจ๋ธ ํ˜ธ์ถœ ์ค‘์— ์˜ˆ์™ธ๊ฐ€ ๋ฐœ์ƒํ•  ๋•Œ.
  • ์ผ๋ฐ˜์ ์ธ ์‚ฌ์šฉ ์‚ฌ๋ก€: ์ •์ƒ์ ์ธ ์˜ค๋ฅ˜ ์ฒ˜๋ฆฌ, ํŠน์ • ์˜ค๋ฅ˜ ๋กœ๊น… ๋˜๋Š” "AI ์„œ๋น„์Šค๋Š” ํ˜„์žฌ ์‚ฌ์šฉํ•  ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค."์™€ ๊ฐ™์€ ๋Œ€์ฒด ์‘๋‹ต ๋ฐ˜ํ™˜.
  • ํ๋ฆ„ ์ œ์–ด:
    • LlmResponse ๊ฐ์ฒด๋ฅผ ๋ฐ˜ํ™˜ํ•˜์—ฌ ์˜ˆ์™ธ๋ฅผ ์–ต์ œํ•˜๊ณ  ๋Œ€์ฒด ๊ฒฐ๊ณผ๋ฅผ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค.
    • None์„ ๋ฐ˜ํ™˜ํ•˜์—ฌ ์›๋ž˜ ์˜ˆ์™ธ๊ฐ€ ๋ฐœ์ƒํ•˜๋„๋ก ํ—ˆ์šฉํ•ฉ๋‹ˆ๋‹ค.

์ฐธ๊ณ : ๋ชจ๋ธ ๊ฐ์ฒด์˜ ์‹คํ–‰์ด LlmResponse๋ฅผ ๋ฐ˜ํ™˜ํ•˜๋ฉด ์‹œ์Šคํ…œ์€ ์‹คํ–‰ ํ๋ฆ„์„ ์žฌ๊ฐœํ•˜๊ณ  after_model_callback์ด ์ •์ƒ์ ์œผ๋กœ ํŠธ๋ฆฌ๊ฑฐ๋ฉ๋‹ˆ๋‹ค.

๋‹ค์Œ ์ฝ”๋“œ ์˜ˆ์‹œ๋Š” ์ด ์ฝœ๋ฐฑ์˜ ๊ธฐ๋ณธ ๊ตฌ๋ฌธ์„ ๋ณด์—ฌ์ค๋‹ˆ๋‹ค.

async def on_model_error_callback(
    self,
    *,
    callback_context: CallbackContext,
    llm_request: LlmRequest,
    error: Exception,
) -> Optional[LlmResponse]:
        ...
    ```

=== "TypeScript"

    ```typescript
    async onModelErrorCallback(
        context: Context,
        llmRequest: LlmRequest,
        error: Error
    ): Promise<LlmResponse | undefined> {
        // ์—ฌ๊ธฐ์— ๊ตฌํ˜„์„ ์ž‘์„ฑํ•ฉ๋‹ˆ๋‹ค
    }
    ```

```java
@Override
public Maybe<LlmResponse> onModelErrorCallback(
  CallbackContext callbackContext, LlmRequest.Builder llmRequest, Throwable error) {
  return Maybe.empty();
}

๋„๊ตฌ ์ฝœ๋ฐฑ

ํ”Œ๋Ÿฌ๊ทธ์ธ์— ๋Œ€ํ•œ ๋„๊ตฌ ์ฝœ๋ฐฑ **(before_tool, after_tool, on_tool_error)**์€ ๋„๊ตฌ ์‹คํ–‰ ์ „ํ›„ ๋˜๋Š” ์˜ค๋ฅ˜ ๋ฐœ์ƒ ์‹œ ๋‹ค์Œ๊ณผ ๊ฐ™์ด ๋ฐœ์ƒํ•ฉ๋‹ˆ๋‹ค.

  • ์—์ด์ „ํŠธ๊ฐ€ ๋„๊ตฌ๋ฅผ ์‹คํ–‰ํ•˜๋ฉด before_tool_callback์ด ๋จผ์ € ์‹คํ–‰๋ฉ๋‹ˆ๋‹ค.
  • ๋„๊ตฌ๊ฐ€ ์„ฑ๊ณต์ ์œผ๋กœ ์‹คํ–‰๋˜๋ฉด after_tool_callback์ด ๋‹ค์Œ์œผ๋กœ ์‹คํ–‰๋ฉ๋‹ˆ๋‹ค.
  • ๋„๊ตฌ๊ฐ€ ์˜ˆ์™ธ๋ฅผ ๋ฐœ์ƒ์‹œํ‚ค๋ฉด on_tool_error_callback์ด ๋Œ€์‹  ํŠธ๋ฆฌ๊ฑฐ๋˜์–ด ์‹คํŒจ๋ฅผ ์ฒ˜๋ฆฌํ•  ๊ธฐํšŒ๋ฅผ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค. on_tool_error_callback์ด dict๋ฅผ ๋ฐ˜ํ™˜ํ•˜๋ฉด after_tool_callback์ด ์ •์ƒ์ ์œผ๋กœ ํŠธ๋ฆฌ๊ฑฐ๋ฉ๋‹ˆ๋‹ค.

์ฃผ์˜: ์ด๋Ÿฌํ•œ ์ฝœ๋ฐฑ์„ ๊ตฌํ˜„ํ•˜๋Š” ํ”Œ๋Ÿฌ๊ทธ์ธ์€ ๋„๊ตฌ ์ˆ˜์ค€ ์ฝœ๋ฐฑ์ด ์‹คํ–‰๋˜๊ธฐ ์ „์— ์‹คํ–‰๋ฉ๋‹ˆ๋‹ค. ๋˜ํ•œ ํ”Œ๋Ÿฌ๊ทธ์ธ ์ˆ˜์ค€ ๋„๊ตฌ ์ฝœ๋ฐฑ์ด None ๋˜๋Š” null ์‘๋‹ต ์ด์™ธ์˜ ๋‹ค๋ฅธ ๊ฐ’์„ ๋ฐ˜ํ™˜ํ•˜๋ฉด ๋„๊ตฌ ์ˆ˜์ค€ ์ฝœ๋ฐฑ์€ ์‹คํ–‰๋˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค (๊ฑด๋„ˆ๋œ€).

๋„๊ตฌ ์˜ค๋ฅ˜ ์‹œ ์ฝœ๋ฐฑ ์„ธ๋ถ€ ์ •๋ณด

๋„๊ตฌ ๊ฐ์ฒด์— ๋Œ€ํ•œ ์˜ค๋ฅ˜ ์‹œ ์ฝœ๋ฐฑ์€ ๋‹ค์Œ๊ณผ ๊ฐ™์ด ์ž‘๋™ํ•ฉ๋‹ˆ๋‹ค.

  • ์‹คํ–‰ ์‹œ์ : ๋„๊ตฌ์˜ run ๋ฉ”์„œ๋“œ ์‹คํ–‰ ์ค‘์— ์˜ˆ์™ธ๊ฐ€ ๋ฐœ์ƒํ•  ๋•Œ.
  • ๋ชฉ์ : ํŠน์ • ๋„๊ตฌ ์˜ˆ์™ธ(APIError์™€ ๊ฐ™์€)๋ฅผ ํฌ์ฐฉํ•˜๊ณ , ์‹คํŒจ๋ฅผ ๋กœ๊น…ํ•˜๊ณ , LLM์— ์‚ฌ์šฉ์ž ์นœํ™”์ ์ธ ์˜ค๋ฅ˜ ๋ฉ”์‹œ์ง€๋ฅผ ๋‹ค์‹œ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค.
  • ํ๋ฆ„ ์ œ์–ด: dict๋ฅผ ๋ฐ˜ํ™˜ํ•˜์—ฌ ์˜ˆ์™ธ๋ฅผ ์–ต์ œํ•˜๊ณ  ๋Œ€์ฒด ๊ฒฐ๊ณผ๋ฅผ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค. None์„ ๋ฐ˜ํ™˜ํ•˜์—ฌ ์›๋ž˜ ์˜ˆ์™ธ๊ฐ€ ๋ฐœ์ƒํ•˜๋„๋ก ํ—ˆ์šฉํ•ฉ๋‹ˆ๋‹ค.

์ฐธ๊ณ : dict๋ฅผ ๋ฐ˜ํ™˜ํ•˜๋ฉด ์‹คํ–‰ ํ๋ฆ„์ด ์žฌ๊ฐœ๋˜๊ณ  after_tool_callback์ด ์ •์ƒ์ ์œผ๋กœ ํŠธ๋ฆฌ๊ฑฐ๋ฉ๋‹ˆ๋‹ค.

๋‹ค์Œ ์ฝ”๋“œ ์˜ˆ์‹œ๋Š” ์ด ์ฝœ๋ฐฑ์˜ ๊ธฐ๋ณธ ๊ตฌ๋ฌธ์„ ๋ณด์—ฌ์ค๋‹ˆ๋‹ค.

async def on_tool_error_callback(
    self,
    *,
    tool: BaseTool,
    tool_args: dict[str, Any],
    tool_context: ToolContext,
    error: Exception,
) -> Optional[dict]:
        ...
    ```

=== "TypeScript"

    ```typescript
    async onToolErrorCallback(
        tool: BaseTool,
        toolArgs: { [key: string]: any },
        context: Context,
        error: Error
    ): Promise<{ [key:string]: any } | undefined> {
        // ์—ฌ๊ธฐ์— ๊ตฌํ˜„์„ ์ž‘์„ฑํ•ฉ๋‹ˆ๋‹ค
    }
    ```

```java
@Override
public Maybe<Map<String, Object>> onToolErrorCallback(
  BaseTool tool, Map<String, Object> toolArgs, ToolContext toolContext, Throwable error) {
  return Maybe.empty();
}

์ด๋ฒคํŠธ ์ฝœ๋ฐฑ

์ด๋ฒคํŠธ ์ฝœ๋ฐฑ(on_event_callback)์€ ์—์ด์ „ํŠธ๊ฐ€ ํ…์ŠคํŠธ ์‘๋‹ต ๋˜๋Š” ๋„๊ตฌ ํ˜ธ์ถœ ๊ฒฐ๊ณผ์™€ ๊ฐ™์€ ์ถœ๋ ฅ์„ ์ƒ์„ฑํ•  ๋•Œ Event ๊ฐ์ฒด๋กœ ์ถœ๋ ฅํ•  ๋•Œ ๋ฐœ์ƒํ•ฉ๋‹ˆ๋‹ค. on_event_callback์€ ๊ฐ ์ด๋ฒคํŠธ์— ๋Œ€ํ•ด ์‹คํ–‰๋˜์–ด ํด๋ผ์ด์–ธํŠธ์— ์ŠคํŠธ๋ฆฌ๋ฐ๋˜๊ธฐ ์ „์— ์ด๋ฒคํŠธ๋ฅผ ์ˆ˜์ •ํ•  ์ˆ˜ ์žˆ๋„๋ก ํ•ฉ๋‹ˆ๋‹ค.

  • ์‹คํ–‰ ์‹œ์ : ์—์ด์ „ํŠธ๊ฐ€ Event๋ฅผ ์ƒ์„ฑํ•œ ํ›„ ์‚ฌ์šฉ์ž์—๊ฒŒ ๋ณด๋‚ด๊ธฐ ์ „์—. ์—์ด์ „ํŠธ ์‹คํ–‰์€ ์—ฌ๋Ÿฌ ์ด๋ฒคํŠธ๋ฅผ ์ƒ์„ฑํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
  • ๋ชฉ์ : ์ด๋ฒคํŠธ ์ˆ˜์ • ๋˜๋Š” ๋ณด๊ฐ•(์˜ˆ: ๋ฉ”ํƒ€๋ฐ์ดํ„ฐ ์ถ”๊ฐ€) ๋˜๋Š” ํŠน์ • ์ด๋ฒคํŠธ์— ๋”ฐ๋ผ ๋ถ€์ž‘์šฉ ํŠธ๋ฆฌ๊ฑฐ์— ์œ ์šฉํ•ฉ๋‹ˆ๋‹ค.
  • ํ๋ฆ„ ์ œ์–ด: ์›๋ณธ ์ด๋ฒคํŠธ๋ฅผ ์žฌ์ •์˜ํ•  Event ๊ฐ์ฒด๋ฅผ ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค. Python์—์„œ ADK๋Š” ๋ฐ˜ํ™˜๋œ ์ด๋ฒคํŠธ๋ฅผ ์›๋ณธ ์ด๋ฒคํŠธ์— ๋ณ‘ํ•ฉํ•ฉ๋‹ˆ๋‹ค. ์ฆ‰, ์„ค์ •ํ•œ ํ•„๋“œ๋งŒ ์ ์šฉ๋˜๋ฉฐ id, invocation_id, timestamp๋Š” ํ•ญ์ƒ ์›๋ณธ ์ด๋ฒคํŠธ์—์„œ ๊ฐ€์ ธ์˜ต๋‹ˆ๋‹ค.

๋‹ค์Œ ์ฝ”๋“œ ์˜ˆ์‹œ๋Š” ์ด ์ฝœ๋ฐฑ์˜ ๊ธฐ๋ณธ ๊ตฌ๋ฌธ์„ ๋ณด์—ฌ์ค๋‹ˆ๋‹ค.

async def on_event_callback(
    self, *, invocation_context: InvocationContext, event: Event
) -> Optional[Event]:
        ...
    ```

=== "TypeScript"

    ```typescript
    async onEventCallback(
        invocationContext: InvocationContext,
        event: Event
    ): Promise<Event | undefined> {
        // ์—ฌ๊ธฐ์— ๊ตฌํ˜„์„ ์ž‘์„ฑํ•ฉ๋‹ˆ๋‹ค
    }
    ```

```java
@Override
public Maybe<Event> onEventCallback(InvocationContext invocationContext, Event event) {
  return Maybe.empty();
}

๋Ÿฌ๋„ˆ ์ข…๋ฃŒ ์ฝœ๋ฐฑ

๋Ÿฌ๋„ˆ ์ข…๋ฃŒ ์ฝœ๋ฐฑ **(after_run_callback)**์€ ์—์ด์ „ํŠธ๊ฐ€ ์ „์ฒด ํ”„๋กœ์„ธ์Šค๋ฅผ ์™„๋ฃŒํ•˜๊ณ  ๋ชจ๋“  ์ด๋ฒคํŠธ๊ฐ€ ์ฒ˜๋ฆฌ๋˜๋ฉด Runner๊ฐ€ ์‹คํ–‰์„ ์™„๋ฃŒํ•  ๋•Œ ๋ฐœ์ƒํ•ฉ๋‹ˆ๋‹ค. after_run_callback์€ ์ •๋ฆฌ ๋ฐ ์ตœ์ข… ๋ณด๊ณ ์— ์™„๋ฒฝํ•œ ์ตœ์ข… ํ›„ํฌ์ž…๋‹ˆ๋‹ค.

  • ์‹คํ–‰ ์‹œ์ : Runner๊ฐ€ ์š”์ฒญ ์‹คํ–‰์„ ์™„์ „ํžˆ ์™„๋ฃŒํ•œ ํ›„.
  • ๋ชฉ์ : ์—ฐ๊ฒฐ ๋‹ซ๊ธฐ ๋˜๋Š” ๋กœ๊ทธ ๋ฐ ์ธก์ • ํ•ญ๋ชฉ ๋ฐ์ดํ„ฐ ์ตœ์ข…ํ™”์™€ ๊ฐ™์€ ์ „์—ญ ์ •๋ฆฌ ์ž‘์—…์— ์ด์ƒ์ ์ž…๋‹ˆ๋‹ค.
  • ํ๋ฆ„ ์ œ์–ด: ์ด ์ฝœ๋ฐฑ์€ ํ•ด์ฒด ์ „์šฉ์ด๋ฉฐ ์ตœ์ข… ๊ฒฐ๊ณผ๋ฅผ ๋ณ€๊ฒฝํ•  ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค.

๋‹ค์Œ ์ฝ”๋“œ ์˜ˆ์‹œ๋Š” ์ด ์ฝœ๋ฐฑ์˜ ๊ธฐ๋ณธ ๊ตฌ๋ฌธ์„ ๋ณด์—ฌ์ค๋‹ˆ๋‹ค.

async def after_run_callback(
    self, *, invocation_context: InvocationContext
) -> None:
        ...
    ```

=== "TypeScript"

    ```typescript
    async afterRunCallback(invocationContext: InvocationContext): Promise<void> {
        // ์—ฌ๊ธฐ์— ๊ตฌํ˜„์„ ์ž‘์„ฑํ•ฉ๋‹ˆ๋‹ค
    }
    ```

```java
@Override
public Completable afterRunCallback(InvocationContext invocationContext) {
  return Completable.complete();
}

Python์—์„œ ADK๋Š” ํ”Œ๋Ÿฌ๊ทธ์ธ์— ๋‘ ๊ฐ€์ง€ ์ˆ˜๋ช… ์ฃผ๊ธฐ ์ด๋ฒคํŠธ๋ฅผ ์ถ”๊ฐ€๋กœ ์•Œ๋ฆฝ๋‹ˆ๋‹ค.

  • on_run_error_callback(*, invocation_context, error): ์ฒ˜๋ฆฌ๋˜์ง€ ์•Š์€ ์˜ˆ์™ธ๋กœ ์ธํ•ด ์‹คํ–‰์ด ์‹คํŒจํ•  ๋•Œ after_run_callback ๋Œ€์‹  ์‹คํ–‰๋ฉ๋‹ˆ๋‹ค. ์ด ์ฝœ๋ฐฑ์€ ์‹คํŒจ๋งŒ ๊ด€์ฐฐํ•˜๋ฉฐ ๋ฐ˜ํ™˜ ๊ฐ’์€ ๋ฌด์‹œ๋˜๊ณ  ์›๋ž˜ ์˜ˆ์™ธ๊ฐ€ ๊ทธ๋Œ€๋กœ ๋ฐœ์ƒํ•ฉ๋‹ˆ๋‹ค.
  • close(): ์‹คํ–‰๋‹น ํ•œ ๋ฒˆ์ด ์•„๋‹ˆ๋ผ await runner.close()๋กœ Runner๋ฅผ ๋‹ซ์„ ๋•Œ ํ”Œ๋Ÿฌ๊ทธ์ธ๋‹น ํ•œ ๋ฒˆ ์‹คํ–‰๋ฉ๋‹ˆ๋‹ค. HTTP ํด๋ผ์ด์–ธํŠธ๋‚˜ ์ธก์ •ํ•ญ๋ชฉ ๋‚ด๋ณด๋‚ด๊ธฐ(exporter)์™€ ๊ฐ™์ด ํ”Œ๋Ÿฌ๊ทธ์ธ์ด ์†Œ์œ ํ•œ ๋ฆฌ์†Œ์Šค๋ฅผ ํ•ด์ œํ•˜๋Š” ๋ฐ ์‚ฌ์šฉํ•˜์„ธ์š”. ๊ฐ close() ํ˜ธ์ถœ์€ ๋Ÿฌ๋„ˆ์˜ plugin_close_timeout(๊ธฐ๋ณธ๊ฐ’ 5์ดˆ)์œผ๋กœ ์ œํ•œ๋ฉ๋‹ˆ๋‹ค.

์–ธ์–ด๋ณ„ ๋น ๋ฅธ ์ฐธ๊ณ 

ํ”Œ๋Ÿฌ๊ทธ์ธ ์ฝœ๋ฐฑ์„ ์–ธ์–ด๋ณ„๋กœ ํ•œ ๋ฒˆ ๋” ์ •๋ฆฌํ•ด ๋‘๋ฉด, English ์›๋ฌธ๊ณผ์˜ ํƒญ ๊ตฌ์กฐ๋„ ๋” ๋น„์Šทํ•ด์ง‘๋‹ˆ๋‹ค.

์‚ฌ์šฉ์ž ๋ฉ”์‹œ์ง€ ์ฝœ๋ฐฑ ์˜ˆ์‹œ

async def on_user_message_callback(self, *, invocation_context, user_message):
    return user_message
async onUserMessageCallback(invocationContext, userMessage) {
  return userMessage;
}
@Override
public Maybe<Content> onUserMessageCallback(
  InvocationContext invocationContext, Content userMessage) {
  return Maybe.empty();
}

๋Ÿฌ๋„ˆ ์ข…๋ฃŒ ์ฝœ๋ฐฑ ์˜ˆ์‹œ

async def after_run_callback(self, *, invocation_context):
    return None
async afterRunCallback(invocationContext) {
  // cleanup
}
@Override
public Completable afterRunCallback(InvocationContext invocationContext) {
  return Completable.complete();
}

๋‹ค์Œ ๋‹จ๊ณ„

ADK ํ”„๋กœ์ ํŠธ์— ํ”Œ๋Ÿฌ๊ทธ์ธ์„ ๊ฐœ๋ฐœํ•˜๊ณ  ์ ์šฉํ•˜๊ธฐ ์œ„ํ•œ ๋‹ค์Œ ๋ฆฌ์†Œ์Šค๋ฅผ ํ™•์ธํ•˜์„ธ์š”.