Skip to content

AI Agents and Their Identity — Why a Persona Matters More Than a Prompt, and What It Costs in Tokens

Giving an agent a clear identity — scope, tools, style, definition of done — is the cheapest way to improve output quality and the most expensive line on your token bill if you do it carelessly. Here's the engineering and the math.

Paweł Rudnicki
11 min
  • AI Agents
  • Claude API
  • Prompt Caching
  • Tokens
  • LLM

Introduction

In 2026, "write a good prompt" is no longer an architecture. The teams I work with have stopped building one giant prompt that handles everything. Instead they build agents: specialised units, each with its own scope of responsibility, its own set of tools and its own identity.

Sounds like cosmetics? It isn't. An agent's identity is simultaneously the cheapest way to improve answer quality and the single most expensive item on your token bill — if you build it without understanding what happens on every request.

This article covers three things: what an agent identity actually consists of, why it changes model behaviour, and exactly how it translates into tokens and money.

What "identity" means for an agent

Identity is not a name and a friendly greeting. In practice it is five layers, and every one of them is load-bearing:

LayerQuestion it answersWhere it lives
Role and scopeWhat is this agent for — and what is it explicitly NOT for?System prompt
Tool boundaryWhat actions can it take at all?Tool definitions
Style and formatHow does it report back? Prose, JSON, a diff?System prompt + structured outputs
Definition of doneWhen is the task finished?System prompt
MemoryWhat does it know from previous sessions?Memory tool / external store

The interesting part is that these layers are not documentation for humans. They are the search space the model operates in. A narrow, explicit identity removes whole classes of wrong decisions before the model makes them.

using Anthropic;
using Anthropic.Models.Messages;

// The identity is a stable artifact — treat it like code, not like a string literal
// scattered across the codebase. Version it, review it, test it.
const string ReleaseAuditorIdentity = """
    You are a release auditor for a .NET MAUI mobile application.

    SCOPE: you review a release candidate diff and report risk. You do not
    write features, you do not refactor, you do not touch CI configuration.

    METHOD: for each changed file, classify risk as low / medium / high and
    justify it with a concrete failure scenario, not a general concern.

    OUTPUT: a table of findings ordered by severity, then a single
    go / no-go recommendation. No preamble, no summary of what you did.

    DONE: every changed file in the diff appears exactly once in the table.
    """;

AnthropicClient client = new();

var response = await client.Messages.Create(new MessageCreateParams
{
    Model = "claude-opus-5",
    MaxTokens = 16000,
    Thinking = new ThinkingConfigAdaptive(),
    OutputConfig = new OutputConfig { Effort = Effort.High },
    System = new List<TextBlockParam>
    {
        new() { Text = ReleaseAuditorIdentity },
    },
    Messages = [new() { Role = Role.User, Content = releaseDiff }],
});

Why identity matters more than prompt wording

It narrows the decision space

An agent without a declared scope treats every request as an invitation to be helpful in general. That is exactly how you get a code reviewer that starts refactoring, or a data extractor that adds friendly commentary you then have to parse around. "SCOPE / not in scope" does more work than three paragraphs of polite instructions.

It survives long loops

A single-turn call barely notices a weak identity. An agent that runs twenty tool calls does. Every iteration is an opportunity to drift — to pick up the framing of the last tool result instead of the framing of the task. A strong identity is re-asserted on every single request, because the system prompt is re-sent every single request. That property is the whole reason drift is manageable, and it is also why identity has a price tag.

It makes the agent testable

An identity with an explicit definition of done is a contract you can write assertions against. "Every changed file appears exactly once" is checkable. "Be thorough" is not.

It is where your security boundary lives

Claude does not know your approval policy. The harness does. Identity plus a deliberately narrow tool surface is how you express that boundary: hard-to-reverse actions get dedicated, gateable tools instead of a general-purpose shell.

The price of identity: tokens

Here is the mechanic that surprises people who move from single calls to agents:

The system prompt and the tool definitions are re-sent with every request in the loop.

An agent run is not one API call. It is N calls, where N is the number of tool round-trips, and each one carries the full identity, the full tool schema set and the entire conversation so far.

Take a realistic setup:

ComponentTokens
Identity (system prompt)1,500
Tool definitions (6 tools)800
Fixed prefix per request2,300

An agent that makes 20 tool calls therefore pays for that prefix 20 times: 46,000 input tokens before a single line of actual task content. On Claude Opus 5 input pricing ($5 / 1M tokens) that's about $0.23 per run just to keep re-introducing the agent to itself — and at 10,000 runs a month, $2,300.

That is the number that makes people delete their system prompts. Don't. Cache them.

Prompt caching: how identity becomes almost free

Prompt caching turns that fixed prefix from a per-request cost into a near-zero one. One invariant governs everything:

Caching is a prefix match. Any byte change anywhere in the prefix invalidates everything after it.

The render order is tools → system → messages. So a cache breakpoint on the last system block covers the tools and the identity together — exactly the 2,300 tokens above.

var response = await client.Messages.Create(new MessageCreateParams
{
    Model = "claude-opus-5",
    MaxTokens = 16000,
    System = new List<TextBlockParam>
    {
        new()
        {
            Text = ReleaseAuditorIdentity,          // byte-identical on every request
            CacheControl = new CacheControlEphemeral(),  // 5-minute TTL by default
        },
    },
    Messages = messages,
});

// Always verify — caching fails silently, never loudly
Console.WriteLine($"written: {response.Usage.CacheCreationInputTokens}");
Console.WriteLine($"read:    {response.Usage.CacheReadInputTokens}");
Console.WriteLine($"full price: {response.Usage.InputTokens}");

The economics: a cache read costs about 0.1× the base input price, a cache write 1.25× for the default 5-minute TTL (or 2× for a 1-hour TTL). Applied to the same 20-iteration run:

ScenarioEffective input tokensCost (Opus 5)
No caching (20 × 2,300)46,000$0.230
Cached (1 write + 19 reads)~7,250$0.036

An 84% reduction on the identity portion of the bill, with no change to the prompt itself. The break-even is trivially fast: with a 5-minute TTL, two requests already pay for the write premium.

One caveat worth knowing: the minimum cacheable prefix is model-dependent and not monotonic across generations — 512 tokens on Claude Opus 5, 1,024 on Sonnet 5, 4,096 on Haiku 4.5. A 3,000-token identity caches on Opus 5 and silently does not on Haiku 4.5. No error, just cache_creation_input_tokens: 0.

The silent cache killers

This is where real money leaks. Every one of these looks harmless in review:

PatternWhy the bill doubles
DateTime.Now interpolated into the system promptPrefix changes every request — nothing ever caches
User or session ID in the identityA separate cache entry per user; zero sharing
Conditional identity sections (if (isPremium) system += ...)Every flag combination is a distinct prefix
Tool list built per user or per requestTools render at position 0 — the entire cache dies
Non-deterministic JSON serialisation of tool schemasByte order differs → prefix differs
Switching model mid-conversationCaches are model-scoped

The fix is architectural, not a matter of marker placement: freeze the identity and push everything volatile to the end of the prompt.

When an operator instruction genuinely has to arrive mid-conversation — a mode switch, a newly injected piece of state — do not edit the top-level system prompt. On Claude Opus 5, Opus 4.8 and Fable 5 you can append a { "role": "system" } message to the messages array instead. It sits after the cached history, so the prefix survives, and unlike text smuggled into a user turn it is a non-spoofable operator channel.

One monolithic agent, or a team of specialists?

Once you accept that identity is cached, the calculus changes. A narrow agent is no longer more expensive than a broad one — and it is measurably better:

DimensionOne generalist agentA team of specialists
Prompt size per requestLarge (all rules for all tasks)Small (only relevant rules)
Cache hit rateLow — the prompt shifts per task typeHigh — each identity is frozen
Context pollutionEverything lands in one windowSub-task reading stays in the subagent
Model choiceOne model for everythingCheap model for reading, strong for judgement
Failure diagnosis"The prompt got worse"Isolated to one agent

The context-pollution row is the one that matters most in practice. A sub-task that reads twenty files does not need to leave twenty file contents in the main agent's window forever. Delegate it, and the main loop receives the conclusion rather than the raw material.

Model choice compounds the effect:

ModelModel IDInput $/1MOutput $/1M
Claude Fable 5claude-fable-5$10.00$50.00
Claude Opus 5claude-opus-5$5.00$25.00
Claude Sonnet 5claude-sonnet-5$3.00$15.00
Claude Haiku 4.5claude-haiku-4-5$1.00$5.00

Reading-heavy, low-judgement sub-tasks on Haiku 4.5 cost a fifth of Opus 5 per input token. But note the constraint from the previous section — do not switch models inside one conversation to chase that saving, because caches are model-scoped and you will pay more than you save. Spawn a subagent with the cheaper model instead and keep the main loop on one model.

Two more levers worth knowing, both on the current models:

  • Effort (output_config.effort, from low to max) controls thinking depth and overall verbosity. Lower effort means fewer, more consolidated tool calls and less preamble — often the single best cost lever for subagents and routine work.

  • Context editing and compaction keep long runs from ballooning: editing clears stale tool results, compaction summarises earlier history server-side when you approach the context window.

A practical token budget

For the release-auditor agent above, running 20 tool calls on a medium-sized diff:

ItemTokensNotes
Identity + tools (cached)~7,250 effective1 write + 19 reads
Tool results (the diff, file reads)~60,000The real driver — prune it
Growing conversation history~35,000 effectiveCached incrementally per turn
Output (findings, reasoning)~8,000Billed at 5× the input rate

The lesson from that table is the part nobody expects: after caching, identity is the cheapest thing in the run. The expensive things are unfiltered tool results and output tokens. Which means the instinct to shorten the system prompt to save money is precisely backwards — a longer, sharper identity that stops the agent from reading three files it didn't need pays for itself many times over.

Summary

Six rules I now apply to every agent I build:

  1. Write the identity as an artifact, not a string literal. Scope, tool boundary, output format, definition of done. Version it, review it, test it.

  2. Assume it is re-sent on every request — because it is. That's what makes it work and what makes it cost.

  3. Cache the prefix and verify the cache. If cache_read_input_tokens is zero across repeated requests, something is invalidating it. Find it.

  4. Freeze the prefix; push volatile content to the end. No timestamps, no user IDs, no conditional sections, no per-request tool sets.

  5. Prefer several narrow agents over one broad one. Better cache behaviour, isolated context, isolated failures, cheaper model choices per sub-task.

  6. Optimise tool results before you optimise the prompt. That's where the tokens actually are.

Giving an agent an identity isn't roleplay and it isn't decoration. It is the interface contract between your system and a non-deterministic component — and once it's cached, it's also the cheapest reliability you can buy.

Note: all token counts and costs above are order-of-magnitude estimates from my own runs. Measure your own with a token-counting endpoint before budgeting against them.

Let's talk about the planned project

The first consultation is free and without obligation. A short description of the process and the expected outcome is enough.

Let's talk about the project