콘텐츠로 이동

콜백을 위한 디자인 패턴 및 모범 사례

콜백은 에이전트 라이프사이클에 개입할 수 있는 강력한 훅(hook)을 제공합니다. 다음은 ADK에서 콜백을 효과적으로 활용하는 방법을 보여주는 일반적인 디자인 패턴과 구현을 위한 모범 사례입니다.

디자인 패턴

이 패턴들은 콜백을 사용하여 에이전트의 동작을 향상시키거나 제어하는 대표적인 방법들을 보여줍니다.

1. 가드레일 및 정책 강제

패턴 개요: 요청이 LLM이나 도구에 도달하기 전에 가로채서 규칙을 강제합니다.

구현: - before_model_callback을 사용하여 LlmRequest 프롬프트를 검사합니다. - before_tool_callback을 사용하여 도구의 인자(argument)를 검사합니다. - 정책 위반(예: 금지된 주제, 비속어)이 감지되면: - 미리 정의된 응답(LlmResponse 또는 dict/Map)을 반환하여 작업을 차단합니다. - 선택적으로 context.state를 업데이트하여 위반 사항을 기록합니다.

사용 사례 예시: before_model_callbackllm_request.contents에 민감한 키워드가 있는지 확인하고, 발견되면 "이 요청을 처리할 수 없습니다"라는 표준 LlmResponse를 반환하여 LLM 호출을 막습니다.

2. 동적 상태 관리

패턴 개요: 콜백 내에서 세션 상태를 읽고 써서 에이전트의 행동을 컨텍스트에 맞게 조정하고 단계 간에 데이터를 전달합니다.

구현: - callback_context.state 또는 tool_context.state에 접근합니다. - 변경 사항(state['key'] = value)은 후속 Event.actions.state_delta에서 자동으로 추적됩니다. - 변경 내용은 SessionService에 의해 영구 저장됩니다.

사용 사례 예시: after_tool_callback이 도구 결과에서 얻은 transaction_idtool_context.state['last_transaction_id']에 저장합니다. 이후 before_agent_callbackstate['user_tier']를 읽어 에이전트의 인사말을 맞춤 설정할 수 있습니다.

3. 로깅 및 모니터링

패턴 개요: 관찰 가능성(observability) 및 디버깅을 위해 특정 라이프사이클 지점에 상세한 로깅을 추가합니다.

구현: - 콜백(예: before_agent_callback, after_tool_callback, after_model_callback)을 구현합니다. - 다음을 포함하는 구조화된 로그를 출력하거나 전송합니다: - 에이전트 이름 - 도구 이름 - 호출 ID (Invocation ID) - 컨텍스트나 인자에서 가져온 관련 데이터

사용 사례 예시: INFO: [Invocation: e-123] Before Tool: search_api - Args: {'query': 'ADK'}와 같은 로그 메시지를 기록합니다.

4. 캐싱

패턴 개요: 결과를 캐싱하여 중복되는 LLM 호출이나 도구 실행을 방지합니다.

구현 단계: 1. 작업 이전: before_model_callback 또는 before_tool_callback에서: - 요청/인자를 기반으로 캐시 키를 생성합니다. - context.state(또는 외부 캐시)에서 이 키를 확인합니다. - 키를 찾으면 캐시된 LlmResponse나 결과를 직접 반환합니다.

  1. 작업 이후: 캐시 미스(cache miss)가 발생한 경우:
    • 해당하는 after_ 콜백을 사용하여 새로운 결과를 캐시에 해당 키로 저장합니다.

사용 사례 예시: get_stock_price(symbol)에 대한 before_tool_callbackstate[f"cache:stock:{symbol}"]을 확인합니다. 값이 있으면 캐시된 가격을 반환하고, 없으면 API 호출을 허용한 뒤 after_tool_callback이 결과를 상태 키에 저장합니다.

5. 요청/응답 수정

패턴 개요: 데이터가 LLM/도구로 전송되기 직전이나 수신된 직후에 데이터를 변경합니다.

구현 옵션: - before_model_callback: llm_request를 수정합니다(예: state를 기반으로 시스템 지침 추가). - after_model_callback: 반환된 LlmResponse를 수정합니다(예: 텍스트 서식 지정, 콘텐츠 필터링). - before_tool_callback: 도구의 args 딕셔너리(Java에서는 Map)를 수정합니다. - after_tool_callback: tool_response 딕셔너리(Java에서는 Map)를 수정합니다.

사용 사례 예시: context.state['lang'] == 'es'인 경우, before_model_callbackllm_request.config.system_instruction에 "사용자 언어 설정: 스페인어"를 추가합니다.

6. 조건부 단계 건너뛰기

패턴 개요: 특정 조건에 따라 표준 작업(에이전트 실행, LLM 호출, 도구 실행)을 방지합니다.

구현: - before_ 콜백에서 값을 반환하여 정상적인 실행을 건너뜁니다: - before_agent_callback에서 Content 반환 - before_model_callback에서 LlmResponse 반환 - before_tool_callback에서 dict 반환 - 프레임워크는 이 반환된 값을 해당 단계의 결과로 해석합니다.

사용 사례 예시: before_tool_callbacktool_context.state['api_quota_exceeded']를 확인합니다. 만약 True이면 {'error': 'API 할당량 초과'}를 반환하여 실제 도구 함수가 실행되는 것을 막습니다.

7. 도구별 작업 (인증 및 요약 제어)

패턴 개요: 주로 인증 및 도구 결과의 LLM 요약 제어와 같이 도구 라이프사이클에 특화된 작업을 처리합니다.

구현: 도구 콜백(before_tool_callback, after_tool_callback) 내에서 ToolContext를 사용합니다:

  • 인증: before_tool_callback에서 자격 증명이 필요하지만 찾을 수 없는 경우(예: tool_context.get_auth_response 또는 상태 확인을 통해), tool_context.request_credential(auth_config)를 호출합니다. 이는 인증 흐름을 시작합니다.
  • 요약: 도구의 원시 딕셔너리 출력을 LLM에 다시 전달하거나 직접 표시해야 할 경우(기본 LLM 요약 단계를 건너뛸 때), tool_context.actions.skip_summarization = True로 설정합니다.

사용 사례 예시: 보안 API를 위한 before_tool_callback은 상태에서 인증 토큰을 확인하고, 없으면 request_credential을 호출합니다. 구조화된 JSON을 반환하는 도구의 after_tool_callbackskip_summarization = True를 설정할 수 있습니다.

8. 아티팩트 처리

패턴 개요: 에이전트 라이프사이클 동안 세션 관련 파일이나 대용량 데이터 블롭을 저장하거나 로드합니다.

구현: - 저장: callback_context.save_artifact / await tool_context.save_artifact를 사용하여 데이터를 저장합니다: - 생성된 보고서 - 로그 - 중간 데이터 - 로드: load_artifact를 사용하여 이전에 저장된 아티팩트를 검색합니다. - 추적: 변경 사항은 Event.actions.artifact_delta를 통해 추적됩니다.

사용 사례 예시: "generate_report" 도구의 after_tool_callbackawait tool_context.save_artifact("report.pdf", report_part)를 사용하여 출력 파일을 저장합니다. before_agent_callbackcallback_context.load_artifact("agent_config.json")를 사용하여 구성 아티팩트를 로드할 수 있습니다.

콜백 모범 사례

설계 원칙

단일 책임 유지: 각 콜백은 로깅, 유효성 검사 등 단 하나의 잘 정의된 목적을 갖도록 설계하십시오. 여러 기능을 합친 거대한 콜백은 피하십시오.

성능 유의: 콜백은 에이전트의 처리 루프 내에서 동기적으로 실행됩니다. 오래 실행되거나 블로킹되는 작업(네트워크 호출, 무거운 계산)을 피하십시오. 필요한 경우 오프로드하되, 이는 복잡성을 증가시킬 수 있음을 인지해야 합니다.

오류 처리

오류를 우아하게 처리: - 콜백 함수 내에서 try...except/catch 블록을 사용하십시오. - 오류를 적절히 로깅하십시오. - 에이전트 호출을 중단할지 또는 복구를 시도할지 결정하십시오. - 콜백 오류로 인해 전체 프로세스가 중단되지 않도록 하십시오.

상태 관리

상태를 신중하게 관리: - context.state를 읽고 쓸 때 신중해야 합니다. - 변경 사항은 현재 호출 내에서 즉시 보이며, 이벤트 처리가 끝날 때 영구 저장됩니다. - 의도하지 않은 부작용을 피하기 위해 광범위한 구조를 수정하기보다는 특정 상태 키를 사용하십시오. - 특히 영구 SessionService 구현 시 명확성을 위해 상태 접두사(State.APP_PREFIX, State.USER_PREFIX, State.TEMP_PREFIX) 사용을 고려하십시오.

신뢰성

멱등성 고려: 콜백이 외부 부작용을 일으키는 작업(예: 외부 카운터 증가)을 수행하는 경우, 프레임워크나 애플리케이션의 잠재적인 재시도에 대비하여 가능하다면 멱등성(동일한 입력으로 여러 번 실행해도 안전함)을 갖도록 설계하십시오.

테스트 및 문서화

철저한 테스트: - 모의(mock) 컨텍스트 객체를 사용하여 콜백 함수를 단위 테스트하십시오. - 통합 테스트를 수행하여 콜백이 전체 에이전트 흐름 내에서 올바르게 작동하는지 확인하십시오.

명확성 보장: - 콜백 함수에 서술적인 이름을 사용하십시오. - 함수의 목적, 실행 시점, 부작용(특히 상태 수정)을 설명하는 명확한 독스트링(docstring)을 추가하십시오.

올바른 컨텍스트 타입 사용: 항상 제공된 특정 컨텍스트 타입(CallbackContext는 에이전트/모델용, ToolContext는 도구용)을 사용하여 적절한 메서드와 속성에 접근할 수 있도록 하십시오.

이러한 패턴과 모범 사례를 적용하면 콜백을 효과적으로 사용하여 ADK에서 더 견고하고, 관찰 가능하며, 맞춤화된 에이전트 동작을 만들 수 있습니다.