A2A 및 Koog 통합
원문: 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}개요
통합을 통해 두 가지 주요 패턴이 가능해졌습니다.
- Kog 에이전트를 A2A 서버로 노출 - A2A 프로토콜을 통해 Koog 에이전트를 검색하고 액세스할 수 있도록 합니다.
- 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 기능은 다음 작업에 사용되는 RequestContext 및 SessionEventProcessor 엔터티에 대한 액세스를 제공합니다.
Koog 에이전트 내부의 A2A 클라이언트와 통신합니다.
기능을 설치하려면 에이전트에서 install 함수를 호출하고 RequestContext 및 SessionEventProcessor와 함께 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 기능을 사용할 수 있습니다.
클라이언트의 원리는 서버와 동일합니다. 즉, 기능을 설치하고 RequestContext 및 SessionEventProcessor와 함께 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 )