Koog 1.0 idioms, gotchas, and scaffolding skills for Kotlin agents on the JVM
89
89%
Does it follow best practices?
Impact
89%
1.78xAverage score across 47 eval scenarios
Advisory
Suggest reviewing before use
Process steps in order. Do not skip ahead.
Persistence (add-persistence) writes checkpoints automatically on a configured schedule (every node, every N steps, every successful turn). Snapshot is caller-triggered — you call snapshot() when you decide it's a useful save point.
Use snapshot when:
If the user's need is "agent should resume after a crash", snapshot is the wrong feature. It is caller-triggered, not automatic. Invoke Skill(skill: "add-persistence") and deliver its install(Persistence) solution. Add one reply sentence telling the developer snapshot is caller-triggered and the Persistence feature checkpoints automatically. The redirect alone is not the deliverable. Finish here. Do not continue to Step 2. If the need is "save state at this specific point", continue.
Proceed immediately to Step 2.
implementation("ai.koog:agents-features-snapshot:1.0.0")Proceed immediately to Step 3.
import ai.koog.agents.features.snapshot.Snapshot
val agent = AIAgent(
promptExecutor = ...,
llmModel = ...,
systemPrompt = "...",
) {
install(Snapshot) {
// optional: storage backend (in-memory, disk, JDBC)
}
}Proceed immediately to Step 4.
Inside a node body (or from outside the run via the agent's API), call snapshot():
// inside a node body
val snapshotId = snapshot() // returns an identifier you store
storeSnapshotId(snapshotId)Restore by passing the snapshot ID to runFromSnapshot:
val snapshotId = loadSnapshotId()
val result = agent.runFromSnapshot(snapshotId, additionalInput = null)Snapshots are the typed-storage half of persistence — AIAgentStorage rides along automatically (values must be @Serializable, see manage-state). Non-serializable types break snapshots silently, same as checkpoints.
Forking — take a snapshot, run one branch, restore, run another:
val branchPoint = snapshot()
val resultA = agent.runFromSnapshot(branchPoint, additionalInput = "variant A input")
val resultB = agent.runFromSnapshot(branchPoint, additionalInput = "variant B input")Useful for A/B testing strategy variants without re-running the prefix.
Finish here.
.gemini
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
scenario-46
scenario-47
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