맞춤형 전략 그래프

·3분 읽기

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

맞춤형 전략 그래프

전략 그래프는 Koog 프레임워크의 상담원 워크플로의 중추입니다. 에이전트가 입력을 처리하는 방법을 정의합니다. 도구와 상호 작용하고 출력을 생성합니다. 전략 그래프는 조건이 있는 가장자리로 연결된 노드로 구성됩니다. 실행 흐름을 결정합니다.

전략 그래프를 생성하면 에이전트의 행동을 특정 요구 사항에 맞게 조정할 수 있습니다. 간단한 챗봇, 복잡한 데이터 처리 파이프라인 또는 그 사이의 모든 것을 구축합니다.

전략 그래프 아키텍처

높은 수준에서 전략 그래프는 다음 구성 요소로 구성됩니다.

  • 전략: 지정된 입력과 함께 strategy 함수를 사용하여 생성된 그래프의 최상위 컨테이너 일반 매개변수를 사용하는 출력 유형.
  • 하위 그래프: 자체 도구 및 컨텍스트 세트를 가질 수 있는 그래프 섹션입니다.
  • 노드: 워크플로의 개별 작업 또는 변환입니다.
  • 가장자리: 전환 조건과 변환을 정의하는 노드 간의 연결입니다.

전략 그래프는 nodeStart라는 특수 노드에서 시작하여 nodeFinish에서 끝납니다. 이들 노드 사이의 경로는 그래프에 지정된 간선과 조건에 따라 결정됩니다.

전략 그래프 구성요소

노드

노드는 전략 그래프의 구성 요소입니다. 각 노드는 특정 작업을 나타냅니다.

Koog 프레임워크는 사전 정의된 노드를 제공하며 node 함수를 사용하여 사용자 정의 노드를 생성할 수도 있습니다.

자세한 내용은 Predefined nodes and componentsCustom nodes을 참조하세요.

가장자리

Edge는 노드를 연결하고 전략 그래프에서 작업 흐름을 정의합니다. edge 함수와 forwardTo 중위 함수를 사용하여 가장자리가 생성됩니다.

코틀린

1edge(sourceNode forwardTo targetNode)

자바

1strategy.edge(sourceNode, targetNode);

정황

조건은 전략 그래프에서 특정 가장자리를 따라갈 시기를 결정합니다. 여러 유형의 조건이 있으며 다음은 몇 가지 일반적인 조건입니다.

조건 유형 설명
onCondition 부울 값을 반환하는 람다 식을 사용하는 범용 조건입니다.
onToolCall LLM이 도구를 호출할 때 일치하는 조건입니다.
onAssistantMessage LLM이 메시지로 응답할 때 일치하는 조건입니다.
onMultipleToolCalls LLM이 여러 도구를 호출할 때 일치하는 조건입니다.
onToolNotCalled LLM이 도구를 호출하지 않을 때 일치하는 조건입니다.

transformed 함수를 사용하여 대상 노드에 전달하기 전에 출력을 변환할 수 있습니다.

코틀린

1edge(sourceNode forwardTo targetNode 2        onCondition { input -> input.length > 10 }3        transformed { input -> input.uppercase() }4)

자바

1strategy.edge(AIAgentEdge.builder()2    .from(sourceNode)3    .to(targetNode)4    .onCondition(input -> input.length() > 10)5    .transformed(input -> input.toUpperCase())6    .build());

하위 그래프

하위 그래프는 자체 도구 및 컨텍스트 세트로 작동하는 전략 그래프의 섹션입니다. 전략 그래프에는 여러 하위 그래프가 포함될 수 있습니다. 각 하위 그래프는 subgraph 함수를 사용하여 정의됩니다.

코틀린

1val strategy = strategy<Input, Output>("strategy-name") {2    val firstSubgraph by subgraph<FirstInput, FirstOutput>("first") {3        // Define nodes and edges for this subgraph4    }5    val secondSubgraph by subgraph<SecondInput, SecondOutput>("second") {6        // Define nodes and edges for this subgraph7    }8}

자바

1var firstSubgraph = AIAgentSubgraph.builder("first")2    .withInput(FirstInput.class)3    .withOutput(FirstOutput.class)4    .define(subgraph -> {5        // Define nodes and edges for this subgraph6    })7    .build();89var secondSubgraph = AIAgentSubgraph.builder("second")10    .withInput(SecondInput.class)11    .withOutput(SecondOutput.class)12    .define(subgraph -> {13        // Define nodes and edges for this subgraph14    })15    .build();

하위 그래프는 도구 레지스트리의 모든 도구를 사용할 수 있습니다. 그러나 하위 그래프에서 사용할 수 있는 이 레지스트리의 도구 하위 집합을 지정하고 이를 subgraph 함수에 대한 인수로 전달할 수 있습니다.

코틀린

1val strategy = strategy<Input, Output>("strategy-name") {2    val firstSubgraph by subgraph<FirstInput, FirstOutput>(3        name = "first",4        tools = listOf(someTool)5    ) {6        // Define nodes and edges for this subgraph7    }8   // Define other subgraphs9}

자바

1var firstSubgraph = AIAgentSubgraph.builder("first")2    .withInput(FirstInput.class)3    .withOutput(FirstOutput.class)4    .limitedTools(someTools)5    .define(subgraph -> {6        // Define nodes and edges for this subgraph7    })8    .build();

기본 전략 그래프 작성

기본 전략 그래프는 다음과 같이 동작합니다.

  1. 입력을 LLM으로 보냅니다.
  2. LLM이 메시지로 응답하면 프로세스가 완료됩니다.
  3. LLM이 도구를 호출하면 도구를 실행합니다.
  4. 도구 결과를 LLM으로 다시 보냅니다.
  5. LLM이 메시지로 응답하면 프로세스가 완료됩니다.
  6. LLM이 다른 도구를 호출하면 도구가 실행되고 프로세스는 4단계부터 반복됩니다.

basic-strategy-graph

다음은 기본 전략 그래프의 예입니다.

코틀린

1val myStrategy = strategy<String, String>("my-strategy") {2    val nodeCallLLM by nodeLLMRequest()3    val executeToolCall by nodeExecuteTool()4    val sendToolResult by nodeLLMSendToolResult()56    edge(nodeStart forwardTo nodeCallLLM)7    edge(nodeCallLLM forwardTo nodeFinish onAssistantMessage { true })8    edge(nodeCallLLM forwardTo executeToolCall onToolCall { true })9    edge(executeToolCall forwardTo sendToolResult)10    edge(sendToolResult forwardTo nodeFinish onAssistantMessage { true })11    edge(sendToolResult forwardTo executeToolCall onToolCall { true })12}

자바

1var graph = AIAgentGraphStrategy.builder("single_run")2    .withInput(String.class)3    .withOutput(String.class);45var nodeCallLLM = AIAgentNode.llmRequest(true, "sendInput");6var nodeExecuteTool = AIAgentNode.executeTool("nodeExecuteTool");7var nodeSendToolResult = AIAgentNode.llmSendToolResult("nodeSendToolResult");89graph.edge(graph.nodeStart, nodeCallLLM);1011graph.edge(AIAgentEdge.builder()12    .from(nodeCallLLM)13    .to(nodeExecuteTool)14    .onIsInstance(Message.Tool.Call.class)15    .build());1617graph.edge(AIAgentEdge.builder()18    .from(nodeCallLLM)19    .to(graph.nodeFinish)20    .onIsInstance(Message.Assistant.class)21    .transformed(Message.Assistant::getContent)22    .build());2324graph.edge(nodeExecuteTool, nodeSendToolResult);2526graph.edge(AIAgentEdge.builder()27    .from(nodeSendToolResult)28    .to(graph.nodeFinish)29    .onIsInstance(Message.Assistant.class)30    .transformed(Message.Assistant::getContent)31    .build());3233graph.edge(AIAgentEdge.builder()34    .from(nodeSendToolResult)35    .to(nodeExecuteTool)36    .onIsInstance(Message.Tool.Call.class)37    .build());3839var strategy = graph.build();

전략 그래프 시각화

JVM에서는 전략 그래프에 대해 Mermaid state diagram을 생성할 수 있습니다.

이전 예에서 생성된 그래프의 경우 다음을 실행할 수 있습니다.

코틀린

1val mermaidDiagram: String = myStrategy.asMermaidDiagram()23println(mermaidDiagram)

자바

1var mermaidDiagram = MermaidDiagramGenerator.INSTANCE.generate(myStrategy);2System.out.println(mermaidDiagram);

출력은 다음과 같습니다.

1---2title: my-strategy3---4stateDiagram5    state "nodeCallLLM" as nodeCallLLM6    state "executeToolCall" as executeToolCall7    state "sendToolResult" as sendToolResult89    [*] --> nodeCallLLM10    nodeCallLLM --> [*] : transformed11    nodeCallLLM --> executeToolCall : onCondition12    executeToolCall --> sendToolResult13    sendToolResult --> [*] : transformed14    sendToolResult --> executeToolCall : onCondition

고급 전략 기술

기록 압축

장기간 실행되는 대화의 경우 기록이 커지고 많은 토큰을 소비할 수 있습니다. 기록을 압축하는 방법을 알아보려면 History compression을 참조하세요.

병렬 도구 실행

여러 도구를 병렬로 실행해야 하는 워크플로의 경우 nodeExecuteMultipleTools 노드를 사용할 수 있습니다.

1val executeMultipleTools by nodeExecuteMultipleTools()2val processMultipleResults by nodeLLMSendMultipleToolResults()34edge(someNode forwardTo executeMultipleTools)5edge(executeMultipleTools forwardTo processMultipleResults)

스트리밍 데이터에 toParallelToolCallsRaw 확장 기능을 사용할 수도 있습니다.

1parseMarkdownStreamToBooks(markdownStream).toParallelToolCallsRaw(BookTool::class).collect()

자세한 내용은 Tools을 참조하세요.

병렬 노드 실행

병렬 노드 실행을 사용하면 여러 노드를 동시에 실행할 수 있어 성능이 향상되고 복잡한 워크플로가 가능해집니다.

병렬 노드 실행을 시작하려면 parallel 메서드를 사용하세요.

1val calc by parallel<String, Int>(2    nodeCalcTokens, nodeCalcSymbols, nodeCalcWords,3) {4    selectByMax { it }5}

위의 코드는 nodeCalcTokens, nodeCalcSymbolsnodeCalcWords 노드를 실행하는 calc라는 노드를 생성합니다. 동시에 결과를 AsyncParallelResult의 인스턴스로 반환합니다.

병렬 노드 실행과 관련된 자세한 내용 및 자세한 참조는 Parallel node execution을 참조하세요.

조건부 분기

특정 조건에 따라 다양한 경로가 필요한 복잡한 워크플로의 경우 조건부 분기를 사용할 수 있습니다.

1val branchA by node<String, String> { input ->2    // Logic for branch A3    "Branch A: $input"4}56val branchB by node<String, String> { input ->7    // Logic for branch B8    "Branch B: $input"9}1011edge(12    (someNode forwardTo branchA)13            onCondition { input -> input.contains("A") }14)15edge(16    (someNode forwardTo branchB)17            onCondition { input -> input.contains("B") }18)

모범 사례

사용자 정의 전략 그래프를 생성할 때 다음 모범 사례를 따르십시오.

  • 간단하게 유지하세요. 간단한 그래프로 시작하고 필요에 따라 복잡성을 추가하세요.
  • 그래프를 더 쉽게 이해할 수 있도록 노드와 간선에 설명적인 이름을 지정하세요.
  • 가능한 모든 경로와 엣지 케이스를 처리합니다.
  • 다양한 입력으로 그래프를 테스트하여 예상대로 작동하는지 확인하세요.
  • 나중에 참조할 수 있도록 그래프의 목적과 동작을 문서화하세요.
  • 사전 정의된 전략이나 일반적인 패턴을 출발점으로 사용하세요.
  • 장기간 실행되는 대화의 경우 기록 압축을 사용하여 토큰 사용량을 줄이세요.
  • 하위 그래프를 사용하여 그래프를 구성하고 도구 액세스를 관리하세요.

사용 예

톤 분석 전략

톤 분석 전략은 기록 압축을 포함하는 도구 기반 전략의 좋은 예입니다.

1fun toneStrategy(name: String, toolRegistry: ToolRegistry): AIAgentGraphStrategy<String, String> {2    return strategy(name) {3        val nodeSendInput by nodeLLMRequest()4        val nodeExecuteTool by nodeExecuteTool()5        val nodeSendToolResult by nodeLLMSendToolResult()6        val nodeCompressHistory by nodeLLMCompressHistory<ReceivedToolResult>()78        // Define the flow of the agent9        edge(nodeStart forwardTo nodeSendInput)1011        // If the LLM responds with a message, finish12        edge(13            (nodeSendInput forwardTo nodeFinish)14                    onAssistantMessage { true }15        )1617        // If the LLM calls a tool, execute it18        edge(19            (nodeSendInput forwardTo nodeExecuteTool)20                    onToolCall { true }21        )2223        // If the history gets too large, compress it24        edge(25            (nodeExecuteTool forwardTo nodeCompressHistory)26                    onCondition { _ -> llm.readSession { prompt.messages.size > 100 } }27        )2829        edge(nodeCompressHistory forwardTo nodeSendToolResult)3031        // Otherwise, send the tool result directly32        edge(33            (nodeExecuteTool forwardTo nodeSendToolResult)34                    onCondition { _ -> llm.readSession { prompt.messages.size <= 100 } }35        )3637        // If the LLM calls another tool, execute it38        edge(39            (nodeSendToolResult forwardTo nodeExecuteTool)40                    onToolCall { true }41        )4243        // If the LLM responds with a message, finish44        edge(45            (nodeSendToolResult forwardTo nodeFinish)46                    onAssistantMessage { true }47        )48    }49}

이 전략은 다음을 수행합니다.

  1. 입력을 LLM으로 보냅니다.
  2. LLM이 메시지로 응답하면 전략이 프로세스를 완료합니다.
  3. LLM이 도구를 호출하면 전략이 도구를 실행합니다.
  4. 기록이 너무 큰 경우(100개 이상의 메시지) 전략은 도구 결과를 보내기 전에 이를 압축합니다.
  5. 그렇지 않으면 전략이 도구 결과를 직접 보냅니다.
  6. LLM이 다른 도구를 호출하면 전략이 이를 실행합니다.
  7. LLM이 메시지로 응답하면 전략이 프로세스를 완료합니다.

문제 해결

사용자 정의 전략 그래프를 생성할 때 몇 가지 일반적인 문제가 발생할 수 있습니다. 다음은 몇 가지 문제 해결 팁입니다.

그래프가 종료 노드에 도달하지 못함

그래프가 종료 노드에 도달하지 않으면 다음을 확인하십시오.

  • 시작 노드의 모든 경로는 결국 종료 노드로 연결됩니다.
  • 조건이 너무 제한적이지 않아 가장자리를 따라갈 수 없습니다.
  • 그래프에는 종료 조건이 없는 사이클이 없습니다.

도구 호출이 실행되고 있지 않습니다.

도구 호출이 실행되고 있지 않으면 다음을 확인하십시오.

  • 도구가 도구 레지스트리에 올바르게 등록되었습니다.
  • LLM 노드에서 도구 실행 노드까지의 에지는 올바른 조건(onToolCall { true })을 갖습니다.

기록이 너무 커집니다.

기록이 너무 커지고 토큰을 너무 많이 소비하는 경우 다음을 고려하세요.

  • 기록 압축 노드를 추가합니다.
  • 조건을 사용하여 히스토리의 크기를 확인하고 너무 커지면 압축합니다.
  • 보다 공격적인 압축 전략을 사용하십시오(예: N 값이 더 작은 FromLastNMessages).

그래프가 예기치 않게 동작합니다.

그래프에 예상치 못한 분기가 발생하는 경우 다음을 확인하세요.

  • 귀하의 조건이 올바르게 정의되었습니다.
  • 조건은 예상된 순서대로 평가됩니다(가장자리는 정의된 순서대로 확인됩니다).
  • 실수로 더 일반적인 조건으로 조건을 무시하는 것은 아닙니다.

성능 문제가 발생합니다

그래프에 성능 문제가 있는 경우 다음을 고려하십시오.

  • 불필요한 노드와 간선을 제거하여 그래프를 단순화합니다.
  • 독립적인 작업을 위해 병렬 도구 실행을 사용합니다.
  • 기록을 압축합니다.
  • 보다 효율적인 노드와 작업을 사용하세요.