langchain4j-tool-function-calling-patterns

安装量: 329
排名: #2813

安装

npx skills add https://github.com/giuseppe-trisciuoglio/developer-kit --skill langchain4j-tool-function-calling-patterns
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.
Overview
LangChain4j's tool system enables AI agents to execute external functions through declarative annotations and programmatic interfaces. Tools are defined using the
@Tool
annotation and automatically registered with AI services, allowing LLMs to perform actions beyond text generation such as database queries, API calls, and calculations.
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
Instructions
Follow these steps to implement tools with LangChain4j:
1. Define Tool Methods
Create methods annotated with
@Tool
in a class:
public
class
WeatherTools
{
@Tool
(
"Get current weather for a city"
)
public
String
getWeather
(
@P
(
"City name"
)
String
city
,
@P
(
"Temperature unit (celsius or fahrenheit)"
,
required
=
false
)
String
unit
)
{
// Implementation
return
weatherService
.
getWeather
(
city
,
unit
)
;
}
}
2. Configure Parameter Descriptions
Use
@P
annotation for clear parameter descriptions that help the LLM understand how to call the tool:
@Tool
(
"Calculate total order amount"
)
public
double
calculateOrderTotal
(
@P
(
"List of product IDs"
)
List
<
String
>
productIds
,
@P
(
"Customer discount percentage"
,
required
=
false
)
Double
discount
)
{
// Implementation
}
3. Register Tools with AI Service
Connect tools to an AI service using the AiServices builder:
MathAssistant
assistant
=
AiServices
.
builder
(
MathAssistant
.
class
)
.
chatModel
(
chatModel
)
.
tools
(
new
Calculator
(
)
,
new
WeatherService
(
)
)
.
build
(
)
;
4. Handle Tool Execution Errors
Implement error handling for tool failures:
AiServices
.
builder
(
Assistant
.
class
)
.
chatModel
(
chatModel
)
.
tools
(
new
ExternalServiceTools
(
)
)
.
toolExecutionErrorHandler
(
(
request
,
exception
)
->
{
log
.
error
(
"Tool execution failed: {}"
,
exception
.
getMessage
(
)
)
;
return
"An error occurred while processing your request"
;
}
)
.
build
(
)
;
5. Monitor Tool Usage
Track tool calls for debugging and analytics:
Result
<
String
>
result
=
assistant
.
chat
(
question
)
;
result
.
toolExecutions
(
)
.
forEach
(
execution
->
log
.
info
(
"Executed tool: {} in {}ms"
,
execution
.
request
(
)
.
name
(
)
,
execution
.
duration
(
)
.
toMillis
(
)
)
)
;
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
Descriptive Names
Use clear, actionable tool names
Parameter Validation
Validate inputs before processing
Error Messages
Provide meaningful error messages
Return Types
Use appropriate return types that LLMs can understand
Performance
Avoid blocking operations in tools
Security Considerations
Permission Checks
Validate user permissions before tool execution
Input Sanitization
Sanitize all tool inputs
Audit Logging
Log tool usage for security monitoring
Rate Limiting
Implement rate limiting for external APIs
Performance Optimization
Concurrent Execution
Use
executeToolsConcurrently()
for independent tools
Caching
Cache frequently accessed data
Monitoring
Monitor tool performance and error rates
Resource Management
Handle external service timeouts gracefully
Examples
Simple Calculator Tool
public
class
CalculatorTools
{
@Tool
(
"Add two numbers"
)
public
double
add
(
@P
(
"First number"
)
double
a
,
@P
(
"Second number"
)
double
b
)
{
return
a
+
b
;
}
@Tool
(
"Multiply two numbers"
)
public
double
multiply
(
@P
(
"First number"
)
double
a
,
@P
(
"Second number"
)
double
b
)
{
return
a
*
b
;
}
}
// Usage
interface
MathAssistant
{
String
ask
(
String
question
)
;
}
MathAssistant
assistant
=
AiServices
.
builder
(
MathAssistant
.
class
)
.
chatModel
(
chatModel
)
.
tools
(
new
CalculatorTools
(
)
)
.
build
(
)
;
String
result
=
assistant
.
ask
(
"What is 15 times 7 plus 3?"
)
;
Database Access Tool
@Component
public
class
DatabaseTools
{
private
final
CustomerRepository
repository
;
@Tool
(
"Get customer information by ID"
)
public
Customer
getCustomer
(
@P
(
"Customer ID"
)
Long
customerId
)
{
return
repository
.
findById
(
customerId
)
.
orElseThrow
(
(
)
->
new
IllegalArgumentException
(
"Customer not found"
)
)
;
}
@Tool
(
"Update customer email address"
)
public
String
updateEmail
(
@P
(
"Customer ID"
)
Long
customerId
,
@P
(
"New email address"
)
String
newEmail
)
{
Customer
customer
=
repository
.
findById
(
customerId
)
.
orElseThrow
(
(
)
->
new
IllegalArgumentException
(
"Customer not found"
)
)
;
customer
.
setEmail
(
newEmail
)
;
repository
.
save
(
customer
)
;
return
"Email updated successfully"
;
}
}
REST API Tool
@Component
public
class
ApiTools
{
private
final
WebClient
webClient
;
@Tool
(
"Get current stock price"
)
public
String
getStockPrice
(
@P
(
"Stock symbol"
)
String
symbol
)
{
return
webClient
.
get
(
)
.
uri
(
"/api/stocks/{symbol}"
,
symbol
)
.
retrieve
(
)
.
bodyToMono
(
String
.
class
)
.
block
(
)
;
}
}
Context-Aware Tool with Memory ID
public
class
UserPreferencesTools
{
@Tool
(
"Get user preferences for a category"
)
public
String
getPreferences
(
@ToolMemoryId
String
userId
,
@P
(
"Preference category (e.g., theme, language)"
)
String
category
)
{
return
preferencesService
.
getPreferences
(
userId
,
category
)
;
}
@Tool
(
"Set user preference"
)
public
String
setPreference
(
@ToolMemoryId
String
userId
,
@P
(
"Preference category"
)
String
category
,
@P
(
"Preference value"
)
String
value
)
{
preferencesService
.
setPreference
(
userId
,
category
,
value
)
;
return
"Preference saved"
;
}
}
Dynamic Tool Provider
public
class
DynamicToolProvider
implements
ToolProvider
{
private
final
Map
<
String
,
Object
>
availableTools
=
new
HashMap
<
>
(
)
;
public
void
registerTool
(
String
name
,
ToolSpecification
spec
,
ToolExecutor
executor
)
{
availableTools
.
put
(
name
,
new
ToolWithSpec
(
spec
,
executor
)
)
;
}
@Override
public
ToolProviderResult
provideTools
(
ToolProviderRequest
request
)
{
var
builder
=
ToolProviderResult
.
builder
(
)
;
String
message
=
request
.
userMessage
(
)
.
singleText
(
)
.
toLowerCase
(
)
;
// Dynamically filter tools based on user message
if
(
message
.
contains
(
"weather"
)
)
{
builder
.
add
(
weatherToolSpec
,
weatherExecutor
)
;
}
if
(
message
.
contains
(
"calculate"
)
||
message
.
contains
(
"math"
)
)
{
builder
.
add
(
calculatorToolSpec
,
calculatorExecutor
)
;
}
return
builder
.
build
(
)
;
}
}
Reference Documentation
For detailed API reference, examples, and advanced patterns, see:
API Reference
- Complete API documentation
Implementation Patterns
- Advanced implementation examples
Examples
- Practical usage examples
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 ) )
返回排行榜