Add Context to Prompts
What Context Is
Context is the background information Claude needs to produce the answer you want, rather than a generic one. It answers questions such as:
- Who is this for?
- Why is it needed, and what will it be used for?
- What facts, documents, or data should the answer be based on?
- What constraints or conventions apply?
Claude knows a great deal in general, but nothing about your specific situation: your users, your product, your codebase, your policies, or today's events. Anthropic's guidance compares Claude to a brilliant but new team member who lacks context on your norms and workflows. Context closes that gap.
Without context: Write a welcome message.
With context: Write a welcome message for students who just enrolled in our free
8-week Python internship program. Most are final-year college students
in India. The message will be sent by email, so it must work as plain text.
Mention that the first live session is on Monday at 7 PM IST.
The second prompt produces a message that fits the actual audience, channel, and facts. The first can only produce a guess.
Why Context Improves Answers
| Without context, Claude must… | With context, Claude can… |
|---|---|
| Guess the audience's level | Pitch explanations at the right level |
| Use general conventions | Follow your conventions |
| Invent plausible details | Use your actual facts |
| Choose an arbitrary format | Match the destination (email, chat, documentation) |
| Interpret instructions literally | Understand the goal and adapt sensibly |
The last row is important. When you explain why, Claude can generalize. For example, telling Claude that output will be read aloud by a text-to-speech system lets it avoid many things that do not work in speech, not just the one thing you thought to mention.
Types of Context
| Type | Examples | Where to put it |
|---|---|---|
| Purpose | "This summary will be used by managers to decide whether to read the full report." | System prompt or user message |
| Audience | "Readers are beginner programmers." | System prompt |
| Domain knowledge | Product details, policies, course structure | System prompt, or retrieved per request |
| Reference material | Documents, articles, code, transcripts | User message, in tags, before the question |
| Situation | Today's date, the user's plan, recent activity | End of the system prompt, or the user message |
| Constraints | "Must work in Python 3.10", "Under 280 characters for social media" | With the task |
Adding Purpose and Audience
These are the cheapest and most effective additions, often one sentence each:
system = (
"You explain programming concepts to final-year college students preparing for "
"technical interviews. They know basic Python but have not studied algorithms formally. "
"Your explanations will be used as revision notes, so favor clear structure and short examples."
)
Why it works: "preparing for technical interviews" tells Claude what matters (common questions, complexity, trade-offs). "Know basic Python" sets the level. "Revision notes" defines the format: structured, concise, and example-based.
Adding Documents and Data
When Claude should base its answer on specific material, include the material in the prompt. Three guidelines matter.
1. Wrap Each Document in Tags with Metadata
For multiple documents, a consistent structure helps Claude keep them apart and cite them:
documents_xml = """<documents>
<document index="1">
<source>refund_policy.md</source>
<document_content>
Refunds are available within 14 days of purchase if less than 30% of the course is completed.
</document_content>
</document>
<document index="2">
<source>certificates.md</source>
<document_content>
Certificates are issued within 24 hours after the final quiz is passed.
</document_content>
</document>
</documents>"""
The <source> tag tells Claude where each piece came from, which is useful when you ask it to cite sources. The index attribute gives each document a short reference.
2. Put Long Material First, the Question Last
Anthropic's guidance for long inputs is to place long documents and data near the top of the prompt, above the instructions and the question. Queries at the end tend to produce better answers, especially with large inputs.
user_content = f"""{documents_xml}
Using only the documents above, answer the student's question.
<question>Can I get a refund? I bought the course 10 days ago and finished 40% of it.</question>"""
3. Ask Claude to Ground Its Answer
For document-based tasks, especially long ones, ask Claude to identify the relevant passages before answering. Quoting first focuses attention on the material that matters:
First, quote the sentences from the documents that are relevant to the question inside
<quotes> tags. Then answer the question inside <answer> tags, based only on those quotes.
If the documents do not contain the answer, say so.
Why grounding helps: the answer is tied to specific evidence, which reduces answers based on assumptions. The quotes also let you, or your code, verify where the answer came from. Lesson 157 covers the API's built-in citations feature.
Adding Situational Context
Claude does not know the current date, the user's account status, or what happened recently unless you provide it:
from datetime import date
situation = f"""<situation>
Today's date: {date.today().isoformat()}
Student plan: Free
Courses completed: Python Basics
</situation>"""
Place changing details like these at the end of the system prompt or in the user message. That keeps the stable part of the prompt identical across requests, which suits prompt caching (Lesson 32).
How Much Context Is Enough?
More context is not always better:
| Too little | Right amount | Too much |
|---|---|---|
| Generic or wrong answers | Answers fit the situation | Higher cost and latency |
| Invented details | Answers based on your facts | Relevant facts buried among irrelevant ones |
| Wrong format or level | Correct format and level | Conflicting information confuses the answer |
Guideline: include what a knowledgeable new colleague would need for this particular task. For large knowledge bases, do not send everything. Select the relevant parts for each request, as shown in the practical example.
Practical Example: Selecting Context from a Knowledge Base
Real applications often have more reference material than should be sent with every request. This example keeps a small knowledge base, selects the most relevant entries for each question using simple keyword matching, and sends only those as context. This is the basic idea behind retrieval-augmented generation (RAG).
"""context_answers.py: answer questions using selected knowledge-base context."""
import re
from anthropic import Anthropic
from anthropic.types import Message
KNOWLEDGE_BASE = [
("refund_policy.md", "Refunds are available within 14 days of purchase if less than 30% of the course is completed."),
("certificates.md", "Certificates are issued within 24 hours after the final quiz is passed. They can be downloaded from the Profile page."),
("live_sessions.md", "Live sessions run every Monday and Thursday at 7 PM IST. Recordings are available within 2 hours."),
("password_reset.md", "To reset a password, use 'Forgot password' on the login page. The reset link expires after 30 minutes."),
("internship.md", "The free internship program lasts 8 weeks. Interns must submit a project every 2 weeks."),
]
SYSTEM = (
"You are the support assistant for an online Python course platform. "
"Answer only from the provided documents. If they do not contain the answer, say you do not know "
"and suggest contacting support@example.com. Keep answers under 80 words."
)
STOPWORDS = {"the", "a", "an", "is", "are", "i", "my", "to", "of", "and", "can", "how", "do", "what", "when", "for", "in", "it"}
MIN_SCORE = 2 # A document must share at least this many meaningful words with the question
def words(text: str) -> set[str]:
return {w for w in re.findall(r"[a-z0-9]+", text.lower()) if w not in STOPWORDS}
def select_context(question: str, limit: int = 2) -> list[tuple[str, str]]:
question_words = words(question)
scored = [(len(question_words & words(f"{source} {content}")), source, content)
for source, content in KNOWLEDGE_BASE]
scored.sort(reverse=True)
return [(source, content) for score, source, content in scored[:limit] if score >= MIN_SCORE]
def build_prompt(question: str) -> str:
docs = select_context(question)
rendered = "\n".join(
f' <document index="{i}">\n <source>{source}</source>\n'
f" <document_content>{content}</document_content>\n </document>"
for i, (source, content) in enumerate(docs, start=1)
)
return f"<documents>\n{rendered}\n</documents>\n\nAnswer the question using the documents above.\n<question>{question}</question>"
def get_text(message: Message) -> str:
return "".join(block.text for block in message.content if block.type == "text").strip()
if __name__ == "__main__":
client = Anthropic()
for question in [
"When are the live sessions, and can I watch recordings?",
"Can I get a refund after 10 days if I finished 40% of the course?",
"Do you offer a Java course?",
]:
prompt = build_prompt(question)
reply = client.messages.create(
model="claude-opus-5-5", max_tokens=1024, system=SYSTEM,
messages=[{"role": "user", "content": prompt}],
)
sources = [source for source, _ in select_context(question)]
print(f"Q: {question}\n context: {sources or 'none'}\nA: {get_text(reply)}\n")
Example output (wording varies):
Q: When are the live sessions, and can I watch recordings?
context: ['live_sessions.md']
A: Live sessions run every Monday and Thursday at 7 PM IST, and recordings are available within 2 hours.
Q: Can I get a refund after 10 days if I finished 40% of the course?
context: ['refund_policy.md']
A: Unfortunately not. Refunds require purchase within 14 days and less than 30% completion; you've completed 40%. For other options, contact support@example.com.
Q: Do you offer a Java course?
context: none
A: I don't know. The information I have doesn't mention a Java course. Please contact support@example.com.
How It Works
words()lowercases the text, extracts words with a regular expression, and removes common stopwords that carry little meaning, such as "the" or "how".select_context()scores each document by how many meaningful words it shares with the question, using set intersection (&). It sorts the documents from highest to lowest score and keeps up tolimitof them.MIN_SCOREfilters out weak matches. Without it, a single shared common word, such as "after" or "course", would be enough to include an unrelated document. For example, the refund question shares only the word "after" withpassword_reset.md, and the Java question shares only "course" withrefund_policy.md. Requiring at least two shared words keeps only genuinely relevant documents.build_prompt()renders the selected documents in the recommended<documents>structure, with the documents first and the question last.- The system prompt requires answers from the documents only, and defines what to do when the answer is missing. The Java question shows this working: with no relevant context, Claude says it does not know instead of inventing a course.
- Only relevant context is sent, which keeps requests small and focused even if the knowledge base grows to hundreds of entries.
Note: Keyword matching is simple and works for small knowledge bases, but a fixed threshold like
MIN_SCOREneeds tuning, and it misses passages that use different words with the same meaning. Production systems usually use embeddings and vector search to find relevant passages by meaning rather than exact words. The prompt structure stays the same.
Common Mistakes
Assuming Claude Knows Your Situation
What happens: answers follow general practice instead of your product, policies, or audience.
Fix: state the purpose, audience, and relevant facts.
Putting the Question Before Long Documents
What happens: answer quality drops on large inputs.
Fix: place long material first and the question last.
Mixing Documents Without Structure
What happens: Claude confuses which information came from which source.
Fix: wrap each document in tags with its source.
Sending Everything "Just in Case"
What happens: higher cost and latency, and relevant facts are diluted by irrelevant ones.
Fix: select the context that matters for each request, and filter out weak matches.
Not Defining Behavior When Context Is Missing
What happens: Claude fills gaps with plausible but unsupported answers.
Fix: instruct Claude to say when the provided material does not contain the answer.
Best Practices
- State purpose and audience in every important prompt; one sentence each is often enough.
- Explain why constraints exist, so Claude can generalize.
- Structure reference material in tags with source metadata.
- Put long documents first and questions last.
- Ask for quotes before answers in document-heavy tasks.
- Provide situational facts such as the date and user details, placed at the end.
- Select relevant context per request instead of sending entire knowledge bases.
- Define the fallback when the answer is not in the context.
Exercise
- Write a prompt, without context, asking Claude to "write release notes", and then a version with purpose, audience, and three real changes. Compare the outputs.
- Add two entries to
KNOWLEDGE_BASEand test questions that should select them. Try changingMIN_SCOREto1and observe which extra documents are selected. - Modify
build_prompt()to ask Claude to quote relevant sentences in<quotes>tags before answering in<answer>tags. Then extract and print only the<answer>part. - Explain why the Java question correctly produced "I don't know" and what would happen without the system prompt's instruction.
Step 2: with MIN_SCORE = 1, the refund question also selects password_reset.md (both contain "after"), and the Java question selects refund_policy.md (both contain "course"). The extra documents are harmless here, but in larger knowledge bases weak matches add cost and can distract from the relevant material.
Step 3: change the instruction in build_prompt():
instruction = (
"First, quote the relevant sentences from the documents above inside <quotes> tags. "
"Then answer the question inside <answer> tags, using only those quotes."
)
Extract the answer with a regular expression:
import re
match = re.search(r"<answer>(.*?)</answer>", get_text(reply), re.DOTALL)
answer = match.group(1).strip() if match else get_text(reply)
re.DOTALL lets . match line breaks, so multi-line answers are captured. The fallback returns the full text if the tags are missing.
Step 4: no document shares enough words with the Java question to pass MIN_SCORE, so select_context() returns nothing, and the system prompt tells Claude to answer only from the documents and to say when it does not know. Without that instruction, Claude might answer from general knowledge, for example by describing what a Java course typically includes. Users could then believe the platform offers a course that does not exist.