Koog 문서 번역 08: Graph-based Agents
원문: Koog Documentation — Graph-based Agents 이 글은 Koog 공식 문서의 Graph-based Agents 페이지를 한국어로 옮긴 번역본입니다. 문서 구조와 링크 의미를 유지하되, MkDocs 전용 UI 문법은 블로그에서 읽기 좋도록 정리했습니다.
그래프 기반 에이전트
그래프 기반 에이전트를 사용하면 동작을 명시적 상태 기계로 모델링합니다. 그래프 전략의 노드는 작업(LLM 호출, 도구 실행)을 나타냅니다. Edge는 노드 간의 데이터 흐름을 나타냅니다.
그래프 기반 에이전트의 주요 장점은 다음과 같습니다.
- 시각화하기 쉬움
- 상태 지속성
- 컴포저블 아키텍처
참고: Prerequisites
환경과 프로젝트가 다음 요구 사항을 충족하는지 확인하세요.
JDK 17+
Kotlin 2.2.0+
Gradle 8.0+ 또는 Maven 3.8+
Koog 패키지을 종속성으로 추가합니다.
Gradle(Kotlin)
12dependencies {34 implementation("ai.koog:koog-agents:0.7.1")56}7Gradle(그루비)
12dependencies {34 implementation 'ai.koog:koog-agents:0.7.1'56}7Maven
12<dependency>34 <groupId>ai.koog</groupId>56 <artifactId>koog-agents-jvm</artifactId>78 <version>0.7.1</version>910</dependency>11LLM 공급자로부터 API 키를 얻거나 Ollama를 통해 로컬 LLM을 실행하세요.
자세한 내용은 Quickstart을 참조하세요.
이 페이지의 예에서는 Ollama를 통해 로컬로 Llama 3.2를 실행한다고 가정합니다.
이 페이지에서는 basic agents에서 사용되는 전략 그래프를 다시 만드는 방법을 설명합니다. LLM에 요청을 보낸 다음 응답을 출력합니다(LLM이 보조 메시지로 응답한 경우). 또는 도구를 실행합니다(LLM이 도구 호출을 요청한 경우). 도구 호출의 경우 에이전트는 도구 결과를 LLM으로 보냅니다. 그런 다음 응답을 출력하거나 도구를 실행합니다.
다음은 전략 그래프의 그림입니다.
1---2config:3 flowchart:4 defaultRenderer: "elk"5---6graph TB7 subgraph nodeStart8 Input9 end10 11 subgraph nodeFinish12 Output13 end14 15 subgraph nodeSendInput16 llmRequest(Request LLM)17 end18 19 subgraph nodeExecuteTool20 executeTool(Execute tool call)21 end22 23 subgraph nodeSendToolResult24 sendToolResult(Request LLM)25 end26 27 Input --String--> llmRequest28 llmRequest --Message.Response--> onToolCall{{onToolCall}}29 llmRequest --Message.Response--> onAssistantMessage{{onAssistantMessage}}30 onAssistantMessage --String--> Output31 onToolCall --Message.Tool.Call--> executeTool --ReceivedToolResult--> sendToolResult32 sendToolResult --Message.Response--> onToolCall33 sendToolResult --Message.Response--> onAssistantMessage전략 그래프 작성
Koog에서는 AIAgentGraphStrategyBuilder을 사용하여 전략을 구현합니다.
모든 노드에 입력 및 출력 유형이 있는 것처럼
전략 전체는 일부 입력 및 출력 유형도 정의합니다.
이 예에서는 입력 및 출력 유형이 문자열이라고 가정합니다.
이는 이 전략을 구현하는 에이전트가 문자열을 예상하고 문자열을 반환한다는 것을 의미합니다.
전략을 생성하려면 두 개의 제네릭을 입력 및 출력 유형으로 사용하여 strategy() 함수를 사용하세요.
전략에 대한 고유 식별자를 제공하고 노드와 에지를 정의합니다.
Kotlin
1val calculatorAgentStrategy = strategy<String, String>("Simple calculator") {2 val nodeSendInput by nodeLLMRequest()3 val nodeExecuteTool by nodeExecuteTool()4 val nodeSendToolResult by nodeLLMSendToolResult()5 6 edge(nodeStart forwardTo nodeSendInput)7 edge(nodeSendInput forwardTo nodeFinish onAssistantMessage { true })8 edge(nodeSendInput forwardTo nodeExecuteTool onToolCall { true })9 edge(nodeExecuteTool forwardTo nodeSendToolResult)10 edge(nodeSendToolResult forwardTo nodeFinish onAssistantMessage { true })11 edge(nodeSendToolResult forwardTo nodeExecuteTool onToolCall { true })12}Java
1var calculatorAgentStrategy = AIAgentGraphStrategy.builder("Simple calculator")2 .withInput(String.class)3 .withOutput(String.class);45var nodeSendInput = AIAgentNode.llmRequest(true, "nodeSendInput");6var nodeExecuteTool = AIAgentNode.executeTool("nodeExecuteTool");7var nodeSendToolResult = AIAgentNode.llmSendToolResult("nodeSendToolResult");89calculatorAgentStrategy.edge(calculatorAgentStrategy.nodeStart, nodeSendInput);10calculatorAgentStrategy.edge(AIAgentEdge.builder()11 .from(nodeSendInput) 12 .to(calculatorAgentStrategy.nodeFinish)13 .onIsInstance(Message.Assistant.class)14 .transformed(Message.Assistant::getContent)15 .build());16calculatorAgentStrategy.edge(AIAgentEdge.builder()17 .from(nodeSendInput)18 .to(nodeExecuteTool)19 .onIsInstance(Message.Tool.Call.class)20 .build());21calculatorAgentStrategy.edge(nodeExecuteTool, nodeSendToolResult);22calculatorAgentStrategy.edge(AIAgentEdge.builder()23 .from(nodeSendToolResult)24 .to(calculatorAgentStrategy.nodeFinish)25 .onIsInstance(Message.Assistant.class)26 .transformed(Message.Assistant::getContent)27 .build());28calculatorAgentStrategy.edge(AIAgentEdge.builder()29 .from(nodeSendToolResult)30 .to(nodeExecuteTool)31 .onIsInstance(Message.Tool.Call.class)32 .build());이 예에서는 predefined nodes만 사용합니다. 하지만 custom nodes을 생성할 수도 있습니다.
모든 전략 그래프에는 nodeStart에서 edges으로 연결된 nodeFinish까지의 경로가 있어야 합니다.
간선에는 특정 간선을 따라갈 시기를 결정하는 조건이 있을 수 있습니다.
Edge는 이전 노드의 출력을 다음 노드로 전달하기 전에 변환할 수도 있습니다.
이는 출력 유형과 입력 유형이 일치하지 않는 노드를 연결하는 데 필요합니다.
이전 예에서 onToolCall { true }은 가장자리가 다음을 따른다는 것을 의미합니다.
이전 노드가 도구 호출 Message.Tool.Call을 반환한 경우에만 해당됩니다.
onAssistantMessage { true }을 사용하면 가장자리가 따라옵니다.
이전 노드가 보조 메시지 Message.Assistant을 반환한 경우에만 가능합니다.
이 기능은 보조 메시지의 내용도 추출하고,
nodeFinish는 문자열을 기대하기 때문에 Message.Assistant를 String로 효과적으로 변환합니다.
참고
onAssistantMessage {true} 대신 다음을 수행할 수 있습니다.
1onIsInstance(Message.Assistant::class) transformed { it.content }또는:
1onCondition { it is Message.Assistant } transformed { it.asAssistantMessage().content }에이전트 생성 및 실행
이 전략을 사용하여 에이전트 인스턴스를 생성하고 실행해 보겠습니다.
Kotlin
1val calculatorAgentStrategy = strategy<String, String>("Simple calculator") {2 val nodeSendInput by nodeLLMRequest()3 val nodeExecuteTool by nodeExecuteTool()4 val nodeSendToolResult by nodeLLMSendToolResult()56 edge(nodeStart forwardTo nodeSendInput)7 edge(nodeSendInput forwardTo nodeFinish onAssistantMessage { true })8 edge(nodeSendInput forwardTo nodeExecuteTool onToolCall { true })9 edge(nodeExecuteTool forwardTo nodeSendToolResult)10 edge(nodeSendToolResult forwardTo nodeFinish onAssistantMessage { true })11 edge(nodeSendToolResult forwardTo nodeExecuteTool onToolCall { true })12}1314val mathAgent = AIAgent(15 promptExecutor = simpleOllamaAIExecutor(),16 llmModel = OllamaModels.Meta.LLAMA_3_2,17 strategy = calculatorAgentStrategy18)1920fun main() = runBlocking {21 val result = mathAgent.run("Multiply 3 by 4, then multiply the result by 5, then add 10, then add 123.")22 println(result)23}Java
1var calculatorAgentStrategy = AIAgentGraphStrategy.builder("Simple calculator")2 .withInput(String.class)3 .withOutput(String.class);45var nodeSendInput = AIAgentNode.llmRequest(true, "nodeSendInput");6var nodeExecuteTool = AIAgentNode.executeTool("nodeExecuteTool");7var nodeSendToolResult = AIAgentNode.llmSendToolResult("nodeSendToolResult");89calculatorAgentStrategy.edge(calculatorAgentStrategy.nodeStart, nodeSendInput);10calculatorAgentStrategy.edge(AIAgentEdge.builder()11 .from(nodeSendInput) 12 .to(calculatorAgentStrategy.nodeFinish)13 .onIsInstance(Message.Assistant.class)14 .transformed(Message.Assistant::getContent)15 .build());16calculatorAgentStrategy.edge(AIAgentEdge.builder()17 .from(nodeSendInput)18 .to(nodeExecuteTool)19 .onIsInstance(Message.Tool.Call.class)20 .build());21calculatorAgentStrategy.edge(nodeExecuteTool, nodeSendToolResult);22calculatorAgentStrategy.edge(AIAgentEdge.builder()23 .from(nodeSendToolResult)24 .to(calculatorAgentStrategy.nodeFinish)25 .onIsInstance(Message.Assistant.class)26 .transformed(Message.Assistant::getContent)27 .build());28calculatorAgentStrategy.edge(AIAgentEdge.builder()29 .from(nodeSendToolResult)30 .to(nodeExecuteTool)31 .onIsInstance(Message.Tool.Call.class)32 .build());3334var promptExecutor = PromptExecutor.builder()35 .ollama("http://localhost:11434")36 .build();3738AIAgent<String, String> mathAgent = AIAgent.builder()39 .promptExecutor(promptExecutor)40 .llmModel(OllamaModels.Meta.LLAMA_3_2)41 .graphStrategy(calculatorAgentStrategy.build())42 .build();4344 String result = mathAgent.run("Multiply 3 by 4, then multiply the result by 5, then add 10, then add 123.", null);45 System.out.println(result);이 에이전트를 실행하면 다음과 같이 응답합니다.
1To calculate this, I'll follow the order of operations:231. Multiply 3 by 4: 3 * 4 = 1242. Multiply the result by 5: 12 * 5 = 6053. Add 10: 60 + 10 = 7064. Add 123: 70 + 123 = 19378The final answer is 193.그러나 이 에이전트에는 도구가 없으므로 LLM은 도구 호출을 반환하지 않습니다. 단순히 전체 답변을 생성합니다. 이것이 효과적으로 일어나는 일입니다:
1---2config:3 flowchart:4 defaultRenderer: "elk"5---6graph LR7 subgraph nodeStart8 Input9 end10 11 subgraph nodeFinish12 Output13 end14 15 subgraph nodeSendInput16 llmRequest(Request LLM)17 end18 19 Input --String--> llmRequest --Message.Response--> onAssistantMessage{{onAssistantMessage}} --String--> Output이 경우에는 정확하더라도 답은 기본 LLM의 산술 능력에 따라 달라집니다. 계산이 올바른지 확인하려면 에이전트에 수학 도구를 제공해야 합니다. 그런 다음 LLM은 계산을 결정적으로 수행하는 도구를 호출하기로 결정할 수 있습니다.
도구 추가
수학 연산을 수행하기 위해 tools을 정의하고 이를 ToolRegistry에 추가합니다.
Kotlin
1@LLMDescription("Tools for performing math operations")2class MathTools : ToolSet {3 @Tool4 @LLMDescription("Adds two numbers and returns the result")5 fun add(a: Int, b: Int): Int {6 // This is not necessary, but it helps to see the tool call in the console output7 println("Adding $a and $b...")8 return a + b9 }10 @Tool11 @LLMDescription("Multiplies two numbers and returns the result")12 fun multiply(a: Int, b: Int): Int {13 // This is not necessary, but it helps to see the tool call in the console output14 println("Multiplying $a and $b...")15 return a * b16 }17}1819val toolRegistry = ToolRegistry {20 tools(MathTools())21}Java
1@LLMDescription("Tools for performing math operations")2public static class MathTools implements ToolSet {3 @Tool4 @LLMDescription("Adds two numbers and returns the result")5 public int add(int a, int b) {6 // This is not necessary, but it helps to see the tool call in the console output7 System.out.println("Adding " + a + " and " + b + "...");8 return a + b;9 }1011 @Tool12 @LLMDescription("Multiplies two numbers and returns the result")13 public int multiply(int a, int b) {14 // This is not necessary, but it helps to see the tool call in the console output15 System.out.println("Multiplying " + a + " and " + b + "...");16 return a * b;17 }18}19public static void main(String[] args) {20 ToolRegistry toolRegistry = ToolRegistry.builder()21 .tools(new MathTools())22 .build();23}에이전트 구성에 도구 레지스트리를 추가합니다.
Kotlin
1val mathAgent = AIAgent(2 promptExecutor = simpleOllamaAIExecutor(),3 llmModel = OllamaModels.Meta.LLAMA_3_2,4 strategy = calculatorAgentStrategy,5 toolRegistry = toolRegistry6)78fun main() = runBlocking {9 val result = mathAgent.run("Multiply 3 by 4, then multiply the result by 5, then add 10, then add 123.")10 println(result)11}Java
1AIAgent<String, String> mathAgent = AIAgent.builder()2 .promptExecutor(promptExecutor)3 .llmModel(OllamaModels.Meta.LLAMA_3_2)4 .graphStrategy(calculatorAgentStrategy.build())5 .toolRegistry(toolRegistry)6 .build();78String result = mathAgent.run("Multiply 3 by 4, then multiply the result by 5, then add 10, then add 123.", null);9System.out.println(result);지금 에이전트를 실행하면 다음과 같이 응답합니다.
1Multiplying 3 and 4...2The output from the first operation was multiplied by 5:35 * 12 = 6045Then, 10 was added to the result:660 + 10 = 7078Finally, 123 was added to the result:970 + 123 = 193이 출력에 따르면 에이전트는 계산을 올바르게 수행했지만 multiply 도구를 한 번만 호출했습니다.
모든 작업에 대해 해당 도구를 호출하는 대신.
에이전트의 역할을 설명하고 시스템 프롬프트에서 적절한 도구 사용에 대한 지침을 제공하여 에이전트을 도울 수 있습니다.
시스템 프롬프트 제공
system prompt은 작업 수행을 위한 에이전트의 역할과 지침을 정의합니다. 이 예에서는 에이전트가 복잡한 다단계 계산을 처리하는 방법을 설명하는 것이 중요합니다.
Kotlin
1val mathAgent = AIAgent(2 promptExecutor = simpleOllamaAIExecutor(),3 llmModel = OllamaModels.Meta.LLAMA_3_2,4 systemPrompt = """5 You are a simple calculator assistant.6 You can add and multiply two numbers using the 'add' and 'multiply' tools.7 When the user provides input, extract the numbers and operations they requested.8 Use the appropriate tool for the first operation, then the next one, and so on, until you calculate the result.9 Always respond with a clear, friendly message showing the calculation and result.10 """.trimIndent(),11 toolRegistry = toolRegistry,12 strategy = calculatorAgentStrategy13)1415fun main() = runBlocking {16 val result = mathAgent.run("Multiply 3 by 4, then multiply the result by 5, then add 10, then add 123.")17 println(result)18}Java
1AIAgent<String, String> mathAgent = AIAgent.builder()2 .promptExecutor(promptExecutor)3 .llmModel(OllamaModels.Meta.LLAMA_3_2)4 .systemPrompt("You are a simple calculator assistant. You can add and multiply two numbers using the 'add' and 'multiply' tools. When the user provides input, extract the numbers and operations they requested. Use the appropriate tool for the first operation, then the next one, and so on, until you calculate the result. Always respond with a clear, friendly message showing the calculation and result.")5 .graphStrategy(calculatorAgentStrategy.build())6 .toolRegistry(toolRegistry)7 .build();89String result = mathAgent.run("Multiply 3 by 4, then multiply the result by 5, then add 10, then add 123.", null);10System.out.println(result);지금 에이전트를 실행하면 다음과 같이 응답합니다.
1Multiplying 3 and 4...2Multiplying 12 and 5...3Adding 60 and 10...4Adding 70 and 123...5The final result is: 193보시다시피 이제 에이전트는 각 작업에 적합한 도구를 올바르게 호출합니다. 환각적인 결과를 초래할 위험을 감수하는 대신 결정론적으로 계산을 수행합니다.
다음 단계
- functional agents 및 planner agents과 비교
- installing features으로 에이전트를 강화하세요
- structured output으로 예측 가능성과 신뢰성을 향상시킵니다.