Files
2025-11-29 18:28:34 +08:00

9.7 KiB

name, description, category, tags, version, allowed-tools
name description category tags version allowed-tools
langchain4j-tool-function-calling-patterns Tool and function calling patterns with LangChain4j. Define tools, handle function calls, and integrate with LLM agents. Use when building agentic applications that interact with tools. ai-development
langchain4j
tools
function-calling
@Tool
ToolProvider
ToolExecutor
dynamic-tools
parameter-descriptions
java
1.1.0 Read, Write, Bash, WebFetch

LangChain4j Tool & Function Calling Patterns

Define tools and enable AI agents to interact with external systems, APIs, and services using LangChain4j's annotation-based and programmatic tool system.

When to Use This Skill

Use this skill when:

  • Building AI applications that need to interact with external APIs and services
  • Creating AI assistants that can perform actions beyond text generation
  • Implementing AI systems that need access to real-time data (weather, stocks, etc.)
  • Building multi-agent systems where agents can use specialized tools
  • Creating AI applications with database read/write capabilities
  • Implementing AI systems that need to integrate with existing business systems
  • Building context-aware AI applications where tool availability depends on user state
  • Developing production AI applications that require robust error handling and monitoring

Setup and Configuration

Basic Tool Registration

// Define tools using @Tool annotation
public class CalculatorTools {
    @Tool("Add two numbers")
    public double add(double a, double b) {
        return a + b;
    }
}

// Register with AiServices builder
interface MathAssistant {
    String ask(String question);
}

MathAssistant assistant = AiServices.builder(MathAssistant.class)
    .chatModel(chatModel)
    .tools(new CalculatorTools())
    .build();

Builder Configuration Options

AiServices.builder(AssistantInterface.class)

    // Static tool registration
    .tools(new Calculator(), new WeatherService())

    // Dynamic tool provider
    .toolProvider(new DynamicToolProvider())

    // Concurrent execution
    .executeToolsConcurrently()

    // Error handling
    .toolExecutionErrorHandler((request, exception) -> {
        return "Error: " + exception.getMessage();
    })

    // Memory for context
    .chatMemoryProvider(userId -> MessageWindowChatMemory.withMaxMessages(20))

    .build();

Core Patterns

Basic Tool Definition

Use @Tool annotation to define methods as executable tools:

public class BasicTools {

    @Tool("Add two numbers")
    public int add(@P("first number") int a, @P("second number") int b) {
        return a + b;
    }

    @Tool("Get greeting")
    public String greet(@P("name to greet") String name) {
        return "Hello, " + name + "!";
    }
}

Parameter Descriptions and Validation

Provide clear parameter descriptions using @P annotation:

public class WeatherService {

    @Tool("Get current weather conditions")
    public String getCurrentWeather(
        @P("City name or coordinates") String location,
        @P("Temperature unit (celsius, fahrenheit)", required = false) String unit) {

        // Implementation with validation
        if (location == null || location.trim().isEmpty()) {
            return "Location is required";
        }

        return weatherClient.getCurrentWeather(location, unit);
    }
}

Complex Parameter Types

Use Java records and descriptions for complex objects:

public class OrderService {

    @Description("Customer order information")
    public record OrderRequest(
        @Description("Customer ID") String customerId,
        @Description("List of items") List<OrderItem> items,
        @JsonProperty(required = false) @Description("Delivery instructions") String instructions
    ) {}

    @Tool("Create customer order")
    public String createOrder(OrderRequest order) {
        return orderService.processOrder(order);
    }
}

Advanced Features

Memory Context Integration

Access user context using @ToolMemoryId:

public class PersonalizedTools {

    @Tool("Get user preferences")
    public String getPreferences(
        @ToolMemoryId String userId,
        @P("Preference category") String category) {

        return preferenceService.getPreferences(userId, category);
    }
}

Dynamic Tool Provisioning

Create tools that change based on context:

public class ContextAwareToolProvider implements ToolProvider {

    @Override
    public ToolProviderResult provideTools(ToolProviderRequest request) {
        String message = request.userMessage().singleText().toLowerCase();
        var builder = ToolProviderResult.builder();

        if (message.contains("weather")) {
            builder.add(weatherToolSpec, weatherExecutor);
        }

        if (message.contains("calculate")) {
            builder.add(calcToolSpec, calcExecutor);
        }

        return builder.build();
    }
}

Immediate Return Tools

Return results immediately without full AI response:

public class QuickTools {

    @Tool(value = "Get current time", returnBehavior = ReturnBehavior.IMMEDIATE)
    public String getCurrentTime() {
        return LocalDateTime.now().format(DateTimeFormatter.ISO_LOCAL_DATE_TIME);
    }
}

Error Handling

Tool Error Handling

Handle tool execution errors gracefully:

AiServices.builder(Assistant.class)
    .chatModel(chatModel)
    .tools(new ExternalServiceTools())
    .toolExecutionErrorHandler((request, exception) -> {
        if (exception instanceof ApiException) {
            return "Service temporarily unavailable: " + exception.getMessage();
        }
        return "An error occurred while processing your request";
    })
    .build();

Resilience Patterns

Implement circuit breakers and retries:

public class ResilientService {

    private final CircuitBreaker circuitBreaker = CircuitBreaker.ofDefaults("external-api");

    @Tool("Get external data")
    public String getExternalData(@P("Data identifier") String id) {
        return circuitBreaker.executeSupplier(() -> {
            return externalApi.getData(id);
        });
    }
}

Integration Examples

Multi-Domain Tool Service

@Service
public class MultiDomainToolService {

    public String processRequest(String userId, String request, String domain) {
        String contextualRequest = String.format("[Domain: %s] %s", domain, request);

        Result<String> result = assistant.chat(userId, contextualRequest);

        // Log tool usage
        result.toolExecutions().forEach(execution ->
            analyticsService.recordToolUsage(userId, domain, execution.request().name()));

        return result.content();
    }
}

Streaming with Tool Execution

interface StreamingAssistant {
    TokenStream chat(String message);
}

StreamingAssistant assistant = AiServices.builder(StreamingAssistant.class)
    .streamingChatModel(streamingChatModel)
    .tools(new Tools())
    .build();

TokenStream stream = assistant.chat("What's the weather and calculate 15*8?");

stream
    .onToolExecuted(execution ->
        System.out.println("Executed: " + execution.request().name()))
    .onPartialResponse(System.out::print)
    .onComplete(response -> System.out.println("Complete!"))
    .start();

Best Practices

Tool Design Guidelines

  1. Descriptive Names: Use clear, actionable tool names
  2. Parameter Validation: Validate inputs before processing
  3. Error Messages: Provide meaningful error messages
  4. Return Types: Use appropriate return types that LLMs can understand
  5. Performance: Avoid blocking operations in tools

Security Considerations

  1. Permission Checks: Validate user permissions before tool execution
  2. Input Sanitization: Sanitize all tool inputs
  3. Audit Logging: Log tool usage for security monitoring
  4. Rate Limiting: Implement rate limiting for external APIs

Performance Optimization

  1. Concurrent Execution: Use executeToolsConcurrently() for independent tools
  2. Caching: Cache frequently accessed data
  3. Monitoring: Monitor tool performance and error rates
  4. Resource Management: Handle external service timeouts gracefully

Reference Documentation

For detailed API reference, examples, and advanced patterns, see:

Common Issues and Solutions

Tool Not Found

Problem: LLM calls tools that don't exist

Solution: Implement hallucination handler:

.hallucinatedToolNameStrategy(request -> {
    return ToolExecutionResultMessage.from(request,
        "Error: Tool '" + request.name() + "' does not exist");
})

Parameter Validation Errors

Problem: Tools receive invalid parameters

Solution: Add input validation and error handlers:

.toolArgumentsErrorHandler((error, context) -> {
    return ToolErrorHandlerResult.text("Invalid arguments: " + error.getMessage());
})

Performance Issues

Problem: Tools are slow or timeout

Solution: Use concurrent execution and resilience patterns:

.executeToolsConcurrently(Executors.newFixedThreadPool(5))
.toolExecutionTimeout(Duration.ofSeconds(30))
  • langchain4j-ai-services-patterns
  • langchain4j-rag-implementation-patterns
  • langchain4j-spring-boot-integration

References