| name | tools |
| description | Implementing the Tool trait, defining parameters JSON schema, and registering tools with AgentExecutor. |
Implement Tool via #[async_trait]. Required methods: name(), description(), run(Value).
Optional: parameters() for OpenAI function-call schema. parse_input() has a sensible default.
## Tool Trait
use async_trait::async_trait;
use serde_json::Value;
use std::error::Error;
use langchainx::tools::Tool;
#[async_trait]
pub trait Tool: Send + Sync {
fn name(&self) -> String;
fn description(&self) -> String;
fn parameters(&self) -> Value;
async fn call(&self, input: &str) -> Result<String, Box<dyn Error>>;
async fn run(&self, input: Value) -> Result<String, Box<dyn Error>>;
async fn parse_input(&self, input: &str) -> Value;
}
## Basic Tool Implementation
use async_trait::async_trait;
use serde_json::Value;
use std::error::Error;
use langchainx::tools::Tool;
pub struct WordCount;
#[async_trait]
impl Tool for WordCount {
fn name(&self) -> String {
"word_count".to_string()
}
fn description(&self) -> String {
"Counts the number of words in a text string. \
Use when you need to know how many words are in a passage."
.to_string()
}
async fn run(&self, input: Value) -> Result<String, Box<dyn Error>> {
let text = input
.as_str()
.ok_or("input must be a string")?;
Ok(format!("{} words", text.split_whitespace().count()))
}
}
## Tool with Structured Input (OpenAI function calling)
Override parameters() to define the JSON schema, then extract fields in run().
use serde_json::{json, Value};
pub struct Calculator;
#[async_trait]
impl Tool for Calculator {
fn name(&self) -> String { "calculator".to_string() }
fn description(&self) -> String {
"Evaluates a math expression. Use for arithmetic.".to_string()
}
fn parameters(&self) -> Value {
json!({
"type": "object",
"properties": {
"expression": {
"type": "string",
"description": "A math expression, e.g. '2 + 2' or '100 / 4'"
}
},
"required": ["expression"]
})
}
async fn run(&self, input: Value) -> Result<String, Box<dyn Error>> {
let expr = input["expression"]
.as_str()
.ok_or("missing 'expression' field")?;
Ok("42".to_string())
}
}
## Registering with AgentExecutor
Tools are passed as Vec<Arc<dyn Tool>> via the agent builder, not directly to AgentExecutor.
use std::sync::Arc;
use langchainx::agent::{AgentExecutor, OpenAIToolsAgentBuilder};
use langchainx::tools::Tool;
let tools: Vec<Arc<dyn Tool>> = vec![
Arc::new(WordCount),
Arc::new(Calculator),
];
let agent = OpenAIToolsAgentBuilder::new()
.tools(&tools)
.llm(OpenAI::default())
.build()?;
let executor = AgentExecutor::from_agent(agent)
.with_max_iterations(10);
## Common Mistake: Tool names with spaces
AgentExecutor normalizes tool names by replacing spaces with underscores. Keep names
lowercase with underscores to avoid mismatches.
fn name(&self) -> String { "Word Count Tool".to_string() }
fn name(&self) -> String { "word_count".to_string() }