Advanced Prompting Techniques
A single well-written prompt only gets you so far. This page covers when to make Claude reason out loud, how to force reliable structured output now that prefilling is deprecated, when to split one task into a validated chain of calls, and how to arrange long documents so Claude actually uses them.
Imagine training a very fast new intern. Sometimes you ask them to show their scratch work before giving an answer. Sometimes you break one big assignment into three smaller hand-offs with a check in between each one. And sometimes the only thing that matters is putting the reference sheet on top of the pile instead of burying it at the bottom before you ask your question. Advanced prompting is just knowing which trick fits which job — and knowing that a couple of old tricks, like finishing Claude's sentences for it, don't work anymore.
Chain of thought: when reasoning helps, and when it's overhead
☺ Like you're 10: It's the difference between showing your math homework and just blurting out a number — showing the work makes it obvious exactly where a wrong turn happened.
Stepping through a problem before answering reduces errors on math, logic, multi-step analysis, and decisions with many competing factors. It has a second benefit for you as a prompt author: seeing the reasoning trace often reveals exactly where your prompt itself was ambiguous, not just where the model went wrong. The caveat, per Anthropic's docs, is blunt: "Without outputting its thought process, no thinking occurs" — asking Claude to "think it through" silently, without producing any visible reasoning, doesn't do anything.
Three escalating forms
The documentation describes three levels of explicit chain-of-thought prompting, in increasing order of control: basic — simply add "Think step-by-step"; guided — outline the specific steps Claude should follow; and structured — use XML tags to separate reasoning from the final output, typically <thinking> for the working and <answer> for the result you'll parse out.
import anthropic
client = anthropic.Anthropic()
response = client.messages.create(
model="claude-sonnet-5",
max_tokens=1024,
messages=[
{
"role": "user",
"content": (
"A store had 120 units of a product. It sold 35% in week one "
"and 20% of what remained in week two. How many units are left?\n\n"
"Work through this step by step inside <thinking> tags, then give "
"the final number inside <answer> tags."
),
}
],
)
for block in response.content:
if block.type == "text":
print(block.text)
import Anthropic from "@anthropic-ai/sdk";
const client = new Anthropic();
const response = await client.messages.create({
model: "claude-sonnet-5",
max_tokens: 1024,
messages: [
{
role: "user",
content:
"A store had 120 units of a product. It sold 35% in week one " +
"and 20% of what remained in week two. How many units are left?\n\n" +
"Work through this step by step inside <thinking> tags, then give " +
"the final number inside <answer> tags.",
},
],
});
for (const block of response.content) {
if (block.type === "text") {
console.log(block.text);
}
}
Current models default to adaptive thinking
Claude Fable 5, Opus 5, and Sonnet 5 support "adaptive thinking" — the model decides for itself when and how much to reason, driven by an effort parameter and the complexity it detects in your query — rather than the older manual extended-thinking mode. Manual step-by-step chain-of-thought prompting is now positioned as a fallback: useful when thinking is turned off, or on Haiku 4.5, which runs the reverse configuration (it supports manual extended thinking via thinking.type: "enabled", but not adaptive thinking). The docs' general recommendation is to prefer a broad instruction like "think thoroughly" over a hand-written step-by-step plan, since Claude's own reasoning often outperforms a human-authored one.
Asking Claude to self-check ("Before you finish, verify your answer against [test criteria]") is a documented way to catch coding and math errors — except on Claude Opus 5, which already self-verifies well. Adding an explicit verification instruction there can cause it to over-verify, adding latency for no accuracy gain. The guidance for Opus 5 specifically is to remove this instruction rather than adapt it.
| Ask for explicit reasoning | Skip it (or trust adaptive thinking) |
|---|---|
| Multi-step math, logic puzzles, or decisions with several competing factors | Single-fact lookups, short classifications, or format-only tasks |
| You need to see where Claude's — or your prompt's — understanding of the problem breaks down | Latency or cost is the binding constraint and the task is well inside the model's default competence |
| Thinking is disabled, or you're on a model without adaptive thinking (e.g. Haiku 4.5 in its default mode) | The model already has adaptive thinking on and can size its own reasoning to the query |
Prefilling: a legacy technique, and what replaces it
☺ Like you're 10: Prefilling used to be like finishing someone's sentence for them so they couldn't wander off-topic. The newest models say "finish your own sentence" — but they'll happily fill out a form for you if you hand them the blank one (that's Structured Outputs).
Historically, you could supply a partial assistant turn — the last message in the array with role: "assistant" — and Claude would continue writing from exactly where you left off. Prefilling { forced the response to start as JSON; prefilling a tag like <analysis> skipped Claude's preamble and forced a structured section to open immediately. It looked like this:
{
"messages": [
{"role": "user", "content": "Return the user's name and age as JSON."},
{"role": "assistant", "content": "{"}
]
}
Prefilling the final assistant turn is now deprecated. Starting with Claude 4.6 models and Claude Mythos Preview — a cutoff that covers today's flagship line, Sonnet 5, Opus 5, and Fable 5 — a request whose last message has role assistant returns a 400 error. Only earlier, pre-4.6 models still accept it, and prefills placed earlier in a conversation (not the final turn) are unaffected. Don't build new code around this pattern.
The docs give explicit replacements for the two most common reasons people used to prefill:
- Forcing a format like JSON — use Structured Outputs (
output_config.formatwithtype: "json_schema"), or define a single extraction tool withstrict: trueand force it viatool_choice. Both guarantee schema-conformant output; prefilling only ever nudged Claude toward it. - Eliminating a preamble like "Here is the requested summary:" — just instruct directly ("Respond with only the summary — no introductory sentence"), or rely on XML tags, Structured Outputs, or tool calling, and strip any stragglers in post-processing.
Foxy: The old tutorial says just prefill the assistant turn with "{" and Claude finishes the JSON for you. Still legit?
Professor Owl: Not on today's models. Sonnet 5, Opus 5, and Fable 5 all reject a request whose last message is from the assistant — a flat 400 error, no exceptions.
Foxy: So how do I force JSON out now?
Sol the Sloth: ...slowly... you just ask properly. Structured Outputs with a json_schema, or a strict tool call. Guaranteed shape, no nudging, no guessing. Worth the extra half-second of setup.
import anthropic
client = anthropic.Anthropic()
response = client.messages.create(
model="claude-sonnet-5",
max_tokens=1024,
messages=[
{"role": "user", "content": "Extract the name and age from: Jordan is 34."}
],
output_config={
"format": {
"type": "json_schema",
"schema": {
"type": "object",
"properties": {
"name": {"type": "string"},
"age": {"type": "integer"},
},
"required": ["name", "age"],
"additionalProperties": False,
},
}
},
)
import Anthropic from "@anthropic-ai/sdk";
const client = new Anthropic();
const response = await client.messages.create({
model: "claude-sonnet-5",
max_tokens: 1024,
messages: [
{ role: "user", content: "Extract the name and age from: Jordan is 34." },
],
output_config: {
format: {
type: "json_schema",
schema: {
type: "object",
properties: {
name: { type: "string" },
age: { type: "integer" },
},
required: ["name", "age"],
additionalProperties: false,
},
},
},
});
Prompt chaining: breaking one big task into a validated sequence
Anthropic's "Building Effective Agents" pattern for prompt chaining decomposes a task into a sequence of steps, where each LLM call processes the previous step's output, with programmatic "gates" checking progress before the next call runs. It's best suited to tasks that decompose cleanly into fixed subtasks — it trades latency for higher accuracy by giving each individual call one thing to do well.
Because adaptive thinking and native subagent orchestration now handle a lot of multistep reasoning inside a single call, the current prompting docs frame explicit chaining as most valuable specifically when you need to inspect intermediate outputs or enforce a fixed pipeline structure. The most common named pattern is self-correction: generate a draft, review it against stated criteria, then refine based on that review — each stage a separate API call, so you can log, evaluate, or branch at any point.
draft = client.messages.create(
model="claude-sonnet-5",
max_tokens=1024,
messages=[
{"role": "user", "content": f"Write a 150-word product description for: {product_brief}"}
],
)
draft_text = next(b.text for b in draft.content if b.type == "text")
review = client.messages.create(
model="claude-sonnet-5",
max_tokens=512,
messages=[
{
"role": "user",
"content": (
"Review this product description against our style guide "
"(no exclamation points, active voice, under 150 words). "
"List concrete issues, or say 'No issues found.'\n\n"
f"<description>\n{draft_text}\n</description>"
),
}
],
)
review_text = next(b.text for b in review.content if b.type == "text")
if "No issues found" not in review_text:
refined = client.messages.create(
model="claude-sonnet-5",
max_tokens=1024,
messages=[
{
"role": "user",
"content": (
"Revise this description to fix the issues listed.\n\n"
f"<description>\n{draft_text}\n</description>\n\n"
f"<issues>\n{review_text}\n</issues>"
),
}
],
)
final_text = next(b.text for b in refined.content if b.type == "text")
else:
final_text = draft_text
const draft = await client.messages.create({
model: "claude-sonnet-5",
max_tokens: 1024,
messages: [
{ role: "user", content: `Write a 150-word product description for: ${productBrief}` },
],
});
const draftText = draft.content.find((b) => b.type === "text")?.text ?? "";
const review = await client.messages.create({
model: "claude-sonnet-5",
max_tokens: 512,
messages: [
{
role: "user",
content: `Review this product description against our style guide (no exclamation points, active voice, under 150 words). List concrete issues, or say "No issues found."\n\n<description>\n${draftText}\n</description>`,
},
],
});
const reviewText = review.content.find((b) => b.type === "text")?.text ?? "";
let finalText = draftText;
if (!reviewText.includes("No issues found")) {
const refined = await client.messages.create({
model: "claude-sonnet-5",
max_tokens: 1024,
messages: [
{
role: "user",
content: `Revise this description to fix the issues listed.\n\n<description>\n${draftText}\n</description>\n\n<issues>\n${reviewText}\n</issues>`,
},
],
});
finalText = refined.content.find((b) => b.type === "text")?.text ?? "";
}
In VS Code, run each chain step as a separate cell in a Jupyter notebook or the Python interactive window. You can inspect draft_text and review_text individually between calls, which makes it obvious whether a bad final result came from a bad draft, a bad review, or a bad refine step.
Long-context prompting: document placement and quote grounding
☺ Like you're 10: It's like handing someone a stack of paperwork — if you actually want them to use page 40, don't bury it at the bottom of the pile after you've already asked your question. Put it on top first.
For inputs of roughly 20,000+ tokens, place the long document content near the top of the prompt — above your query, instructions, and examples. The docs state this ordering "improves performance across all models," and that putting the query at the end can improve response quality "by up to 30 percent in tests," especially for complex multi-document inputs. Structure multi-document content with XML: wrap each document in <document> tags containing <source> and <document_content> subtags, adding an index attribute when there are several.
document_block = """<documents>
<document index="1">
<source>annual_report_2023.pdf</source>
<document_content>{annual_report}</document_content>
</document>
<document index="2">
<source>competitor_analysis_q2.xlsx</source>
<document_content>{competitor_analysis}</document_content>
</document>
</documents>
Analyze the annual report and competitor analysis. Identify strategic
advantages and recommend Q3 focus areas.""".format(
annual_report=annual_report_text,
competitor_analysis=competitor_analysis_text,
)
response = client.messages.create(
model="claude-sonnet-5",
max_tokens=2048,
messages=[{"role": "user", "content": document_block}],
)
For grounding, ask Claude to extract the relevant passages into <quotes> tags before doing the actual task, rather than jumping straight to analysis. This is the same word-for-word quote-extraction technique documented in Anthropic's hallucination-reduction guidance for documents over 20k tokens: it keeps Claude anchored to what the source actually says instead of drifting toward plausible-sounding but unsupported claims, and it gives you a visible trail to check its work against.
Common anti-patterns
Anthropic's own golden rule for judging a prompt: show it to a colleague with minimal context and ask them to follow it. If they'd be confused about what to do, Claude will be too — Claude is, in the docs' words, "a brilliant but new employee who lacks context on your norms and workflows."
"Create an analytics dashboard. Include as many relevant features and interactions as possible. Go beyond the basics to create a fully-featured implementation."
"Create an analytics dashboard." — technically an instruction, but it leaves scope, features, and level of polish entirely to guesswork.
"Your response should be composed of smoothly flowing prose paragraphs." — a positive instruction that tells Claude what to produce.
"Do not use markdown." — a prohibition with no positive alternative; Claude has to infer what an acceptable response looks like, and negative instructions are followed less reliably than positive ones.
Break "summarize this report, translate the summary to Spanish, and format it as a bulleted email" into three chained calls — summarize, translate, format — validating each output before the next call runs.
One giant prompt bundling all three steps together, with no way to catch a bad summary before Claude translates and formats it into a polished, confidently wrong email.
Pick a task you currently do with a single prompt that bundles two or three instructions together (for example, "summarize this email thread and draft a reply"). Split it into a two-call chain using the Messages API — one call produces the summary, a second call takes that summary as input and drafts the reply. Log both raw responses and compare: did splitting the task change the quality of either output, and would you add a validation gate between the calls?
You should now be able to explain why Fable 5, Opus 5, and Sonnet 5 mostly don't need hand-written chain-of-thought anymore, why a prompt that prefills the assistant turn will fail on those same models and what to use instead, when a validated call-by-call chain beats one giant prompt, and why long documents belong near the top of the prompt rather than the bottom. Next up: Vision & Multimodal Prompting, where the same "be specific, structure with XML" instincts carry over to images.
Check your answers
- When should you still write manual, step-by-step chain-of-thought instructions instead of just trusting Claude to reason on its own? Mainly as a fallback — when extended thinking is turned off, or on a model without adaptive thinking, such as Haiku 4.5 in its default mode. On Opus 5 specifically, skip added self-verification instructions too, since the model already self-checks well and an extra prompt just adds latency.
- Your code prefills the final assistant turn with
{to force JSON output — will that still work on Sonnet 5? No. Starting with Claude 4.6 models and Claude Mythos Preview, a request whose last message has roleassistantreturns a 400 error. Use Structured Outputs (output_config.formatwithtype: "json_schema") or a strict extraction tool forced viatool_choiceinstead. - Why split a task into a chain of separate API calls instead of one big prompt? Chaining trades latency for reliability: each call does one thing well, and a programmatic gate between calls lets you inspect, log, or branch on intermediate output — the draft → review → refine self-correction loop is the most common form.