Koog 1.0 idioms, gotchas, and scaffolding skills for Kotlin agents on the JVM
86
88%
Does it follow best practices?
Impact
86%
1.86xAverage score across 45 eval scenarios
Advisory
Suggest reviewing before use
This skill is an action router — pick the step that matches the user's intent and execute only that step. Do not run other steps; do not parallelize.
Available actions:
AIAgentStorage + createStorageKey)HistoryCompressionStrategy)LongTermMemory feature)storage on AIAgentContext is the typed key-value store. Keys are created once at file scope; values are read/written inside node bodies:
import ai.koog.agents.core.agent.context.createStorageKey
val unfinishedNodesKey = createStorageKey<MutableList<NodeRef>>("unfinishedNodes")
val currentNodeKey = createStorageKey<NodeRef>("currentNode")
// inside a node body — `this` is AIAgentContext
storage.set(currentNodeKey, ref)
val current = storage.get(currentNodeKey)Constraints (1.0):
@Serializable — storage is checkpointed (KG-673). Non-serializable types (thread-locals, open file descriptors, raw clients) break checkpointing silentlyAIAgentStorageKey equality is name-based — two keys with the same string name collide regardless of file locationAIAgentStorage() constructor was removed; use AIAgentStorage(serializer) when constructing one manuallytoMap() was removed — iterate via the key set if you need to inspectstateManager (also on AIAgentContext) is for agent-lifecycle state, not application data. Don't pile arbitrary data into stateManager.
Finish here.
Long agentic runs blow the context window. Compression rules:
Default to HistoryCompressionStrategy.WholeHistory (a single TL;DR) unless the user explicitly asks for last-N, time-window, chunked, or fact-extraction shape. Write the modified node body to disk with an explicit Path: label (same convention as scaffold-agent):
Path: src/main/kotlin/com/example/Strategy.kt — boundary node with the replaceHistoryWithTLDR call (or whichever file defines the strategy / boundary node)Create the file if it doesn't exist. Do not respond with prose only.
// inside the boundary node body (between exploration and drafting, for example)
llm.writeSession {
replaceHistoryWithTLDR() // WholeHistory is the default; pass strategy = HistoryCompressionStrategy.X only when overriding
}Other strategy variants (use only when the user names them — pass via replaceHistoryWithTLDR(strategy = HistoryCompressionStrategy.X) and add import ai.koog.agents.core.dsl.extension.HistoryCompressionStrategy):
HistoryCompressionStrategy.NoCompression — keep everythingHistoryCompressionStrategy.WholeHistoryMultipleSystemMessages — multi-message summaryHistoryCompressionStrategy.FromLastNMessages(n) — keep the last N, drop the restHistoryCompressionStrategy.FromTimestamp(instant) — keep messages after timestampHistoryCompressionStrategy.Chunked(chunkSize) — chunk-by-chunk summarizationHistoryCompressionStrategy.FactRetrieval(concepts) — extract structured facts about named conceptsFactRetrieval was extracted from the removed AgentMemory feature in 1.0 — it's now usable standalone in agents-core, no memory feature required.
Finish here.
LongTermMemory)AgentMemory was removed in 1.0. Use LongTermMemory for memory that persists across agent runs.
Add the dependency:
implementation("ai.koog:agents-features-longterm-memory:1.0.0")
// for Bedrock AgentCore backend (one option):
implementation("ai.koog:agents-features-longterm-memory-aws:1.0.0")Install the feature inside AIAgent(...)'s trailing lambda:
import ai.koog.agents.features.longterm.memory.LongTermMemory
import ai.koog.agents.features.longterm.memory.FailurePolicy
val agent = AIAgent(
promptExecutor = ...,
llmModel = ...,
systemPrompt = "...",
) {
install(LongTermMemory) {
searchQueryProvider = ... // was QueryExtractor pre-1.0
documentExtractor = ... // was ExtractionStrategy pre-1.0
failurePolicy = FailurePolicy.PROPAGATE // or .SILENTLY_SKIP, .LOG
// backend-specific config (Bedrock, in-process, etc.)
}
}1.0 renames in LongTermMemory (apply when migrating):
QueryExtractor → SearchQueryProviderExtractionStrategy → DocumentExtractorIngestionTiming was removed — strategies now manage ingestion timing internallyIf the user's need is "agent should remember things within one run but not across runs," they don't need LongTermMemory — Step 1 (storage) covers it.
Finish here.
evals
scenario-1
scenario-2
scenario-3
scenario-4
scenario-5
scenario-6
scenario-7
scenario-8
scenario-9
scenario-10
scenario-11
scenario-12
scenario-13
scenario-14
scenario-15
scenario-16
scenario-17
scenario-18
scenario-19
scenario-20
scenario-21
scenario-22
scenario-23
scenario-24
scenario-25
scenario-26
scenario-27
scenario-28
scenario-29
scenario-30
scenario-31
scenario-32
scenario-33
scenario-34
scenario-35
scenario-36
scenario-37
scenario-38
scenario-39
scenario-40
scenario-41
scenario-42
scenario-43
scenario-44
scenario-45
skills
add-observability
add-persistence
add-rag
add-structured-output
add-token-budgeting
add-tool
cache-llm-calls
define-prompt
domain-model-subtask-pipeline
references
enable-prompt-caching
handle-agent-events
manage-state
migrate-from-0-x
model-planner-subtasks
persist-chat-history
query-sql-from-agent
scaffold-agent
snapshot-and-restore
test-koog-agents
trace-agent-internals
use-attachments
use-functional-agent
use-llm-node-variants
use-planner
wire-a2a
wire-acp-server
wire-ktor-server
wire-mcp-server
wire-spring-boot