Tool Definition
Tools extend an agent's capabilities by allowing it to perform specific actions or access external systems. Aigentic provides a flexible DSL for defining and using tools.
Tool Parameters
Tool parameters define the in- and output structure for a tool. Aigentic has capabilities to automatically derive parameters from data classes annotated with the @AigenticParameter annotation:
@AigenticParameter
data class MessageParameters(
@Description("The Slack channel to send the message to (e.g., '#general', '@username')")
val channel: String,
@Description("The message text content to send to the Slack channel")
val text: String,
)
@AigenticParameter
data class SlackWebhookResponse(
@Description("Indicates whether the Slack webhook message was sent successfully")
val isSuccess: Boolean,
)
@AigenticParameter
The @AigenticParameter annotation marks data classes that serve as input or output parameters for tools or agents in the Aigentic framework. This annotation enables Aigentic to automatically derive parameter schemas for the LLM.
Key features:
- Applied at data classes
- Has an optional
descriptionproperty that provides a high-level description of the parameter object
Importance of Descriptions
The description property in @AigenticParameter is sent with the agent's output type, so the LLM knows what the object it returns is for.
Where descriptions end up:
- Agent output type: the
@AigenticParameterdescription is sent with the response schema. - Tool input type: only the properties are sent, as the tool's arguments. Describe the tool itself in the
descriptionofcreateTooloraddTool. - Nested object: the
@Descriptionon the property that holds it is sent.
@Description
The @Description annotation marks properties within parameter data classes with additional information about their meaning. These descriptions are used by Aigentic to generate accurate documentation and parameter schemas the LLM.
Key features:
- Applied at the property level
- Requires a string value that describes the property's purpose
- Helps LLMs understand the expected input and output formats
- Improves tool usability by providing clear guidance on parameter usage
Importance of Property Descriptions
Property descriptions greatly enhance LLM interactions. Each property description is included in the tool schemas sent to the LLM, providing details about:
- The specific purpose of each property
- Expected formats and valid values
- Relationships between properties
- Examples of proper usage (when included in the description)
Clear property descriptions help the LLM create more accurate parameter objects, reducing errors and making your tools work better. Adding more context through descriptions helps the LLM understand how to use your tools properly.
Optional properties
Make a property nullable (T?) when its value can be missing. Aigentic tells the LLM the property can be null, and on Gemini and VertexAI it can also leave the property out. Both end up as null in your object. Non-nullable properties stay required.
@AigenticParameter
data class Invoice(
val invoiceNumber: String,
@Description("The total amount, null when the invoice doesn't mention it")
val total: Double?,
)
This helps extraction agents most: instead of making up a placeholder like 0 or "", the LLM can return null when the source doesn't contain the value. If an optional nested object has a property that can also be missing, make that property nullable too.
Creating Tools
Aigentic provides several ways to create tools:
Standalone Tool Creation
Use the createTool function with type parameters to create a standalone reusable tool:
val slackWebhookTrigger = createTool<MessageParameters, SlackWebhookResponse>("slackWebhookTrigger", "Posts a webhook which sends a message in a channel") { parameters: MessageParameters ->
// Some code to call a Slack webhook with the MessageParameters provided by the LLM
SlackWebhookResponse(isSuccess = true)
}
This tool can be added to agents:
agent {
addTool(slackWebhookTrigger)
}
Inline Tool Definition
You can define tools inline:
agent {
addTool<MessageParameters, SlackWebhookResponse>("slackWebhookTrigger", "Posts a webhook which sends a message in a channel") { parameters: MessageParameters ->
// Code to call a Slack webhook
SlackWebhookResponse(true)
}
}
Best Practices
When creating tools for Aigentic agents, follow these best practices:
- Clear Descriptions: Provide clear and concise descriptions for your tools using the
@AigenticParameterand@Descriptionannotations - Type Safety: Use strongly typed input and output types
- Error Handling: A tool that throws ends the run with
Outcome.Fatal. Return an error value instead when the model should be able to recover - Idempotency: Make tools idempotent when possible
- Security: Validate inputs and handle sensitive data carefully
// Example of a well-designed tool
val emailTool = createTool<EmailRequest, EmailResponse>(
name = "sendEmail",
description = "Send an email to a recipient"
) { request ->
// Return validation errors to the model instead of throwing
if (request.to.isEmpty()) return@createTool EmailResponse(success = false, error = "Recipient email cannot be empty")
if (request.subject.isEmpty()) return@createTool EmailResponse(success = false, error = "Email subject cannot be empty")
runCatching {
val result = emailService.sendEmail(
to = request.to,
subject = request.subject,
body = request.body
)
EmailResponse(success = true, messageId = result.messageId)
}.getOrElse { error ->
EmailResponse(success = false, error = error.message)
}
}