-
Notifications
You must be signed in to change notification settings - Fork 4
System Prompts
Control how the agent behaves by providing custom instructions, using the Claude Code preset, or combining both for tailored agent workflows.
System prompts define the agent's persona, constraints, and objectives. They are sent as the --system-prompt CLI flag and shape every response the agent produces during a query. Without a system prompt, the agent uses the Claude Code CLI's built-in defaults.
The SDK offers three approaches:
| Approach | Method | Best For |
|---|---|---|
| Custom string | systemPrompt('...') |
Full control over instructions |
| Claude Code preset | useClaudeCodePrompt() |
Leveraging Claude Code's built-in coding prompt |
| Preset with additions | useClaudeCodePrompt('...') |
Coding prompt plus your own rules |
Pass a plain string to systemPrompt() to fully replace the default behaviour with your own instructions.
use ClaudeAgentSDK\Options\ClaudeAgentOptions;
use ClaudeAgentSDK\Facades\ClaudeAgent;
$options = ClaudeAgentOptions::make()
->systemPrompt('You are a Laravel expert. Always follow PSR-12 coding standards. Explain your reasoning before making changes.')
->tools(['Read', 'Edit', 'Grep'])
->permission('dontAsk');
$result = ClaudeAgent::query('Refactor the User model to use value objects', $options);The string is sent directly to the CLI as --system-prompt "Your prompt here".
Tip: Keep custom prompts focused. A concise, well-structured prompt outperforms a long, rambling one.
useClaudeCodePrompt() sends a JSON payload that activates Claude Code's built-in coding-focused system prompt. This preset is optimized for reading, writing, and reasoning about code.
use ClaudeAgentSDK\Options\ClaudeAgentOptions;
use ClaudeAgentSDK\Facades\ClaudeAgent;
$options = ClaudeAgentOptions::make()
->useClaudeCodePrompt()
->tools(['Read', 'Edit', 'Bash', 'Grep', 'Glob']);
$result = ClaudeAgent::query('Find and fix any N+1 queries in the controllers', $options);Under the hood, this sends the following JSON as the system prompt:
{"type": "preset", "preset": "claude_code"}Pass a string to useClaudeCodePrompt() to keep the preset behaviour and append your own rules. This is the recommended approach for most agent tasks -- you get Claude Code's coding intelligence plus project-specific guidance.
use ClaudeAgentSDK\Options\ClaudeAgentOptions;
use ClaudeAgentSDK\Facades\ClaudeAgent;
$options = ClaudeAgentOptions::make()
->useClaudeCodePrompt('Also follow PSR-12 coding standards. Write PHPUnit tests for every change. Never modify migration files.')
->tools(['Read', 'Edit', 'Bash', 'Grep', 'Glob']);
$result = ClaudeAgent::query('Add soft deletes to the Post model', $options);Under the hood, this sends:
{"type": "preset", "preset": "claude_code", "append": "Also follow PSR-12 coding standards. Write PHPUnit tests for every change. Never modify migration files."}Tip: The
appendtext is added after the preset prompt, so you can reference concepts the preset already establishes (like file editing conventions) without repeating them.
For maximum control, pass a raw array to systemPrompt(). This is useful when you need to construct the prompt payload dynamically.
use ClaudeAgentSDK\Options\ClaudeAgentOptions;
use ClaudeAgentSDK\Facades\ClaudeAgent;
$options = ClaudeAgentOptions::make()
->systemPrompt([
'type' => 'preset',
'preset' => 'claude_code',
'append' => 'Follow the coding standards defined in .editorconfig.',
]);
$result = ClaudeAgent::query('Clean up the service layer', $options);When an array is provided, it is JSON-encoded before being passed to the CLI. This gives you the same result as useClaudeCodePrompt() but with explicit control over every key.
Writing effective system prompts for autonomous agents differs from writing prompts for chat. The agent will make decisions, use tools, and iterate without your input.
Bad: "You help with code."
Good: "You are a PHP code reviewer. Analyze files for security vulnerabilities, performance issues, and PSR-12 violations."
Tell the agent what it should not do. Autonomous agents can be overzealous without guardrails.
Do not modify test files. Do not delete any files.
Do not run destructive database commands.
Only edit files inside the app/ directory.
Give the agent information about your project that it cannot discover from the code alone.
This is a Laravel 11 application using Inertia.js with Vue 3.
The database is PostgreSQL. We use Pest for testing.
API routes are versioned under routes/api/v2/.
Help the agent know when it is done.
Your task is complete when all modified files pass phpstan level 8
and all existing tests still pass.
use ClaudeAgentSDK\Options\ClaudeAgentOptions;
use ClaudeAgentSDK\Facades\ClaudeAgent;
$options = ClaudeAgentOptions::make()
->systemPrompt('You are a senior PHP code reviewer. Analyze the given files for bugs, security vulnerabilities, performance issues, and coding standard violations. Provide actionable feedback. Do not modify any files.')
->tools(['Read', 'Grep', 'Glob']);
$result = ClaudeAgent::query('Review app/Services/PaymentService.php', $options);use ClaudeAgentSDK\Options\ClaudeAgentOptions;
use ClaudeAgentSDK\Facades\ClaudeAgent;
$options = ClaudeAgentOptions::make()
->useClaudeCodePrompt('Generate Laravel migrations following these rules: use descriptive names, always include down() methods, use appropriate column types, add indexes for foreign keys.')
->tools(['Read', 'Write', 'Bash', 'Glob'])
->permission('acceptEdits');
$result = ClaudeAgent::query('Create a migration for a comments table with polymorphic relations', $options);use ClaudeAgentSDK\Options\ClaudeAgentOptions;
use ClaudeAgentSDK\Facades\ClaudeAgent;
$options = ClaudeAgentOptions::make()
->useClaudeCodePrompt('Write Pest PHP tests. Cover happy paths, edge cases, and error conditions. Use factories and fake data. Run the tests after writing them to verify they pass.')
->tools(['Read', 'Write', 'Edit', 'Bash', 'Grep', 'Glob'])
->permission('dontAsk');
$result = ClaudeAgent::query('Write tests for app/Services/OrderService.php', $options);use ClaudeAgentSDK\Options\ClaudeAgentOptions;
use ClaudeAgentSDK\Facades\ClaudeAgent;
$options = ClaudeAgentOptions::make()
->systemPrompt('You are a technical writer. Read the API route files and controllers, then generate OpenAPI 3.0 YAML documentation. Include request/response schemas, status codes, and example payloads.')
->tools(['Read', 'Grep', 'Glob', 'Write'])
->permission('acceptEdits');
$result = ClaudeAgent::query('Document all API endpoints in routes/api.php', $options);use ClaudeAgentSDK\Options\ClaudeAgentOptions;
use ClaudeAgentSDK\Facades\ClaudeAgent;
$options = ClaudeAgentOptions::make()
->useClaudeCodePrompt('You are investigating a bug. Read logs, trace code paths, and identify root causes. Do not fix the bug -- only diagnose it and explain what is happening and why.')
->tools(['Read', 'Grep', 'Glob', 'Bash'])
->permission('dontAsk')
->maxTurns(20);
$result = ClaudeAgent::query('Users report that order totals are sometimes negative. Investigate.', $options);The system prompt and tool restrictions work together. The prompt tells the agent what to do; the tool list controls how it can act. A well-designed combination prevents the agent from exceeding its intended scope.
use ClaudeAgentSDK\Options\ClaudeAgentOptions;
use ClaudeAgentSDK\Facades\ClaudeAgent;
// Read-only investigator: prompt says "do not modify", tools enforce it
$options = ClaudeAgentOptions::make()
->systemPrompt('Analyze the codebase for potential memory leaks. Report findings but do not modify any files.')
->tools(['Read', 'Grep', 'Glob']) // no Write, Edit, or Bash
->permission('dontAsk');
$result = ClaudeAgent::query('Check for memory leaks in the queue workers', $options);Warning: If your prompt instructs the agent to write files but you have not included
WriteorEditin the tools list, the agent will be unable to comply. Always ensure the prompt and tools are aligned.
When using Hooks, the system prompt can reference hook behaviour:
use ClaudeAgentSDK\Options\ClaudeAgentOptions;
use ClaudeAgentSDK\Facades\ClaudeAgent;
$options = ClaudeAgentOptions::make()
->useClaudeCodePrompt('A linter runs automatically after every file edit. If the linter fails, fix the issues before moving on.')
->tools(['Read', 'Edit', 'Bash', 'Grep'])
->preToolUse('php artisan lint:check', '/Edit|Write/', 30)
->permission('dontAsk');
$result = ClaudeAgent::query('Refactor the AuthController to use form requests', $options);-
Options Reference -- Full reference for every
ClaudeAgentOptionsmethod - Hooks -- Run shell commands before or after tool use
- Subagents -- Define specialized agents with their own system prompts
- Structured Output -- Combine system prompts with JSON schema output
-
Configuration -- Set application-wide defaults in
config/claude-agent.php