Prompt and Context Engineering Basics
9 examples to get you started with Prompt and Context Engineering - 6 basic and 3 intermediate.
Search across all documentation pages
9 examples to get you started with Prompt and Context Engineering - 6 basic and 3 intermediate.
pip install anthropic.export ANTHROPIC_API_KEY=sk-ant-....client.messages.create(...) from the anthropic Python package.The starting point most teams accidentally ship: pasting an entire file when only a few lines matter.
import anthropic
client = anthropic.Anthropic()
with open("user_service.py") as f:
entire_file = f.read() # could be 2,000+ lines
response = client.messages.create(
model="claude-sonnet-5",
max_tokens=300,
messages=[{
"role": "user",
"content": f"Does this function validate the email format?\n\n{entire_file}",
}],
)entire_file is billed as an input token on every call.The same question, answered by sending only the function in question.
import anthropic
client = anthropic.Anthropic()
relevant_function = '''
def validate_email(address: str) -> bool:
return "@" in address and "." in address.split("@")[-1]
'''
response = client.messages.create(
model="claude-sonnet-5",
max_tokens=300,
messages=[{
"role": "user",
"content": f"Does this function validate the email format?\n\n{relevant_function}",
}],
)Before trusting that trimming actually helped, measure it.
import anthropic
client = anthropic.Anthropic()
def count_tokens(text: str, model: str = "claude-sonnet-5") -> int:
result = client.messages.count_tokens(
model=model,
messages=[{"role": "user", "content": text}],
)
return result.input_tokens
full_file_tokens = count_tokens(open("user_service.py").read())
snippet_tokens = count_tokens(relevant_function)
print(f"Full file: {full_file_tokens} tokens")
print(f"Snippet: {snippet_tokens} tokens")count_tokens reports input tokens without generating a completion, so measuring is nearly free.Boilerplate instructions repeated in every call waste tokens just as much as an oversized document.
SYSTEM_PROMPT = "You are a code reviewer. Answer in one short paragraph."
def review(snippet: str, question: str) -> str:
response = client.messages.create(
model="claude-sonnet-5",
max_tokens=200,
system=SYSTEM_PROMPT,
messages=[{"role": "user", "content": f"{question}\n\n{snippet}"}],
)
return response.content[0].textsnippet, question) is the only thing that changes per call.In a multi-turn chat, old turns that no longer matter still cost tokens if you keep resending them.
def trimmed_history(messages: list[dict], keep_last: int = 4) -> list[dict]:
# Keep only the most recent turns; drop everything older.
return messages[-keep_last:]
full_history = [
{"role": "user", "content": "What does this repo do?"},
{"role": "assistant", "content": "It's a billing service..."},
{"role": "user", "content": "Add a retry to the webhook handler."},
{"role": "assistant", "content": "Here's the updated function..."},
{"role": "user", "content": "Now add a unit test for it."},
]
response = client.messages.create(
model="claude-sonnet-5",
max_tokens=400,
messages=trimmed_history(full_history, keep_last=4),
)messages is resent as input tokens on the next call, so history grows costlier the longer a conversation runs.trimmed_history is a blunt tool: it keeps recency, not relevance, so use it when older turns genuinely stop mattering.The same discipline applies to JSON or database rows, not just source files.
raw_record = {
"id": "usr_9f2",
"email": "a@example.com",
"created_at": "2024-01-02T00:00:00Z",
"internal_flags": {"beta": True, "region_shard": "us-east-2"},
"last_login_ip": "10.0.4.12",
"support_notes": "..." * 500, # long free-text field
}
def trim_for_prompt(record: dict) -> dict:
return {k: record[k] for k in ("id", "email", "created_at")}
response = client.messages.create(
model="claude-sonnet-5",
max_tokens=100,
messages=[{
"role": "user",
"content": f"Summarize this account: {trim_for_prompt(raw_record)}",
}],
)trim_for_prompt is an explicit allowlist, which is safer than an ad hoc denylist that silently keeps new fields added later.support_notes are exactly the kind of thing that quietly inflates token counts.Instead of manually picking a snippet, rank candidates by relevance and keep only the top few.
import anthropic
client = anthropic.Anthropic()
def select_relevant_snippets(question: str, candidates: list[str], keep: int = 2) -> list[str]:
# A cheap relevance pass: ask a fast model to score each candidate.
scored = []
for snippet in candidates:
response = client.messages.create(
model="claude-haiku-4-5",
max_tokens=5,
messages=[{
"role": "user",
"content": f"Question: {question}\nSnippet:\n{snippet}\n\nRelevance 0-10, number only:",
}],
)
score = int(response.content[0].text.strip() or 0)
scored.append((score, snippet))
scored.sort(reverse=True, key=lambda pair: pair[0])
return [snippet for _, snippet in scored[:keep]]
top_snippets = select_relevant_snippets(
"How is a failed payment retried?",
candidates=[billing_module_src, auth_module_src, retry_queue_src],
keep=2,
)A context window budget caps how much can be sent, and trimming is how you stay under it.
MAX_CONTEXT_TOKENS = 4000
def build_prompt_within_budget(question: str, snippets: list[str]) -> str:
prompt = question
used = count_tokens(prompt)
for snippet in snippets:
snippet_tokens = count_tokens(snippet)
if used + snippet_tokens > MAX_CONTEXT_TOKENS:
break # stop adding snippets once the budget is hit
prompt += f"\n\n{snippet}"
used += snippet_tokens
return prompt
final_prompt = build_prompt_within_budget(
"Summarize the recent changes to the retry logic.",
snippets=[retry_queue_diff, billing_module_diff, changelog_excerpt],
)MAX_CONTEXT_TOKENS turns "keep the prompt small" into a rule the code enforces, not a habit engineers have to remember.Put the earlier pieces together and report the actual cost impact of trimming.
def compare_strategies(question: str, full_document: str, trimmed_snippet: str) -> dict:
return {
"full_document_tokens": count_tokens(f"{question}\n\n{full_document}"),
"trimmed_tokens": count_tokens(f"{question}\n\n{trimmed_snippet}"),
}
result = compare_strategies(
"Does this file handle retries correctly?",
full_document=open("billing_service.py").read(),
trimmed_snippet=relevant_function,
)
savings_pct = 100 * (1 - result["trimmed_tokens"] / result["full_document_tokens"])
print(f"Token reduction: {savings_pct:.0f}%")count_tokens call from example 3, applied at the pipeline level instead of one prompt at a time.Stack versions: Written against the Claude model lineup current as of ~June 2026 - Claude Fable 5, Claude Opus 4.8, Claude Sonnet 5 (the default), and Claude Haiku 4.5 - and the official
anthropicPython SDK (latest 0.x release). Model names, pricing, and SDK versions move quickly - verify current specifics at platform.claude.com/docs before relying on them.
Reviewed by Chris St. John·Last updated Jul 19, 2026