Time: 30 minutes | Difficulty: Beginner
Now that you understand the concepts, let's build your first working AI agent! In this tutorial, we'll create a simple agent with a single tool using the Claude PHP Agent Framework.
By the end of this tutorial, you'll be able to:
- Create a basic agent with the framework
- Define a tool with proper input schema
- Execute a simple agent task
- Understand the agent's decision-making process
- Debug agent behavior with callbacks
We'll create a Calculator Agent that can:
- Receive math questions from users
- Recognize when calculation is needed
- Use a calculator tool to compute exact answers
- Respond with the results
This simple agent demonstrates the complete Request β Reason β Act β Observe cycle.
Make sure you have:
- PHP 8.1+ installed
- Composer installed
- Claude PHP Agent Framework installed
- Anthropic API key configured
composer require claude-php/claude-php-agentCreate a .env file:
ANTHROPIC_API_KEY=your-api-key-hereTools give agents capabilities beyond their knowledge. Let's create a calculator tool:
<?php
use ClaudeAgents\Tools\Tool;
$calculatorTool = Tool::create('calculate')
->description(
'Perform precise mathematical calculations. ' .
'Supports basic arithmetic: +, -, *, /, and parentheses.'
)
->stringParam(
'expression',
'The mathematical expression to evaluate (e.g., "2 + 2", "15 * 8")'
)
->handler(function (array $input): string {
$expression = $input['expression'];
// Validate: only allow safe characters
if (!preg_match('/^[0-9+\-*\/().\s]+$/', $expression)) {
return "Error: Invalid expression";
}
try {
// In production, use a proper math parser library
// like mossadal/math-parser or nxp/math-executor
$result = eval("return {$expression};");
return (string)$result;
} catch (Exception $e) {
return "Error: " . $e->getMessage();
}
});- Name: Simple, descriptive identifier
- Description: Helps Claude understand when to use the tool
- Parameters: Define what input the tool needs
- Handler: The actual function that executes
eval() with user input! Use a proper math parser library.
Now let's create an agent and give it our calculator tool:
<?php
require_once __DIR__ . '/vendor/autoload.php';
use ClaudeAgents\Agent;
use ClaudeAgents\Config\AgentConfig;
use ClaudePhp\ClaudePhp;
// Load environment
$dotenv = Dotenv\Dotenv::createImmutable(__DIR__);
$dotenv->load();
// Initialize Claude client
$client = new ClaudePhp(
apiKey: $_ENV['ANTHROPIC_API_KEY']
);
// Configure agent
$config = AgentConfig::fromArray([
'model' => 'claude-sonnet-4-20250514',
'max_tokens' => 1024,
'max_iterations' => 5,
]);
// Create agent with calculator tool
$agent = Agent::create($client)
->withConfig($config)
->withTool($calculatorTool)
->withSystemPrompt(
'You are a helpful assistant with access to a calculator. ' .
'Use the calculator for precise mathematical calculations.'
);Let's ask it to solve a math problem:
<?php
echo "Asking: What is 157 Γ 89?\n\n";
$response = $agent->run('What is 157 Γ 89?');
echo "Answer: {$response}\n";1. Agent receives: "What is 157 Γ 89?"
2. Agent reasons: "This requires calculation, I should use the calculator tool"
3. Agent acts: Calls calculate("157 * 89")
4. Tool executes: Returns "13973"
5. Agent observes: Received result 13973
6. Agent responds: "157 Γ 89 equals 13,973"
Want to see what the agent is thinking? Add callbacks or use the helper utilities with logging:
<?php
$agent = Agent::create($client)
->withConfig($config)
->withTool($calculatorTool)
->onIteration(function ($iteration, $response, $context) {
echo "\nβββ Iteration {$iteration} βββ\n";
echo "Stop Reason: {$response->stop_reason}\n";
if (isset($response->usage)) {
echo "Tokens: {$response->usage->input_tokens} in, ";
echo "{$response->usage->output_tokens} out\n";
}
})
->onToolCall(function ($tool, $input, $result) {
echo "\nπ§ Tool Called: {$tool->name()}\n";
echo "Input: " . json_encode($input) . "\n";
echo "Result: {$result}\n";
});
$response = $agent->run('What is (25 * 17) + (100 / 4)?');<?php
use ClaudeAgents\Helpers\AgentHelpers;
// Create a console logger for debugging
$logger = AgentHelpers::createConsoleLogger('agent', 'debug');
// Run with built-in ReAct loop and logging
$result = AgentHelpers::runAgentLoop(
client: $client,
messages: [['role' => 'user', 'content' => 'What is (25 * 17) + (100 / 4)?']],
tools: [$calculatorTool],
toolExecutor: fn($name, $input) => ($calculatorTool->handler())($input),
config: [
'max_iterations' => 10,
'debug' => true,
'logger' => $logger,
]
);βββ Iteration 1 βββ
Stop Reason: tool_use
Tokens: 245 in, 87 out
π§ Tool Called: calculate
Input: {"expression":"(25 * 17) + (100 / 4)"}
Result: 450
βββ Iteration 2 βββ
Stop Reason: end_turn
Tokens: 385 in, 45 out
Answer: (25 Γ 17) + (100 Γ· 4) = 425 + 25 = 450
Monitor costs by tracking token usage:
<?php
$totalInputTokens = 0;
$totalOutputTokens = 0;
$agent = Agent::create($client)
->withConfig($config)
->withTool($calculatorTool)
->onIteration(function ($iteration, $response, $context)
use (&$totalInputTokens, &$totalOutputTokens) {
if (isset($response->usage)) {
$totalInputTokens += $response->usage->input_tokens;
$totalOutputTokens += $response->usage->output_tokens;
}
});
$response = $agent->run('Calculate 157 Γ 89');
echo "\nπ Token Usage:\n";
echo "Input Tokens: {$totalInputTokens}\n";
echo "Output Tokens: {$totalOutputTokens}\n";
echo "Total: " . ($totalInputTokens + $totalOutputTokens) . "\n";
// Estimate cost (Claude 3.5 Sonnet pricing)
$inputCost = ($totalInputTokens / 1_000_000) * 3.00;
$outputCost = ($totalOutputTokens / 1_000_000) * 15.00;
$totalCost = $inputCost + $outputCost;
echo "Estimated Cost: $" . number_format($totalCost, 6) . "\n";Here's a complete working example:
<?php
require_once __DIR__ . '/vendor/autoload.php';
use ClaudeAgents\Agent;
use ClaudeAgents\Config\AgentConfig;
use ClaudeAgents\Tools\Tool;
use ClaudePhp\ClaudePhp;
// Load environment
$dotenv = Dotenv\Dotenv::createImmutable(__DIR__);
$dotenv->load();
// Initialize client
$client = new ClaudePhp(apiKey: $_ENV['ANTHROPIC_API_KEY']);
// Define calculator tool
$calculator = Tool::create('calculate')
->description('Perform mathematical calculations')
->stringParam('expression', 'Math expression to evaluate')
->handler(function (array $input): string {
$expr = $input['expression'];
if (!preg_match('/^[0-9+\-*\/().\s]+$/', $expr)) {
return "Error: Invalid expression";
}
try {
return (string)eval("return {$expr};");
} catch (Exception $e) {
return "Error: " . $e->getMessage();
}
});
// Configure agent
$config = AgentConfig::fromArray([
'model' => 'claude-sonnet-4-20250514',
'max_tokens' => 1024,
'max_iterations' => 5,
]);
// Create and run agent
$agent = Agent::create($client)
->withConfig($config)
->withTool($calculator)
->withSystemPrompt('You are a helpful math assistant with a calculator.')
->onIteration(function ($iter, $resp, $ctx) {
echo "Iteration {$iter}: {$resp->stop_reason}\n";
});
// Test questions
$questions = [
'What is 157 Γ 89?',
'Calculate (25 * 17) + (100 / 4)',
'What is 15% of 250?',
];
foreach ($questions as $question) {
echo "\nββββββββββββββββββββββββββββββββββββββ\n";
echo "Q: {$question}\n";
echo "A: " . $agent->run($question) . "\n";
}Save the code above as my_first_agent.php and run:
php my_first_agent.phpPossible causes:
- Tool description doesn't match the task
- Agent thinks it can answer without the tool
- System prompt doesn't encourage tool use
Fix: Make descriptions more specific:
// β Bad
->description('Do math')
// β
Good
->description('Perform precise mathematical calculations with guaranteed accuracy')Cause: The expression variable isn't properly sanitized.
Fix: Always validate input:
if (!preg_match('/^[0-9+\-*\/().\s]+$/', $expression)) {
return "Error: Invalid expression";
}Causes:
- Too many iterations
- Verbose system prompts
- Tool descriptions too long
Fix: Optimize configuration:
$config = AgentConfig::fromArray([
'max_iterations' => 3, // Reduce if tasks are simple
'max_tokens' => 512, // Reduce if responses are short
]);Before moving on, make sure you understand:
- How to create a tool with Tool::create()
- How to define tool parameters
- How to create an agent with Agent::create()
- How to run an agent with ->run()
- How to add callbacks for debugging
- How to track token usage
Congratulations! You've built your first agent. But real tasks often require multiple steps and multiple tools.
Tutorial 2: ReAct Loop Basics β
Learn how to implement proper multi-step reasoning with the ReAct pattern!
- Agents need tools to extend their capabilities
- Tools have three parts: name, description, and handler
- Callbacks enable debugging and monitoring
- Token tracking helps manage costs
- Start simple and add complexity gradually