Claude is most useful when it handles the part of a workflow that is hard to express with ordinary rules: reading messy language, noticing intent, and suggesting a next step. It is much less useful when it is asked to own every decision. In this first lesson, it will sort one support ticket into a small set of queues. Your application will still decide what happens next.
That is a good first AI feature because it is narrow, observable, and easy to improve. You can read the ticket, see the model’s proposed label, compare it with a human’s judgement, and safely send uncertain cases for review. The same shape later applies to summarising notes, extracting a draft from a document, or preparing a reply for an agent.
| Part | What it does | What it must not do |
|---|---|---|
| Your application | Authenticates the user, fetches the ticket, validates the label, and stores the result. | Hand credentials or authorization decisions to the model. |
| Claude | Reads the ticket and proposes one allowed queue. | Issue refunds, alter records, or invent a queue. |
| A person or existing workflow | Reviews uncertain items and takes consequential actions. | Assume a plausible answer is automatically correct. |
Think of Claude as a capable colleague who can read a pile of unstructured notes quickly, not as a replacement for the rest of your application. It can recognise that “I was charged twice” belongs with billing even when the customer does not use your exact internal vocabulary. That judgement is valuable, but it remains a proposal.
Identity, permissions, money movement, database writes, and policy enforcement should stay in ordinary code. Those rules need predictable behavior, audit trails, and a safe failure mode. If the model returns an unexpected answer, your code should be able to say “this needs review” without breaking the customer experience.
A common first mistake is to build “an assistant for our whole business.” That sounds exciting, but it gives you no clear definition of success. A ticket router has four labels, a known input, and a visible outcome. When it makes a poor choice, you can add a better example, refine the instruction, or expand the review path.
Start with a decision a person already makes repeatedly. Ask the people who do the work which examples are confusing, which labels are acceptable, and which cases must always be escalated. Their answers are more useful than a generic list of prompt-writing tips.
The Messages API request below has two pieces of instruction. The system instruction defines the job and the permitted labels. The user message carries the current ticket. Keeping those roles separate makes the request easier to review and helps prevent old ticket text from quietly changing your application rules.
Notice what is missing: no customer profile, no order history, no API key in source code, and no request to perform an action. Send the minimum context needed for the task. Extra data does not make a feature more professional; it increases the information you have to protect and test.
A model can answer with an extra sentence, a misspelled label, or a reasonable category that your workflow does not support. That does not mean the model has failed; it means the application needs a boundary. Normalize the response, compare it with your allowed list, and direct anything else to a human queue.
This simple validation layer is one of the habits that separates a demo from a useful feature. Later lessons will show stricter structured-output contracts, but the principle starts here: models suggest; the application accepts, rejects, or requests review.
A happy-path example is useful for checking that the wiring works. It does not tell you whether the feature is ready. Add short, realistic cases: a customer asking for two things, a ticket with no clear queue, an angry message with little detail, and text that tries to override the instruction. Decide the expected safe outcome before you look at the response.
For a first release, a tiny spreadsheet or Python list is enough. Compare the expected queue with the proposed queue, note ambiguous cases, and improve the instruction only when you can explain why. This is slower than chasing a perfect prompt, but it produces something a team can maintain.
If you can make this small classifier work, you already understand the important shape of a reliable Claude feature: a narrow task, minimal context, a model proposal, code-side validation, and a way to learn from mistakes. The later lessons build on that foundation instead of replacing it.
Next, set up the client cleanly and make a first request in your own project. Do not rush to add a conversational interface or autonomous actions. Make one useful decision dependable first; the larger experience can grow around it.
Use a virtual environment so this experiment stays separate from the rest of your machine. Keep the API key in your environment; the client reads it when it starts.
python -m venv .venv
.venv\Scripts\activate
pip install anthropic
set ANTHROPIC_API_KEY=your_key_here
set CLAUDE_MODEL=your_model_id
This is intentionally small. The request sends one ticket and asks for exactly one of four allowed labels.
import os
from anthropic import Anthropic
client = Anthropic()
response = client.messages.create(
model=os.environ["CLAUDE_MODEL"],
max_tokens=40,
system=(
"Sort support tickets into one label: billing, technical, account, or other. "
"Reply with the label only."
),
messages=[
{
"role": "user",
"content": "I was charged twice for my monthly subscription.",
}
],
)
proposal = response.content[0].text.strip()
print(proposal)
Small validation code turns unexpected model text into a safe review state instead of an accidental new queue.
ALLOWED_QUEUES = {"billing", "technical", "account", "other"}
def route_ticket(proposal: str) -> str:
label = proposal.strip().lower().rstrip(".")
return label if label in ALLOWED_QUEUES else "needs_review"
print(route_ticket("Billing."))
print(route_ticket("urgent-refund"))
billing
needs_review
A record that distinguishes what the model suggested from what the workflow did makes review conversations much easier.
ALLOWED_QUEUES = {"billing", "technical", "account", "other"}
def route_ticket(proposal: str) -> str:
label = proposal.strip().lower().rstrip(".")
return label if label in ALLOWED_QUEUES else "needs_review"
def ticket_record(ticket_id: str, proposal: str) -> dict:
route = route_ticket(proposal)
return {
"ticket_id": ticket_id,
"model_proposal": proposal,
"workflow_status": "queued" if route != "needs_review" else "review",
"queue": route if route != "needs_review" else None,
}
print(ticket_record("T-104", "mystery queue")["workflow_status"])
review
Two examples are enough to establish the habit: decide the expected label first, then compare the returned proposal.
ALLOWED_QUEUES = {"billing", "technical", "account", "other"}
def route_ticket(proposal: str) -> str:
label = proposal.strip().lower().rstrip(".")
return label if label in ALLOWED_QUEUES else "needs_review"
cases = [
{"ticket": "I cannot reset my password", "expected": "account", "proposal": "account"},
{"ticket": "The invoice shows an extra charge", "expected": "billing", "proposal": "billing"},
]
passed = sum(route_ticket(case["proposal"]) == case["expected"] for case in cases)
print(f"{passed} of {len(cases)} cases matched")
2 of 2 cases matched
Fifteen focused minutes
0 of 3 completed
No. A focused feature such as classification, extraction, or draft preparation is usually a better first project because its outcome is easier to inspect and evaluate.
Send only the minimum data needed for the approved task and apply your organisation’s privacy, retention, and access rules. Do not send sensitive context merely because it might be useful.
A model can return extra text, a spelling variation, or an unsupported label. Validation gives your workflow a predictable safe response when that happens.
Explore 500+ free tutorials across 20+ languages and frameworks.