Class-based tools
This section explains the API designed for scenarios that require enhanced flexibility and customized behavior. With this approach, you have full control over a tool, including its parameters, metadata, execution logic, and how it is registered and invoked.
This level of control is ideal for creating sophisticated tools that extend basic use cases, enabling seamless integration into agent sessions and workflows.
This page describes how to implement a tool, manage tools through registries, call them, and use within node-based agent architectures.
Note
The API is multiplatform. This lets you use the same tools across different platforms.
Tool implementation
The Koog framework provides the following approaches for implementing tools:
- Using the base class
Tool
for all tools. You should use this class when you need to return non-text results or require complete control over the tool behavior. - Using the
SimpleTool
class that extends the baseTool
class and simplifies the creation of tools that return text results. You should use this approach for scenarios where the tool only needs to return a text.
Both approaches use the same core components but differ in implementation and the results they return.
Tool class
The Tool<Args, Result>
abstract class is the base class for creating tools in Koog.
It lets you create tools that accept specific argument types (Args
) and return results of various types (Result
).
Each tool consists of the following components:
Component |
Description |
---|---|
Args |
The serializable data class that defines arguments required for the tool. This class must implement the ToolArgs interface. For tools that do not require arguments, you can use the built-in ToolArgs.Empty implementation. |
Result |
The type of result that the tool returns. This must implement the ToolResult interface, which can be ToolResult.Text , ToolResult.Boolean , ToolResult.Number , or a custom implementation of ToolResult.JSONSerializable . |
argsSerializer |
The overridden variable that defines how the arguments for the tool are deserialized. See also argsSerializer. |
descriptor |
The overridden variable that specifies tool metadata: - name - description - requiredParameters (empty by default)- optionalParameters (empty by default)See also descriptor. |
execute() |
The function that implements the logic of the tool. It takes arguments of type Args and returns a result of type Result . See also execute(). |
Tip
Ensure your tools have clear descriptions and well-defined parameter names to make it easier for the LLM to understand and use them properly.
Usage example
Here is an example of a custom tool implementation using the Tool
class that returns a numeric result:
// Implement a simple calculator tool that adds two digits
object CalculatorTool : Tool<CalculatorTool.Args, ToolResult.Number>() {
// Arguments for the calculator tool
@Serializable
data class Args(
val digit1: Int,
val digit2: Int
) : ToolArgs {
init {
require(digit1 in 0..9) { "digit1 must be a single digit (0-9)" }
require(digit2 in 0..9) { "digit2 must be a single digit (0-9)" }
}
}
// Serializer for the Args class
override val argsSerializer = Args.serializer()
// Tool descriptor
override val descriptor: ToolDescriptor = ToolDescriptor(
name = "calculator",
description = "A simple calculator that can add two digits (0-9).",
requiredParameters = listOf(
ToolParameterDescriptor(
name = "digit1",
description = "The first digit to add (0-9)",
type = ToolParameterType.Integer
),
ToolParameterDescriptor(
name = "digit2",
description = "The second digit to add (0-9)",
type = ToolParameterType.Integer
)
)
)
// Function to add two digits
override suspend fun execute(args: Args): ToolResult.Number {
val sum = args.digit1 + args.digit2
return ToolResult.Number(sum)
}
}
After implementing your tool, you need to add it to a tool registry and then use it with an agent. For details, see Tool registry.
For more details, see API reference.
SimpleTool class
The SimpleTool<Args>
abstract class extends Tool<Args, ToolResult.Text>
and simplifies the creation of tools that return text results.
Each simple tool consists of the following components:
Component |
Description |
---|---|
Args |
The serializable data class that defines arguments required for the custom tool. |
argsSerializer |
The overridden variable that defines how the arguments for the tool are serialized. See also argsSerializer. |
descriptor |
The overridden variable that specifies tool metadata: - name - description - requiredParameters (empty by default)- optionalParameters (empty by default)See also descriptor. |
doExecute() |
The overridden function that describes the main action performed by the tool. It takes arguments of type Args and returns a String . See also doExecute(). |
Tip
Ensure your tools have clear descriptions and well-defined parameter names to make it easier for the LLM to understand and use them properly.
Usage example
Here is an example of a custom tool implementation using SimpleTool
:
// Create a tool that casts a string expression to a double value
object CastToDoubleTool : SimpleTool<CastToDoubleTool.Args>() {
// Define tool arguments
@Serializable
data class Args(val expression: String, val comment: String) : ToolArgs
// Serializer for the Args class
override val argsSerializer = Args.serializer()
// Tool descriptor
override val descriptor = ToolDescriptor(
name = "cast_to_double",
description = "casts the passed expression to double or returns 0.0 if the expression is not castable",
requiredParameters = listOf(
ToolParameterDescriptor(
name = "expression", description = "An expression to case to double", type = ToolParameterType.String
)
),
optionalParameters = listOf(
ToolParameterDescriptor(
name = "comment",
description = "A comment on how to process the expression",
type = ToolParameterType.String
)
)
)
// Function that executes the tool with the provided arguments
override suspend fun doExecute(args: Args): String {
return "Result: ${castToDouble(args.expression)}, " + "the comment was: ${args.comment}"
}
// Function to cast a string expression to a double value
private fun castToDouble(expression: String): Double {
return expression.toDoubleOrNull() ?: 0.0
}
}
After implementing your tool, you need to add it to a tool registry and then use it with an agent. For details, see Tool registry.