Files
gh-giuseppe-trisciuoglio-de…/skills/langchain4j-tool-function-calling-patterns/references/implementation-patterns.md
2025-11-29 18:28:34 +08:00

14 KiB

LangChain4j Tool & Function Calling - Implementation Patterns

Comprehensive implementation patterns for tool and function calling with LangChain4j.

Core Tool Definition Patterns

Basic Tool Definition with @Tool Annotation

The @Tool annotation converts regular Java methods into tools that LLMs can discover and execute.

Basic Tool Definition:

public class CalculatorTools {

    @Tool("Adds two given numbers")
    public double add(double a, double b) {
        return a + b;
    }

    @Tool("Multiplies two given numbers")
    public double multiply(double a, double b) {
        return a * b;
    }

    @Tool("Calculates the square root of a given number")
    public double squareRoot(double x) {
        return Math.sqrt(x);
    }

    @Tool("Calculates power of a number")
    public double power(double base, double exponent) {
        return Math.pow(base, exponent);
    }
}

Advanced Tool with Parameter Descriptions:

public class WeatherService {

    @Tool("Get current weather conditions for a specific location")
    public String getCurrentWeather(@P("The name of the city or location") String location) {
        try {
            WeatherData weather = weatherClient.getCurrentWeather(location);
            return String.format("Weather in %s: %s, %.1f°C, humidity %.0f%%, wind %.1f km/h",
                    location, weather.getCondition(), weather.getTemperature(),
                    weather.getHumidity(), weather.getWindSpeed());
        } catch (Exception e) {
            return "Sorry, I couldn't retrieve weather information for " + location;
        }
    }
}

Parameter Handling and Validation

Optional Parameters:

public class DatabaseTools {

    @Tool("Search for users in the database")
    public List<User> searchUsers(
            @P("Search term for user name or email") String searchTerm,
            @P(value = "Maximum number of results to return", required = false) Integer limit,
            @P(value = "Sort order: ASC or DESC", required = false) String sortOrder) {

        int actualLimit = limit != null ? limit : 10;
        String actualSort = sortOrder != null ? sortOrder : "ASC";

        return userRepository.searchUsers(searchTerm, actualLimit, actualSort);
    }
}

Complex Parameter Types:

public class OrderManagementTools {

    @Description("Customer order information")
    public static class OrderRequest {
        @Description("Customer ID who is placing the order")
        private Long customerId;

        @Description("List of items to order")
        private List<OrderItem> items;

        @Description("Shipping address for the order")
        private Address shippingAddress;

        @Description("Preferred delivery date (optional)")
        @JsonProperty(required = false)
        private LocalDate preferredDeliveryDate;
    }

    @Tool("Create a new customer order")
    public String createOrder(OrderRequest orderRequest) {
        try {
            // Validation and processing logic
            Order order = orderService.createOrder(orderRequest);
            return String.format("Order created successfully! Order ID: %s, Total: $%.2f",
                    order.getId(), order.getTotal());
        } catch (Exception e) {
            return "Failed to create order: " + e.getMessage();
        }
    }
}

Memory Context Integration

@ToolMemoryId for User Context

Tools can access conversation memory context to provide personalized and contextual responses:

public class PersonalizedTools {

    @Tool("Get personalized recommendations based on user preferences")
    public String getRecommendations(@ToolMemoryId String userId,
                                   @P("Type of recommendation: books, movies, restaurants") String type) {
        UserPreferences prefs = preferenceService.getUserPreferences(userId);
        List<String> history = historyService.getSearchHistory(userId, type);
        return recommendationEngine.getRecommendations(type, prefs, history);
    }
}

Dynamic Tool Provisioning

ToolProvider for Context-Aware Tools

public class DynamicToolProvider implements ToolProvider {

    @Override
    public ToolProviderResult provideTools(ToolProviderRequest request) {
        String userId = extractUserId(request);
        UserPermissions permissions = permissionService.getUserPermissions(userId);
        String userMessage = request.userMessage().singleText().toLowerCase();

        ToolProviderResult.Builder resultBuilder = ToolProviderResult.builder();

        // Always available tools
        addBasicTools(resultBuilder);

        // Conditional tools based on permissions
        if (permissions.canAccessFinancialData()) {
            addFinancialTools(resultBuilder);
        }

        if (permissions.canModifyUserData()) {
            addUserManagementTools(resultBuilder);
        }

        return resultBuilder.build();
    }
}

Programmatic Tool Definition

public class ProgrammaticToolsService {

    public Map<ToolSpecification, ToolExecutor> createDatabaseTools(DatabaseConfig config) {
        Map<ToolSpecification, ToolExecutor> tools = new HashMap<>();

        // Query tool
        ToolSpecification querySpec = ToolSpecification.builder()
            .name("execute_database_query")
            .description("Execute a SQL query on the database")
            .parameters(JsonObjectSchema.builder()
                .addStringProperty("query", "SQL query to execute")
                .addBooleanProperty("readOnly", "Whether this is a read-only query")
                .required("query", "readOnly")
                .build())
            .build();

        ToolExecutor queryExecutor = (request, memoryId) -> {
            Map<String, Object> args = fromJson(request.arguments());
            String query = args.get("query").toString();
            boolean readOnly = (Boolean) args.get("readOnly");
            return databaseService.executeQuery(query, readOnly);
        };

        tools.put(querySpec, queryExecutor);
        return tools;
    }
}

AI Services as Tools

AI Services can be used as tools by other AI Services, enabling hierarchical architectures:

// Specialized Expert Services
interface DataAnalysisExpert {
    @UserMessage("You are a data analysis expert. Analyze this data and provide insights: {{data}}")
    @Tool("Expert data analysis and insights")
    String analyzeData(@V("data") String data);
}

// Router Agent that delegates to experts
interface ExpertRouter {
    @UserMessage("""
        Analyze the user request and determine which expert(s) should handle it:
        - Use the data analysis expert for data-related questions
        - Use the security expert for security-related concerns

        User request: {{it}}
        """)
    String routeToExperts(String request);
}

@Service
public class ExpertConsultationService {
    public ExpertConsultationService(ChatModel chatModel) {
        // Build expert services
        DataAnalysisExpert dataExpert = AiServices.create(DataAnalysisExpert.class, chatModel);

        // Build router with experts as tools
        this.router = AiServices.builder(ExpertRouter.class)
            .chatModel(chatModel)
            .tools(dataExpert)
            .build();
    }
}

Advanced Tool Patterns

Immediate Return Tools

public class DirectResponseTools {

    @Tool(value = "Get current user information", returnBehavior = ReturnBehavior.IMMEDIATE)
    public String getCurrentUserInfo(@ToolMemoryId String userId) {
        User user = userService.findById(userId);
        return String.format("""
            User Information:
            Name: %s
            Email: %s
            Role: %s
            """, user.getName(), user.getEmail(), user.getRole());
    }
}

Concurrent Tool Execution

public class ConcurrentTools {

    @Tool("Get stock price for a company")
    public String getStockPrice(@P("Stock symbol") String symbol) {
        try {
            Thread.sleep(1000);
            return stockApiService.getPrice(symbol);
        } catch (InterruptedException e) {
            return "Error retrieving stock price";
        }
    }

    @Tool("Get company news")
    public String getCompanyNews(@P("Company symbol") String symbol) {
        // Similar implementation
    }
}

// Configure for concurrent execution
Assistant assistant = AiServices.builder(Assistant.class)
    .chatModel(chatModel)
    .tools(new ConcurrentTools())
    .executeToolsConcurrently() // Execute tools in parallel
    .build();

Error Handling and Resilience

Tool Execution Error Handling

public class ResilientTools {

    private final CircuitBreaker circuitBreaker;
    private final RetryTemplate retryTemplate;

    @Tool("Get external data with resilience patterns")
    public String getExternalData(@P("Data source identifier") String sourceId) {
        return circuitBreaker.executeSupplier(() -> {
            return retryTemplate.execute(context -> {
                try {
                    return externalApiService.fetchData(sourceId);
                } catch (ApiException e) {
                    if (e.isRetryable()) {
                        throw e; // Will be retried
                    }
                    return "Data temporarily unavailable: " + e.getMessage();
                }
            });
        });
    }
}

Graceful Degradation

public class FallbackTools {

    @Tool("Get weather information with fallback providers")
    public String getWeather(@P("Location name") String location) {
        // Try primary provider first
        for (DataProvider provider : dataProviders) {
            try {
                WeatherData weather = provider.getWeather(location);
                if (weather != null) {
                    return formatWeather(weather, provider.getName());
                }
            } catch (Exception e) {
                // Continue to next provider
            }
        }
        return "Weather information is currently unavailable for " + location;
    }
}

Streaming and Tool Execution

Streaming with Tool Callbacks

interface StreamingToolAssistant {
    TokenStream chat(String message);
}

StreamingToolAssistant assistant = AiServices.builder(StreamingToolAssistant.class)
    .streamingChatModel(streamingChatModel)
    .tools(new CalculatorTools(), new WeatherService())
    .build();

TokenStream stream = assistant.chat("What's the weather in Paris and calculate 15 + 27?");

stream
    .onToolExecuted(toolExecution -> {
        System.out.println("Tool executed: " + toolExecution.request().name());
        System.out.println("Result: " + toolExecution.result());
    })
    .onPartialResponse(partialResponse -> {
        System.out.print(partialResponse);
    })
    .start();

Accessing Tool Execution Results

interface AnalyticsAssistant {
    Result<String> analyze(String request);
}

AnalyticsAssistant assistant = AiServices.builder(AnalyticsAssistant.class)
    .chatModel(chatModel)
    .tools(new DataAnalysisTools(), new DatabaseTools())
    .build();

Result<String> result = assistant.analyze("Analyze sales data for Q4 2023");

// Access the response
String response = result.content();

// Access tool execution details
List<ToolExecution> toolExecutions = result.toolExecutions();
for (ToolExecution execution : toolExecutions) {
    System.out.println("Tool: " + execution.request().name());
    System.out.println("Duration: " + execution.duration().toMillis() + "ms");
}

Complete Tool-Enabled Application

Spring Boot Integration

@RestController
@RequestMapping("/api/assistant")
@RequiredArgsConstructor
public class ToolAssistantController {

    private final ToolEnabledAssistant assistant;

    @PostMapping("/chat")
    public ResponseEntity<ChatResponse> chat(@RequestBody ChatRequest request) {
        try {
            Result<String> result = assistant.chat(request.getUserId(), request.getMessage());

            ChatResponse response = ChatResponse.builder()
                .response(result.content())
                .toolsUsed(extractToolNames(result.toolExecutions()))
                .tokenUsage(result.tokenUsage())
                .build();

            return ResponseEntity.ok(response);
        } catch (Exception e) {
            return ResponseEntity.badRequest().body(
                ChatResponse.error("Error processing request: " + e.getMessage())
            );
        }
    }
}

interface ToolEnabledAssistant {
    Result<String> chat(@MemoryId String userId, String message);
    List<ToolInfo> getAvailableTools(String userId);
}

Performance Optimization

Tool Performance Monitoring

@Component
public class ToolPerformanceMonitor {

    @EventListener
    public void handleToolExecution(ToolExecutionEvent event) {
        // Record execution metrics
        Timer.Sample sample = Timer.start(meterRegistry);
        sample.stop(Timer.builder("tool.execution.duration")
                .tag("tool", event.getToolName())
                .tag("success", String.valueOf(event.isSuccessful()))
                .register(meterRegistry));

        // Record error rates
        if (!event.isSuccessful()) {
            meterRegistry.counter("tool.execution.errors",
                    "tool", event.getToolName(),
                    "error_type", event.getErrorType())
                .increment();
        }
    }
}

Testing Framework

@Component
public class ToolTestingFramework {

    public ToolValidationResult validateTool(Object toolInstance, String methodName) {
        try {
            TestAssistant testAssistant = AiServices.builder(TestAssistant.class)
                .chatModel(testChatModel)
                .tools(toolInstance)
                .build();

            String response = testAssistant.testTool(methodName);
            return ToolValidationResult.builder()
                .toolName(methodName)
                .isValid(response != null && !response.contains("Error"))
                .response(response)
                .build();

        } catch (Exception e) {
            return ToolValidationResult.builder()
                .toolName(methodName)
                .isValid(false)
                .error(e.getMessage())
                .build();
        }
    }
}