Tool Integration
View SourceTools extend an LLM agent's capabilities by allowing it to perform actions and access information outside of its training data. This guide explains how to create, register, and manage tools in the LLMAgent library.
Tool Architecture
In LLMAgent, tools follow a simple structure:
- Name: A unique identifier for the tool
- Description: Explains what the tool does and how to use it
- Execute Function: A function that implements the tool's behavior
Tools are registered with AgentForge and are available for the LLM to use during a conversation.
Defining Tools
A tool is defined as a map with the following structure:
tool = %{
name: "weather_api",
description: "Get the current weather for a location. Requires a 'location' parameter.",
execute: fn args ->
location = Map.get(args, "location", "")
# API call logic here
%{temperature: 22, conditions: "sunny", location: location}
end
}
The execute
function takes a map of arguments and returns the tool's result. The function should be designed to handle errors gracefully and return structured data.
Registering Tools
Tools are registered when creating a conversation flow:
tools = [
%{name: "calculator", description: "...", execute: &MyApp.Tools.calculate/1},
%{name: "weather", description: "...", execute: &MyApp.Tools.get_weather/1}
]
{flow, state} = LLMAgent.Flows.conversation("You are a helpful assistant.", tools)
Tool Handler Mechanism
When the LLM decides to use a tool, it generates a :tool_call
signal. The tool_handler
function in LLMAgent.Handlers
processes this signal:
- Extracts the tool name and arguments
- Looks up the tool by name
- Executes the tool function
- Creates a
:tool_result
signal with the result - Adds the tool call and result to the conversation history
Here's a simplified version of how the tool handler works:
def tool_handler(%{type: :tool_call, data: %{name: tool_name, args: tool_args}}, state) do
case AgentForge.Tools.get(tool_name) do
{:ok, tool_fn} ->
try do
result = tool_fn.(tool_args)
result_signal = Signals.tool_result(tool_name, result)
new_state = Store.add_tool_call(state, tool_name, tool_args, result)
{{:emit, result_signal}, new_state}
rescue
e ->
error_signal = Signals.error(Exception.message(e), tool_name)
{{:emit, error_signal}, state}
end
{:error, reason} ->
error_signal = Signals.error("Tool not found: #{reason}", tool_name)
{{:emit, error_signal}, state}
end
end
Tool Result Processing
After a tool is executed, the result is processed by the tool_result_handler
:
- The tool result is added to the conversation history
- A new
:thinking
signal is generated - The LLM is called again to process the tool result
This allows the LLM to use tool results to inform its next actions.
Example Tools
Calculator Tool
calculator_tool = %{
name: "calculator",
description: "Perform mathematical calculations. Accepts an 'expression' parameter.",
execute: fn args ->
expr = Map.get(args, "expression", "")
case Code.eval_string(expr) do
{result, _} -> %{result: result}
_ -> %{error: "Invalid expression"}
end
end
}
Web Search Tool
search_tool = %{
name: "web_search",
description: "Search the web for information. Requires a 'query' parameter.",
execute: fn args ->
query = Map.get(args, "query", "")
case MyApp.SearchClient.search(query) do
{:ok, results} -> %{results: results}
{:error, reason} -> %{error: reason}
end
end
}
Database Tool
db_tool = %{
name: "database_query",
description: "Query a database. Requires a 'query' parameter.",
execute: fn args ->
query = Map.get(args, "query", "")
case Repo.query(query) do
{:ok, results} -> %{results: results}
{:error, reason} -> %{error: reason}
end
end
}
Best Practices
Security Considerations
Be careful with tools that:
- Execute arbitrary code
- Make external API calls
- Access sensitive data
- Modify system state
Always validate and sanitize inputs, especially when the tool has side effects.
Error Handling
Tools should handle errors gracefully and provide useful error messages:
def execute(args) do
try do
# Tool logic
%{result: result}
rescue
e in HTTPError -> %{error: "API error: #{e.message}"}
e in Timeout -> %{error: "Request timed out"}
_ -> %{error: "Unknown error occurred"}
end
end
Structured Results
Return structured data that's easy for the LLM to understand:
# Good
%{
weather: %{
temperature: 22,
conditions: "sunny",
humidity: 65
},
location: "New York"
}
# Not as good
"Weather in New York: 22°C, sunny, 65% humidity"
Tool Description
Write clear, detailed descriptions that explain:
- What the tool does
- What parameters it requires
- What format the parameters should be in
- What the tool will return
Good descriptions help the LLM use tools correctly.
Next Steps
- Learn about custom agents
- Explore LLM provider integration
- See advanced usage patterns