Chain-of-Thought & Self-Reflection in LLMs: Full Guide
# Chain-of-thought prompting improves LLM reasoning by making the model think step by step before answering. Learn when to use it, how to structure it, and when self-reflection and extended thinking add more value.
Chain-of-thought (CoT) prompting is the simplest technique for improving LLM reasoning quality. Instead of asking for an immediate answer, you ask the model to reason through the problem step by step. For complex tasks, this reliably improves accuracy by 20–40% with no additional model calls required.
What Chain-of-Thought Does
A model answering directly makes a single prediction. With CoT, it generates a reasoning trace first, then derives the answer from that trace. The intermediate reasoning grounds the final answer and makes errors visible.
Without CoT:
Input: "If a train travels 120 miles in 2 hours, how long to travel 450 miles?"
Output: "3 hours" ← wrong
With CoT:
Input: Same + "Think step by step"
Output:
"Step 1: Speed = 120 miles / 2 hours = 60 mph
Step 2: Time = 450 miles / 60 mph = 7.5 hours
Answer: 7.5 hours" ← correct
The model caught its own error because the intermediate steps made it visible.
Basic CoT Implementation
The simplest form — just add reasoning instructions to your prompt:
// Standard CoT trigger phrases
const COT_PROMPTS = {
explicit: "Let's think step by step.",
structured: "Work through this systematically:",
analytical: "Before answering, analyze the problem:",
verification: "Think through this carefully, then verify your answer:",
};
async function withCoT(question: string, domain: "math" | "logic" | "analysis" | "code"): Promise<string> {
const coTInstruction = {
math: "Show all calculations. Double-check arithmetic.",
logic: "Identify premises, reason through implications, check for contradictions.",
analysis: "Consider multiple perspectives before drawing conclusions.",
code: "Think about edge cases and potential bugs before writing code.",
}[domain];
const response = await anthropic.messages.create({
model: "claude-sonnet-4-6",
max_tokens: 1500,
messages: [{
role: "user",
content: `${question}
${coTInstruction} Show your reasoning before giving the final answer.`,
}],
});
return response.content[0].text;
}
Structured CoT with XML Tags
Claude responds particularly well to structured XML reasoning:
const STRUCTURED_COT_SYSTEM = `When solving problems, use this format:
<reasoning>
Step-by-step analysis goes here.
Consider edge cases.
Check your work.
</reasoning>
<answer>
Final answer here.
</answer>`;
async function structuredReasoning(problem: string): Promise<{ reasoning: string; answer: string }> {
const response = await anthropic.messages.create({
model: "claude-sonnet-4-6",
max_tokens: 2000,
system: STRUCTURED_COT_SYSTEM,
messages: [{ role: "user", content: problem }],
});
const text = response.content[0].text;
const reasoning = text.match(/<reasoning>([\s\S]*?)<\/reasoning>/)?.[1]?.trim() ?? "";
const answer = text.match(/<answer>([\s\S]*?)<\/answer>/)?.[1]?.trim() ?? text;
return { reasoning, answer };
}
This gives you programmatic access to the reasoning trace — useful for logging, debugging, and quality checks.
Few-Shot CoT Examples
Providing examples of reasoning dramatically improves consistency:
const CODE_REVIEW_COT_EXAMPLES = `
Example 1:
Question: Is this code safe? const query = \`SELECT * FROM users WHERE id = \${userId}\`
Reasoning:
1. This is a template literal SQL query
2. The userId variable is directly interpolated
3. An attacker could pass userId = "1 OR 1=1" to get all users
4. This is a SQL injection vulnerability
5. Fix: use parameterized query: db.query("SELECT * FROM users WHERE id = $1", [userId])
Answer: CRITICAL SQL injection vulnerability. Must use parameterized queries.
Example 2:
...
`;
const response = await anthropic.messages.create({
model: "claude-sonnet-4-6",
system: `You review code for security vulnerabilities. ${CODE_REVIEW_COT_EXAMPLES}`,
messages: [{ role: "user", content: `Review: ${code}` }],
max_tokens: 1000,
});
Few-shot CoT examples train the model on the desired reasoning style and depth without fine-tuning.
Self-Consistency: Sample and Vote
Run the same question multiple times and take the majority answer:
async function selfConsistencyCoT(
question: string,
samples = 5
): Promise<{ answer: string; confidence: number }> {
// Generate multiple reasoning chains
const responses = await Promise.all(
Array.from({ length: samples }, () =>
anthropic.messages.create({
model: "claude-sonnet-4-6",
max_tokens: 1000,
messages: [{
role: "user",
content: `${question}\n\nThink step by step and give a definitive answer.`,
}],
temperature: 0.7, // slight variance to get different chains
}).then((r) => r.content[0].text)
)
);
// Extract final answers
const answers = responses.map((r) => {
const match = r.match(/(?:answer|therefore|conclusion|result)[:\s]+([^\n.]+)/i);
return match?.[1]?.trim() ?? r.split("\n").pop() ?? r;
});
// Find majority answer
const counts = answers.reduce((acc, a) => {
acc[a] = (acc[a] ?? 0) + 1;
return acc;
}, {} as Record<string, number>);
const [topAnswer, topCount] = Object.entries(counts).sort((a, b) => b[1] - a[1])[0];
return { answer: topAnswer, confidence: topCount / samples };
}
Self-consistency is expensive (5× token cost) but significantly more reliable for math and logic tasks.
Self-Reflection (Critique and Revise)
Ask the model to critique its own response:
async function withSelfReflection(task: string): Promise<string> {
// Initial response
const initial = await anthropic.messages.create({
model: "claude-sonnet-4-6",
max_tokens: 1500,
messages: [{ role: "user", content: task }],
});
const draft = initial.content[0].text;
// Self-critique
const critique = await anthropic.messages.create({
model: "claude-sonnet-4-6",
max_tokens: 500,
messages: [
{ role: "user", content: task },
{ role: "assistant", content: draft },
{ role: "user", content: "Review your response. What are the weaknesses, errors, or omissions? Be critical." },
],
});
const criticism = critique.content[0].text;
// Skip revision if critique is positive
if (/looks (good|correct|complete)|no (issues|errors|problems)/i.test(criticism)) {
return draft;
}
// Revise
const revised = await anthropic.messages.create({
model: "claude-sonnet-4-6",
max_tokens: 1500,
messages: [
{ role: "user", content: task },
{ role: "assistant", content: draft },
{ role: "user", content: "Review your response. What are the weaknesses, errors, or omissions? Be critical." },
{ role: "assistant", content: criticism },
{ role: "user", content: "Now provide a revised, improved response that addresses these issues." },
],
});
return revised.content[0].text;
}
Extended Thinking (Claude-Specific)
Claude's extended thinking mode enables deep reasoning within the model:
const response = await anthropic.messages.create({
model: "claude-opus-4-8",
max_tokens: 16000,
thinking: {
type: "enabled",
budget_tokens: 10000, // how much thinking the model can do
},
messages: [{ role: "user", content: complexMathProblem }],
});
// Separate thinking from response
const thinkingBlocks = response.content.filter((b) => b.type === "thinking");
const responseBlocks = response.content.filter((b) => b.type === "text");
console.log("Model's reasoning:", thinkingBlocks.map((b) => b.thinking).join("\n"));
console.log("Answer:", responseBlocks.map((b) => b.text).join("\n"));
Extended thinking is significantly better than CoT prompting for: complex math, multi-step proofs, code debugging, and strategic planning. Use it when task complexity warrants the cost (3–5× token overhead).
When to Use Each Technique
| Technique | Best for | Token cost | Latency | |----------|----------|-----------|---------| | Basic CoT | Moderate reasoning tasks | 1.5–2× | +20% | | Structured CoT | Auditable reasoning | 1.5–2× | +20% | | Few-shot CoT | Domain-specific tasks | 2–3× | +30% | | Self-consistency | Math, logic, high-stakes | 5× | +5× | | Self-reflection | Quality-critical text | 3× | +3× | | Extended thinking | Complex problems | 4–10× | +4× |
For most production use cases, structured CoT is the sweet spot — meaningful improvement at modest cost.
FAQ
Does CoT always improve results? Not for simple tasks — asking a model to "think step by step" about "what's 2+2?" wastes tokens. Use CoT when the task requires multi-step reasoning, not for factual retrieval.
Should I show the reasoning to users? For debugging: yes. For user-facing apps: usually no — show only the final answer. Optionally provide a "show reasoning" toggle for power users.
Can I use CoT with structured output?
Yes — generate reasoning in the <reasoning> block, then extract structured data in the <answer> block. This combines the accuracy benefits of CoT with type-safe output.
Is extended thinking worth the cost? For tasks where correctness is critical (math proofs, legal analysis, security review): yes. For routine tasks: use basic CoT. Run an eval to compare quality vs. cost for your specific use case.
How do I prevent CoT from making the model overthink simple questions? Route by complexity: use CoT only for questions classified as "medium" or "hard" difficulty. See Agent Planning & Reasoning for difficulty classification patterns.