Few-Shot, Chain of Thought, Step-by-Step, and Self-Checks; When None of These Work
What problem each of the four most common prompting techniques solves, what it costs, and under what circumstances none of them can save you.
Today's Goals
- Judge, for a given task, whether to give examples, how many, and where examples most often bury a trap
- Explain the difference between chain of thought, step-by-step, and self-checks, and write a usable prompt for each
- Recognize three typical situations where prompting techniques fail, and name what to switch to instead
Yesterday we turned a fuzzy requirement into a work ticket with all four elements filled in. Today we add four techniques on top of that ticket, but the emphasis is not on the techniques themselves — there is no shortage of articles about those. The emphasis is on what each one solves, what it costs, and when you should not reach for it. When you have read the walkthrough and finished the lab, come back to the top of the page and check off the three goals.
Plain-Language Walkthrough
Few-shot examples: showing the model a couple of finished jobs
Recall the new colleague from Day 1, the one who can only be reached through a written ticket. Ask the new colleague to write a status report your team's way: you could describe the format in 300 words, or just staple two well-written past reports behind the ticket and say "match this." Almost everyone picks the second option, because two samples are much harder to misread than 300 words of specification.
Few-shot examples (few-shot) are exactly that move: put a few input-and-output pairs in the prompt, then hand over the real input and let the model continue the pattern. Why it works follows directly from yesterday's mental model — the model is completing text, and an example directly demonstrates what the continuation should look like, far more precisely than any description of format and edge rules. It shines on the rules that are awkward to state in words. Take "when one piece of feedback contains both a bug and a feature request, classify it by whichever the user mentioned first." One example settles that.
In code, the examples can go straight into the user message, or be assembled as alternating user and assistant turns so the model believes it has already answered this way a few times:
import Anthropic from '@anthropic-ai/sdk'
const client = new Anthropic()
// Each example covers a different case; the last one is the edge sample
// (mixed feedback goes by whichever was mentioned first).
const examples: Array<[string, string]> = [
['Tapping my avatar after login does nothing', 'bug'],
['Could you add a dark mode', 'feature'],
['Do you support exporting to PDF', 'question'],
['The list loads slowly, and I would also like to filter by tag', 'bug'],
]
// Assemble alternating user / assistant turns; the model treats them as prior conversation.
const shots = examples.flatMap(([input, label]) => [
{ role: 'user' as const, content: `Feedback: ${input}` },
{ role: 'assistant' as const, content: label },
])
const res = await client.messages.create({
model: 'claude-sonnet-5',
max_tokens: 10,
system:
'Classify user feedback as exactly one of "bug", "feature", or "question". Output only the label. If one piece of feedback covers several, go by whichever was mentioned first.',
messages: [
...shots,
{
role: 'user',
content:
'Feedback: Excel export misaligns columns when a title contains a comma, and could you support CSV while you are at it?',
},
],
})
console.log(res.content[0].type === 'text' ? res.content[0].text : '')from anthropic import Anthropic
client = Anthropic()
# Each example covers a different case; the last one is the edge sample
# (mixed feedback goes by whichever was mentioned first).
examples = [
("Tapping my avatar after login does nothing", "bug"),
("Could you add a dark mode", "feature"),
("Do you support exporting to PDF", "question"),
("The list loads slowly, and I would also like to filter by tag", "bug"),
]
# Assemble alternating user / assistant turns; the model treats them as prior conversation.
shots = []
for text, label in examples:
shots.append({"role": "user", "content": f"Feedback: {text}"})
shots.append({"role": "assistant", "content": label})
res = client.messages.create(
model="claude-sonnet-5",
max_tokens=10,
system='Classify user feedback as exactly one of "bug", "feature", or "question". Output only the label. If one piece of feedback covers several, go by whichever was mentioned first.',
messages=[
*shots,
{
"role": "user",
"content": "Feedback: Excel export misaligns columns when a title contains a comma, and could you support CSV while you are at it?",
},
],
)
print(res.content[0].text)Look at the fourth example. The first three only teach what each of the three labels looks like; the fourth teaches what to do when a case is mixed. It is an edge sample, put there deliberately. If your model keeps getting one particular edge case wrong, the problem is usually not too few examples — it is that none of your examples covers that case.
The traps in examples: order, count, overfitting, and bias
Examples work so well that many people's first instinct is to add more. But every example has a cost, and the cost is not only money.
Count. Every example eats context window and gets billed; five examples may run longer than your task description. More importantly, examples exist to cover distinct kinds of cases, not to pile up volume. Two examples that both show a missing required field teach the model that validation has exactly one kind. Four examples covering four kinds of bad input teach it that validation has four. The count follows from how many cases you need to cover, which is usually two to five.
Overfitting. Give too many, and the model starts imitating their surface features — length, phrasing, even punctuation habits — instead of the rule behind them. If all three of your example outputs happen to be two sentences long, the model will lean toward two sentences, even for an input that needs four.
Bias. This is the sneakiest one. If three of your four classification examples are labeled bug, the model will lean toward bug more or less regardless of the input. Keep the label distribution roughly even across examples, or at the very least know which lean you have introduced.
Order. Most models are more sensitive to the later examples, and especially to the last one. So putting the example most like your real input last, and putting the edge sample last, are both well-founded moves. Conversely, if you notice the output always looks like the final example, order is what you are seeing.
There is one more trap, an extension of yesterday's format box: an example is the strongest format signal you can send, stronger than a description. You write "no pleasantries" in the format box, and then your example output opens with "Sure, here is my analysis" — what the model learned is that a pleasantry comes first. The output half of every example must match the format box word for word: section count, code-block count, language tags. Check that before you ship the prompt.
Chain of thought: make it think before it answers
The colleague does the math in their head vs. writes out each step on the back of the ticket — you can catch the mistake if you can see the steps. Say "just tell me the total" and they compute it silently, and if they slip, you will never spot it. Say "write each step on the back of the ticket" and every line has to face the line above it, which makes a slip less likely and, when it happens, immediately visible. Chain of thought is that same request to the model: ask it to lay out its reasoning step by step and only then give the answer.
Why it works ties back to completion again. The model generates token by token, and the intermediate results it writes down become context for everything after, so each step supplies checking material for the next. When it jumps straight to a conclusion, those intermediate results flash by unwritten and never get a chance to be checked.
That makes it clear which tasks it helps: multi-step arithmetic, judgments that require ruling out distractors, decisions that trade off several conditions. Conversely, it adds almost nothing on single-step tasks. Classification, extraction, format conversion — anything where one look gives you the answer — only gets slower, more expensive, and longer if you make the model write three paragraphs of analysis first, because that last step still faces the same single judgment.
Do the arithmetic on the cost: the output goes from one number to seven or eight lines, and both latency and price scale with output length. That is money well spent on a task where a wrong answer is hard to notice, and wasted on a task where a wrong answer is obvious at a glance. There is one more easily overlooked boundary: what the chain of thought writes down is a plausible-looking process, not the model's actual internal computation. It can help you find errors, but it is not evidence that the model really computed things that way.
One usage detail matters: require the model to put the final answer in a fixed position and a fixed format, such as "on its own final line, formatted as Answer: X dollars." Otherwise your program has to hunt for the answer inside a wall of reasoning, which is worse than parsing free text. Reasoning-oriented models from the past couple of years have a thinking process built in, so in most cases you no longer need to write "think step by step" — but you still have to specify the answer's format and position.
Step-by-step and self-checks: several tickets, then a review pass
Hand the new colleague one ticket that says "review this code, fix every problem, add tests, and write a README." What happens? The review is thorough, the fixes are decent, the tests get sloppy, and the README is three lines. They are not slacking. One ticket holds only so much, and four jobs split across it leave none of them enough room. Models behave identically: one response has a length ceiling, and the more tasks you cram in, the smaller the output budget each task gets — detailed at the front, rough at the back.
Step-by-step (multi-call) solves that by splitting it into four separate tickets, four separate hand-offs — each with its own budget, and each one can hand its output to the next. Call one reviews and outputs only a problem list. Call two takes that list and fixes only the severe items. Call three writes a test for each place that changed. Call four writes the documentation. Every step has its own output budget and its own format requirements, and a later step can take an earlier step's output as its input — something a single call cannot do. That is exactly where it differs from chain of thought: chain of thought makes the model think in more detail inside one call, on the same output budget; step-by-step means several calls, so the budget multiplies and intermediate results can travel. The test to apply is whether your task is failing because the thinking is too shallow, or because it does not fit in one response.
import Anthropic from '@anthropic-ai/sdk'
const client = new Anthropic()
async function ask(system: string, user: string): Promise<string> {
const res = await client.messages.create({
model: 'claude-sonnet-5',
max_tokens: 4096,
system,
messages: [{ role: 'user', content: user }],
})
return res.content[0].type === 'text' ? res.content[0].text : ''
}
// Step one only reviews: its own output budget, its own format.
const issues = await ask(
'You are a code reviewer. Output only a problem list, one per line as "location: problem: severity", sorted by severity descending. Do not change the code.',
`Review this TODO API:\n${code}`
)
// Step two takes step one's output as input: the essential difference from chain of thought.
const fixed = await ask(
'You are a backend engineer. Fix only the high-severity items on the list. Output the full revised code in one code block, with no commentary.',
`Problem list:\n${issues}\n\nOriginal code:\n${code}`
)
console.log(fixed)from anthropic import Anthropic
client = Anthropic()
def ask(system: str, user: str) -> str:
res = client.messages.create(
model="claude-sonnet-5",
max_tokens=4096,
system=system,
messages=[{"role": "user", "content": user}],
)
return res.content[0].text
# Step one only reviews: its own output budget, its own format.
issues = ask(
'You are a code reviewer. Output only a problem list, one per line as "location: problem: severity", sorted by severity descending. Do not change the code.',
f"Review this TODO API:\n{code}",
)
# Step two takes step one's output as input: the essential difference from chain of thought.
fixed = ask(
"You are a backend engineer. Fix only the high-severity items on the list. Output the full revised code in one code block, with no commentary.",
f"Problem list:\n{issues}\n\nOriginal code:\n{code}",
)
print(fixed)Step-by-step costs you round-trip latency, plus the work of carrying context between calls in your own code. That is precisely what Agent-style tooling automates — handing the splitting, the passing, and the choice of next step to a loop. For today, split by hand, so you can feel where each step's boundary sits.
Self-checks address a different failure. The model invents a plausible helper-function name while writing code and never looks back. A self-check writes the looking back into the task: "when you are done, go through every function name, variable name, and import path referenced in the tests and confirm it genuinely exists in the code given to you; change or remove any that do not, and say which." Two things make it work. First, name what to check — "check for problems" does nothing, "verify one by one that each reference exists" does something. Second, require the check's results as output, for example a reference-audit list, which turns the self-check from a verbal promise into a verifiable artifact. The self-check can itself be wrong, so the real backstop is still running the tests; the self-check only pushes the error rate down.
When none of these work
Now the most important part of the day, and the part almost no tutorial covers: some tasks cannot be done correctly no matter how well the prompt is written. Recognizing them is worth more than any technique, because it saves you hours of thrashing on prompt wording. There are three failure modes.
Missing knowledge. You ask the model whether the version of the validation library in your project has a known vulnerability. Its knowledge has a cutoff date, and it knows nothing about anything disclosed afterward. Your private data and your company's internal conventions are equally invisible. Worse, it may produce a vulnerability identifier in a perfectly correct format that does not exist — the stricter the format, the more convincing the invention looks. The test is one sentence: if this information first appeared yesterday, could the model possibly know it? If not, do not expect a prompt to fix it. The fix lives outside the model: paste the material into the context, or wire up a retrieval system so it can look things up on demand — which is the subject of this platform's RAG course.
Missing tools. The task requires acting on or querying the outside world: running a command, hitting a database, sending a request, reading a file. The model can only produce text, so anything a person who can only type cannot do, it cannot do either. That is the test verbatim: could a person who can only type, and cannot touch anything, complete this? If not, the model needs tools — let it output which tool to call with which arguments, have your code execute that, and feed the result back. That is tool calling, and this platform's MCP course is devoted to it.
The task itself is written wrong. The requirement contradicts itself ("do not change the endpoint, but make the response paginated"), or what you asked for is not what you want (you said "optimize performance" and what you meant was "stop timing out"). The test: would two people reading this requirement do opposite things? If yes, go fix the requirement — no prompt change will help.
The three modes share one property: none of their fixes lives at the prompt layer. That is why yesterday framed prompt engineering as systematically supplying missing context — when what is missing is not context but knowledge, tools, or a coherent requirement, the discipline has reached its boundary. There is a cheap habit for evaluation: put a few samples in your test set that the model could not possibly know, and watch whether it honestly says it does not know or manufactures a respectable-looking answer. You will use that when D4 builds a test set.
Upgrading the running example's prompt one version
Back to the task that runs through the whole course: add input validation and unit tests to the TODO API. Yesterday's ticket already has the four elements, so run it past today's ordered questions. Are examples missing? Yes — the three-section output is described in words, but the model still occasionally slips a transition sentence between sections, and two examples written strictly to the format box suppress that. The two examples should cover different kinds of bad input, say a missing required field and an invalid date, with their output halves matching the format box word for word. Is chain of thought needed? No — writing validation and tests is not multi-step reasoning, and adding it only lengthens the output. Does it fit in one response? Two jobs, four validation cases, five tests: it is close to the ceiling but it fits. If you see the test half getting sloppy, split it into two steps. Are the same factual errors recurring? Yes — the tests occasionally reference a helper function that does not exist, so add a self-check that names the references to verify and asks for the audit list as output.
The upgraded ticket is twice as long as yesterday's, and every added paragraph answers a problem you actually observed. That is the correct use of technique: observe what the output got wrong, then pick the matching technique, rather than listing the techniques first and hunting for somewhere to apply them. Tomorrow we push the ticket's format box to its limit — turning the output into structured data a program can read directly.
Source Reading
Hands-On Lab
Today's lab is document-shaped again. Open labs/prompt-engineering-5days/day-02-before-after-rewrites and do not look at the solution first:
- Read the five "before" prompts in
starter/rewrites.md. For each, decide first whether what it lacks is examples, reasoning room, splitting, or a review pass, and write that decision into the technique column. - Write the "after" for each case using exactly one primary technique, then add one or two sentences on why another technique would not work here.
- After rewriting case five, run it both before and after, confirm it still gets the task wrong, and then write down which failure mode it belongs to and the fix that lives outside the prompt.
- Fill in part two: add two few-shot examples for the running example task, then check them against D1's format box for section count, code-block count, and language tags.
- Only once everything is filled in, open
solution/rewrites.mdand compare. A different choice of technique is not necessarily wrong — check whether your reasoning holds up.
Interview Questions
Today's three questions are in the question bank below, covering the cost of few-shot examples, the difference between chain of thought and step-by-step, and the boundary where prompting fails. Every analysis gives you a reusable test sentence; walk the derivation yourself before reading the answer points.
Checklist and Tomorrow
- Judge, for a given task, whether to give examples, how many, and where examples most often bury a trap
- Explain the difference between chain of thought, step-by-step, and self-checks, and write a usable prompt for each
- Recognize three typical situations where prompting techniques fail, and name what to switch to instead
- All 5 acceptance criteria of the five rewrites pass
- You can answer at least 2 of the 3 interview questions without looking at the points
Tomorrow (D3) we push the ticket's format box to the limit: instead of prose for a human to read, the model outputs structured data a program can consume directly. You will see why the sentence "please output JSON" is not enough, what a schema actually constrains, which limits each provider's structured-output switch carries, and how to fall back when parsing fails. D3 is also this course's first runnable lab — a script that extracts fixed fields out of a requirement description, with an offline mock mode so you can do it without an API key.
Interview questions
Why does few-shot prompting work, what goes wrong when you give too many examples, and how do you decide how many to include?few-shot 为什么有效?示例给多了会出什么问题?你怎么决定给几个?
Common in ChinaCommon overseasBasic#few-shot#prompt-techniquesHow to reason about it · think before answering
- The screen is whether you treat examples as signals for format and boundaries rather than as magic that makes the model smarter. 'More examples, better model' reads as untested.
- Mechanism first: the model completes text, and examples show the continuation directly, which is harder to misread than prose describing a format or an edge rule. Examples are the strongest format signal.
- Then the cost: each example consumes context and money; too many cause overfitting to surface features such as length, wording and order, and amplify accidental bias — three bug examples out of four nudges everything toward bug.
- Conclusion: the count follows the number of distinct cases you need to cover, typically two to five, each a different case, with at least one boundary sample.
- Follow-ups: does order matter? Yes, models weight the last example more, so place the one closest to the target input last. And if examples contradict the instructions, the model usually follows the examples, so they must match the format spec exactly.
分析过程 · 先想清楚再作答
- 这题在筛「有没有把示例当成格式与边界的信号,而不是当成让模型变聪明的魔法」。答成「示例越多模型越懂」会暴露没在生产里调过提示词。
- 拆法:先答原理——模型在补全,示例直接展示了「下文该长什么样」,比文字描述格式和边界规则更不容易被误读;示例是最强的格式信号。
- 再答代价:每个示例都占上下文与费用;示例过多会让模型过拟合示例的表面特征(长度、措辞、顺序),还会把示例里无意带进去的偏见放大,比如四个示例里三个是 bug,它就更倾向判 bug。
- 结论:数量由「要覆盖几种类型」决定而不是越多越好,通常两到五个,每个覆盖一种不同的情况,并且至少一个是边界样本。
- 可预期的追问:示例的顺序有影响吗?有,多数模型对最后一个示例更敏感,所以把最像目标输入的放最后;另一个追问是示例和说明冲突时模型听谁的,答案是多半听示例,所以示例必须与格式栏逐字一致。
Key points
- Examples show the continuation directly, which beats prose for conveying format and edge rules
- Too many examples cost context and money, overfit surface features, and amplify class bias
- Pick the count by how many distinct cases need coverage, typically two to five with one boundary case
- Order matters — put the closest match last; when examples and instructions conflict the model follows the examples
答题要点
- 示例直接展示下文该长什么样,比文字描述格式和边界规则更不容易被误读
- 示例过多的代价:占上下文与费用、过拟合表面特征、放大示例里的类别偏见
- 数量按「要覆盖几种不同情况」定,通常两到五个,至少一个边界样本
- 顺序有影响,最像目标输入的放最后;示例与说明冲突时模型多半听示例
Where does chain-of-thought prompting help most, where is it a waste, and how does it differ from splitting a task into steps?思维链在什么任务上提升明显,在什么任务上是浪费?它和「分步」有什么区别?
Common in ChinaCommon overseasIntermediate#chain-of-thought#prompt-techniquesHow to reason about it · think before answering
- The discriminators are 'waste' and 'difference'. Anyone can say thinking first helps; the interviewer wants to hear when you deliberately skip it.
- Its value comes from intermediate results checking the next step, so it shines on multi-step reasoning, arithmetic and judgments with distractors; on single-step tasks such as classification, extraction or format conversion it adds latency, cost and length with little gain.
- Difference: chain-of-thought keeps everything in one call and one output budget; splitting uses several calls, each with its own budget and format, and later steps can consume earlier outputs. Use CoT when the model thinks too shallowly, split when one answer cannot hold the work.
- Conclusion: ask whether the task needs multi-step reasoning at all; if yes, ask whether one output can hold it — CoT if so, split if not.
- Follow-ups: with reasoning models that think internally, do you still write 'think step by step'? Usually no, but you still pin the answer format and position. And can you trust the written reasoning? It is a plausible narrative, not the actual computation — use it as a check, not as proof.
分析过程 · 先想清楚再作答
- 题眼在「浪费」和「区别」。只会说「让模型先思考再回答效果更好」的候选人没有算过账,面试官想听的是你什么时候会主动不用它。
- 拆法:思维链的价值来自「中间结果可以校验下一步」,所以它在多步推理、算术、需要排除干扰项的判断上提升明显;在单步判断(分类、抽取、格式转换)上几乎没有增益,只有更慢更贵更长的输出。
- 区别:思维链是一次调用内让模型写出中间过程,输出预算还是同一份;分步是拆成多次调用,每步有独立的预算、独立的格式、并且后一步可以拿前一步的输出做输入。任务是「想得不够细」用思维链,任务是「一次装不下」用分步。
- 结论:先问任务是否需要多步推理,不需要就不用;需要的话再问一次输出装不装得下,装得下用思维链,装不下拆步。
- 追问:推理类模型内置了思考过程,还要写思维链吗?多数情况不用再写「一步步想」,但仍要指定最终答案的格式与位置,否则解析会很痛苦;另一个追问是思维链的内容能不能信,答案是它是「看起来合理的过程」而非真实的内部计算,只能当辅助校验不能当证据。
Key points
- CoT helps because intermediate results check the next step; strong on multi-step reasoning and arithmetic, wasted on single-step judgments
- Cost is longer output, higher latency and spend, so skip it when no reasoning is needed
- Versus splitting: CoT stays in one call with one budget; splitting uses multiple calls with independent budgets that can chain outputs
- With reasoning models you rarely need 'think step by step' but still pin the answer format and location
答题要点
- 思维链的价值是中间结果校验下一步,多步推理与算术上提升明显,单步判断上是浪费
- 代价是更长的输出、更高的延迟与费用,所以不需要推理的任务要主动不用
- 与分步的区别:思维链是一次调用内写过程,输出预算不变;分步是多次调用,每步独立预算且可传递输出
- 推理模型内置思考后一般不必再写「一步步想」,但仍要指定答案格式与位置
What are the signs of a task that no amount of prompt engineering will fix, and what do you do when you hit one?提示词写得再好也做不对的任务有哪些特征?遇到这类任务你会怎么办?
Common in ChinaCommon overseasIntermediate#prompt-limits#failure-modesHow to reason about it · think before answering
- This tests whether you know where prompting ends. Piling techniques onto a hopeless task signals poor judgment; saying 'this is not a prompting problem' signals maturity.
- Three failure classes. Missing knowledge: the fact postdates training or lives in your private data, and the model may fabricate a well-formatted answer. Missing tools: the task needs an action or query against the world. Wrong task: the requirement is contradictory or you actually want something else.
- One test each: could the model plausibly know something that appeared yesterday? Could a person who can only type complete this? Would two readers of the requirement do opposite things?
- Conclusion: paste the material or add retrieval for missing knowledge; add tool use or compute in code for missing tools; fix the requirement for a wrong task. None of these is a prompt change.
- Follow-ups: stricter formats make fabrications look more credible — require sources or verifiable identifiers and validate in code. And to catch these early, seed the test set with a few unknowable items and check that the model admits it does not know.
分析过程 · 先想清楚再作答
- 这题考的是「知道提示词的边界在哪」。一直往提示词上堆技巧的候选人会被判为缺乏判断力;能说出「这题不该用提示词解」才是成熟的信号。
- 拆法:把失效分三类。缺知识——信息在模型训练截止之后或本来就在你的私有数据里,模型不可能知道,还可能编出格式正确的假答案;缺工具——任务需要对外部世界做动作或查询(跑命令、查库、发请求),文字生成做不到;任务写错——需求本身自相矛盾或者你要的其实是另一件事。
- 每类给一个判据:缺知识问「这信息是昨天才出现的,模型有可能知道吗」;缺工具问「一个只能打字的人能完成这件事吗」;任务写错问「两个人读这个需求会不会得出相反的做法」。
- 结论:缺知识就把资料贴进上下文或接检索;缺工具就接工具调用或在代码里做完再让模型解读;任务写错回去改需求。三种都不是提示词层面的解法。
- 追问几乎必然是「格式越严格假答案越像真的怎么办」——答案是对事实类输出要求带出处或可验证的标识,并在代码里校验;以及「怎么在评估里提前发现这类任务」,答案是测试集里放几条模型不可能知道的样本,看它是否老实说不知道。
Key points
- Three failure classes: missing knowledge, missing tools, and a wrongly specified task
- Missing knowledge is dangerous because the model fabricates well-formatted answers, and stricter formats make them more convincing
- Fixes live outside the prompt: paste material or add retrieval, add tool use or compute in code, or fix the requirement
- Seed the test set with unknowable items to check the model admits ignorance
答题要点
- 三类失效:缺知识(截止日期之后或私有数据)、缺工具(需要对外部世界做动作)、任务写错(需求自相矛盾)
- 缺知识的危险在于模型会编出格式正确的假答案,格式越严越像真的
- 解法都在提示词之外:贴资料或接检索、接工具调用或代码先算、回去改需求
- 测试集里放几条模型不可能知道的样本,检查它会不会老实说不知道
Comments
Sign in to join the discussion
No comments yet — be the first.