A2A 및 Koog 통합

·3분 읽기

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

A2A 및 Koog 통합

Koog는 A2A 프로토콜과의 원활한 통합을 제공하므로 Koog 에이전트를 A2A 서버로 노출하고 연결할 수 있습니다. Koog 에이전트를 다른 A2A 호환 에이전트로 전환합니다.

종속성

A2A Koog 통합에는 사용 사례에 따라 특정 기능 모듈이 필요합니다.

Koog 에이전트를 A2A 서버로 노출하는 경우

build.gradle.kts에 다음 종속성을 추가합니다.

1dependencies {2    // Koog A2A server integration feature3    implementation("ai.koog:agents-features-a2a-server:$koogVersion")45    // HTTP JSON-RPC transport6    implementation("ai.koog:a2a-transport-server-jsonrpc-http:$koogVersion")78    // Ktor server engine (choose one that fits your needs)9    implementation("io.ktor:ktor-server-netty:$ktorVersion")10}

Koog 에이전트를 A2A 에이전트에 연결하는 경우

build.gradle.kts에 다음 종속성을 추가합니다.

1dependencies {2    // Koog A2A client integration feature3    implementation("ai.koog:agents-features-a2a-client:$koogVersion")45    // HTTP JSON-RPC transport6    implementation("ai.koog:a2a-transport-client-jsonrpc-http:$koogVersion")78    // Ktor client engine (choose one that fits your needs)9    implementation("io.ktor:ktor-client-cio:$ktorVersion")10}

개요

통합을 통해 두 가지 주요 패턴이 가능해졌습니다.

  1. Kog 에이전트를 A2A 서버로 노출 - A2A 프로토콜을 통해 Koog 에이전트를 검색하고 액세스할 수 있도록 합니다.
  2. Kog 에이전트를 A2A 에이전트에 연결 - Koog 에이전트가 다른 A2A 호환 에이전트와 통신할 수 있도록 합니다.

Koog 에이전트를 A2A 서버로 노출

A2A 기능으로 Koog 에이전트 정의

먼저 Koog 에이전트를 정의해 보겠습니다. 에이전트의 논리는 다양할 수 있지만 다음은 기본 단일 실행 에이전트의 예입니다. 도구. 에이전트는 사용자로부터 메시지를 받아 llm에 전달합니다. llm 응답에 도구 호출이 포함된 경우 에이전트는 도구를 실행하고 결과를 llm에 전달합니다. llm 응답에 보조 메시지가 포함되어 있으면 에이전트는 보조 메시지를 사용자에게 보내고 완료합니다.

입력 크기 조정 시 에이전트는 작업 제출 이벤트를 입력 메시지와 함께 A2A 클라이언트에 보냅니다. 각 도구 호출에서 에이전트는 도구 호출 및 결과와 함께 작업 작업 이벤트를 A2A 클라이언트에 보냅니다. 보조 메시지에서 에이전트는 보조 메시지와 함께 작업 완료 이벤트를 A2A 클라이언트에 보냅니다.

1/**2 * Create a Koog agent with A2A feature3 */4@OptIn(ExperimentalUuidApi::class)5private fun createAgent(6    context: RequestContext<MessageSendParams>,7    eventProcessor: SessionEventProcessor,8) = AIAgent(9    promptExecutor = MultiLLMPromptExecutor(10        LLMProvider.Google to GoogleLLMClient("api-key")11    ),12    toolRegistry = ToolRegistry {13        // Declare tools here14    },15    strategy = strategy<A2AMessage, Unit>("test") {16        val nodeSetup by node<A2AMessage, Unit> { inputMessage ->17            // Convenience function to transform A2A message into Koog message18            val input = inputMessage.toKoogMessage()19            llm.writeSession {20                appendPrompt {21                    message(input)22                }23            }24            // Send update event to A2A client25            withA2AAgentServer {26                sendTaskUpdate("Request submitted: ${input.content}", TaskState.Submitted)27            }28        }2930        // Calling llm31        val nodeLLMRequest by node<Unit, Message> {32            llm.writeSession {33                requestLLM()34            }35        }3637        // Executing tool38        val nodeProcessTool by node<Message.Tool.Call, Unit> { toolCall ->39            withA2AAgentServer {40                sendTaskUpdate("Executing tool: ${toolCall.content}", TaskState.Working)41            }4243            val toolResult = environment.executeTool(toolCall)4445            llm.writeSession {46                appendPrompt {47                    tool {48                        result(toolResult)49                    }50                }51            }52            withA2AAgentServer {53                sendTaskUpdate("Tool result: ${toolResult.content}", TaskState.Working)54            }55        }5657        // Sending assistant message58        val nodeProcessAssistant by node<String, Unit> { assistantMessage ->59            withA2AAgentServer {60                sendTaskUpdate(assistantMessage, TaskState.Completed)61            }62        }6364        edge(nodeStart forwardTo nodeSetup)65        edge(nodeSetup forwardTo nodeLLMRequest)6667        // If a tool call is returned from llm, forward to the tool processing node and then back to llm68        edge(nodeLLMRequest forwardTo nodeProcessTool onToolCall { true })69        edge(nodeProcessTool forwardTo nodeLLMRequest)7071        // If an assistant message is returned from llm, forward to the assistant processing node and then to finish72        edge(nodeLLMRequest forwardTo nodeProcessAssistant onAssistantMessage { true })73        edge(nodeProcessAssistant forwardTo nodeFinish)74    },75    agentConfig = AIAgentConfig(76        prompt = prompt("agent") { system("You are a helpful assistant.") },77        model = GoogleModels.Gemini2_5Pro,78        maxAgentIterations = 1079    ),80) {81    install(A2AAgentServer) {82        this.context = context83        this.eventProcessor = eventProcessor84    }85}8687/**88 * Convenience function to send task update event to A2A client89 * @param content The message content90 * @param state The task state91 */92@OptIn(ExperimentalUuidApi::class)93private suspend fun A2AAgentServer.sendTaskUpdate(94    content: String,95    state: TaskState,96) {97    val message = A2AMessage(98        messageId = Uuid.random().toString(),99        role = Role.Agent,100        parts = listOf(101            TextPart(content)102        ),103        contextId = context.contextId,104        taskId = context.taskId,105    )106107    val task = Task(108        id = context.taskId,109        contextId = context.contextId,110        status = TaskStatus(111            state = state,112            message = message,113            timestamp = Clock.System.now(),114        )115    )116    eventProcessor.sendTaskEvent(task)117}

A2AAgentServer 기능 메커니즘

A2AAgentServer은 Koog 에이전트와 A2A 프로토콜 간의 원활한 통합을 가능하게 하는 Koog 에이전트 기능입니다. A2AAgentServer 기능은 다음 작업에 사용되는 RequestContextSessionEventProcessor 엔터티에 대한 액세스를 제공합니다. Koog 에이전트 내부의 A2A 클라이언트와 통신합니다.

기능을 설치하려면 에이전트에서 install 함수를 호출하고 RequestContextSessionEventProcessor와 함께 A2AAgentServer 기능을 전달합니다.

1// Install the feature2install(A2AAgentServer) {3    this.context = context4    this.eventProcessor = eventProcessor5}

Koog 에이전트 전략에서 이러한 엔터티에 액세스하기 위해 이 기능은 에이전트 노드가 실행 컨텍스트 내에서 A2A 서버 기능에 액세스할 수 있도록 하는 withA2AAgentServer 기능을 제공합니다. 설치된 A2AAgentServer 기능을 검색하여 이를 액션 블록의 수신자로 제공합니다.

1// Usage within agent nodes2withA2AAgentServer {3    // 'this' is now A2AAgentServer instance4    eventProcessor.sendTaskUpdate("Processing your request...", TaskState.Working)5}

A2A 서버 시작

서버 Koog 에이전트를 실행한 후 A2A 프로토콜을 통해 검색하고 액세스할 수 있습니다.

1val agentCard = AgentCard(2    name = "Koog Agent",3    url = "http://localhost:9999/koog",4    description = "Simple universal agent powered by Koog",5    version = "1.0.0",6    protocolVersion = "0.3.0",7    preferredTransport = TransportProtocol.JSONRPC,8    capabilities = AgentCapabilities(streaming = true),9    defaultInputModes = listOf("text"),10    defaultOutputModes = listOf("text"),11    skills = listOf(12        AgentSkill(13            id = "koog",14            name = "Koog Agent",15            description = "Universal agent powered by Koog. Supports tool calling.",16            tags = listOf("chat", "tool"),17        )18    )19)20// Server setup21val server = A2AServer(agentExecutor = KoogAgentExecutor(), agentCard = agentCard)22val transport = HttpJSONRPCServerTransport(server)23transport.start(engineFactory = Netty, port = 8080, path = "/chat", wait = true)

Koog 에이전트를 A2A 에이전트에 연결

A2A 클라이언트 생성 및 A2A 서버에 연결

1val transport = HttpJSONRPCClientTransport(url = "http://localhost:9999/koog")2val agentCardResolver =3    UrlAgentCardResolver(baseUrl = "http://localhost:9999", path = "/koog")4val client = A2AClient(transport = transport, agentCardResolver = agentCardResolver)56val agentId = "koog"7client.connect()

Koog 에이전트를 생성하고 A2AAgentClient 기능에 A2A 클라이언트 추가

Koog 에이전트에서 A2A 에이전트에 연결하려면 A2A 에이전트에 연결하기 위한 클라이언트 API를 제공하는 A2AAgentClient 기능을 사용할 수 있습니다. 클라이언트의 원리는 서버와 동일합니다. 즉, 기능을 설치하고 RequestContextSessionEventProcessor와 함께 A2AAgentClient 기능을 전달합니다.

1val agent = AIAgent(2    promptExecutor = MultiLLMPromptExecutor(3        LLMProvider.Google to GoogleLLMClient("api-key")4    ),5    toolRegistry = ToolRegistry {6        // declare tools here7    },8    strategy = strategy<String, Unit>("test") {910        val nodeCheckStreaming by nodeA2AClientGetAgentCard().transform { it.capabilities.streaming }1112        val nodeA2ASendMessageStreaming by nodeA2AClientSendMessageStreaming()13        val nodeA2ASendMessage by nodeA2AClientSendMessage()1415        val nodeProcessStreaming by node<Flow<Response<Event>>, Unit> {16            it.collect { response ->17                when (response.data) {18                    is Task -> {19                        // Process task20                    }2122                    is A2AMessage -> {23                        // Process message24                    }2526                    is TaskStatusUpdateEvent -> {27                        // Process task status update28                    }2930                    is TaskArtifactUpdateEvent -> {31                        // Process task artifact update32                    }33                }34            }35        }3637        val nodeProcessEvent by node<CommunicationEvent, Unit> { event ->38            when (event) {39                is Task -> {40                    // Process task41                }4243                is A2AMessage -> {44                    // Process message45                }46            }47        }4849        // If streaming is supported, send a message, process response and finish50        edge(nodeStart forwardTo nodeCheckStreaming transformed { agentId })51        edge(52            nodeCheckStreaming forwardTo nodeA2ASendMessageStreaming53                onCondition { it == true } transformed { buildA2ARequest(agentId) }54        )55        edge(nodeA2ASendMessageStreaming forwardTo nodeProcessStreaming)56        edge(nodeProcessStreaming forwardTo nodeFinish)5758        // If streaming is not supported, send a message, process response and finish59        edge(60            nodeCheckStreaming forwardTo nodeA2ASendMessage61                onCondition { it == false } transformed { buildA2ARequest(agentId) }62        )63        edge(nodeA2ASendMessage forwardTo nodeProcessEvent)64        edge(nodeProcessEvent forwardTo nodeFinish)6566        // If streaming is not supported, send a message, process response and finish67        edge(nodeCheckStreaming forwardTo nodeFinish onCondition { it == null }68            transformed { println("Failed to get agents card") }69        )7071    },72    agentConfig = AIAgentConfig(73        prompt = prompt("agent") { system("You are a helpful assistant.") },74        model = GoogleModels.Gemini2_5Pro,75        maxAgentIterations = 1076    ),77) {78    install(A2AAgentClient) {79        this.a2aClients = mapOf(agentId to client)80    }81}828384@OptIn(ExperimentalUuidApi::class)85private fun AIAgentGraphContextBase.buildA2ARequest(agentId: String): A2AClientRequest<MessageSendParams> =86    A2AClientRequest(87        agentId = agentId,88        callContext = ClientCallContext.Default,89        params = MessageSendParams(90            message = A2AMessage(91                messageId = Uuid.random().toString(),92                role = Role.User,93                parts = listOf(94                    TextPart(agentInput as String)95                )96            )97        )98    )