Koog 1.0 idioms, gotchas, and scaffolding skills for Kotlin agents on the JVM
91
89%
Does it follow best practices?
Impact
92%
1.73xAverage score across 47 eval scenarios
Advisory
Suggest reviewing before use
Embed and retrieve from a docs corpus as an agent tool
Exposes retrieval as a Tool, not as an augmenter
0%
100%
Builds an embedder from a provider
0%
100%
Builds a vector store and indexes documents
0%
100%
Adds the RAG and embeddings dependencies
0%
100%
Registers the search tool with the agent's ToolRegistry
0%
100%
Search tool returns formatted results, not raw vector hits
0%
100%
Expose an agent over a Ktor HTTP route
Installs the Koog Ktor plugin
0%
66%
Adds the koog-ktor dependency
0%
65%
Configures the prompt executor inside the install block
0%
33%
Reads the OpenAI API key from the environment
0%
90%
Adds a POST /agent route
0%
86%
Resolves the agent from the plugin attribute
0%
30%
Consume a remote A2A agent as a tool
Uses A2AClient to call the remote
0%
100%
Adds a2a-core and a2a-client dependencies
0%
100%
Wraps the A2A call inside a tool
0%
100%
Reads the bearer token from the environment
0%
100%
Registers the wrapper tool with the agent
0%
100%
Install Langfuse exporter via the OpenTelemetry feature
Adds the agents-features-opentelemetry dependency
0%
100%
Installs OpenTelemetry in the trailing lambda
0%
100%
Calls addLangfuseExporter inside the install block
0%
100%
Sets langfuse.session.id from the environment
0%
100%
Turns on verbose mode
0%
100%
Does not stack a second backend integration
0%
100%
Compress conversation history with TL;DR mid-run
Calls replaceHistoryWithTLDR inside llm.writeSession
0%
100%
Compresses only at the boundary node, not every node
0%
100%
Picks WholeHistory or equivalent TL;DR strategy
0%
100%
Does not introduce LongTermMemory
0%
100%
Compression happens before the next LLM call in drafting
0%
100%
Install Trace feature with edge and node events for local debugging
Installs the Trace feature
0%
100%
Adds the agents-features-trace dependency
0%
100%
Captures EDGE_EVALUATION events
0%
100%
Captures NODE_ENTRY events
0%
100%
Sends trace to a local sink
0%
100%
Attach a user-uploaded image via Attachment.image()
Uses Attachment.image to wrap the file
0%
100%
Constructs a Message.User with attachments
0%
100%
Sends via nodeLLMSendMessage, not nodeLLMRequest
0%
100%
Strategy is typed strategy<File, String>
0%
100%
Returns text from a final nodeFinish edge
0%
100%
PDF and URL image attachments in one agent call
PDF via Attachment.file
0%
100%
Image via Attachment.image(url)
0%
100%
Anthropic model with PDF support
0%
100%
No manual file encoding
0%
100%
Both attachments in one Message.User
0%
100%
Runtime attachment construction in node body
0%
100%
Sends via nodeLLMSendMessage
0%
100%
Sub-agent-as-tool via AIAgentService.fromAgent
Uses AIAgentService.fromAgent to wrap the find agent
0%
100%
Calls createAgentTool with typed input/output
0%
100%
Provides agentDescription and inputDescription for the LLM
0%
100%
Preserves the find agent's separate configuration
0%
100%
Registers the wrapped tool in the main agent's ToolRegistry
0%
100%
Does not introduce a strategy graph or subgraph
0%
100%
Construct PlannerAIAgent with Planners.llmBased
Picks the LLM-based planner primitive
0%
100%
Adds the separate planner module dependency
0%
75%
Passes the planner as the strategy parameter
0%
100%
Sets a much higher maxIterations than the default
0%
100%
Includes the lookup tools in the registry
0%
100%
Uses top-level AIAgent factory
0%
100%
Wire an ACP server with cancellation support
Installs the ACP feature
0%
100%
Adds the agents-features-acp dependency
0%
100%
Makes the long-running tool honor cancellation
0%
100%
Notes that catching CancellationException would defeat cancel
0%
100%
Mentions the example or feature module's API
0%
100%
Does not block on coroutine internals
0%
100%
Install the Sql feature with read-only mode and schema scope
Installs the Sql feature in read-only mode
0%
100%
Scopes the schema to the named tables
0%
100%
Caps result rows
0%
100%
Reads DB credentials from environment
0%
100%
Pins schema in system prompt
0%
66%
Adds the agents-features-sql dependency and JDBC driver
0%
60%
Build a single-block FunctionalAIAgent
Uses the functional-agent factory
0%
100%
Strategy block is a single suspending block
0%
100%
Trims the LLM response inside the block
0%
100%
Includes the system prompt for formal-English rewriting
0%
100%
Reads OPENAI_API_KEY from environment
0%
100%
No tools registered
0%
100%
Test with TestPromptExecutor.scripted and an event recorder
Adds agents-test as testImplementation
0%
100%
Uses a scripted/mocked prompt executor
0%
100%
Records tool-call sequence via handleEvents
0%
100%
Asserts ordering — lookup_priority before classification
0%
100%
Tools run their real implementations
0%
100%
Single agent.run call per test
0%
100%
Configure two LLM providers via Spring Boot autoconfig
Configures both providers under ai.koog.<provider>
0%
100%
Reads both keys from environment variables
0%
100%
Migrates service code from LLMClient to MultiLLMPromptExecutor
0%
100%
Routes by model at the call site
0%
100%
Does not introduce a custom @Bean LLMClient override
0%
100%
Does not remove the starter dependency
0%
100%
Annotated ToolSet for a String-in/String-out function
Picks the annotated ToolSet style
0%
100%
Annotates parameters with @LLMDescription
0%
100%
Annotates the class and method with @LLMDescription
0%
100%
Registers via ToolRegistry DSL
0%
100%
Passes the registry into AIAgent at construction
0%
100%
Does not introduce typed args classes
0%
100%
Custom strategy graph with subgraphWithVerification and fix
Uses subgraphWithTask and subgraphWithVerification
0%
100%
Conditional edge from verify drives the loop
0%
100%
Fix loops back to verify
0%
100%
Each subgraph has its own tools set
0%
100%
Verifier uses a cheaper model than fixer
0%
100%
Top-level strategy<Input, Output>(name) declaration
0%
100%
cacheControl breakpoint on a long Anthropic system prompt
Uses cacheControl on the system segment
0%
100%
Mentions Anthropic minimum-token requirement
0%
100%
Places the breakpoint at end of system content
0%
100%
Uses the prompt DSL builder
0%
100%
References observability for cache-hit verification
0%
100%
Does not change the agent's model
0%
100%
Stream LLM reply via nodeLLMRequestStreaming
Uses nodeLLMRequestStreaming
0%
100%
Supplies a chunk handler lambda
0%
100%
Names that the handler must not block
0%
100%
Wires the streaming node into a top-level strategy<...>
0%
100%
Does not include tool nodes
0%
100%
Cap token spend with history compression on overrun
Installs the Tokenizer feature
0%
100%
Sets a 50000 runBudget
0%
100%
Picks BudgetAction.CompressHistory
0%
100%
Adds the tokenizer dependencies
0%
100%
Does not lower the model to a cheaper one
0%
100%
Notes how the cap surfaces in observability if installed
0%
20%
Build a prompt DSL with few-shot examples
Uses the prompt { ... } DSL builder
0%
100%
System turn names the classification task
0%
100%
Each example is a user/assistant pair
0%
100%
Examples are inline literal strings
0%
100%
Imports prompt builder from ai.koog.prompt.dsl
0%
100%
Does not introduce a PromptAugmenter
0%
100%
Install the Persistence feature for planner crash recovery
Installs the Persistence feature
0%
100%
Adds the JDBC persistence backend dependency
0%
100%
Configures the backend with the JDBC connection
0%
100%
Uses Persistence type names, not Persistency
0%
100%
Shows resume path with runFromCheckpoint
0%
100%
Notes serializability requirement for storage
0%
0%
Scaffold a new Koog 1.0 OpenAI project from scratch
Uses 1.0+ umbrella coordinate
0%
100%
Targets JDK 17 toolchain
0%
100%
Uses top-level AIAgent factory
0%
100%
Reads API key from environment
0%
100%
Wires simpleOpenAIExecutor and a current OpenAI model
0%
100%
Omits the default singleRunStrategy
0%
100%
Runs the agent inside runBlocking
0%
100%
Initializes git in the new directory
0%
100%
Application plugin wired with mainClass
0%
100%
Caller-triggered snapshot with runFromSnapshot fork
Installs the Snapshot feature
0%
100%
Adds the agents-features-snapshot dependency
0%
100%
Calls snapshot() at the decision point
0%
100%
Demonstrates two runFromSnapshot calls
0%
100%
Notes serializability requirement
0%
100%
Replace removed AgentMemory with LongTermMemory
Replaces AgentMemory with LongTermMemory
0%
100%
Renames queryExtractor and extractionStrategy
0%
100%
Removes IngestionTiming
0%
100%
Drops .invoke from the AIAgent call
0%
100%
Adds the new LongTermMemory dependency
0%
100%
Bumps JDK target to 17
0%
100%
Updates all Koog coordinates to 1.0+
0%
100%
Compose PlannerNode tree with parallel inside sequential
Uses PlannerNode.parallel inside PlannerNode.sequential
0%
60%
Builds the agent with PlannerAIAgent / Planners.*
0%
60%
Tracks current subtask via storage key
0%
25%
Adds the planner module dependency
0%
100%
Sets maxIterations well above the default
0%
100%
Does not over-engineer with graph subgraphs
0%
60%
Scaffold a Koog 1.0 Anthropic project into an empty existing directory
Uses simpleAnthropicExecutor
0%
0%
Selects a current Anthropic model
0%
0%
Reads ANTHROPIC_API_KEY from the environment
0%
0%
Accepts the existing-empty-directory state without overwrite
0%
100%
Uses 1.0+ umbrella coordinate
0%
0%
Top-level AIAgent factory call
0%
0%
Install JDBC chat-history feature for per-user session resume
Installs the JDBC chat-history feature
0%
60%
Adds the JDBC chat-history dependency
0%
85%
Reads DB credentials from environment
0%
100%
Threads userId as sessionId on agent.run
0%
92%
Does not require user to thread message history manually
0%
100%
Tool<TArgs, TResult> subclass with typed input and output
Subclasses Tool<TArgs, TResult>
0%
60%
Declares a ToolDescriptor
0%
100%
Implements suspend execute(args) returning the typed result
0%
75%
Registers with the ToolRegistry DSL
0%
100%
Reuses the existing @Serializable data classes
0%
0%
Does not introduce a ToolSet wrapper
0%
100%
Merge MCP registry with existing local ToolRegistry via +
Composes the two registries with +
0%
100%
Preserves the existing CalculatorTools registration
0%
100%
Uses the modern HTTP MCP transport
0%
100%
Adds the agents-mcp dependency
0%
100%
Constructs the merged registry before AIAgent(...)
0%
100%
Calls the suspending MCP builder inside a coroutine context
0%
100%
Build a Koog strategy with a tool-handling loop and conditional branching; produce correct member-vs-extension imports for the DSL primitives
Imports the `onToolCalls` extension explicitly
0%
100%
Imports the `onTextMessage` extension explicitly
0%
100%
Does not invent a member import for `forwardTo`
0%
100%
Uses both edge primitives the loop needs
0%
100%
Chains `nodeExecuteTools` to `nodeLLMSendToolResults` explicitly
0%
100%
Wires the loop back through `nodeExecuteTool` on continued tool calls
0%
100%
Connect to an SSE-only MCP server; produce correct extension imports for `fromSseUrl` and the `-jvm:1.0.0-beta` dependency line
Picks the SSE transport
0%
100%
Imports the `fromSseUrl` extension explicitly
0%
100%
Pins the MCP artifact to `1.0.0-beta`
0%
100%
Uses the `-jvm` suffix on the artifact
0%
100%
Builds the registry before constructing the agent
0%
100%
Calls the suspending builder inside a coroutine context
0%
100%
Wire Streamable HTTP MCP transport to a remote server
Uses the Streamable HTTP transport
0%
100%
Adds the agents-mcp dependency
0%
100%
Passes the server URL to the transport configuration
0%
100%
Builds the registry before constructing the agent
0%
100%
Calls the suspending builder inside a coroutine context
0%
100%
Reads the GitHub token from the environment
0%
100%
Passes the token as a bearer header in the transport config
0%
100%
Wire stdio MCP transport against a local subprocess
Uses the process-based MCP transport
0%
100%
Launches the process with ProcessBuilder
0%
100%
Adds the agents-mcp dependency
0%
100%
Builds the registry before constructing the agent
0%
100%
Calls the suspending builder inside a coroutine context
0%
100%
Does not import Ktor or HTTP-client types
0%
100%
Build a support triage agent with four typed phases, sliced tools, and a verify/adjust loop
Tools sliced into communication / read / write ToolSets
0%
100%
Each phase grants only the matching tool subset
0%
100%
Typed @LLMDescription'd data classes for inter-phase handoffs
0%
100%
Uses subgraphWithTask<In, Out> for each phase
0%
100%
Verification + adjust loop with CriticResult branching
0%
100%
Mixed model selection per phase
0%
100%
Declares the required Gradle dependencies
0%
100%
Print tool-call trace via handleEvents
Installs the event-handler feature
0%
100%
Adds the agents-features-event-handler dependency
0%
100%
Subscribes to onToolCallStarting
0%
100%
Subscribes to onToolCallFinished
0%
100%
Uses distinct visual markers for start vs end
0%
100%
Does not also install OpenTelemetry
0%
100%
Expose an existing ToolSet as a stdio MCP server
Calls startStdioMcpServer
0%
100%
Adds the agents-mcp-server dependency
0%
100%
Wraps the existing ToolSet in a ToolRegistry
0%
100%
Keeps the process alive after startup
0%
100%
Does not reach for the client transport surface
0%
100%
Routes tool results through an explicit send-results node
0%
100%
Adapts the LLM client to the decoupled HTTP transport
0%
88%
Swaps kotlin.time.Clock for KoogClock
0%
100%
Raises the JDK and Kotlin toolchain minima
0%
100%
Drops .invoke from the AIAgent construction
0%
100%
Bumps all Koog coordinates to 1.0 with no mixed versions
0%
100%
Chooses ACP for the tooling-client case
0%
100%
Adds the ACP dependency
0%
100%
Correct ACP import
0%
100%
install(ACP) in the AIAgent trailing lambda
0%
100%
Configures the endpoint inside install(ACP)
0%
100%
Notes progress events surface to the client
50%
100%
Rationale for ACP over A2A
0%
100%
Redirect from Snapshot to Persistence for automatic crash recovery
Distinguishes caller-triggered from continuous save
32%
100%
Routes to the Persistence feature
73%
100%
Does NOT recommend installing the snapshot feature
100%
80%
Acknowledges the developer's word choice without capitulating
10%
100%
Mentions the cost of frequent checkpoints
0%
100%
Refuse to scaffold over an existing non-empty directory
Detects the non-empty state before writing
100%
100%
Refuses to proceed without explicit confirmation
100%
100%
Asks an explicit yes/no overwrite question
75%
100%
Names what would be overwritten
100%
100%
Leaves the directory unchanged on refusal
100%
100%
Redirect from planner to graph DSL when topology is fixed
Names the topology as the disqualifying signal
92%
92%
Redirects to the graph DSL
26%
93%
Does not install agents-planner
90%
100%
Acknowledges the developer's framing without capitulating
93%
93%
Mentions the cost penalty of an unwarranted planner
100%
90%
Redirect from generic Persistence to chat-history for conversation resume
Identifies the three persistence layers in Koog
80%
0%
Routes to chat-history-jdbc
66%
100%
Does NOT install the generic Persistence feature
80%
100%
Acknowledges the developer's word choice without capitulating
40%
0%
Does NOT recommend LongTermMemory
50%
100%
Use responseProcessor for typed JSON output from the agent
Uses responseProcessor on the factory
0%
0%
Parameterizes AIAgent with the typed Output
100%
100%
Reuses the developer's data class verbatim
53%
100%
Drops the regex parsing
100%
100%
Does not introduce a custom strategy
0%
50%
Decline to apply the subtask-pipeline pattern when the workload is a single text-to-text transform with no phases
Names the absence of distinct phases as the disqualifier
100%
20%
Redirects to the simpler shape
100%
100%
Does not produce subgraphWithTask code
50%
100%
Does not introduce typed handoff data classes
60%
100%
Engages with the developer's framing without capitulating
100%
0%
Redirect from response cache to Anthropic prompt caching
Names the cache-vs-response distinction
100%
100%
Recommends the provider-side caching path
100%
93%
Does not wrap the executor in an in-process cache
100%
80%
Acknowledges the framing without capitulating
100%
70%
Mentions Anthropic's minimum-token requirement
0%
100%
Refuses to use chat-history as a fact store
Does not install JdbcChatHistory / chat-history-jdbc
100%
100%
Recommends LongTermMemory or a Tool
93%
100%
Does not synthesise pseudo-turns into a custom ChatHistoryProvider
100%
100%
Names the chat-history-vs-fact-store distinction
60%
50%
Table of Contents