Back to Blog

Claude Agent SDK - How to Customise System Prompts for Your AI Agents

April 1, 20266 min readMichael Ridland

System prompts are the single most underrated part of building AI agents. Everyone focuses on model selection, tool integration, and orchestration patterns. But the system prompt - that initial set of instructions that tells the model how to behave - determines more about your agent's quality than almost anything else.

Anthropic's Claude Agent SDK gives you three distinct ways to modify system prompts, and understanding when to use each one is worth the time investment. We've been building with Claude agents at Team 400 for a while now, and the system prompt strategy you choose has a direct impact on how reliable and useful your agents end up being.

The Default Behaviour Matters

Here's something that trips people up: the Agent SDK uses a minimal system prompt by default. It includes basic tool instructions but skips Claude Code's coding guidelines, response style, and project context. If you want the full Claude Code experience, you need to explicitly request it:

const messages = [];
for await (const message of query({
  prompt: "Refactor the authentication module",
  options: {
    systemPrompt: {
      type: "preset",
      preset: "claude_code"
    }
  }
})) {
  messages.push(message);
}

Without preset: "claude_code", you get a much more barebones agent. That's fine for simple tasks, but for anything involving code generation, file manipulation, or complex reasoning, you want the full preset. We've seen agents produce noticeably worse code when running on the minimal prompt - more verbose, less idiomatic, more likely to introduce bugs.

Method 1 - CLAUDE.md Files

CLAUDE.md files are the simplest way to give your agent project-specific context. Drop a CLAUDE.md file in your project root (or .claude/CLAUDE.md), and it acts as persistent memory for every session.

Here's what a realistic one looks like:

# Project Guidelines

## Code Style
- TypeScript strict mode, no any types
- Prefer functional components in React
- All API endpoints must have input validation

## Testing
- Run npm test before committing
- Jest for units, Playwright for E2E
- Minimum 80% coverage on new code

## Architecture
- Services layer between controllers and repositories
- All database access through Prisma ORM
- Feature flags via LaunchDarkly

The critical detail most people miss: CLAUDE.md files don't load automatically in the SDK. You must explicitly specify settingSources:

for await (const message of query({
  prompt: "Add a new API endpoint for user preferences",
  options: {
    systemPrompt: {
      type: "preset",
      preset: "claude_code"
    },
    settingSources: ["project"] // This is required
  }
})) {
  messages.push(message);
}

Without settingSources: ["project"], your beautifully crafted CLAUDE.md sits there doing nothing. I've debugged this exact issue on multiple projects - the agent seems to ignore project conventions, and the root cause is always that nobody told the SDK to load the CLAUDE.md file.

You can also use settingSources: ["user"] to load ~/.claude/CLAUDE.md for global instructions, or combine both: settingSources: ["project", "user"].

Method 2 - Output Styles

Output styles are reusable prompt configurations saved as markdown files. Think of them as personas or modes that you can switch between.

You create them as markdown files in ~/.claude/output-styles/ (user-level) or .claude/output-styles/ (project-level):

---
name: Code Reviewer
description: Thorough code review assistant
---

You are an expert code reviewer.

For every code submission:
1. Check for security issues first
2. Evaluate performance implications
3. Suggest concrete improvements with code examples
4. Rate overall quality from 1-10 with justification

Output styles load through the settings system, so you still need settingSources configured. The advantage over CLAUDE.md is that styles are swappable - you can have different styles for different tasks and switch between them with /output-style [name].

We use this for client projects where different team members need different agent behaviours. A junior developer might want a more explanatory style. A senior architect might want terse, opinion-heavy feedback. Same agent, different style.

Method 3 - systemPrompt with Append

The most flexible approach for SDK users. You take the Claude Code preset and append your own instructions:

for await (const message of query({
  prompt: "Help me optimise the database queries",
  options: {
    systemPrompt: {
      type: "preset",
      preset: "claude_code",
      append: "Always explain query performance implications. Prefer indexed lookups over table scans. Suggest EXPLAIN ANALYZE output when recommending changes."
    }
  }
})) {
  messages.push(message);
}

This preserves all the built-in Claude Code behaviour while layering your specific requirements on top. It's the approach we use most often when building custom agent workflows for clients, because it gives you precise control without having to replicate the entire system prompt.

The append approach is particularly useful for:

  • Task-specific agents where you want different behaviour for different workflows
  • Dynamic instructions where the prompt changes based on runtime context
  • A/B testing different prompt strategies to see which produces better results

Which Method Should You Use?

After building AI agents for a range of Australian businesses, here's our practical recommendation:

Use CLAUDE.md for project conventions. Coding standards, architecture decisions, common commands - anything the whole team should follow. Commit it to git. Review it like code.

Use output styles for role-based behaviour. When you need the agent to behave differently depending on who's using it or what task they're performing.

Use systemPrompt append for programmatic customisation. When you're building an SDK-based application and need runtime control over agent behaviour.

Combine all three when it makes sense. They're not mutually exclusive. CLAUDE.md handles the baseline, output styles handle the persona, and append handles the task-specific instructions. The SDK layers them together.

Common Mistakes

A few things we've seen go wrong:

Overloading the system prompt. More instructions isn't always better. If your CLAUDE.md is 500 lines long, the model spends more tokens processing instructions and less on your actual task. Keep it focused.

Contradicting the base prompt. If you append instructions that conflict with Claude Code's built-in behaviour, you get unpredictable results. The model tries to satisfy both and ends up doing neither well.

Not testing prompt changes. System prompt modifications change agent behaviour in ways that aren't always obvious. Test your changes against a set of representative tasks before rolling them out to the team.

Forgetting settingSources. I've mentioned it twice already and I'll say it again. If your CLAUDE.md isn't being loaded, check settingSources. It's the most common configuration mistake we see.

Building Better AI Agents

System prompt customisation is one piece of a larger puzzle. The quality of your AI agents depends on model selection, tool design, error handling, evaluation, and the prompts that tie it all together. But the system prompt is where you have the most direct control over behaviour, and getting it right makes everything else easier.

If you're building AI agents with the Claude Agent SDK - or any other framework - and want help getting the architecture right, our team works on AI agent development and agentic automation across a range of industries. We also offer AI consulting for organisations that are still figuring out where agents fit into their technology strategy.

The system prompt isn't glamorous. But it's where good agents start.