Base starter module for the Embabel Agent Framework providing core dependencies for building agentic flows on the JVM with Spring Boot integration and GOAP-based intelligent path finding.
Step-by-step guide for creating LLM tools, MatryoshkaTools for progressive disclosure, and tool groups.
import com.embabel.agent.api.annotation.LlmTool;
import com.embabel.agent.api.annotation.EmbabelComponent;
@EmbabelComponent
public class FileTools {
@LlmTool(
description = "Read the contents of a file from the filesystem",
name = "read_file"
)
public String readFile(
@LlmTool.Param(description = "Path to the file to read", required = true)
String filePath
) {
return Files.readString(Path.of(filePath));
}
@LlmTool(
description = "Write content to a file on the filesystem",
name = "write_file"
)
public void writeFile(
@LlmTool.Param(description = "Path to the file to write", required = true)
String filePath,
@LlmTool.Param(description = "Content to write to the file", required = true)
String content
) {
Files.writeString(Path.of(filePath), content);
}
@LlmTool(
description = "List files in a directory",
name = "list_files"
)
public List<String> listFiles(
@LlmTool.Param(description = "Directory path to list", required = true)
String directoryPath
) {
return Arrays.asList(new File(directoryPath).list());
}
}import com.embabel.agent.api.annotation.LlmTool
import com.embabel.agent.api.annotation.EmbabelComponent
@EmbabelComponent
class DatabaseTools {
@LlmTool(
description = "Execute a SQL query and return results",
name = "sql_query"
)
fun executeQuery(
@LlmTool.Param(description = "SQL query to execute", required = true)
query: String
): List<Map<String, Any>> {
return database.execute(query)
}
@LlmTool(
description = "Get database schema information",
name = "get_schema",
returnDirect = true
)
fun getSchema(): String {
return database.describeSchema()
}
@LlmTool(
description = "Count records in a table",
name = "count_records"
)
fun countRecords(
@LlmTool.Param(description = "Table name", required = true)
tableName: String
): Int {
return database.count(tableName)
}
}@EmbabelComponent
public class SearchTools {
@LlmTool(
description = "Search for documents with optional filters",
name = "search_documents"
)
public List<Document> searchDocuments(
@LlmTool.Param(description = "Search query", required = true)
String query,
@LlmTool.Param(description = "Maximum number of results", required = false)
Integer maxResults,
@LlmTool.Param(description = "Sort field", required = false)
String sortBy
) {
int limit = maxResults != null ? maxResults : 10;
String sort = sortBy != null ? sortBy : "relevance";
return searchService.search(query, limit, sort);
}
}@EmbabelComponent
class ApiTools {
@LlmTool(
description = "Make HTTP request with optional headers",
name = "http_request"
)
fun httpRequest(
@LlmTool.Param(description = "URL to request", required = true)
url: String,
@LlmTool.Param(description = "HTTP method", required = false)
method: String? = "GET",
@LlmTool.Param(description = "Request headers", required = false)
headers: Map<String, String>? = emptyMap()
): String {
return httpClient.request(url, method ?: "GET", headers ?: emptyMap())
}
}Use returnDirect = true to bypass LLM processing and return results directly.
@EmbabelComponent
public class QuickAccessTools {
@LlmTool(
description = "Get current system time",
name = "current_time",
returnDirect = true // Return directly without LLM processing
)
public String getCurrentTime() {
return Instant.now().toString();
}
@LlmTool(
description = "Get system environment variable",
name = "get_env",
returnDirect = true
)
public String getEnvironmentVariable(
@LlmTool.Param(description = "Environment variable name", required = true)
String name
) {
return System.getenv(name);
}
}@EmbabelComponent
class SystemTools {
@LlmTool(
description = "Get system memory info",
name = "memory_info",
returnDirect = true
)
fun getMemoryInfo(): Map<String, Long> {
val runtime = Runtime.getRuntime()
return mapOf(
"total" to runtime.totalMemory(),
"free" to runtime.freeMemory(),
"used" to (runtime.totalMemory() - runtime.freeMemory())
)
}
}MatryoshkaTools present a single facade tool initially, then reveal specific tools based on category selection.
import com.embabel.agent.api.annotation.MatryoshkaTools;
import com.embabel.agent.api.annotation.LlmTool;
import com.embabel.agent.api.annotation.EmbabelComponent;
@EmbabelComponent
@MatryoshkaTools(
name = "database_operations",
description = "Database tools - select category: query, insert, update, or delete",
removeOnInvoke = true,
categoryParameter = "category"
)
public class DatabaseOperationsTools {
@LlmTool(
description = "Execute SELECT query",
category = "query"
)
public List<Map<String, Object>> select(
@LlmTool.Param(description = "SQL SELECT statement", required = true)
String sql
) {
return database.executeQuery(sql);
}
@LlmTool(
description = "Insert new record",
category = "insert"
)
public int insert(
@LlmTool.Param(description = "Table name", required = true)
String table,
@LlmTool.Param(description = "Record data", required = true)
Map<String, Object> data
) {
return database.insert(table, data);
}
@LlmTool(
description = "Update existing record",
category = "update"
)
public int update(
@LlmTool.Param(description = "Table name", required = true)
String table,
@LlmTool.Param(description = "WHERE clause", required = true)
String where,
@LlmTool.Param(description = "Updated data", required = true)
Map<String, Object> data
) {
return database.update(table, where, data);
}
@LlmTool(
description = "Delete record",
category = "delete"
)
public int delete(
@LlmTool.Param(description = "Table name", required = true)
String table,
@LlmTool.Param(description = "WHERE clause", required = true)
String where
) {
return database.delete(table, where);
}
}import com.embabel.agent.api.annotation.MatryoshkaTools
import com.embabel.agent.api.annotation.LlmTool
import com.embabel.agent.api.annotation.EmbabelComponent
@EmbabelComponent
@MatryoshkaTools(
name = "file_operations",
description = "File system tools - choose: basic, advanced, or search",
removeOnInvoke = true
)
class FileOperationsTools {
@LlmTool(
description = "List files in directory",
category = "basic"
)
fun listFiles(
@LlmTool.Param(description = "Directory path", required = true)
path: String
): List<String> {
return fileService.list(path)
}
@LlmTool(
description = "Read file contents",
category = "basic"
)
fun readFile(
@LlmTool.Param(description = "File path", required = true)
path: String
): String {
return fileService.read(path)
}
@LlmTool(
description = "Copy file with options",
category = "advanced"
)
fun copyFile(
@LlmTool.Param(description = "Source path", required = true)
source: String,
@LlmTool.Param(description = "Destination path", required = true)
dest: String
) {
fileService.copy(source, dest)
}
@LlmTool(
description = "Move file",
category = "advanced"
)
fun moveFile(
@LlmTool.Param(description = "Source path", required = true)
source: String,
@LlmTool.Param(description = "Destination path", required = true)
dest: String
) {
fileService.move(source, dest)
}
@LlmTool(
description = "Search files by pattern",
category = "search"
)
fun searchFiles(
@LlmTool.Param(description = "Search pattern", required = true)
pattern: String
): List<String> {
return fileService.search(pattern)
}
@LlmTool(
description = "Find files by content",
category = "search"
)
fun findByContent(
@LlmTool.Param(description = "Search text", required = true)
text: String
): List<String> {
return fileService.findByContent(text)
}
}Organize related tools into groups that can be required by actions.
import com.embabel.agent.api.annotation.ToolGroup;
import com.embabel.agent.api.Tool;
@EmbabelComponent
public class AnalysisToolGroups {
@ToolGroup(role = "statistical-analysis")
public List<Tool> getStatisticalTools() {
return List.of(
Tool.create("mean", "Calculate mean", this::calculateMean),
Tool.create("median", "Calculate median", this::calculateMedian),
Tool.create("std_dev", "Calculate standard deviation", this::calculateStdDev),
Tool.create("correlation", "Calculate correlation", this::calculateCorrelation)
);
}
@ToolGroup(role = "data-visualization")
public List<Tool> getVisualizationTools() {
return List.of(
Tool.create("scatter_plot", "Create scatter plot", this::createScatterPlot),
Tool.create("bar_chart", "Create bar chart", this::createBarChart),
Tool.create("line_chart", "Create line chart", this::createLineChart),
Tool.create("histogram", "Create histogram", this::createHistogram)
);
}
private double calculateMean(Map<String, Object> args) {
List<Double> values = (List<Double>) args.get("values");
return values.stream().mapToDouble(Double::doubleValue).average().orElse(0.0);
}
private double calculateMedian(Map<String, Object> args) {
List<Double> values = (List<Double>) args.get("values");
Collections.sort(values);
return values.get(values.size() / 2);
}
private double calculateStdDev(Map<String, Object> args) {
// Implementation
return 0.0;
}
private double calculateCorrelation(Map<String, Object> args) {
// Implementation
return 0.0;
}
private String createScatterPlot(Map<String, Object> args) {
// Implementation
return "scatter_plot.png";
}
private String createBarChart(Map<String, Object> args) {
// Implementation
return "bar_chart.png";
}
private String createLineChart(Map<String, Object> args) {
// Implementation
return "line_chart.png";
}
private String createHistogram(Map<String, Object> args) {
// Implementation
return "histogram.png";
}
}import com.embabel.agent.api.annotation.ToolGroup
import com.embabel.agent.api.Tool
@EmbabelComponent
class DataToolGroups {
@ToolGroup(role = "data-cleaning")
fun getCleaningTools(): List<Tool> {
return listOf(
Tool.create("remove_nulls", "Remove null values", ::removeNulls),
Tool.create("remove_duplicates", "Remove duplicate rows", ::removeDuplicates),
Tool.create("normalize", "Normalize data values", ::normalizeData),
Tool.create("handle_outliers", "Handle outlier values", ::handleOutliers)
)
}
@ToolGroup(role = "data-transformation")
fun getTransformationTools(): List<Tool> {
return listOf(
Tool.create("pivot", "Pivot table data", ::pivotData),
Tool.create("aggregate", "Aggregate data", ::aggregateData),
Tool.create("join", "Join datasets", ::joinDatasets),
Tool.create("filter", "Filter data by criteria", ::filterData)
)
}
private fun removeNulls(args: Map<String, Any>): Dataset {
// Implementation
return Dataset()
}
private fun removeDuplicates(args: Map<String, Any>): Dataset {
// Implementation
return Dataset()
}
private fun normalizeData(args: Map<String, Any>): Dataset {
// Implementation
return Dataset()
}
private fun handleOutliers(args: Map<String, Any>): Dataset {
// Implementation
return Dataset()
}
private fun pivotData(args: Map<String, Any>): Dataset {
// Implementation
return Dataset()
}
private fun aggregateData(args: Map<String, Any>): Dataset {
// Implementation
return Dataset()
}
private fun joinDatasets(args: Map<String, Any>): Dataset {
// Implementation
return Dataset()
}
private fun filterData(args: Map<String, Any>): Dataset {
// Implementation
return Dataset()
}
}@Agent(description = "Data analyst")
public class DataAnalystAgent {
@Action(
description = "Perform statistical analysis",
toolGroupRequirements = {"statistical-analysis"}
)
public StatisticalReport analyzeData(Dataset data, @Provided Ai ai) {
// This action requires statistical-analysis tools
return ai.withLlm(OpenAiModels.GPT_4_TURBO)
.createObject("Analyze this dataset: " + data);
}
@Action(
description = "Create visualization",
toolGroupRequirements = {"data-visualization"}
)
public Visualization visualizeData(Dataset data, @Provided Ai ai) {
// This action requires data-visualization tools
return ai.withLlm(OpenAiModels.GPT_4_TURBO)
.createObject("Create a visualization for: " + data);
}
@Action(
description = "Full analysis with charts",
toolGroupRequirements = {"statistical-analysis", "data-visualization"}
)
public FullReport fullAnalysis(Dataset data, @Provided Ai ai) {
// This action requires both tool groups
return ai.withLlm(OpenAiModels.GPT_4_TURBO)
.createObject("Perform full analysis with charts: " + data);
}
}@Agent(description = "Data processor")
class DataProcessorAgent {
@Action(
description = "Clean and transform data",
toolGroupRequirements = ["data-cleaning", "data-transformation"]
)
fun processData(raw: RawData, @Provided ai: Ai): ProcessedData {
return ai.withLlm(GeminiModels.GEMINI_2_5_PRO)
.createObject("Clean and transform: $raw")
}
}Create tools programmatically without annotations.
import com.embabel.agent.api.Tool;
@EmbabelComponent
public class CalculatorTools {
public List<Tool> createCalculatorTools() {
return List.of(
Tool.create("add", "Add two numbers", (args) -> {
double a = (Double) args.get("a");
double b = (Double) args.get("b");
return a + b;
}),
Tool.create("subtract", "Subtract two numbers", (args) -> {
double a = (Double) args.get("a");
double b = (Double) args.get("b");
return a - b;
}),
Tool.create("multiply", "Multiply two numbers", (args) -> {
double a = (Double) args.get("a");
double b = (Double) args.get("b");
return a * b;
}),
Tool.create("divide", "Divide two numbers", (args) -> {
double a = (Double) args.get("a");
double b = (Double) args.get("b");
if (b == 0) throw new IllegalArgumentException("Division by zero");
return a / b;
})
);
}
}import com.embabel.agent.api.Tool
@EmbabelComponent
class StringTools {
fun createStringTools(): List<Tool> {
return listOf(
Tool.create("uppercase", "Convert string to uppercase") { args ->
(args["text"] as String).uppercase()
},
Tool.create("lowercase", "Convert string to lowercase") { args ->
(args["text"] as String).lowercase()
},
Tool.create("reverse", "Reverse a string") { args ->
(args["text"] as String).reversed()
},
Tool.create("length", "Get string length") { args ->
(args["text"] as String).length
}
)
}
}Wrap agents as tools for hierarchical agent composition.
import com.embabel.agent.api.AgenticTool;
@EmbabelComponent
public class AgentToolProvider {
public List<Tool> createAgenticTools() {
return List.of(
// Wrap agents as tools
AgenticTool.fromAgent(DataAnalyzerAgent.class),
AgenticTool.fromAgent("ReportGeneratorAgent"),
AgenticTool.fromAgent(summarizerAgentInstance)
);
}
@Action(description = "Use agentic tools for complex processing")
public Result processWithAgenticTools(
Input input,
@Provided Ai ai
) {
// Agents are exposed as tools to the LLM
List<Tool> tools = createAgenticTools();
return ai.withLlm(OpenAiModels.GPT_4_TURBO)
.withTools(tools)
.createObject("Process this input: " + input);
}
}import com.embabel.agent.api.AgenticTool
@EmbabelComponent
class AgentToolProvider {
fun createAgenticTools(): List<Tool> {
return listOf(
AgenticTool.fromAgent(DataAnalyzerAgent::class.java),
AgenticTool.fromAgent("ReportGeneratorAgent"),
AgenticTool.fromAgent(summarizerAgentInstance)
)
}
@Action(description = "Complex processing with agent tools")
fun processWithAgenticTools(
input: Input,
@Provided ai: Ai
): Result {
val tools = createAgenticTools()
return ai.withLlm(OpenAiModels.GPT_4_TURBO)
.withTools(tools)
.createObject("Process: $input")
}
}import com.embabel.agent.api.*;
import com.embabel.agent.api.annotation.*;
@Agent(description = "Data processing agent with custom tools")
public class DataProcessingAgent {
@Action(
description = "Process data with custom tools",
toolGroups = {"data-tools"}
)
public ProcessedData processData(
RawData data,
@Provided Ai ai,
@Provided ActionContext context
) {
// Create custom tools
List<Tool> tools = createDataTools();
context.updateProgress("Processing data with " + tools.size() + " tools");
// Use tools with LLM
return ai.withLlm(OpenAiModels.GPT_4_TURBO)
.withTools(tools)
.createObject("Process and transform: " + data);
}
@ToolGroup(role = "data-tools")
private List<Tool> createDataTools() {
return List.of(
// Simple lambda tools
Tool.create("validate", "Validate data format", this::validateData),
Tool.create("normalize", "Normalize data values", this::normalizeData),
Tool.create("aggregate", "Aggregate data points", this::aggregateData),
// MatryoshkaTool for complex operations
MatryoshkaTool.fromInstance(new DataTransformTools()),
// AgenticTool for delegating to specialized agents
AgenticTool.fromAgent(ValidationAgent.class),
AgenticTool.fromAgent("EnrichmentAgent")
);
}
private ValidationResult validateData(Map<String, Object> args) {
RawData data = (RawData) args.get("data");
return validator.validate(data);
}
private NormalizedData normalizeData(Map<String, Object> args) {
RawData data = (RawData) args.get("data");
return normalizer.normalize(data);
}
private AggregatedData aggregateData(Map<String, Object> args) {
RawData data = (RawData) args.get("data");
String method = (String) args.get("method");
return aggregator.aggregate(data, method);
}
}
@MatryoshkaTools(
name = "data_transforms",
description = "Data transformation operations - select: convert, reshape, or filter"
)
class DataTransformTools {
@LlmTool(description = "Convert data format", category = "convert")
public ConvertedData convert(
@LlmTool.Param(description = "Source data") RawData data,
@LlmTool.Param(description = "Target format") String format
) {
return converter.convert(data, format);
}
@LlmTool(description = "Reshape data structure", category = "reshape")
public ReshapedData reshape(
@LlmTool.Param(description = "Source data") RawData data,
@LlmTool.Param(description = "Target shape") String shape
) {
return reshaper.reshape(data, shape);
}
@LlmTool(description = "Filter data by criteria", category = "filter")
public FilteredData filter(
@LlmTool.Param(description = "Source data") RawData data,
@LlmTool.Param(description = "Filter expression") String expression
) {
return filterer.filter(data, expression);
}
}import com.embabel.agent.api.*
import com.embabel.agent.api.annotation.*
@Agent(description = "Analysis agent with tool suite")
class AnalysisAgent {
@Action(
description = "Analyze data with specialized tools",
toolGroups = ["analysis-tools"]
)
fun analyzeData(
data: Dataset,
@Provided ai: Ai,
@Provided context: ActionContext
): AnalysisReport {
// Create tool suite
val tools = createAnalysisTools()
context.updateProgress("Analyzing with ${tools.size} specialized tools")
// Execute with LLM
return ai.withLlm(GeminiModels.GEMINI_2_5_PRO)
.withTools(tools)
.createObject("Analyze: $data")
}
@ToolGroup(role = "analysis-tools")
fun createAnalysisTools(): List<Tool> {
return listOf(
// Function reference tools
Tool.create("summarize", "Summarize data", ::summarizeData),
Tool.create("findOutliers", "Find outliers", ::findOutliers),
Tool.create("detectTrends", "Detect trends", ::detectTrends),
// MatryoshkaTool for progressive disclosure
MatryoshkaTool.fromInstance(StatisticalTools()),
// AgenticTool for complex analysis
AgenticTool.fromAgent(DeepAnalysisAgent::class.java)
)
}
private fun summarizeData(args: Map<String, Any>): Summary {
val data = args["data"] as Dataset
return summarizer.summarize(data)
}
private fun findOutliers(args: Map<String, Any>): List<Outlier> {
val data = args["data"] as Dataset
return outlierDetector.detect(data)
}
private fun detectTrends(args: Map<String, Any>): List<Trend> {
val data = args["data"] as Dataset
return trendDetector.detect(data)
}
}
@MatryoshkaTools(
name = "statistical_tools",
description = "Statistical analysis - choose: descriptive, inferential, or advanced"
)
class StatisticalTools {
@LlmTool(description = "Calculate descriptive statistics", category = "descriptive")
fun descriptiveStats(
@LlmTool.Param(description = "Data values") values: List<Double>
): DescriptiveStats {
return statistician.descriptive(values)
}
@LlmTool(description = "Perform hypothesis testing", category = "inferential")
fun hypothesisTest(
@LlmTool.Param(description = "Sample data") sample: List<Double>,
@LlmTool.Param(description = "Test type") testType: String
): TestResult {
return statistician.test(sample, testType)
}
@LlmTool(description = "Regression analysis", category = "advanced")
fun regression(
@LlmTool.Param(description = "Independent variables") x: List<List<Double>>,
@LlmTool.Param(description = "Dependent variable") y: List<Double>
): RegressionModel {
return statistician.regress(x, y)
}
}tessl i tessl/maven-com-embabel-agent--embabel-agent-starter@0.3.1docs