Running a one‑person shop means you wear every hat—sales, support, fulfillment. You need a conversational agent that can qualify leads, answer FAQs, and book calls without swallowing your budget or your sanity. Most tutorials show you a flashy demo then leave you stranded when the model hallucinates or hits a rate limit. After reading this you’ll have a working pipeline, a checklist for picking the right model, and a clear path to debug the most common failures.
What most guides get wrong about prompt design
Many guides tell you to write a long, detailed system prompt that tries to cover every possible edge case. In practice that makes the model slower, more expensive, and prone to over‑fitting to the examples you gave. I’ve seen prompts that run over 2 000 tokens just to explain tone, format, and fallback rules. The result? Higher latency and a bill that spikes after a few dozen conversations.
Instead, start with a minimal core instruction and add only the constraints that actually cause failures in your logs. For a lead‑qualification bot I use this starter:
You are a helpful assistant that qualifies inbound leads for a web design agency.
Ask about budget, timeline, and decision‑maker. If the user says they are just browsing, politely end the chat.
From there I add a single line for each failure mode I observe: “If the user mentions a competitor, note it but do not engage.” Keep the total under 400 tokens and you’ll see faster responses and lower cost.
How do you handle context window limits without losing coherence?
This is the question I get most often from operators who try to feed entire chat histories into the model. The context window of most affordable models is 4 k or 8 k tokens, which sounds plenty until you realize each message includes metadata, formatting, and the model’s own previous replies. After about ten turns the window fills and the model starts dropping the earliest messages, which can make it forget the user’s budget or timeline.
My solution is to keep a short summary of the conversation in a separate memory store and inject only the last three user‑assistant pairs plus the summary. The summary is generated by the same model with a prompt like “Summarize the key facts from this conversation in one sentence.” This keeps the token count low while preserving the essential context.
Here’s the flow I run in a simple Python loop:
- Receive new user message.
- Append it to the short‑term buffer (last three pairs).
- If the buffer exceeds three pairs, summarize the oldest pair and add the summary to the long‑term memory.
- Build the final prompt: system instruction + long‑term summary + short‑term buffer.
- Call the model, get the reply, append the reply to the buffer.
- Return the reply to the user.
With this pattern I’ve kept the average prompt under 900 tokens even after thirty exchanges.
A concrete example: using Claude 3 Haiku for lead qualification
Let’s walk through a real setup I use for a freelance web‑design business. I chose Claude 3 Haiku because its price is $0.25 per million input tokens and $1.25 per million output tokens—cheaper than GPT‑4o for the volume I need. The free tier gives you 5 000 credits per month, which is enough for about twenty‑five short qualification chats.
First, I create a small wrapper around the Anthropic API:
import anthropic
client = anthropic.Anthropic(api_key=os.getenv('ANTHROPIC_KEY'))
def qualify_lead(user_msg, history):
# history is a list of {'role': 'user'|'assistant', 'content': str}
summary = get_long_term_summary(history) # helper that calls the model to summarize
short = history[-6:] # last three pairs
prompt = f"""You are a helpful assistant that qualifies inbound leads for a web design agency.
Ask about budget, timeline, and decision‑maker. If the user says they are just browsing, politely end the chat.
Summary of earlier conversation: {summary}
Conversation so far:
{''.join(f"{m['role']}: {m['content']}\n" for m in short)}
User: {user_msg}
Assistant:"""
resp = client.messages.create(
model='claude-3-haiku-20240307',
max_tokens=300,
temperature=0.2,
messages=[{'role': 'user', 'content': prompt}]
)
return resp.content[0].text.strip()
I then expose this function via a simple webhook that my landing page calls with fetch. The total cost for a typical chat of eight messages is about $0.0015, which means I can run hundreds of chats a month for under $5.
What I love about this approach is how easy it is to swap the model—if I ever need more reasoning power I just change the model string to ‘claude-3-sonnet’ and keep the same prompt structure. (Which, yes, is nice when you’re experimenting.)
