05: Prompt Engineering Basics

Chapter 05: Prompt Engineering Basics
Section titled “Chapter 05: Prompt Engineering Basics”Overview
Section titled “Overview”Prompt engineering is the art and science of crafting effective instructions for AI models. The quality of Claude’s responses is directly proportional to the quality of your prompts. A well-engineered prompt can mean the difference between generic, unhelpful outputs and precise, actionable results.
This chapter provides comprehensive coverage of prompt engineering fundamentals, including clear instruction writing, few-shot learning, chain-of-thought reasoning, role-playing techniques, structured output formats, and practical PHP implementation patterns. You’ll learn to write prompts that consistently produce high-quality results.
What You’ll Learn:
- Core principles of effective prompting
- Few-shot learning with positive and negative examples
- Chain-of-thought reasoning techniques
- Role-playing and persona assignment
- Structured output formatting (JSON, XML, CSV)
- Prompt templates and reusability
- Prompt length optimization and token management
- Iterative prompt refinement methodologies
- Systematic prompt testing and evaluation
- Common pitfalls and how to avoid them
Prerequisites
Section titled “Prerequisites”Before starting, ensure you have:
- ✓ Completed Chapters 01-04
- ✓ PHP 8.4+ with good understanding
- ✓ Anthropic API key configured
- ✓ Experience with Claude API from previous chapters
Estimated Time: ~45-55 minutes
What You’ll Build
Section titled “What You’ll Build”By the end of this chapter, you will have created:
- A
FewShotClassifierclass for sentiment analysis and text classification - A
ChainOfThoughtSolverclass for complex problem-solving with reasoning - A
PersonaBuilderclass for creating dynamic AI personas and roles - A
PromptTemplateclass for reusable prompt templates with variable substitution - A
PromptLibraryclass for managing a collection of pre-built prompts - A
PromptLengthManagerclass for optimizing prompt size and token usage - A
PromptRefinerclass for iterative prompt improvement - A
PromptTesterclass for systematic prompt testing and evaluation - A
PromptVersionTrackerclass for tracking prompt changes over time - Multiple example scripts demonstrating prompt engineering techniques
- Understanding of few-shot learning (including negative examples), chain-of-thought reasoning, and role-playing
- Knowledge of structured output formats (JSON, XML, CSV, Markdown)
- Skills in prompt optimization, iterative refinement, and testing methodologies
- Best practices for writing effective prompts that produce consistent results
Objectives
Section titled “Objectives”By the end of this chapter, you will be able to:
- Write clear, specific, and context-rich prompts that produce high-quality results
- Implement few-shot learning techniques including positive and negative examples
- Apply chain-of-thought reasoning for complex problem-solving tasks
- Create dynamic personas and role-based prompts for specialized use cases
- Generate structured outputs in JSON, XML, CSV, and Markdown formats
- Build reusable prompt templates and libraries for consistent prompting
- Optimize prompt length and manage token usage effectively
- Refine prompts iteratively using systematic testing and analysis
- Test prompts systematically with proper metrics and A/B testing
- Identify and avoid common prompt engineering pitfalls
- Track prompt versions and measure improvements over time
Principles of Effective Prompts
Section titled “Principles of Effective Prompts”1. Be Clear and Specific
Section titled “1. Be Clear and Specific”Vague prompts produce vague results. Be explicit about what you want.
<?phpdeclare(strict_types=1);
require __DIR__ . '/../vendor/autoload.php';
use ClaudePhp\ClaudePhp;
$client = new ClaudePhp( apiKey: $_ENV['ANTHROPIC_API_KEY']);
// ✗ Bad: Vague prompt$vagueResponse = $client->messages()->create([ 'model' => 'claude-sonnet-4-5-20250929', 'max_tokens' => 1024, 'messages' => [ ['role' => 'user', 'content' => 'Tell me about PHP.') ]);
echo "Vague prompt result:\n";echo $vagueResponse->content[0]->text . "\n\n";
// ✓ Good: Specific prompt$specificResponse = $client->messages()->create([ 'model' => 'claude-sonnet-4-5-20250929', 'max_tokens' => 1024, 'messages' => [ [ 'role' => 'user', 'content' => 'Explain the benefits of PHP 8.4\'s typed class constants in 3 bullet points. Include one code example.' ) ]);
echo "Specific prompt result:\n";echo $specificResponse->content[0]->text . "\n\n";2. Provide Context
Section titled “2. Provide Context”Give Claude the information it needs to understand your request.
<?phpdeclare(strict_types=1);
require __DIR__ . '/../vendor/autoload.php';
use ClaudePhp\ClaudePhp;
$client = new ClaudePhp( apiKey: $_ENV['ANTHROPIC_API_KEY']);
$userCode = <<<'PHP'class UserRepository { public function find($id) { return DB::table('users')->where('id', $id)->first(); }}PHP;
// ✗ Bad: No context$noContextResponse = $client->messages()->create([ 'model' => 'claude-sonnet-4-5-20250929', 'max_tokens' => 1024, 'messages' => [ [ 'role' => 'user', 'content' => "Review this code:\n\n{$userCode}" ) ]);
// ✓ Good: With context$withContextResponse = $client->messages()->create([ 'model' => 'claude-sonnet-4-5-20250929', 'max_tokens' => 1024, 'messages' => [ [ 'role' => 'user', 'content' => "You are reviewing a Laravel 11 application that handles sensitive user data. Review this repository class for security issues, type safety, and error handling:\n\n{$userCode}" ) ]);
echo "With context:\n";echo $withContextResponse->content[0]->text . "\n";3. Specify Output Format
Section titled “3. Specify Output Format”Tell Claude exactly how you want the response formatted.
<?phpdeclare(strict_types=1);
require __DIR__ . '/../vendor/autoload.php';
use ClaudePhp\ClaudePhp;
$client = new ClaudePhp( apiKey: $_ENV['ANTHROPIC_API_KEY']);
$response = $client->messages()->create([ 'model' => 'claude-sonnet-4-5-20250929', 'max_tokens' => 1024, 'messages' => [ [ 'role' => 'user', 'content' => 'List 5 PHP design patterns. Format as JSON with fields: name, category, use_case, difficulty. Return only valid JSON.' ) ]);
echo $response->content[0]->text . "\n";
// Parse the JSON$text = $response->content[0]->text;if (preg_match('/```json\s*(\{.*?\}|\[.*?\]\s*```/s', $text, $matches)) { $json = $matches[1];} else { $json = $text;}
$data = json_decode($json, true);print_r($data);4. Use Delimiters for Clarity
Section titled “4. Use Delimiters for Clarity”Separate instructions from data using clear delimiters.
<?phpdeclare(strict_types=1);
require __DIR__ . '/../vendor/autoload.php';
use ClaudePhp\ClaudePhp;
$client = new ClaudePhp( apiKey: $_ENV['ANTHROPIC_API_KEY']);
$emailContent = "Hi, I need help with my order #12345. It hasn't arrived yet. Please refund ASAP!";
$response = $client->messages()->create([ 'model' => 'claude-sonnet-4-5-20250929', 'max_tokens' => 512, 'messages' => [ [ 'role' => 'user', 'content' => <<<PROMPTAnalyze the customer email below and extract:1. Order number2. Issue category (delivery, refund, product, etc.)3. Urgency level (low, medium, high)4. Sentiment (positive, neutral, negative)
Return as JSON.
--- EMAIL STARTS ---{$emailContent}--- EMAIL ENDS ---PROMPT ) ]);
echo $response->content[0]->text . "\n";Few-Shot Learning
Section titled “Few-Shot Learning”Few-shot learning involves providing examples to guide Claude’s responses.
Zero-Shot (No Examples)
Section titled “Zero-Shot (No Examples)”<?phpdeclare(strict_types=1);
require __DIR__ . '/../vendor/autoload.php';
use ClaudePhp\ClaudePhp;
$client = new ClaudePhp( apiKey: $_ENV['ANTHROPIC_API_KEY']);
// No examples provided$response = $client->messages()->create([ 'model' => 'claude-sonnet-4-5-20250929', 'max_tokens' => 512, 'messages' => [ [ 'role' => 'user', 'content' => 'Classify this text as positive, negative, or neutral: "The product is okay, nothing special."' ) ]);
echo "Zero-shot classification:\n";echo $response->content[0]->text . "\n\n";One-Shot (One Example)
Section titled “One-Shot (One Example)”<?phpdeclare(strict_types=1);
require __DIR__ . '/../vendor/autoload.php';
use ClaudePhp\ClaudePhp;
$client = new ClaudePhp( apiKey: $_ENV['ANTHROPIC_API_KEY']);
$response = $client->messages()->create([ 'model' => 'claude-sonnet-4-5-20250929', 'max_tokens' => 512, 'messages' => [ [ 'role' => 'user', 'content' => <<<PROMPTClassify customer feedback sentiment.
Example:Input: "This product exceeded my expectations! Absolutely love it."Output: positive
Now classify this:Input: "The product is okay, nothing special."Output:PROMPT ) ]);
echo "One-shot classification:\n";echo $response->content[0]->text . "\n\n";Few-Shot (Multiple Examples)
Section titled “Few-Shot (Multiple Examples)”<?phpdeclare(strict_types=1);
require __DIR__ . '/../vendor/autoload.php';
use ClaudePhp\ClaudePhp;
$client = new ClaudePhp( apiKey: $_ENV['ANTHROPIC_API_KEY']);
$response = $client->messages()->create([ 'model' => 'claude-sonnet-4-5-20250929', 'max_tokens' => 512, 'messages' => [ [ 'role' => 'user', 'content' => <<<PROMPTClassify customer feedback sentiment as: positive, negative, or neutral.
Examples:
Input: "This product exceeded my expectations! Absolutely love it."Output: positive
Input: "Terrible quality, broke after one day. Very disappointed."Output: negative
Input: "The product is okay, nothing special."Output: neutral
Input: "Fast shipping and good packaging."Output: positive
Now classify this:Input: "Not what I expected, but it works fine."Output:PROMPT ) ]);
echo "Few-shot classification:\n";echo $response->content[0]->text . "\n\n";Few-Shot with Structured Output
Section titled “Few-Shot with Structured Output”<?phpdeclare(strict_types=1);
namespace App\Services;
use ClaudePhp\ClaudePhp;
class FewShotClassifier{ public function __construct( private readonly ClaudePhp $client ) {}
public function classify(string $text, array $examples, array $categories): string { $examplesText = $this->formatExamples($examples); $categoriesText = implode(', ', $categories);
$prompt = <<<PROMPTClassify the text into one of these categories: {$categoriesText}
Examples:{$examplesText}
Now classify this:Input: {$text}Output:PROMPT;
$response = $this->client->messages()->create([ 'model' => 'claude-sonnet-4-5-20250929', 'max_tokens' => 100, 'temperature' => 0.0, 'messages' => [ [ 'role' => 'user', 'content' => $prompt ] ] ]);
return trim($response->content[0]->text); }
private function formatExamples(array $examples): string { $formatted = [];
foreach ($examples as $example) { $formatted[] = "Input: {$example['input']}\nOutput: {$example['output']}"; }
return implode("\n\n", $formatted); }}Usage:
<?phpdeclare(strict_types=1);
require __DIR__ . '/../vendor/autoload.php';
use App\Services\FewShotClassifier;use ClaudePhp\ClaudePhp;
$client = new ClaudePhp( apiKey: $_ENV['ANTHROPIC_API_KEY']);
$classifier = new FewShotClassifier($client);
$examples = [ ['input' => 'This Laravel package is amazing!', 'output' => 'positive'], ['input' => 'Terrible documentation, bugs everywhere.', 'output' => 'negative'], ['input' => 'It works as expected.', 'output' => 'neutral'],];
$result = $classifier->classify( text: 'Great performance improvements in this version!', examples: $examples, categories: ['positive', 'negative', 'neutral']);
echo "Classification: {$result}\n";Negative Examples (What NOT to Do)
Section titled “Negative Examples (What NOT to Do)”Sometimes showing Claude what not to do is as effective as showing what to do. Negative examples help establish boundaries and clarify edge cases.
<?phpdeclare(strict_types=1);
require __DIR__ . '/../vendor/autoload.php';
use ClaudePhp\ClaudePhp;
$client = new ClaudePhp( apiKey: $_ENV['ANTHROPIC_API_KEY']);
// Classification with negative examples$response = $client->messages()->create([ 'model' => 'claude-sonnet-4-5-20250929', 'max_tokens' => 512, 'messages' => [ [ 'role' => 'user', 'content' => <<<PROMPTClassify customer feedback as: positive, negative, or neutral.
Positive examples:Input: "This product exceeded my expectations! Absolutely love it."Output: positive
Input: "Fast shipping and excellent customer service."Output: positive
Negative examples (what NOT to classify as positive):Input: "It's okay, nothing special."Output: neutral (NOT positive - this is neutral feedback)
Input: "The product works but I expected more."Output: neutral (NOT positive - mixed sentiment)
Now classify this:Input: "Not what I expected, but it works fine."Output:PROMPT ) ]);
echo "Classification with negative examples:\n";echo $response->content[0]->text . "\n\n";When to use negative examples:
- Edge case clarification: When boundaries between categories are unclear
- Error prevention: To prevent common misclassifications
- Constraint definition: To establish what responses are unacceptable
- Quality control: To filter out low-quality or inappropriate outputs
<?phpdeclare(strict_types=1);
require __DIR__ . '/../vendor/autoload.php';
use ClaudePhp\ClaudePhp;
$client = new ClaudePhp( apiKey: $_ENV['ANTHROPIC_API_KEY']);
$code = <<<'PHP'function getUser($id) { return DB::query("SELECT * FROM users WHERE id = " . $id);}PHP;
$response = $client->messages()->create([ 'model' => 'claude-sonnet-4-5-20250929', 'max_tokens' => 1024, 'messages' => [ [ 'role' => 'user', 'content' => <<<PROMPTReview this PHP code for security issues.
Good security practices:- Use prepared statements- Validate and sanitize input- Use parameterized queries
Bad practices (what NOT to do):- String concatenation in SQL queries (SQL injection risk)- Direct user input in database queries- No input validation
Code to review:{$code}
Identify security issues and explain why they're problematic.PROMPT ) ]);
echo $response->content[0]->text . "\n";Chain-of-Thought Reasoning
Section titled “Chain-of-Thought Reasoning”Chain-of-thought prompting encourages Claude to show its reasoning process.
Basic Chain-of-Thought
Section titled “Basic Chain-of-Thought”<?phpdeclare(strict_types=1);
require __DIR__ . '/../vendor/autoload.php';
use ClaudePhp\ClaudePhp;
$client = new ClaudePhp( apiKey: $_ENV['ANTHROPIC_API_KEY']);
// Without chain-of-thought$direct = $client->messages()->create([ 'model' => 'claude-sonnet-4-5-20250929', 'max_tokens' => 512, 'messages' => [ [ 'role' => 'user', 'content' => 'Should I use a singleton pattern for my database connection in PHP?' ) ]);
echo "Direct answer:\n";echo $direct->content[0]->text . "\n\n";
// With chain-of-thought$cot = $client->messages()->create([ 'model' => 'claude-sonnet-4-5-20250929', 'max_tokens' => 1024, 'messages' => [ [ 'role' => 'user', 'content' => 'Should I use a singleton pattern for my database connection in PHP? Think through this step by step, considering: 1) Performance implications, 2) Testing challenges, 3) Modern alternatives like dependency injection.' ) ]);
echo "Chain-of-thought answer:\n";echo $cot->content[0]->text . "\n\n";Explicit Step-by-Step Instructions
Section titled “Explicit Step-by-Step Instructions”<?phpdeclare(strict_types=1);
require __DIR__ . '/../vendor/autoload.php';
use ClaudePhp\ClaudePhp;
$client = new ClaudePhp( apiKey: $_ENV['ANTHROPIC_API_KEY']);
$code = <<<'PHP'function processOrder($order) { $total = 0; foreach ($order['items'] as $item) { $total += $item['price'] * $item['quantity']; } if ($order['discount'] { $total = $total - ($total * $order['discount']; } return $total;}PHP;
$response = $client->messages()->create([ 'model' => 'claude-sonnet-4-5-20250929', 'max_tokens' => 2048, 'messages' => [ [ 'role' => 'user', 'content' => <<<PROMPTRefactor this PHP function following these steps:
1. Identify issues (type safety, error handling, naming)2. List specific improvements needed3. Provide refactored code with PHPDoc4. Explain the changes made
Code to refactor:{$code}PROMPT ) ]);
echo $response->content[0]->text . "\n";Self-Consistency Chain-of-Thought
Section titled “Self-Consistency Chain-of-Thought”<?phpdeclare(strict_types=1);
namespace App\Services;
use ClaudePhp\ClaudePhp;
class ChainOfThoughtSolver{ public function __construct( private readonly ClaudePhp $client ) {}
public function solve(string $problem, int $attempts = 3): array { $solutions = [];
for ($i = 0; $i < $attempts; $i++) { $response = $this->client->messages()->create([ 'model' => 'claude-sonnet-4-5-20250929', 'max_tokens' => 2048, 'temperature' => 0.7, 'messages' => [ [ 'role' => 'user', 'content' => <<<PROMPT{$problem}
Think through this step by step:1. Analyze the problem2. Consider different approaches3. Evaluate trade-offs4. Recommend solution5. Provide implementation
Format your response with clear sections.PROMPT ) ] );
$solutions[] = $response->content[0]->text; }
return [ 'solutions' => $solutions, 'consensus' => $this->findConsensus($solutions) ]; }
private function findConsensus(array $solutions): string { // Ask Claude to find consensus among solutions $combined = implode("\n\n--- SOLUTION ---\n\n", $solutions);
$response = $this->client->messages()->create([ 'model' => 'claude-sonnet-4-5-20250929', 'max_tokens' => 1024, 'messages' => [ [ 'role' => 'user', 'content' => <<<PROMPTI have multiple solutions to a problem. Identify the consensus or best approach:
{$combined}
Provide a concise summary of the best solution.PROMPT ) ] );
return $response->content[0]->text; }}Role-Playing and Personas
Section titled “Role-Playing and Personas”Assign Claude a specific role or expertise to improve response quality.
Basic Role Assignment
Section titled “Basic Role Assignment”<?phpdeclare(strict_types=1);
require __DIR__ . '/../vendor/autoload.php';
use ClaudePhp\ClaudePhp;
$client = new ClaudePhp( apiKey: $_ENV['ANTHROPIC_API_KEY']);
// Different roles produce different perspectives$roles = [ 'senior_developer' => 'You are a senior PHP developer with 15 years of experience. Focus on maintainability and best practices.', 'security_expert' => 'You are a security expert specializing in web applications. Focus on vulnerabilities and security implications.', 'performance_engineer' => 'You are a performance optimization engineer. Focus on speed, efficiency, and scalability.',];
$code = <<<'PHP'function getUser($id) { return DB::query("SELECT * FROM users WHERE id = " . $id);}PHP;
foreach ($roles as $roleName => $rolePrompt) { $response = $client->messages()->create([ 'model' => 'claude-sonnet-4-5-20250929', 'max_tokens' => 1024, 'system' => $rolePrompt, 'messages' => [ [ 'role' => 'user', 'content' => "Review this code:\n\n{$code}" ) ] );
echo "=== {$roleName} ===\n"; echo $response->content[0]->text . "\n\n";}Multi-Attribute Personas
Section titled “Multi-Attribute Personas”<?phpdeclare(strict_types=1);
require __DIR__ . '/../vendor/autoload.php';
use ClaudePhp\ClaudePhp;
$client = new ClaudePhp( apiKey: $_ENV['ANTHROPIC_API_KEY']);
$persona = <<<PERSONAYou are an expert PHP architect with these attributes:- 10+ years experience with Laravel- Specialized in e-commerce platforms- Strong advocate for SOLID principles- Prefers composition over inheritance- Values type safety and static analysis- Writes comprehensive tests
When reviewing code:1. Focus on architecture and design patterns2. Suggest improvements for maintainability3. Provide specific, actionable feedback4. Include code examplesPERSONA;
$code = <<<'PHP'class OrderProcessor { public function process($order) { // Validate order if (!$order) return false;
// Calculate total $total = 0; foreach ($order->items as $item) { $total += $item->price; }
// Save to database DB::save($order);
// Send email Mail::send($order->email, 'Order confirmed');
return true; }}PHP;
$response = $client->messages()->create([ 'model' => 'claude-sonnet-4-5-20250929', 'max_tokens' => 3000, 'system' => $persona, 'messages' => [ [ 'role' => 'user', 'content' => "Review this OrderProcessor class and suggest architectural improvements:\n\n{$code}" ) ]);
echo $response->content[0]->text . "\n";Persona Builder Pattern
Section titled “Persona Builder Pattern”<?phpdeclare(strict_types=1);
namespace App\Services;
class PersonaBuilder{ private string $role = ''; private int $experience = 0; private array $specializations = []; private array $principles = []; private array $guidelines = [];
public function role(string $role): self { $this->role = $role; return $this; }
public function experience(int $years): self { $this->experience = $years; return $this; }
public function specialization(string $specialization): self { $this->specializations[] = $specialization; return $this; }
public function principle(string $principle): self { $this->principles[] = $principle; return $this; }
public function guideline(string $guideline): self { $this->guidelines[] = $guideline; return $this; }
public function build(): string { $parts = [];
if ($this->role) { $experienceText = $this->experience > 0 ? " with {$this->experience}+ years of experience" : '';
$parts[] = "You are a {$this->role}{$experienceText}."; }
if (!empty($this->specializations)) { $parts[] = "\nSpecializations:"; foreach ($this->specializations as $spec) { $parts[] = "- {$spec}"; } }
if (!empty($this->principles)) { $parts[] = "\nCore principles you follow:"; foreach ($this->principles as $principle) { $parts[] = "- {$principle}"; } }
if (!empty($this->guidelines)) { $parts[] = "\nWhen responding:"; foreach ($this->guidelines as $i => $guideline) { $parts[] = ($i + 1) . ". {$guideline}"; } }
return implode("\n", $parts); }}Usage:
<?phpdeclare(strict_types=1);
require __DIR__ . '/../vendor/autoload.php';
use App\Services\PersonaBuilder;use ClaudePhp\ClaudePhp;
$client = new ClaudePhp( apiKey: $_ENV['ANTHROPIC_API_KEY']);
$persona = (new PersonaBuilder()) ->role('Laravel Security Consultant') ->experience(12) ->specialization('OWASP Top 10 vulnerabilities') ->specialization('Authentication and authorization') ->specialization('Input validation and sanitization') ->principle('Security first, always') ->principle('Defense in depth') ->principle('Least privilege principle') ->guideline('Identify specific vulnerabilities') ->guideline('Explain the risk level') ->guideline('Provide secure code examples') ->guideline('Reference OWASP guidelines') ->build();
echo "Generated Persona:\n{$persona}\n\n";
$code = <<<'PHP'Route::get('/user/{id}', function($id) { $user = DB::select("SELECT * FROM users WHERE id = " . $id); return view('profile', ['user' => $user];});PHP;
$response = $client->messages()->create([ 'model' => 'claude-sonnet-4-5-20250929', 'max_tokens' => 2048, 'system' => $persona, 'messages' => [ [ 'role' => 'user', 'content' => "Review this Laravel route for security issues:\n\n{$code}" ) ]);
echo "Security Review:\n";echo $response->content[0]->text . "\n";Structured Output Formats
Section titled “Structured Output Formats”JSON Output
Section titled “JSON Output”<?phpdeclare(strict_types=1);
require __DIR__ . '/../vendor/autoload.php';
use ClaudePhp\ClaudePhp;
$client = new ClaudePhp( apiKey: $_ENV['ANTHROPIC_API_KEY']);
$response = $client->messages()->create([ 'model' => 'claude-sonnet-4-5-20250929', 'max_tokens' => 1024, 'messages' => [ [ 'role' => 'user', 'content' => <<<PROMPTExtract information from this text and return as JSON:
"John Doe, senior developer at Acme Corp, can be reached at john@acme.com or 555-1234. He specializes in PHP and Laravel."
Required JSON structure:{ "name": "string", "title": "string", "company": "string", "email": "string", "phone": "string", "skills": ["array", "of", "strings"]}
Return only valid JSON, no explanation.PROMPT ) ]);
$text = $response->content[0]->text;
// Extract JSONif (preg_match('/```json\s*(\{.*?\})\s*```/s', $text, $matches)) { $jsonText = $matches[1];} else { $jsonText = $text;}
$data = json_decode($jsonText, true);
echo "Extracted data:\n";print_r($data);XML Output
Section titled “XML Output”<?phpdeclare(strict_types=1);
require __DIR__ . '/../vendor/autoload.php';
use ClaudePhp\ClaudePhp;
$client = new ClaudePhp( apiKey: $_ENV['ANTHROPIC_API_KEY']);
$response = $client->messages()->create([ 'model' => 'claude-sonnet-4-5-20250929', 'max_tokens' => 1024, 'messages' => [ [ 'role' => 'user', 'content' => <<<PROMPTGenerate PHPUnit test configuration XML for a Laravel project with these requirements:- Test suite: Unit tests in tests/Unit- Test suite: Feature tests in tests/Feature- Coverage: HTML report in coverage/- Stop on failure: yes- Colors: enabled
Return only valid XML, no explanation.PROMPT ) ]);
$xml = $response->content[0]->text;
// Extract XML from markdown if wrappedif (preg_match('/```xml\s*(.*?)\s*```/s', $xml, $matches)) { $xml = $matches[1];}
echo $xml . "\n";
// Validate XML$doc = new DOMDocument();if ($doc->loadXML($xml)) { echo "\n✓ Valid XML generated\n";}CSV Output
Section titled “CSV Output”<?phpdeclare(strict_types=1);
require __DIR__ . '/../vendor/autoload.php';
use ClaudePhp\ClaudePhp;
$client = new ClaudePhp( apiKey: $_ENV['ANTHROPIC_API_KEY']);
$response = $client->messages()->create([ 'model' => 'claude-sonnet-4-5-20250929', 'max_tokens' => 1024, 'messages' => [ [ 'role' => 'user', 'content' => <<<PROMPTGenerate a CSV file with 5 sample PHP developers with these columns:name, email, years_experience, primary_framework, hourly_rate
Include header row. Return only CSV, no explanation.PROMPT ) ]);
$csv = $response->content[0]->text;
// Extract CSV from markdown if wrappedif (preg_match('/```csv\s*(.*?)\s*```/s', $csv, $matches)) { $csv = $matches[1];} elseif (preg_match('/```\s*(.*?)\s*```/s', $csv, $matches)) { $csv = $matches[1];}
echo $csv . "\n";
// Parse CSV$lines = str_getcsv($csv, "\n");$data = array_map('str_getcsv', $lines);
echo "\nParsed data:\n";print_r($data);Markdown Output
Section titled “Markdown Output”<?phpdeclare(strict_types=1);
require __DIR__ . '/../vendor/autoload.php';
use ClaudePhp\ClaudePhp;
$client = new ClaudePhp( apiKey: $_ENV['ANTHROPIC_API_KEY']);
$response = $client->messages()->create([ 'model' => 'claude-sonnet-4-5-20250929', 'max_tokens' => 2048, 'messages' => [ [ 'role' => 'user', 'content' => <<<PROMPTCreate technical documentation for a Laravel middleware that handles API rate limiting.
Format as Markdown with:- Title (h1)- Overview section- Installation instructions- Configuration section with code example- Usage examples- Best practicesPROMPT ) ]);
$markdown = $response->content[0]->text;
echo $markdown . "\n";
// Optionally save to filefile_put_contents('middleware-docs.md', $markdown);echo "\n✓ Saved to middleware-docs.md\n";Prompt Templates
Section titled “Prompt Templates”Template Engine
Section titled “Template Engine”<?phpdeclare(strict_types=1);
namespace App\Services;
class PromptTemplate{ public function __construct( private readonly string $template ) {}
public function render(array $variables): string { $result = $this->template;
foreach ($variables as $key => $value) { $placeholder = "{{" . $key . "}}"; $result = str_replace($placeholder, $value, $result); }
return $result; }
public static function codeReview(): self { return new self(<<<'TEMPLATE'You are a {{role}} with {{experience}} years of experience.
Review the following {{language}} code for:1. {{aspect1}}2. {{aspect2}}3. {{aspect3}}
Code to review:{{code}}
Provide:- List of issues found- Severity of each issue (critical, high, medium, low)- Specific recommendations- Refactored code if neededTEMPLATE ); }
public static function dataExtraction(): self { return new self(<<<'TEMPLATE'Extract the following information from the text below:{{fields}}
Return as JSON with this structure:{{json_structure}}
Text to analyze:--- START ---{{text}}--- END ---
Return only valid JSON, no explanation.TEMPLATE ); }
public static function documentation(): self { return new self(<<<'TEMPLATE'Generate {{doc_type}} documentation for this {{code_type}}:
{{code}}
Include:{{requirements}}
Format: {{format}}TEMPLATE ); }}Usage:
<?phpdeclare(strict_types=1);
require __DIR__ . '/../vendor/autoload.php';
use App\Services\PromptTemplate;use ClaudePhp\ClaudePhp;
$client = new ClaudePhp( apiKey: $_ENV['ANTHROPIC_API_KEY']);
// Code review template$template = PromptTemplate::codeReview();
$prompt = $template->render([ 'role' => 'Senior Laravel Developer', 'experience' => '10', 'language' => 'PHP', 'aspect1' => 'Security vulnerabilities', 'aspect2' => 'Performance issues', 'aspect3' => 'Code maintainability', 'code' => 'function getUser($id) { return DB::query("SELECT * FROM users WHERE id = " . $id); }'];
echo "Generated prompt:\n{$prompt}\n\n";
$response = $client->messages()->create([ 'model' => 'claude-sonnet-4-5-20250929', 'max_tokens' => 2048, 'messages' => [['role' => 'user', 'content' => $prompt]]);
echo "Response:\n{$response->content[0]->text}\n";Prompt Library
Section titled “Prompt Library”<?phpdeclare(strict_types=1);
namespace App\Services;
class PromptLibrary{ private const PROMPTS = [ 'code_review' => [ 'template' => 'Review this {{language}} code for {{focus}}:\n\n{{code}}', 'defaults' => ['language' => 'PHP', 'focus' => 'best practices'] ], 'bug_fix' => [ 'template' => 'Debug this {{language}} code. The error is: {{error}}\n\nCode:\n{{code}}', 'defaults' => ['language' => 'PHP'] ], 'optimization' => [ 'template' => 'Optimize this {{language}} code for {{metric}}:\n\n{{code}}', 'defaults' => ['language' => 'PHP', 'metric' => 'performance'] ], 'documentation' => [ 'template' => 'Generate {{doc_type}} for this {{language}} {{code_type}}:\n\n{{code}}', 'defaults' => ['doc_type' => 'PHPDoc', 'language' => 'PHP', 'code_type' => 'function'] ], 'test_generation' => [ 'template' => 'Generate {{test_framework}} tests for this {{language}} {{code_type}}:\n\n{{code}}', 'defaults' => ['test_framework' => 'PHPUnit', 'language' => 'PHP', 'code_type' => 'class'] ], ];
public static function get(string $name, array $variables = []: string { if (!isset(self::PROMPTS[$name]) { throw new \InvalidArgumentException("Prompt template '{$name}' not found"); }
$prompt = self::PROMPTS[$name]; $merged = array_merge($prompt['defaults'] ?? [], $variables);
return self::renderTemplate($prompt['template'], $merged); }
private static function renderTemplate(string $template, array $variables): string { foreach ($variables as $key => $value) { $template = str_replace("{{" . $key . "}}", $value, $template); }
return $template; }
public static function list(): array { return array_keys(self::PROMPTS); }}Usage:
<?phpdeclare(strict_types=1);
require __DIR__ . '/../vendor/autoload.php';
use App\Services\PromptLibrary;use ClaudePhp\ClaudePhp;
$client = new ClaudePhp( apiKey: $_ENV['ANTHROPIC_API_KEY']);
// List available promptsecho "Available prompts:\n";foreach (PromptLibrary::list() as $name) { echo "- {$name}\n";}echo "\n";
// Use a prompt$code = <<<'PHP'function calculateTotal($items) { $total = 0; foreach ($items as $item) { $total += $item['price']; } return $total;}PHP;
$prompt = PromptLibrary::get('test_generation', [ 'code' => $code, 'test_framework' => 'PHPUnit', 'code_type' => 'function'];
echo "Generated prompt:\n{$prompt}\n\n";
$response = $client->messages()->create([ 'model' => 'claude-sonnet-4-5-20250929', 'max_tokens' => 2048, 'messages' => [['role' => 'user', 'content' => $prompt]]);
echo "Generated tests:\n{$response->content[0]->text}\n";Prompt Length and Token Management
Section titled “Prompt Length and Token Management”Understanding prompt length and token limits is crucial for effective prompt engineering. Claude has a context window limit, and managing your prompt size ensures you have room for responses and can control costs.
Understanding Token Limits
Section titled “Understanding Token Limits”Claude models have different context window sizes:
- Claude Sonnet 4: Up to 200,000 tokens (~150,000 words)
- Claude Opus 4: Up to 200,000 tokens
- Claude Haiku 3: Up to 200,000 tokens
Important considerations:
- Both input (prompt) and output (response) count toward token usage
- Longer prompts = higher costs
- Very long prompts may reduce response quality
- Reserve tokens for the response (
max_tokens)
Estimating Prompt Length
Section titled “Estimating Prompt Length”<?phpdeclare(strict_types=1);
require __DIR__ . '/../vendor/autoload.php';
use ClaudePhp\ClaudePhp;
$client = new ClaudePhp( apiKey: $_ENV['ANTHROPIC_API_KEY']);
// Simple token estimation (rough: 1 token ≈ 4 characters)function estimateTokens(string $text): int{ return (int) ceil(strlen($text) / 4);}
$prompt = <<<PROMPTYou are a PHP code reviewer. Review this code for security issues:
function getUser(\$id) { return DB::query("SELECT * FROM users WHERE id = " . \$id);}PROMPT;
$estimatedTokens = estimateTokens($prompt);echo "Estimated prompt tokens: {$estimatedTokens}\n";echo "Recommended 'max_tokens' => " . max(512, 200000 - $estimatedTokens - 1000) . "\n";Optimizing Prompt Length
Section titled “Optimizing Prompt Length”Strategy 1: Remove Redundancy
<?phpdeclare(strict_types=1);
require __DIR__ . '/../vendor/autoload.php';
use ClaudePhp\ClaudePhp;
$client = new ClaudePhp( apiKey: $_ENV['ANTHROPIC_API_KEY']);
// ✗ Bad: Verbose and repetitive$verbosePrompt = <<<PROMPTYou are a PHP developer. You need to review PHP code.Please review the following PHP code.Look for security issues in the PHP code.Review the code carefully for any security vulnerabilities.PROMPT;
// ✓ Good: Concise and clear$concisePrompt = <<<PROMPTReview this PHP code for security vulnerabilities:
function getUser(\$id) { return DB::query("SELECT * FROM users WHERE id = " . \$id);}PROMPT;
echo "Verbose: " . strlen($verbosePrompt) . " chars\n";echo "Concise: " . strlen($concisePrompt) . " chars\n";echo "Saved: " . (strlen($verbosePrompt) - strlen($concisePrompt)) . " characters\n";Strategy 2: Use Abbreviations and Shorthand
<?phpdeclare(strict_types=1);
require __DIR__ . '/../vendor/autoload.php';
use ClaudePhp\ClaudePhp;
$client = new ClaudePhp( apiKey: $_ENV['ANTHROPIC_API_KEY']);
// For classification tasks, use compact format$compactPrompt = <<<PROMPTClassify sentiment: pos/neg/neu
Examples:"Great!" → pos"Terrible" → neg"Okay" → neu
Classify: "Not bad, could be better"PROMPT;
// Claude understands abbreviations in context$response = $client->messages()->create([ 'model' => 'claude-sonnet-4-5-20250929', 'max_tokens' => 50, 'messages' => [['role' => 'user', 'content' => $compactPrompt]]);
echo $response->content[0]->text . "\n";Strategy 3: Prioritize Important Information
<?phpdeclare(strict_types=1);
require __DIR__ . '/../vendor/autoload.php';
use ClaudePhp\ClaudePhp;
$client = new ClaudePhp( apiKey: $_ENV['ANTHROPIC_API_KEY']);
// Put critical instructions first$optimizedPrompt = <<<PROMPTExtract: name, email, phone from text below. Return JSON only.
Text:John Doe, john@example.com, 555-1234PROMPT;
// Less important context can be shortened or omitted if space is tightManaging Long Contexts
Section titled “Managing Long Contexts”When working with long documents or codebases:
<?phpdeclare(strict_types=1);
namespace App\Services;
class PromptLengthManager{ private const MAX_PROMPT_TOKENS = 150000; // Reserve space for response private const TOKENS_PER_CHAR = 4; // Rough estimate
public function truncateIfNeeded(string $prompt, int $maxTokens = self::MAX_PROMPT_TOKENS): string { $estimatedTokens = $this->estimateTokens($prompt);
if ($estimatedTokens <= $maxTokens) { return $prompt; }
// Truncate intelligently (preserve structure) $maxChars = ($maxTokens * self::TOKENS_PER_CHAR) - 100; // Safety margin
// Try to preserve important parts (instructions, examples) if (preg_match('/^(.*?)(Examples?:.*?)(Text:|Code:.*)$/s', $prompt, $matches)) { $instructions = $matches[1]; $examples = $matches[2]; $data = $matches[3];
$availableForData = $maxChars - strlen($instructions) - strlen($examples); $truncatedData = substr($data, 0, $availableForData) . "\n\n[... truncated ...]";
return $instructions . $examples . $truncatedData; }
// Fallback: simple truncation return substr($prompt, 0, $maxChars) . "\n\n[... truncated ...]"; }
private function estimateTokens(string $text): int { return (int) ceil(strlen($text) / self::TOKENS_PER_CHAR); }
public function getRecommendedMaxTokens(string $prompt): int { $promptTokens = $this->estimateTokens($prompt); $available = self::MAX_PROMPT_TOKENS - $promptTokens - 1000; // Safety margin return max(512, min(8192, $available)); // Reasonable range }}Usage:
<?phpdeclare(strict_types=1);
require __DIR__ . '/../vendor/autoload.php';
use App\Services\PromptLengthManager;use ClaudePhp\ClaudePhp;
$client = new ClaudePhp( apiKey: $_ENV['ANTHROPIC_API_KEY']);
$manager = new PromptLengthManager();$longPrompt = str_repeat("This is a very long prompt. ", 10000);
$optimized = $manager->truncateIfNeeded($longPrompt);$maxTokens = $manager->getRecommendedMaxTokens($optimized);
echo "Original length: " . strlen($longPrompt) . " chars\n";echo "Optimized length: " . strlen($optimized) . " chars\n";echo "Recommended 'max_tokens' => {$maxTokens}\n";Best Practices
Section titled “Best Practices”- Keep prompts focused: Remove unnecessary words and redundancy
- Put instructions first: Most important information at the beginning
- Use examples efficiently: 3-5 examples usually sufficient
- Monitor token usage: Track costs and optimize long-running applications
- Reserve response space: Set
max_tokensappropriately based on prompt length - Split when necessary: For very long contexts, consider chunking or summarization
Iterative Prompt Refinement
Section titled “Iterative Prompt Refinement”Prompt engineering is an iterative process. Rarely does the first prompt produce perfect results. This section covers a systematic approach to improving prompts.
The Refinement Cycle
Section titled “The Refinement Cycle”The iterative refinement process follows this cycle:
- Write initial prompt
- Test with sample inputs
- Analyze results for issues
- Refine prompt based on findings
- Repeat until quality meets requirements
<?phpdeclare(strict_types=1);
namespace App\Services;
use ClaudePhp\ClaudePhp;
class PromptRefiner{ public function __construct( private readonly ClaudePhp $client ) {}
public function refine( string $initialPrompt, array $testCases, int $iterations = 3 ): string { $currentPrompt = $initialPrompt;
for ($i = 0; $i < $iterations; $i++) { echo "Iteration " . ($i + 1) . ":\n";
// Test current prompt $results = $this->testPrompt($currentPrompt, $testCases);
// Analyze results $issues = $this->analyzeResults($results);
if (empty($issues)) { echo "✓ Prompt quality acceptable\n"; break; }
// Refine based on issues $currentPrompt = $this->suggestImprovements($currentPrompt, $issues); echo "\n"; }
return $currentPrompt; }
private function testPrompt(string $prompt, array $testCases): array { $results = [];
foreach ($testCases as $testCase) { $response = $this->client->messages()->create([ 'model' => 'claude-sonnet-4-5-20250929', 'max_tokens' => 512, 'messages' => [ [ 'role' => 'user', 'content' => $prompt . "\n\nInput: " . $testCase['input'] ) ] );
$results[] = [ 'input' => $testCase['input'], 'expected' => $testCase['expected'], 'actual' => trim($response->content[0]->text), 'match' => $this->matches($testCase['expected'], trim($response->content[0]->text)) ]; }
return $results; }
private function analyzeResults(array $results): array { $issues = []; $matchCount = 0;
foreach ($results as $result) { if (!$result['match'] { $issues[] = [ 'input' => $result['input'], 'expected' => $result['expected'], 'actual' => $result['actual'], 'type' => 'mismatch' ]; } else { $matchCount++; } }
echo "Accuracy: {$matchCount}/" . count($results) . "\n";
return $issues; }
private function suggestImprovements(string $prompt, array $issues): string { // Ask Claude to suggest improvements $issueSummary = $this->formatIssues($issues);
$refinementPrompt = <<<PROMPTAnalyze this prompt and suggest improvements based on these test failures:
Current prompt:{$prompt}
Test failures:{$issueSummary}
Suggest specific improvements to the prompt that would fix these issues.Return only the improved prompt, no explanation.PROMPT;
$response = $this->client->messages()->create([ 'model' => 'claude-sonnet-4-5-20250929', 'max_tokens' => 1024, 'messages' => [['role' => 'user', 'content' => $refinementPrompt]] ]);
return trim($response->content[0]->text); }
private function formatIssues(array $issues): string { $formatted = []; foreach ($issues as $issue) { $formatted[] = "Input: {$issue['input']}\nExpected: {$issue['expected']}\nGot: {$issue['actual']}"; } return implode("\n\n", $formatted); }
private function matches(string $expected, string $actual): bool { // Simple matching - can be enhanced return strtolower(trim($expected)) === strtolower(trim($actual)); }}Usage:
<?phpdeclare(strict_types=1);
require __DIR__ . '/../vendor/autoload.php';
use App\Services\PromptRefiner;use ClaudePhp\ClaudePhp;
$client = new ClaudePhp( apiKey: $_ENV['ANTHROPIC_API_KEY']);
$refiner = new PromptRefiner($client);
$initialPrompt = "Classify sentiment: positive or negative";
$testCases = [ ['input' => 'Great product!', 'expected' => 'positive'], ['input' => 'Terrible service', 'expected' => 'negative'], ['input' => 'It\'s okay', 'expected' => 'neutral'], // This will fail initially];
$refinedPrompt = $refiner->refine($initialPrompt, $testCases, iterations: 3);
echo "\nFinal refined prompt:\n{$refinedPrompt}\n";Refinement Strategies
Section titled “Refinement Strategies”1. Add Missing Context
// Initial (too vague)$v1 = "Review this code";
// Refined (adds context)$v2 = "You are a security expert. Review this PHP code for SQL injection vulnerabilities.";2. Clarify Output Format
// Initial (unclear format)$v1 = "List the issues";
// Refined (specific format)$v2 = "List security issues as JSON array: [{\"issue\": \"description\", \"severity\": \"high|medium|low\"}]";3. Add Examples
// Initial (no examples)$v1 = "Classify sentiment";
// Refined (with examples)$v2 = "Classify sentiment:\n\nExamples:\n\"Great!\" → positive\n\"Terrible\" → negative\n\nClassify: {text}";4. Fix Edge Cases
// Initial (misses edge cases)$v1 = "Extract email addresses";
// Refined (handles edge cases)$v2 = "Extract email addresses. Handle formats: user@domain.com, user+tag@domain.co.uk. Return empty array if none found.";Tracking Prompt Versions
Section titled “Tracking Prompt Versions”<?phpdeclare(strict_types=1);
namespace App\Services;
class PromptVersionTracker{ private array $versions = [];
public function saveVersion(string $prompt, array $metrics = []: string { $version = [ 'id' => uniqid('v', true), 'prompt' => $prompt, 'timestamp' => time(), 'metrics' => $metrics, 'length' => strlen($prompt), 'estimated_tokens' => (int) ceil(strlen($prompt) / 4) ];
$this->versions[] = $version; return $version['id']; }
public function getVersion(string $id): ?array { foreach ($this->versions as $version) { if ($version['id'] === $id) { return $version; } } return null; }
public function compareVersions(string $id1, string $id2): array { $v1 = $this->getVersion($id1); $v2 = $this->getVersion($id2);
if (!$v1 || !$v2) { throw new \InvalidArgumentException('Version not found'); }
return [ 'length_diff' => $v2['length'] - $v1['length'], 'token_diff' => $v2['estimated_tokens'] - $v1['estimated_tokens'], 'metrics_diff' => $this->compareMetrics($v1['metrics'], $v2['metrics'] ]; }
private function compareMetrics(array $m1, array $m2): array { $diff = []; foreach ($m2 as $key => $value) { if (isset($m1[$key]) { $diff[$key] = $value - $m1[$key]; } else { $diff[$key] = $value; } } return $diff; }
public function getLatestVersion(): ?array { return end($this->versions) ?: null; }}Prompt Testing Methodology
Section titled “Prompt Testing Methodology”Systematic testing ensures your prompts work reliably across different inputs. This section covers testing strategies and metrics.
Testing Framework
Section titled “Testing Framework”<?phpdeclare(strict_types=1);
namespace App\Services;
use ClaudePhp\ClaudePhp;
class PromptTester{ public function __construct( private readonly ClaudePhp $client ) {}
public function test( string $prompt, array $testCases, array $options = [] ): array { $results = []; $temperature = $options['temperature'] ?? 0.0; $runs = $options['runs_per_case'] ?? 1;
foreach ($testCases as $testCase) { $caseResults = [];
for ($i = 0; $i < $runs; $i++) { $response = $this->client->messages()->create([ 'model' => 'claude-sonnet-4-5-20250929', 'max_tokens' => $options['max_tokens'] ?? 512, 'temperature' => $temperature, 'messages' => [ [ 'role' => 'user', 'content' => $this->formatPrompt($prompt, $testCase['input']) ] ] ]);
$caseResults[] = [ 'response' => trim($response->content[0]->text), 'tokens' => $response->usage->inputTokens + $response->usage->outputTokens, 'latency_ms' => $this->estimateLatency($response) ]; }
$results[] = [ 'input' => $testCase['input'], 'expected' => $testCase['expected'] ?? null, 'runs' => $caseResults, 'consistency' => $this->checkConsistency($caseResults), 'accuracy' => $this->checkAccuracy($caseResults, $testCase['expected'] ?? null) ]; }
return [ 'results' => $results, 'summary' => $this->generateSummary($results) ]; }
private function formatPrompt(string $prompt, string $input): string { return str_replace('{input}', $input, $prompt); }
private function checkConsistency(array $runs): float { if (count($runs) < 2) { return 1.0; }
$responses = array_map(fn($r) => $r['response'], $runs); $unique = count(array_unique($responses));
return 1.0 - (($unique - 1) / count($runs)); }
private function checkAccuracy(array $runs, ?string $expected): ?float { if ($expected === null) { return null; }
$matches = 0; foreach ($runs as $run) { if ($this->matches($expected, $run['response']) { $matches++; } }
return $matches / count($runs); }
private function matches(string $expected, string $actual): bool { return strtolower(trim($expected)) === strtolower(trim($actual)); }
private function estimateLatency($response): int { // Estimate based on response time (would need actual timing in production) return 0; }
private function generateSummary(array $results): array { $totalCases = count($results); $consistentCases = 0; $accurateCases = 0; $totalTokens = 0;
foreach ($results as $result) { if ($result['consistency'] >= 0.8) { $consistentCases++; } if ($result['accuracy'] !== null && $result['accuracy'] >= 0.8) { $accurateCases++; } foreach ($result['runs'] as $run) { $totalTokens += $run['tokens']; } }
return [ 'total_cases' => $totalCases, 'consistency_rate' => $consistentCases / $totalCases, 'accuracy_rate' => $accurateCases / $totalCases, 'avg_tokens_per_case' => $totalTokens / ($totalCases * count($results[0]['runs']) ]; }}Usage:
<?phpdeclare(strict_types=1);
require __DIR__ . '/../vendor/autoload.php';
use App\Services\PromptTester;use ClaudePhp\ClaudePhp;
$client = new ClaudePhp( apiKey: $_ENV['ANTHROPIC_API_KEY']);
$tester = new PromptTester($client);
$prompt = "Classify sentiment as positive or negative:\n\nInput: {input}\nOutput:";
$testCases = [ ['input' => 'Great product!', 'expected' => 'positive'], ['input' => 'Terrible service', 'expected' => 'negative'], ['input' => 'It works fine', 'expected' => 'positive'], // Ambiguous];
$results = $tester->test($prompt, $testCases, [ 'runs_per_case' => 3, 'temperature' => 0.0];
echo "Test Summary:\n";print_r($results['summary'];
echo "\nDetailed Results:\n";foreach ($results['results'] as $result) { echo "Input: {$result['input']}\n"; echo "Consistency: " . ($result['consistency'] * 100) . "%\n"; echo "Accuracy: " . ($result['accuracy'] * 100 ?? 'N/A') . "%\n\n";}Key Testing Metrics
Section titled “Key Testing Metrics”- Accuracy: Percentage of correct outputs
- Consistency: How often same input produces same output
- Latency: Response time
- Token Usage: Cost per request
- Edge Case Handling: Performance on unusual inputs
A/B Testing Prompts
Section titled “A/B Testing Prompts”<?phpdeclare(strict_types=1);
require __DIR__ . '/../vendor/autoload.php';
use App\Services\PromptTester;use ClaudePhp\ClaudePhp;
$client = new ClaudePhp( apiKey: $_ENV['ANTHROPIC_API_KEY']);
$tester = new PromptTester($client);
// Version A: Simple prompt$promptA = "Classify: {input}";
// Version B: Detailed prompt with examples$promptB = <<<PROMPTClassify sentiment as positive or negative.
Examples:"Great!" → positive"Terrible" → negative
Classify: {input}PROMPT;
$testCases = [ ['input' => 'Amazing product!', 'expected' => 'positive'], ['input' => 'Very disappointed', 'expected' => 'negative'],];
$resultsA = $tester->test($promptA, $testCases);$resultsB = $tester->test($promptB, $testCases);
echo "Version A Accuracy: " . ($resultsA['summary']['accuracy_rate'] * 100) . "%\n";echo "Version B Accuracy: " . ($resultsB['summary']['accuracy_rate'] * 100) . "%\n";
if ($resultsB['summary']['accuracy_rate'] > $resultsA['summary']['accuracy_rate'] { echo "\n✓ Version B performs better\n";} else { echo "\n✓ Version A performs better\n";}Common Pitfalls and Solutions
Section titled “Common Pitfalls and Solutions”Pitfall 1: Ambiguous Instructions
Section titled “Pitfall 1: Ambiguous Instructions”<?php# ✗ Bad: Ambiguous$bad = "Make this code better.";
# ✓ Good: Specific$good = "Refactor this code to: 1) Add type hints, 2) Extract magic numbers to constants, 3) Add error handling for null values.";Pitfall 2: Missing Context
Section titled “Pitfall 2: Missing Context”<?php# ✗ Bad: No context$bad = "Is this secure?";
# ✓ Good: With context$good = "Review this user authentication function for security vulnerabilities. The app handles financial transactions and must comply with PCI DSS.";Pitfall 3: Unclear Output Format
Section titled “Pitfall 3: Unclear Output Format”<?php# ✗ Bad: Vague format$bad = "List the benefits.";
# ✓ Good: Specific format$good = "List 5 benefits of using PHP 8.4. Format as JSON array with fields: benefit (string), impact (string), example (string).";Pitfall 4: Too Many Instructions
Section titled “Pitfall 4: Too Many Instructions”<?php# ✗ Bad: Overwhelming$bad = "Analyze the code, identify bugs, refactor it, write tests, generate documentation, suggest performance improvements, check security, and create a deployment guide.";
# ✓ Good: Focused$good = "Analyze this code for security vulnerabilities. Focus specifically on: SQL injection, XSS, CSRF, and authentication issues.";Pitfall 5: Inconsistent Examples
Section titled “Pitfall 5: Inconsistent Examples”<?php# ✗ Bad: Inconsistent examples$bad = <<<PROMPTExamples:Input: "Great product!" → positiveInput: "It's terrible" → Output: negativeInput: "Okay" -> Classification: neutral
Classify: "I love it"PROMPT;
# ✓ Good: Consistent format$good = <<<PROMPTExamples:Input: "Great product!"Output: positive
Input: "It's terrible"Output: negative
Input: "Okay"Output: neutral
Classify this:Input: "I love it"Output:PROMPT;Exercises
Section titled “Exercises”Exercise 1: Dynamic Prompt Builder
Section titled “Exercise 1: Dynamic Prompt Builder”Build a flexible prompt building system:
<?phpclass DynamicPromptBuilder{ public function forTask(string $task): self { // TODO: Set task type return $this; }
public function withContext(string $context): self { // TODO: Add context return $this; }
public function withExamples(array $examples): self { // TODO: Add few-shot examples return $this; }
public function withFormat(string $format): self { // TODO: Specify output format return $this; }
public function build(): string { // TODO: Construct final prompt }}Exercise 2: Prompt Validator
Section titled “Exercise 2: Prompt Validator”Create a validator to check prompt quality:
<?phpclass PromptValidator{ public function validate(string $prompt): array { // TODO: Check for clarity // TODO: Check for specificity // TODO: Check for context // TODO: Check for output format specification // TODO: Return validation results with suggestions }}Exercise 3: A/B Prompt Tester
Section titled “Exercise 3: A/B Prompt Tester”Build a system to test prompt variations:
<?phpclass PromptABTester{ public function test(array $prompts, string $input): array { // TODO: Run each prompt variation // TODO: Collect responses // TODO: Compare quality metrics // TODO: Return best performing prompt }}Solution Hints
Exercise 1: Use builder pattern with method chaining, store components in properties, assemble in build() method using string concatenation or template engine.
Exercise 2: Check for question marks, specific requirements, word count, presence of examples. Provide specific improvement suggestions for each issue found.
Exercise 3: Send same input to Claude multiple times with different prompts, measure response length/quality, compare token usage, use Claude to judge which response is better.
Troubleshooting
Section titled “Troubleshooting”Inconsistent responses?
- Lower temperature (0.0-0.3) for deterministic outputs
- Add more specific instructions
- Use few-shot examples to guide behavior
- Check for ambiguous language in prompt
Response doesn’t match format?
- Explicitly state format requirements
- Provide example of desired output
- Add “Return only [format], no explanation”
- Use delimiters to separate instructions from data
Claude refuses to follow instructions?
- Check if instructions conflict with safety guidelines
- Rephrase to be clearer and more specific
- Break complex tasks into smaller steps
- Provide context for why you need the output
Poor quality outputs?
- Add relevant context
- Use appropriate model (Opus for complex tasks)
- Implement chain-of-thought reasoning
- Provide few-shot examples
Claude-PHP-SDK Features
Section titled “Claude-PHP-SDK Features”The examples in this chapter use the official Claude-PHP-SDK, which provides complete 1-for-1 functionality with the official Python SDK. Key features demonstrated include:
Token Counting
Section titled “Token Counting”// Count tokens before making API calls$count = $client->messages()->countTokens([ 'model' => 'claude-sonnet-4-5-20250929', 'messages' => [['role' => 'user', 'content' => 'Hello!']]]);
echo "Token count: " . $count->input_tokens;Comprehensive Error Handling
Section titled “Comprehensive Error Handling”use ClaudePhp\Exceptions\{ APIConnectionError, RateLimitError, AuthenticationError, APIStatusError};
try { $response = $client->messages()->create([...]);} catch (RateLimitError $e) { echo "Rate limited. Retry after: " . $e->response->getHeaderLine('retry-after');} catch (AuthenticationError $e) { echo "Invalid API key";}Additional SDK Capabilities
Section titled “Additional SDK Capabilities”- Streaming Responses: Real-time response processing
- Tool Use: Function calling and custom tools
- Vision API: Image analysis and processing
- Batch Processing: Cost-effective bulk operations
- Structured Outputs: JSON schema enforcement
Further Reading
Section titled “Further Reading”- Official PHP SDK Documentation — The official Anthropic PHP SDK on GitHub
- Claude-PHP-SDK — PHP SDK for Claude (
composer require claude-php/claude-php-sdk ^0.2) - Anthropic API Documentation — Complete API reference and guides
- PHP SDK Composer Package — Official package on Packagist
Wrap-up
Section titled “Wrap-up”Congratulations! You’ve completed a comprehensive guide to prompt engineering for Claude. Here’s what you’ve accomplished:
- ✓ Mastered prompt engineering fundamentals including clarity, context, and format specification
- ✓ Built reusable prompt components including
FewShotClassifier,ChainOfThoughtSolver, andPersonaBuilder - ✓ Created prompt template systems with
PromptTemplateandPromptLibraryfor consistent prompting - ✓ Learned advanced techniques including few-shot learning (with negative examples), chain-of-thought reasoning, and role-playing
- ✓ Implemented structured output generation for JSON, XML, CSV, and Markdown formats
- ✓ Optimized prompt length and learned token management strategies
- ✓ Mastered iterative refinement with systematic testing and version tracking
- ✓ Built testing frameworks for evaluating prompt quality and consistency
- ✓ Identified common pitfalls and learned how to avoid them in your prompts
Key Concepts Learned
Section titled “Key Concepts Learned”- Prompt Quality: Clear, specific, and context-rich prompts produce significantly better results
- Few-Shot Learning: Providing examples (both positive and negative) guides Claude’s responses and improves accuracy
- Chain-of-Thought: Encouraging step-by-step reasoning helps with complex problem-solving
- Personas: Assigning roles and expertise improves response quality for specialized tasks
- Structured Outputs: Explicit format requirements ensure consistent, parseable responses
- Templates: Reusable prompt templates save time and ensure consistency across applications
- Token Management: Optimizing prompt length reduces costs and improves response quality
- Iterative Refinement: Systematic testing and refinement cycles improve prompt effectiveness
- Testing Methodology: Proper metrics and A/B testing validate prompt improvements
Next Steps
Section titled “Next Steps”You now have the foundation to write effective prompts that consistently produce high-quality results. In the next chapter, you’ll learn about streaming responses, which allow you to display Claude’s output in real-time as it’s generated, creating a more interactive user experience.
Key Takeaways
Section titled “Key Takeaways”- ✓ Clarity is crucial - Be specific about what you want
- ✓ Context matters - Provide relevant background information
- ✓ Examples guide - Few-shot learning (including negative examples) improves accuracy
- ✓ Format explicitly - Always specify desired output format
- ✓ Roles work - Persona assignment improves responses
- ✓ Chain-of-thought - Helps with complex reasoning tasks
- ✓ Templates save time - Reusable prompts ensure consistency
- ✓ Optimize length - Shorter prompts are often more effective and cost less
- ✓ Iterate systematically - Test, analyze, refine, repeat
- ✓ Test thoroughly - Use proper metrics and A/B testing to validate improvements
- ✓ Track versions - Monitor prompt changes and measure impact
- ✓ Delimiters help - Separate instructions from data clearly
- ✓ Temperature matters - Lower for consistency, higher for creativity
Further Reading
Section titled “Further Reading”- Anthropic Prompt Engineering Guide — Official guide to prompt engineering best practices
- Anthropic System Prompts Documentation — Learn about system prompts and role assignment
- Chain-of-Thought Prompting Research — Original research paper on chain-of-thought reasoning
- Chapter 06: Streaming Responses — Learn to stream Claude’s responses in real-time
- Chapter 07: System Prompts and Roles — Advanced system prompt techniques
- Chapter 15: Structured Outputs — Deep dive into generating structured data
Continue to Chapter 06: Streaming Responses to learn how to stream Claude’s responses in real-time.
💻 Code Samples
Section titled “💻 Code Samples”All code examples from this chapter are available in the GitHub repository:
Clone and run locally:
git clone https://github.com/dalehurley/codewithphp.gitcd codewithphp/code/claude-php/chapter-05composer installcp .env.example .env# Add your API key to .envphp examples/01-clarity.php