맞춤 기능

·3분 읽기

원문: Koog Documentation — custom-features 이 글은 Koog 공식 문서의 custom-features 페이지를 한국어로 옮긴 번역본입니다. 문서 구조와 링크 의미를 유지하되, MkDocs 전용 UI 문법은 블로그에서 읽기 좋도록 정리했습니다.

맞춤 기능

기능은 런타임 시 AI 에이전트의 기능을 확장하고 향상시키는 방법을 제공합니다. 모듈식으로 설계되었습니다. 구성 가능하므로 필요에 따라 혼합하고 일치시킬 수 있습니다.

Koog에서 바로 사용할 수 있는 features 외에도 다음을 구현할 수도 있습니다. 적절한 기능 인터페이스를 확장하여 자체 기능을 구현합니다. 이 페이지에서는 현재 Koog API를 사용하여 고유한 기능을 위한 기본 구성 요소를 제시합니다.

기능 인터페이스

Koog는 사용자 정의 기능을 구현하기 위해 확장할 수 있는 다음 인터페이스를 제공합니다.

참고 그래프 기반, 기능적, 플래너 에이전트에 설치할 수 있는 사용자 정의 기능을 생성하려면 다음을 수행해야 합니다. 모든 인터페이스를 구현합니다.

사용자 정의 기능 구현

사용자 정의 기능을 구현하려면 다음 단계에 따라 기능 구조를 생성해야 합니다.

  1. 피쳐 클래스를 작성합니다.
  2. 구성 클래스를 정의합니다. 구성 클래스는 FeatureConfig 클래스의 확장입니다.
  3. 다음 인터페이스 중 일부 또는 전부를 구현하는 동반 객체를 만듭니다: AIAgentGraphFeature, AIAgentFunctionalFeature, AIAgentPlannerFeature.
  4. 에이전트 파이프라인에서 기능 식별 및 검색에 사용되는 고유한 스토리지 키를 기능에 제공하세요. 그만큼 키는 에이전트에 등록된 모든 기능을 포함하는 에이전트 파이프라인의 내부 맵 내부에서 사용됩니다. 에이전트를 실행하면 등록된 모든 기능을 처리해야 하며 키는 이 맵에서 기능을 검색하는 데 사용됩니다.
  5. 필요한 메서드를 구현합니다.

아래 코드 샘플은 그래프 기반으로 설치할 수 있는 사용자 정의 기능을 구현하기 위한 일반적인 패턴을 보여줍니다. 기능 및 계획 에이전트:

1class MyFeature(val someProperty: String) {2    class Config : FeatureConfig() {3        var configProperty: String = "default"4    }56    companion object Feature : AIAgentGraphFeature<Config, MyFeature>, AIAgentFunctionalFeature<Config, MyFeature>, AIAgentPlannerFeature<Config, MyFeature> {7        // Unique storage key for retrieval in contexts8        override val key = createStorageKey<MyFeature>("my-feature")9        override fun createInitialConfig(agentConfig: AIAgentConfig): Config = Config()1011        // Feature installation for graph-based agents12        override fun install(config: Config, pipeline: AIAgentGraphPipeline) : MyFeature {13            val feature = MyFeature(config.configProperty)1415            pipeline.interceptAgentStarting(this) { context ->16                // Event handler implementation17            }18            return feature19        }2021        // Feature installation for functional agents22        override fun install(config: Config, pipeline: AIAgentFunctionalPipeline) : MyFeature {23            val feature = MyFeature(config.configProperty)2425            pipeline.interceptAgentStarting(this) { context ->26                // Event handler implementation27            }28            return feature29        }3031        // Feature installation for planner agents32        override fun install(config: Config, pipeline: AIAgentPlannerPipeline) : MyFeature {33            val feature = MyFeature(config.configProperty)3435            pipeline.interceptAgentStarting(this) { context ->36                // Event handler implementation37            }38            return feature39        }40    }41}

에이전트를 생성할 때 install 방법을 사용하여 기능을 설치하십시오.

1val agent = AIAgent(2    promptExecutor = simpleOpenAIExecutor(System.getenv("OPENAI_API_KEY")),3    systemPrompt = "You are a helpful assistant. Answer user questions concisely.",4    llmModel = OpenAIModels.Chat.GPT4o5) {6    install(MyFeature) {7        configProperty = "value"8    }9}

파이프라인 인터셉터

인터셉터는 에이전트 실행 파이프라인에 연결할 수 있는 에이전트 수명 주기의 다양한 지점을 나타냅니다. 사용자 정의 논리를 구현하십시오. Koog에는 다양한 관찰에 사용할 수 있는 사전 정의된 인터셉터가 포함되어 있습니다. 이벤트.

다음은 기능의 install 메서드에서 등록할 수 있는 인터셉터입니다. 나열된 인터셉터는 다음과 같습니다. 유형별로 그룹화하여 그래프 기반, 기능적, 플래너 에이전트 파이프라인에 적용합니다. 소음을 줄이고 비용을 최적화하기 위해 실제 기능을 개발할 때 해당 기능에 필요한 인터셉터만 등록하세요.

에이전트 및 환경 수명주기:

  • interceptEnvironmentCreated: 에이전트 환경이 생성될 때 이를 변환합니다.
  • interceptAgentStarting: 에이전트 실행이 시작되기 전에 호출됩니다.
  • interceptAgentCompleted: 에이전트 실행이 성공적으로 완료되면 호출됩니다.
  • interceptAgentExecutionFailed: 에이전트 실행이 실패할 때 호출됩니다.
  • interceptAgentClosing: 에이전트 실행이 종료되기 직전에 호출됩니다(정리 지점).

전략 수명주기:

  • interceptStrategyStarting: 전략 실행이 시작되기 전에 호출됩니다.
  • interceptStrategyCompleted: 전략 실행이 성공적으로 완료되면 호출됩니다.

LLM 통화 수명 주기:

  • interceptLLMCallStarting: LLM 호출 전에 호출됩니다.
  • interceptLLMCallFailed: LLM 호출이 실패할 때 호출됩니다(기본 프롬프트 실행기 또는 조정 호출이 발생함).
  • interceptLLMCallCompleted: LLM 호출 후 호출됩니다.

LLM 스트리밍 수명주기:

  • interceptLLMStreamingStarting: 스트리밍이 시작되기 전에 호출됩니다.
  • interceptLLMStreamingFrameReceived: 수신된 각 스트림 프레임에 대해 호출됩니다.
  • interceptLLMStreamingFailed: 스트리밍이 실패할 때 호출됩니다.
  • interceptLLMStreamingCompleted: 스트리밍이 완료된 후 호출됩니다.

도구 호출 수명 주기:

  • interceptToolCallStarting: 도구 호출 전에 호출됩니다.
  • interceptToolValidationFailed: 도구 입력 유효성 검사가 실패할 때 호출됩니다.
  • interceptToolCallFailed: 도구 실행이 실패할 때 호출됩니다.
  • interceptToolCallCompleted: 도구가 완료된 후(결과 포함) 호출됩니다.

그래프 기반 에이전트에 특정한 인터셉터

다음 인터셉터는 AIAgentGraphPipeline에서만 사용할 수 있으며 노드 및 하위 그래프 수명 주기 이벤트를 관찰할 수 있게 해줍니다.

노드 실행 수명 주기:

  • interceptNodeExecutionStarting: 노드 실행이 시작되기 전에 호출됩니다.
  • interceptNodeExecutionCompleted: 노드 실행이 완료된 후 호출됩니다.
  • interceptNodeExecutionFailed: 오류로 인해 노드 실행이 실패할 때 호출됩니다.

하위 그래프 실행 수명 주기:

  • interceptSubgraphExecutionStarting: 하위 그래프가 실행되기 직전에 호출됩니다.
  • interceptSubgraphExecutionCompleted: 하위 그래프 실행이 완료된 후 호출됩니다.
  • interceptSubgraphExecutionFailed: 하위 그래프 실행이 실패할 때 호출됩니다.

특정 유형의 이벤트를 처리하는 기능의 경우 해당 파이프라인 인터셉터를 등록해야 합니다.

에이전트 이벤트 필터링

에이전트에 기능을 설치할 때 해당 기능에 등록된 모든 이벤트를 처리하고 싶지 않을 수 있습니다. 받는 사람 일부 이벤트를 필터링하려면 FeatureConfig.setEventFilter 기능을 사용하여 필터를 적용합니다.

다음 예에서는 기능에 대해 LLM 호출 시작 및 종료 이벤트만 허용하는 방법을 보여줍니다.

1install(MyFeature) {2    setEventFilter { context ->3        context.eventType is AgentLifecycleEventType.LLMCallStarting ||4            context.eventType is AgentLifecycleEventType.LLMCallCompleted5    }6}

기능에 대한 이벤트 필터링 비활성화

기능 논리가 전체 에이전트 이벤트 구조에 의존하는 경우 이벤트 필터링으로 인해 예기치 않은 동작이 발생할 수 있습니다. 받는 사람 이를 방지하려면 기능을 구현할 때 setEventFilter을 재정의하여 이벤트 필터링을 비활성화해야 합니다. 기능을 설치할 때 설정된 사용자 정의 필터를 무시하도록 기능을 구성합니다.

전체 에이전트 이벤트 스트림 처리에 의존하는 기능의 예는 OpenTelemetry입니다. 상속된 범위 구조를 구성하기 위해 에이전트 이벤트 구조를 완성합니다.

다음은 기능에 대한 이벤트 필터링을 비활성화하는 방법의 예입니다.

1class MyFeatureConfig : FeatureConfig() {2    override fun setEventFilter(filter: (AgentLifecycleEventContext) -> Boolean) {3        // Deactivate event filtering for the feature4        throw UnsupportedOperationException("Event filtering is not allowed.")5    }6}

예: 기본 로깅 기능

다음 예에서는 에이전트 수명 주기 이벤트를 기록하는 기본 로깅 기능을 구현하는 방법을 보여줍니다. 특징으로는 그래프 기반, 기능적 및 플래너 에이전트를 사용할 수 있어야 하며, 모든 에이전트 유형에 공통적인 인터셉터는 다음과 같습니다. 코드 중복을 피하기 위해 installCommon 메서드에 구현되었습니다. 개인에게 특정한 인터셉터 에이전트 유형은 installGraphPipeline, installFunctionalPipelineinstallPlannerPipeline에서 구현됩니다. 방법.

1class LoggingFeature(val loggerName: String) {2    class Config : FeatureConfig() {3        var loggerName: String = "agent-logs"4    }56    companion object Feature :7        AIAgentGraphFeature<Config, LoggingFeature>,8        AIAgentFunctionalFeature<Config, LoggingFeature>,9        AIAgentPlannerFeature<Config, LoggingFeature> {1011        override val key = createStorageKey<LoggingFeature>("logging-feature")1213        override fun createInitialConfig(agentConfig: AIAgentConfig): Config = Config()1415        override fun install(config: Config, pipeline: AIAgentGraphPipeline) : LoggingFeature {16            val logging = LoggingFeature(config.loggerName)17            val logger = KotlinLogging.logger(config.loggerName)1819            installGraphPipeline(pipeline, logger)2021            return logging22        }2324        override fun install(config: Config, pipeline: AIAgentFunctionalPipeline) : LoggingFeature {25            val logging = LoggingFeature(config.loggerName)26            val logger = KotlinLogging.logger(config.loggerName)2728            installFunctionalPipeline(pipeline, logger)2930            return logging31        }3233        override fun install(config: Config, pipeline: AIAgentPlannerPipeline) : LoggingFeature {34            val logging = LoggingFeature(config.loggerName)35            val logger = KotlinLogging.logger(config.loggerName)3637            installPlannerPipeline(pipeline, logger)3839            return logging40        }4142        private fun installCommon(43            pipeline: AIAgentPipeline,44            logger: KLogger,45        ) {46            pipeline.interceptAgentStarting(this) { e ->47                logger.info { "Agent starting: runId=${e.runId}" }48            }49            pipeline.interceptStrategyStarting(this) { e ->50                logger.info { "Strategy ${e.strategy.name} starting" }51            }52            pipeline.interceptLLMCallStarting(this) { e ->53                logger.info { "Making LLM call with ${e.tools.size} tools" }54            }55            pipeline.interceptLLMCallCompleted(this) { e ->56                logger.info { "Received ${e.responses.size} response(s)" }57            }58        }5960        private fun installGraphPipeline(61            pipeline: AIAgentGraphPipeline,62            logger: KLogger,63        ) {64            installCommon(pipeline, logger)6566            pipeline.interceptNodeExecutionStarting(this) { e ->67                logger.info { "Node ${e.node.name} input: ${e.input}" }68            }69            pipeline.interceptNodeExecutionCompleted(this) { e ->70                logger.info { "Node ${e.node.name} output: ${e.output}" }71            }72        }7374        private fun installFunctionalPipeline(75            pipeline: AIAgentFunctionalPipeline,76            logger: KLogger77        ) {78            installCommon(pipeline, logger)79        }8081        private fun installPlannerPipeline(82            pipeline: AIAgentPlannerPipeline,83            logger: KLogger84        ) {85            installCommon(pipeline, logger)86        }87    }88}

다음은 에이전트에 사용자 정의 로깅 기능을 설치하는 방법의 예입니다. 예제에서는 기본 기능을 보여줍니다. 로거 이름을 지정할 수 있는 사용자 정의 구성 속성 loggerName과 함께 설치:

1val agent = AIAgent(2    promptExecutor = simpleOpenAIExecutor(System.getenv("OPENAI_API_KEY")),3    systemPrompt = "You are a helpful assistant. Answer user questions concisely.",4    llmModel = OpenAIModels.Chat.GPT4o5) {6    install(LoggingFeature) {7        loggerName = "my-custom-logger"8    }9}1011agent.run("What is Kotlin?")