自定义工具
phi-agent 不内置任何工具 — 你通过实现 Tool trait 来创建自己的工具。
Tool Trait
#[async_trait]
pub trait Tool: Send + Sync {
fn name(&self) -> &'static str;
fn definition(&self) -> Value;
async fn call(&self, args: &Value, ctx: &ToolContext) -> AgentResult<ToolOutput>;
}
需要实现三个方法:
| 方法 | 作用 |
|---|---|
name() |
LLM 调用此工具时使用的唯一标识 |
definition() |
JSON Schema 描述参数(发送给 LLM) |
call() |
实际逻辑 — 接收解析后的参数,返回结果 |
示例:天气工具
use agent_base::{AgentResult, Tool, ToolContext, ToolControlFlow, ToolOutput};
use async_trait::async_trait;
use serde_json::{Value, json};
struct WeatherTool;
#[async_trait]
impl Tool for WeatherTool {
fn name(&self) -> &'static str {
"get_weather"
}
fn definition(&self) -> Value {
json!({
"name": "get_weather",
"description": "获取指定城市的当前天气",
"parameters": {
"type": "object",
"properties": {
"city": {
"type": "string",
"description": "城市名称,例如 '北京'"
}
},
"required": ["city"]
}
})
}
async fn call(&self, args: &Value, _ctx: &ToolContext) -> AgentResult<ToolOutput> {
let city = args["city"].as_str().unwrap_or("unknown");
// 生产环境中,这里调用真实的天气 API
Ok(ToolOutput {
summary: format!("{} 天气:22°C,晴", city),
control_flow: ToolControlFlow::Continue,
raw: None,
truncation: None,
})
}
}
注册工具
let builder = base_agent_builder(llm_client)
.system_prompt(build_system_prompt())
.register_tool(WeatherTool); // ← 在这里注册
let agent = PhiAgent::build(builder, config)?;
ToolOutput
ToolOutput 结构体字段说明:
ToolOutput {
summary: "Done".into(), // 展示给用户的摘要
raw: json!({"temp": 22}), // 完整数据(可以很大)
control_flow: ToolControlFlow::Continue, // Continue 或 Break
truncation: None, // 如果输出被截断,在此标记
}
最佳实践
- 一个文件一个工具 — 保持工具实现聚焦、可测试
- 校验参数 — 不要信任 LLM 提供的类型一定正确
- 优雅处理错误 — 返回有意义的错误信息,让 LLM 能据此调整
- 保持
definition()准确 — 如果 LLM 的理解和实际行为不一致,工具调用会失败 - 为长操作设置超时 — 对网络调用使用
tokio::time::timeout
完整示例
参见 examples/custom-tool.rs 了解一个带计算器工具的完整可运行示例。