하위 그래프 생성 및 구성
원문: Koog Documentation — custom-subgraphs 이 글은 Koog 공식 문서의 custom-subgraphs 페이지를 한국어로 옮긴 번역본입니다. 문서 구조와 링크 의미를 유지하되, MkDocs 전용 UI 문법은 블로그에서 읽기 좋도록 정리했습니다.
하위 그래프 생성 및 구성
다음 섹션에서는 에이전트 워크플로에 대한 하위 그래프 생성 시 코드 템플릿과 일반적인 패턴을 제공합니다.
기본 하위 그래프 생성
사용자 정의 하위 그래프는 일반적으로 다음 패턴을 사용하여 생성됩니다.
- 지정된 도구 선택 전략이 포함된 하위 그래프:
코틀린
1strategy<StrategyInput, StrategyOutput>("strategy-name") {2 val subgraphIdentifier by subgraph<Input, Output>(3 name = "subgraph-name",4 toolSelectionStrategy = ToolSelectionStrategy.ALL5 ) {6 // Define nodes and edges for this subgraph7 }89 nodeStart then subgraphIdentifier then nodeFinish10}자바
1var strategyBuilder = AIAgentGraphStrategy.builder("strategy-name")2 .withInput(String.class)3 .withOutput(String.class);45var subgraphIdentifier = AIAgentSubgraph.builder("subgraph-name")6 .withToolSelectionStrategy(ToolSelectionStrategy.ALL.INSTANCE)7 .withInput(String.class)8 .withOutput(String.class)9 .define(subgraph -> {10 // Define nodes and edges for this subgraph11 })12 .build();1314var strategy = strategyBuilder15 .edge(strategyBuilder.nodeStart, subgraphIdentifier)16 .edge(subgraphIdentifier, strategyBuilder.nodeFinish)17 .build();- 지정된 도구 목록(정의된 도구 레지스트리의 도구 하위 집합)이 포함된 하위 그래프:
코틀린
1strategy<StrategyInput, StrategyOutput>("strategy-name") {2 val subgraphIdentifier by subgraph<Input, Output>(3 name = "subgraph-name",4 tools = listOf(firstTool, secondTool)5 ) {6 // Define nodes and edges for this subgraph7 }8}자바
1var strategyBuilder = AIAgentGraphStrategy.builder("strategy-name")2 .withInput(String.class)3 .withOutput(String.class);45var subgraphIdentifier = AIAgentSubgraph.builder("subgraph-name")6 .limitedTools(List.of(firstTool, secondTool))7 .withInput(String.class)8 .withOutput(String.class)9 .define(subgraph -> {10 // Define nodes and edges for this subgraph11 })12 .build();1314var strategy = strategyBuilder15 .edge(strategyBuilder.nodeStart, subgraphIdentifier)16 .edge(subgraphIdentifier, strategyBuilder.nodeFinish)17 .build();매개변수 및 매개변수 값에 대한 자세한 내용은 subgraph API reference을 참조하세요. 더 알아보기
도구에 대한 자세한 내용은 Tools을 참조하세요.
다음 코드 샘플은 사용자 정의 하위 그래프의 실제 구현을 보여줍니다.
코틀린
1strategy<String, String>("my-strategy") {2 val mySubgraph by subgraph<String, String>(3 tools = listOf(firstTool, secondTool)4 ) {5 // Define nodes and edges for this subgraph6 val sendInput by nodeLLMRequest()7 val executeToolCall by nodeExecuteTool()8 val sendToolResult by nodeLLMSendToolResult()910 edge(nodeStart forwardTo sendInput)11 edge(sendInput forwardTo executeToolCall onToolCall { true })12 edge(executeToolCall forwardTo sendToolResult)13 edge(sendToolResult forwardTo nodeFinish onAssistantMessage { true })14 }15}자바
1var strategyBuilder = AIAgentGraphStrategy.builder("my-strategy")2 .withInput(String.class)3 .withOutput(String.class);45var sendInput = AIAgentNode.llmRequest();6var executeToolCall = AIAgentNode.executeTool();7var sendToolResult = AIAgentNode.llmSendToolResult();89var mySubgraph = AIAgentSubgraph.builder()10 .limitedTools(List.of(firstTool, secondTool))11 .withInput(String.class)12 .withOutput(String.class)13 .define(subgraph -> {14 // Define nodes and edges for this subgraph15 subgraph16 .edge(subgraph.nodeStart, sendInput)17 .edge(AIAgentEdge.builder()18 .from(sendInput)19 .to(executeToolCall)20 .onIsInstance(Message.Tool.Call.class)21 .build()22 )23 .edge(executeToolCall, sendToolResult)24 .edge(AIAgentEdge.builder()25 .from(sendToolResult)26 .to(subgraph.nodeFinish)27 .onIsInstance(Message.Assistant.class)28 .transformed(Message.Assistant::getContent)29 .build()30 )31 .build();3233 })34 .build();3536var strategy = strategyBuilder37 .edge(strategyBuilder.nodeStart, mySubgraph)38 .edge(mySubgraph, strategyBuilder.nodeFinish)39 .build();하위 그래프에서 도구 구성
여러 가지 방법으로 하위 그래프에 대한 도구를 구성할 수 있습니다.
- 하위 그래프 정의에서 직접:
코틀린
1val mySubgraph by subgraph<String, String>(2 tools = listOf(AskUser)3 ) {4 // Subgraph definition5 }자바
1var mySubgraph = AIAgentSubgraph.builder()2 .limitedTools(List.of(AskUser.INSTANCE))3 .withInput(String.class)4 .withOutput(String.class)5 .define(subgraph -> {6 // Subgraph definition7 })8 .build();- 도구 레지스트리에서:
코틀린
1val mySubgraph by subgraph<String, String>(2 tools = listOf(toolRegistry.getTool("AskUser"))3) {4 // Subgraph definition5}자바
1var mySubgraph = AIAgentSubgraph.builder()2 .limitedTools(List.of(toolRegistry.getTool("AskUser")))3 .withInput(String.class)4 .withOutput(String.class)5 .define(subgraph -> {6 // Subgraph definition7 })8 .build();- 실행 중 동적으로:
코틀린
1// Make a set of tools2this.llm.writeSession {3 tools = tools.filter { it.name in listOf("first_tool_name", "second_tool_name") }4}자바
1var node = AIAgentNode.builder("node_name")2 .withInput(String.class)3 .withOutput(String.class)4 .withAction((input, ctx) -> {5 // Make a set of tools6 ctx.getLlm().writeSession(session -> {7 session.setTools(session.getTools().stream()8 .filter(t -> List.of("first_tool_name", "second_tool_name").contains(t.getName()))9 .collect(Collectors.toList()));10 return null;11 });12 return input;13 })14 .build();고급 하위 그래프 기술
다중 부분 전략
복잡한 워크플로는 각각 프로세스의 특정 부분을 처리하는 여러 하위 그래프로 나눌 수 있습니다.
코틀린
1strategy("complex-workflow") {2 val inputProcessing by subgraph<String, A>(3 ) {4 // Process the initial input5 }67 val reasoning by subgraph<A, B>(8 ) {9 // Perform reasoning based on the processed input10 }1112 val toolRun by subgraph<B, C>(13 // Optional subset of tools from the tool registry14 tools = listOf(firstTool, secondTool)15 ) {16 // Run tools based on the reasoning17 }1819 val responseGeneration by subgraph<C, String>(20 ) {21 // Generate a response based on the tool results22 }2324 nodeStart then inputProcessing then reasoning then toolRun then responseGeneration then nodeFinish2526}자바
1var strategyBuilder = AIAgentGraphStrategy.builder("complex-workflow")2 .withInput(String.class)3 .withOutput(String.class);45var inputProcessing = AIAgentSubgraph.builder()6 .withInput(String.class)7 .withOutput(String.class)8 .define(subgraph -> {9 // Process the initial input10 })11 .build();1213var reasoning = AIAgentSubgraph.builder()14 .withInput(String.class)15 .withOutput(String.class)16 .define(subgraph -> {17 // Perform reasoning based on the processed input18 })19 .build();2021var toolRun = AIAgentSubgraph.builder()22 // Optional subset of tools from the tool registry23 .limitedTools(List.of(firstTool, secondTool))24 .withInput(String.class)25 .withOutput(String.class)26 .define(subgraph -> {27 // Run tools based on the reasoning28 })29 .build();3031var responseGeneration = AIAgentSubgraph.builder()32 .withInput(String.class)33 .withOutput(String.class)34 .define(subgraph -> {35 // Generate a response based on the tool results36 })37 .build();3839var strategy = strategyBuilder40 .edge(strategyBuilder.nodeStart, inputProcessing)41 .edge(inputProcessing, reasoning)42 .edge(reasoning, toolRun)43 .edge(toolRun, responseGeneration)44 .edge(responseGeneration, strategyBuilder.nodeFinish)45 .build();모범 사례
하위 그래프로 작업할 때 다음 모범 사례를 따르십시오.
복잡한 워크플로를 하위 그래프로 나누기: 각 하위 그래프에는 명확하고 집중적인 책임이 있어야 합니다.
필요한 컨텍스트만 전달: 후속 하위 그래프가 올바르게 작동하는 데 필요한 정보만 전달합니다.
하위 그래프 종속성 문서화: 각 하위 그래프가 이전 하위 그래프에서 기대하는 것과 후속 하위 그래프에 무엇을 제공하는지 명확하게 문서화합니다.
하위 그래프를 별도로 테스트: 각 하위 그래프를 전략에 통합하기 전에 다양한 입력에서 올바르게 작동하는지 확인하세요.
토큰 사용 고려: 특히 하위 그래프 간에 대규모 기록을 전달할 때 토큰 사용에 주의하세요.
문제 해결
도구를 사용할 수 없음
하위 그래프에서 도구를 사용할 수 없는 경우:
- 도구가 도구 레지스트리에 올바르게 등록되었는지 확인하십시오.
하위 그래프가 정의되고 예상된 순서로 실행되지 않습니다.
하위 그래프가 정의된 순서대로 실행되지 않는 경우:
- 전략 정의를 확인하여 하위 그래프가 올바른 순서로 나열되어 있는지 확인하세요.
- 각 하위 그래프가 출력을 다음 하위 그래프로 올바르게 전달하는지 확인하세요.
- 하위 그래프가 하위 그래프의 나머지 부분과 연결되어 있고 처음부터 끝까지 도달할 수 있는지 확인하세요. 조건부 가장자리에 주의하세요. 하위 그래프나 노드에서 차단되지 않도록 계속하려면 가능한 모든 조건을 다루어야 합니다.
예
다음 예에서는 실제 시나리오에서 에이전트 전략을 생성하기 위해 하위 그래프를 사용하는 방법을 보여줍니다.
코드 샘플에는 researchSubgraph, planSubgraph 및 executeSubgraph의 세 가지 정의된 하위 그래프가 포함되어 있습니다. 여기서 각 하위 그래프에는 보조 흐름 내에서 정의되고 고유한 목적이 있습니다.
코틀린
1// Define the agent strategy2val strategy = strategy<String, String>("assistant") {34 // A subgraph that includes a tool call5 val researchSubgraph by subgraph<String, String>(6 "research_subgraph",7 tools = listOf(WebSearchTool())8 ) {9 val nodeCallLLM by nodeLLMRequest("call_llm")10 val nodeExecuteTool by nodeExecuteTool()11 val nodeSendToolResult by nodeLLMSendToolResult()1213 edge(nodeStart forwardTo nodeCallLLM)14 edge(nodeCallLLM forwardTo nodeExecuteTool onToolCall { true })15 edge(nodeExecuteTool forwardTo nodeSendToolResult)16 edge(nodeSendToolResult forwardTo nodeExecuteTool onToolCall { true })17 edge(nodeCallLLM forwardTo nodeFinish onAssistantMessage { true })18 }1920 val planSubgraph by subgraph(21 "plan_subgraph",22 tools = listOf()23 ) {24 val nodeUpdatePrompt by node<String, Unit> { research ->25 llm.writeSession {26 rewritePrompt {27 prompt("research_prompt") {28 system(29 "You are given a problem and some research on how it can be solved." +30 "Make step by step a plan on how to solve given task."31 )32 user("Research: $research")33 }34 }35 }36 }37 val nodeCallLLM by nodeLLMRequest("call_llm")3839 edge(nodeStart forwardTo nodeUpdatePrompt)40 edge(nodeUpdatePrompt forwardTo nodeCallLLM transformed { "Task: $agentInput" })41 edge(nodeCallLLM forwardTo nodeFinish onAssistantMessage { true })42 }4344 val executeSubgraph by subgraph<String, String>(45 "execute_subgraph",46 tools = listOf(DoAction(), DoAnotherAction()),47 ) {48 val nodeUpdatePrompt by node<String, Unit> { plan ->49 llm.writeSession {50 rewritePrompt {51 prompt("execute_prompt") {52 system(53 "You are given a task and detailed plan how to execute it." +54 "Perform execution by calling relevant tools."55 )56 user("Execute: $plan")57 user("Plan: $plan")58 }59 }60 }61 }62 val nodeCallLLM by nodeLLMRequest("call_llm")63 val nodeExecuteTool by nodeExecuteTool()64 val nodeSendToolResult by nodeLLMSendToolResult()6566 edge(nodeStart forwardTo nodeUpdatePrompt)67 edge(nodeUpdatePrompt forwardTo nodeCallLLM transformed { "Task: $agentInput" })68 edge(nodeCallLLM forwardTo nodeExecuteTool onToolCall { true })69 edge(nodeExecuteTool forwardTo nodeSendToolResult)70 edge(nodeSendToolResult forwardTo nodeExecuteTool onToolCall { true })71 edge(nodeCallLLM forwardTo nodeFinish onAssistantMessage { true })72 }7374 nodeStart then researchSubgraph then planSubgraph then executeSubgraph then nodeFinish75}자바
1// Define the agent strategy2var strategyBuilder = AIAgentGraphStrategy.builder("assistant")3 .withInput(String.class)4 .withOutput(String.class);56// A subgraph that includes a tool call7var nodeCallLLM = AIAgentNode.llmRequest();8var nodeExecuteTool = AIAgentNode.executeTool();9var nodeSendToolResult = AIAgentNode.llmSendToolResult();1011var researchSubgraph = AIAgentSubgraph.builder("research_subgraph")12 .limitedTools(new WebSearchToolSet())13 .withInput(String.class)14 .withOutput(String.class)15 .define(subgraph -> {16 subgraph17 .edge(subgraph.nodeStart, nodeCallLLM)18 .edge(AIAgentEdge.builder()19 .from(nodeCallLLM)20 .to(nodeExecuteTool)21 .onIsInstance(Message.Tool.Call.class)22 .build()23 )24 .edge(nodeExecuteTool, nodeSendToolResult)25 .edge(AIAgentEdge.builder()26 .from(nodeSendToolResult)27 .to(nodeExecuteTool)28 .onIsInstance(Message.Tool.Call.class)29 .build()30 )31 .edge(AIAgentEdge.builder()32 .from(nodeCallLLM)33 .to(subgraph.nodeFinish)34 .onIsInstance(Message.Assistant.class)35 .transformed(Message.Assistant::getContent)36 .build()37 )38 .build();39 })40 .build();4142var nodeUpdatePrompt = AIAgentNode.builder()43 .withInput(String.class)44 .withOutput(String.class)45 .withAction((research, ctx) -> {46 ctx.getLlm().writeSession(session -> {47 session.setPrompt(Prompt.builder("research_prompt")48 .system(49 "You are given a problem and some research on how it can be solved." +50 "Make step by step a plan on how to solve given task."51 )52 .user("Research: " + research)53 .build());54 return null;55 });56 return "Task: " + ctx.getAgentInput();57 })58 .build();59var nodeCallLLMPlan = AIAgentNode.llmRequest();6061var planSubgraph = AIAgentSubgraph.builder("plan_subgraph")62 .limitedTools(Collections.emptyList())63 .withInput(String.class)64 .withOutput(String.class)65 .define(subgraph -> {66 subgraph67 .edge(subgraph.nodeStart, nodeUpdatePrompt)68 .edge(nodeUpdatePrompt, nodeCallLLMPlan)69 .edge(AIAgentEdge.builder()70 .from(nodeCallLLMPlan)71 .to(subgraph.nodeFinish)72 .onIsInstance(Message.Assistant.class)73 .transformed(Message.Assistant::getContent)74 .build()75 )76 .build();77 })78 .build();7980var nodeUpdatePromptExecute = AIAgentNode.builder()81 .withInput(String.class)82 .withOutput(String.class)83 .withAction((plan, ctx) -> {84 ctx.getLlm().writeSession(session -> {85 session.setPrompt(Prompt.builder("execute_prompt")86 .system(87 "You are given a task and detailed plan how to execute it." +88 "Perform execution by calling relevant tools."89 )90 .user("Execute: " + plan)91 .user("Plan: " + plan)92 .build());93 return null;94 });95 return "Task: " + ctx.getAgentInput();96 })97 .build();9899var nodeCallLLMExecute = AIAgentNode.llmRequest();100var nodeExecuteToolExecute = AIAgentNode.executeTool();101var nodeSendToolResultExecute = AIAgentNode.llmSendToolResult();102103var executeSubgraph = AIAgentSubgraph.builder("execute_subgraph")104 .limitedTools(new ActionToolSet())105 .withInput(String.class)106 .withOutput(String.class)107 .define(subgraph -> {108 subgraph109 .edge(subgraph.nodeStart, nodeUpdatePromptExecute)110 .edge(nodeUpdatePromptExecute, nodeCallLLMExecute)111 .edge(AIAgentEdge.builder()112 .from(nodeCallLLMExecute)113 .to(nodeExecuteToolExecute)114 .onIsInstance(Message.Tool.Call.class)115 .build()116 )117 .edge(nodeExecuteToolExecute, nodeSendToolResultExecute)118 .edge(AIAgentEdge.builder()119 .from(nodeSendToolResultExecute)120 .to(nodeExecuteToolExecute)121 .onIsInstance(Message.Tool.Call.class)122 .build()123 )124 .edge(AIAgentEdge.builder()125 .from(nodeCallLLMExecute)126 .to(subgraph.nodeFinish)127 .onIsInstance(Message.Assistant.class)128 .transformed(Message.Assistant::getContent)129 .build()130 )131 .build();132 })133 .build();134135var strategy = strategyBuilder136 .edge(strategyBuilder.nodeStart, researchSubgraph)137 .edge(researchSubgraph, planSubgraph)138 .edge(planSubgraph, executeSubgraph)139 .edge(executeSubgraph, strategyBuilder.nodeFinish)140 .build();