기록 압축
원문: Koog Documentation — history-compression 이 글은 Koog 공식 문서의 history-compression 페이지를 한국어로 옮긴 번역본입니다. 문서 구조와 링크 의미를 유지하되, MkDocs 전용 UI 문법은 블로그에서 읽기 좋도록 정리했습니다.
기록 압축
AI 에이전트는 사용자 메시지, 보조자 응답, 도구 호출 및 도구 응답을 포함하는 메시지 기록을 유지 관리합니다. 에이전트가 전략을 따르면서 이 기록은 상호작용할 때마다 증가합니다.
장기간 실행되는 대화의 경우 기록이 커지고 많은 토큰을 소비할 수 있습니다. 기록 압축은 전체 메시지 목록을 다음을 포함하는 하나 이상의 메시지로 요약하여 이를 줄이는 데 도움이 됩니다. 추가 에이전트 작동에 필요한 중요한 정보만 제공됩니다.
기록 압축은 에이전트 시스템의 주요 과제를 해결합니다.
- 컨텍스트 사용을 최적화합니다. 집중적이고 작은 컨텍스트는 LLM 성능을 향상시키고 실패가 초과되는 것을 방지합니다. 토큰 제한.
- 성능이 향상됩니다. 기록을 압축하면 LLM이 처리하는 메시지 수가 줄어들어 결과적으로 더 빨라집니다. 응답.
- 정확성을 높입니다. 관련 정보에 집중하면 LLM이 집중력을 유지하고 작업을 완료하는 데 도움이 됩니다. 산만 함.
- 비용이 절감됩니다. 관련 없는 메시지를 줄이면 토큰 사용량이 줄어들어 API 호출의 전체 비용이 절감됩니다.
기록을 압축하는 경우
기록 압축은 에이전트 워크플로의 특정 단계에서 수행됩니다.
- 에이전트 전략의 논리적 단계(하위 그래프) 사이.
- 문맥이 너무 길어지는 경우.
기록 압축 구현
에이전트에서 기록 압축을 구현하는 데는 두 가지 주요 접근 방식이 있습니다.
- 전략 그래프에서
- 커스텀 노드에서
전략 그래프의 기록 압축
전략 그래프의 히스토리를 압축하려면 현재 메시지를 압축하는 사전 정의된 노드를 사용해야 합니다. 간략한 요약으로 역사:
- Kotlin:
nodeLLMCompressHistory - 자바:
AIAgentNode.llmCompressHistory()
자세한 내용과 구체적인 예를 보려면 History compression node을 참조하세요.
압축을 수행하기로 결정한 단계에 따라 다음 시나리오를 사용할 수 있습니다.
기록이 너무 길어지면 압축하려면 Edge에서 메시지 수를 확인하세요. 조건을 지정하고 기록 압축 노드를 추가합니다. 기록 길이를 확인하려면 다음을 수행하십시오.
Kotlin: 도우미 확장 프로그램을 정의합니다.
Java:
.onCondition()에서 인라인 람다 표현식을 사용합니다.
코틀린
1// Define that the history is too long if there are more than 100 messages2private suspend fun AIAgentContext.historyIsTooLong(): Boolean = llm.readSession { prompt.messages.size > 100 }34val strategy = strategy<String, String>("execute-with-history-compression") {5 val callLLM by nodeLLMRequest()6 val executeTool by nodeExecuteTool()7 val sendToolResult by nodeLLMSendToolResult()89 // Compress the LLM history and keep the current ReceivedToolResult for the next node10 val compressHistory by nodeLLMCompressHistory<ReceivedToolResult>()1112 edge(nodeStart forwardTo callLLM)13 edge(callLLM forwardTo nodeFinish onAssistantMessage { true })14 edge(callLLM forwardTo executeTool onToolCall { true })1516 // Compress history after executing any tool if the history is too long 17 edge(executeTool forwardTo compressHistory onCondition { historyIsTooLong() })18 edge(compressHistory forwardTo sendToolResult)19 // Otherwise, proceed to the next LLM request20 edge(executeTool forwardTo sendToolResult onCondition { !historyIsTooLong() })2122 edge(sendToolResult forwardTo executeTool onToolCall { true })23 edge(sendToolResult forwardTo nodeFinish onAssistantMessage { true })24}자바
1var graph = AIAgentGraphStrategy.builder("execute-with-history-compression")2 .withInput(String.class)3 .withOutput(String.class);45var callLLM = AIAgentNode.llmRequest();6var executeTool = AIAgentNode.executeTool();7var sendToolResult = AIAgentNode.llmSendToolResult();89// Compress the LLM history and keep the current ReceivedToolResult for the next node10var compressHistory = AIAgentNode11 .llmCompressHistory("compressHistory")12 .withInput(ReceivedToolResult.class)13 .build();1415// Edge from start to callLLM16graph.edge(graph.nodeStart, callLLM);1718// Edge from callLLM to finish on assistant message19graph.edge(AIAgentEdge.builder()20 .from(callLLM)21 .to(graph.nodeFinish)22 .onIsInstance(Message.Assistant.class)23 .transformed(Message.Assistant::getContent)24 .build());2526// Edge from callLLM to executeTool on tool call27graph.edge(AIAgentEdge.builder()28 .from(callLLM)29 .to(executeTool)30 .onIsInstance(Message.Tool.Call.class)31 .build());3233// Compress history after executing any tool if the history is too long34graph.edge(AIAgentEdge.builder()35 .from(executeTool)36 .to(compressHistory)37 .onCondition((toolResult, ctx) ->38 ctx.getLlm().readSession(session ->39 session.getPrompt().getMessages().size() > 10040 )41 )42 .build());4344graph.edge(compressHistory, sendToolResult);4546// Otherwise, proceed to the next LLM request47graph.edge(AIAgentEdge.builder()48 .from(executeTool)49 .to(sendToolResult)50 .onCondition((toolResult, ctx) ->51 ctx.getLlm().readSession(session ->52 session.getPrompt().getMessages().size() <= 10053 )54 )55 .build());5657// Edge from sendToolResult to executeTool on tool call58graph.edge(AIAgentEdge.builder()59 .from(sendToolResult)60 .to(executeTool)61 .onIsInstance(Message.Tool.Call.class)62 .build());6364// Edge from sendToolResult to finish on assistant message65graph.edge(AIAgentEdge.builder()66 .from(sendToolResult)67 .to(graph.nodeFinish)68 .onIsInstance(Message.Assistant.class)69 .transformed(Message.Assistant::getContent)70 .build());이 예에서 전략은 각 도구 호출 후 기록이 너무 긴지 확인합니다. 도구 결과를 LLM으로 다시 보내기 전에 기록이 압축됩니다. 이렇게 하면 컨텍스트가 커지는 것을 방지할 수 있습니다. 긴 대화.
- 전략의 논리적 단계(하위 그래프) 간의 기록을 압축하려면 다음과 같이 전략을 구현할 수 있습니다. 다음과 같습니다:
코틀린
1val strategy = strategy<String, String>("execute-with-history-compression") {2 val collectInformation by subgraph<String, String> {3 // Some steps to collect the information4 }5 val compressHistory by nodeLLMCompressHistory<String>()6 val makeTheDecision by subgraph<String, String> {7 // Some steps to make the decision based on the current compressed history and collected information8 }9 10 nodeStart then collectInformation then compressHistory then makeTheDecision11}자바
1var graph = AIAgentGraphStrategy.builder("execute-with-history-compression")2 .withInput(String.class)3 .withOutput(String.class);45// Subgraph to collect information6var collectInformation = AIAgentSubgraph.builder("collectInformation")7 .withInput(String.class)8 .withOutput(String.class)9 .limitedTools(Collections.emptyList())10 .withTask(input -> "Collect information based on: " + input)11 .build();1213// Compress history after collecting information14var compressHistory = AIAgentNode15 .llmCompressHistory("compressHistory")16 .withInput(String.class)17 .build();1819// Subgraph to make decision based on compressed history20var makeTheDecision = AIAgentSubgraph.builder("makeTheDecision")21 .withInput(String.class)22 .withOutput(String.class)23 .limitedTools(Collections.emptyList())24 .withTask(input -> "Make a decision based on the information")25 .build();2627// Build the flow: start -> collectInformation -> compressHistory -> makeTheDecision -> finish28graph.edge(graph.nodeStart, collectInformation);29graph.edge(collectInformation, compressHistory);30graph.edge(compressHistory, makeTheDecision);31graph.edge(makeTheDecision, graph.nodeFinish);이 예에서는 정보 수집 단계를 완료한 후 이력을 압축한 후 다음 단계로 진행합니다. 의사결정 단계.
사용자 정의 노드의 기록 압축
사용자 정의 노드를 구현하는 경우 replaceHistoryWithTLDR() 함수(Kotlin)를 사용하여 기록을 압축할 수 있습니다.
다음과 같이:
코틀린
1llm.writeSession {2 replaceHistoryWithTLDR()3}이 접근 방식을 사용하면 사용자 정의 노드 로직의 어느 지점에서든 압축을 구현할 수 있는 유연성이 향상됩니다. 특정 요구 사항.
사용자 정의 노드에 대해 자세히 알아보려면 Custom nodes을 참조하세요.
기록 압축 전략
선택적 strategy 매개변수를 사용하여 압축 프로세스를 사용자 정의할 수 있습니다.
- Kotlin: 전략을
nodeLLMCompressHistory(strategy=...)또는replaceHistoryWithTLDR(strategy=...)에 전달합니다. - 자바:
.compressionStrategy()빌더 메소드를 사용합니다.
프레임워크는 몇 가지 기본 제공 전략을 제공합니다.
전체 히스토리(기본값)
전체 기록을 달성된 내용을 요약하는 하나의 TLDR 메시지로 압축하는 기본 전략 멀리. 이 전략은 전체 대화에 대한 인식을 유지하려는 대부분의 일반적인 사용 사례에 적합합니다. 토큰 사용량을 줄이면서 컨텍스트를 파악하세요.
다음과 같이 사용할 수 있습니다.
- 전략 그래프에서:
코틀린
1val compressHistory by nodeLLMCompressHistory<ProcessedInput>(2 strategy = HistoryCompressionStrategy.WholeHistory3)자바
1// Using WholeHistory strategy in a compression node2var compressHistory = AIAgentNode3 .llmCompressHistory("compressHistory")4 .withInput(String.class)5 .compressionStrategy(HistoryCompressionStrategy.WholeHistory)6 .build();78// Note: This example only shows the node creation.9// You would need to add edges and other nodes to complete the graph.- 사용자 정의 노드에서:
코틀린
1llm.writeSession {2 replaceHistoryWithTLDR(strategy = HistoryCompressionStrategy.WholeHistory)3}자바
1ctx.getLlm().writeSession(session -> {2 session.replaceHistoryWithTLDR(HistoryCompressionStrategy.WholeHistory);3 return null;4});FromLastN메시지
이 전략은 마지막 n 메시지만 TLDR 메시지로 압축하고 이전 메시지를 완전히 삭제합니다.
이는 에이전트의 최신 성과(또는 최근에 발견된 사실, 최신 컨텍스트)만 확인할 때 유용합니다.
문제 해결과 관련이 있습니다.
다음과 같이 사용할 수 있습니다.
- 전략 그래프에서:
코틀린
1val compressHistory by nodeLLMCompressHistory<ProcessedInput>(2 strategy = HistoryCompressionStrategy.FromLastNMessages(5)3)자바
1// Using FromLastNMessages strategy to compress only the last 5 messages2var compressHistory = AIAgentNode3 .llmCompressHistory("compressHistory")4 .withInput(String.class)5 .compressionStrategy(HistoryCompressionStrategy.FromLastNMessages(5))6 .build();78// Note: This example only shows the node creation.9// You would need to add edges and other nodes to complete the graph.- 사용자 정의 노드에서:
코틀린
1llm.writeSession {2 replaceHistoryWithTLDR(strategy = HistoryCompressionStrategy.FromLastNMessages(5))3}자바
1ctx.getLlm().writeSession(session -> {2 session.replaceHistoryWithTLDR(HistoryCompressionStrategy.FromLastNMessages(5));3 return null;4});청크
이 전략은 전체 메시지 기록을 고정된 크기의 청크로 나누고 각 청크를 독립적으로 압축합니다. TLDR 메시지. 이는 지금까지 수행된 작업에 대한 간결한 TLDR이 필요할 뿐만 아니라 현재 진행 중인 작업을 추적하고 싶을 때 유용합니다. 전반적인 진행 상황과 일부 오래된 정보도 중요할 수 있습니다.
다음과 같이 사용할 수 있습니다.
- 전략 그래프에서:
코틀린
1val compressHistory by nodeLLMCompressHistory<ProcessedInput>(2 strategy = HistoryCompressionStrategy.Chunked(10)3)자바
1// Using Chunked strategy to compress history in chunks of 10 messages2var compressHistory = AIAgentNode3 .llmCompressHistory("compressHistory")4 .withInput(String.class)5 .compressionStrategy(HistoryCompressionStrategy.Chunked(10))6 .build();78// Note: This example only shows the node creation.9// You would need to add edges and other nodes to complete the graph.- 사용자 정의 노드에서:
코틀린
1llm.writeSession {2 replaceHistoryWithTLDR(strategy = HistoryCompressionStrategy.Chunked(10))3}자바
1ctx.getLlm().writeSession(session -> {2 session.replaceHistoryWithTLDR(HistoryCompressionStrategy.Chunked(10));3 return null;4});기록에서 사실 검색
전략은 제공된 개념 목록과 관련된 특정 사실을 기록에서 검색하고 검색합니다. 전체 기록을 이러한 사실로 변경하고 향후 LLM 요청에 대한 컨텍스트로 남겨 둡니다. 이는 LLM이 작업을 더 잘 수행하는 데 어떤 정확한 사실이 관련되는지에 대한 아이디어가 있을 때 유용합니다.
다음과 같이 사용할 수 있습니다.
- 전략 그래프에서:
코틀린
1val compressHistory by nodeLLMCompressHistory<ProcessedInput>(2 strategy = RetrieveFactsFromHistory(3 Concept(4 keyword = "user_preferences",5 // Description to the LLM -- what specifically to search for6 description = "User's preferences for the recommendation system, including the preferred conversation style, theme in the application, etc.",7 // LLM would search for multiple relevant facts related to this concept:8 factType = FactType.MULTIPLE9 ),10 Concept(11 keyword = "product_details",12 // Description to the LLM -- what specifically to search for13 description = "Brief details about products in the catalog the user has been checking",14 // LLM would search for multiple relevant facts related to this concept:15 factType = FactType.MULTIPLE16 ),17 Concept(18 keyword = "issue_solved",19 // Description to the LLM -- what specifically to search for20 description = "Was the initial user's issue resolved?",21 // LLM would search for a single answer to the question:22 factType = FactType.SINGLE23 )24 )25)자바
1// Using RetrieveFactsFromHistory strategy to extract specific facts2var compressHistory = AIAgentNode3 .llmCompressHistory("compressHistory")4 .withInput(ReceivedToolResult.class)5 .compressionStrategy(new RetrieveFactsFromHistory(6 new Concept(7 "user_preferences",8 "User's preferences for the recommendation system, including the preferred conversation style, theme in the application, etc.",9 FactType.MULTIPLE10 ),11 new Concept(12 "product_details",13 "Brief details about products in the catalog the user has been checking",14 FactType.MULTIPLE15 ),16 new Concept(17 "issue_solved",18 "Was the initial user's issue resolved?",19 FactType.SINGLE20 )21 ))22 .build();2324 // Note: This example only shows the node creation.25 // You would need to add edges and other nodes to complete the graph.- 사용자 정의 노드에서:
코틀린
1llm.writeSession {2 replaceHistoryWithTLDR(3 strategy = RetrieveFactsFromHistory(4 Concept(5 keyword = "user_preferences", 6 // Description to the LLM -- what specifically to search for7 description = "User's preferences for the recommendation system, including the preferred conversation style, theme in the application, etc.",8 // LLM would search for multiple relevant facts related to this concept:9 factType = FactType.MULTIPLE10 ),11 Concept(12 keyword = "product_details",13 // Description to the LLM -- what specifically to search for14 description = "Brief details about products in the catalog the user has been checking",15 // LLM would search for multiple relevant facts related to this concept:16 factType = FactType.MULTIPLE17 ),18 Concept(19 keyword = "issue_solved",20 // Description to the LLM -- what specifically to search for21 description = "Was the initial user's issue resolved?",22 // LLM would search for a single answer to the question:23 factType = FactType.SINGLE24 )25 )26 )27}자바
1ctx.getLlm().writeSession(session -> {2 session.replaceHistoryWithTLDR(new RetrieveFactsFromHistory(3 new Concept(4 "user_preferences", 5 // Description to the LLM -- what specifically to search for6 "User's preferences for the recommendation system, including the preferred conversation style, theme in the application, etc.",7 // LLM would search for multiple relevant facts related to this concept:8 FactType.MULTIPLE9 ),10 new Concept(11 "product_details",12 // Description to the LLM -- what specifically to search for13 "Brief details about products in the catalog the user has been checking",14 // LLM would search for multiple relevant facts related to this concept:15 FactType.MULTIPLE16 ),17 new Concept(18 "issue_solved",19 // Description to the LLM -- what specifically to search for20 "Was the initial user's issue resolved?",21 // LLM would search for a single answer to the question:22 FactType.SINGLE23 )24 ));25 return null;26});사용자 정의 기록 압축 전략 구현
참고 사용자 정의 기록 압축 전략은 Kotlin에서만 사용할 수 있습니다.
HistoryCompressionStrategy 추상 클래스를 확장하여 자신만의 기록 압축 전략을 만들 수 있으며
compress 방법을 구현합니다.
예는 다음과 같습니다.
코틀린
1class MyCustomCompressionStrategy : HistoryCompressionStrategy() {2 override suspend fun compress(3 llmSession: AIAgentLLMWriteSession,4 memoryMessages: List<Message>5 ) {6 // 1. Process the current history in llmSession.prompt.messages7 // 2. Create new compressed messages8 // 3. Update the prompt with the compressed messages910 // Save original messages to preserve them11 val originalMessages = llmSession.prompt.messages12 13 // Example implementation:14 val importantMessages = llmSession.prompt.messages.filter {15 // Your custom filtering logic16 it.content.contains("important")17 }.filterIsInstance<Message.Response>()18 19 // Note: you can also make LLM requests using the `llmSession` and ask the LLM to do some job for you using, for example, `llmSession.requestLLMWithoutTools()`20 // Or you can change the current model: `llmSession.model = AnthropicModels.Opus_4_6` and ask some other LLM model -- but don't forget to change it back after2122 // Compose the prompt with the filtered messages23 val compressedMessages = composeMessageHistory(24 originalMessages,25 importantMessages,26 memoryMessages27 )28 }29}이 예에서 사용자 정의 전략은 "중요"라는 단어가 포함된 메시지를 필터링하고 압축된 역사.
그런 다음 다음과 같이 사용할 수 있습니다.
- 전략 그래프에서:
코틀린
1val compressHistory by nodeLLMCompressHistory<ProcessedInput>(2 strategy = MyCustomCompressionStrategy()3)- 사용자 정의 노드에서:
코틀린
1llm.writeSession {2 replaceHistoryWithTLDR(strategy = MyCustomCompressionStrategy())3}압축 중 메모리 보존
모든 기록 압축 방법은 메모리 보존을 지원하며, 이는 메모리 관련 메시지를 기록해야 하는지 여부를 결정합니다.
압축 중에 보존됩니다. Kotlin에서는 preserveMemory 매개변수를 사용합니다. Java에서는 .preserveMemory()을 사용합니다.
빌더 방식.
이는 메모리에서 검색된 사실을 포함하거나 메모리 기능이 활성화되지 않았음을 나타내는 메시지입니다.
메모리 보존을 활성화하려면:
Kotlin:
preserveMemory매개변수를 사용합니다.자바:
.preserveMemory()빌더 메소드를 사용합니다.전략 그래프에서:
코틀린
1val compressHistory by nodeLLMCompressHistory<ProcessedInput>(2 strategy = HistoryCompressionStrategy.WholeHistory,3 preserveMemory = true4)자바
1// Using WholeHistory strategy with preserveMemory=true2var compressHistory = AIAgentNode3 .llmCompressHistory("compressHistory")4 .withInput(String.class)5 .compressionStrategy(HistoryCompressionStrategy.WholeHistory)6 .preserveMemory(true)7 .build();89// Note: This example only shows the node creation.10// You would need to add edges and other nodes to complete the graph.- 사용자 정의 노드에서:
코틀린
1llm.writeSession {2 replaceHistoryWithTLDR(3 strategy = HistoryCompressionStrategy.WholeHistory,4 preserveMemory = true5 )6}자바
1ctx.getLlm().writeSession(session -> {2 session.replaceHistoryWithTLDR(3 /** strategy */ HistoryCompressionStrategy.WholeHistory,4 /** preserveMemory */ true5 );6 return null;7});