Last quarter I needed to send 500 personalized cold emails, scrape LinkedIn for leads, and generate invoices — all while keeping costs under $30/mo. I tried a few SaaS tools, but the per‑seat pricing added up fast and the data never stayed in my control. After a weekend of tinkering, I built a self hosted ai stack that handles each task with local LLMs and a few scripts.
Why does my self hosted ai agent keep hallucinating invoice numbers?
When I first hooked a local LLM to my invoicing workflow, the model would invent client names or add extra line items that never existed. The problem wasn’t the model size; it was the prompt. I was asking the model to “write an invoice” without giving it a strict template or any grounding data. The model treated the request as a creative writing exercise and started filling in gaps with plausible‑sounding fiction.
What fixed it was turning the generation into a fill‑in‑the‑blank task. I passed the structured data (client name, amount, due date) as JSON and asked the model to only format it into HTML. The prompt became something like:
# You are a precise formatter. Given the following JSON, output a valid HTML invoice snippet. Do not add any extra text.
{ "client": "Acme Corp", "amount": 1250, "due_date": "2026-09-30" }
With that constraint, hallucinations dropped to zero. The lesson: never rely on a model to invent data; give it the data and ask it to transform.
What most guides get wrong about local LLMs
Many tutorials tell you to download the biggest model you can fit on your GPU and start prompting. In practice, a 7B parameter model runs fine on a $5/mo VPS with CPU‑only inference, while a 13B model needs a GPU that costs $40/mo or more. The bigger model rarely yields a noticeable quality gain for structured tasks like email copy or invoice formatting.
I’ve seen guides recommend paying for a hosted API just to avoid the hassle of quantization. That’s unnecessary if you use Ollama with a 4‑bit quantized model. The download is ~3GB, the RAM usage stays under 2GB, and latency is acceptable for batch jobs.
Concrete example: cold email pipeline with Ollama and Python
Here’s the end‑to‑end flow I use every week:
- Scrape LinkedIn profiles with Apify (free tier gives 1000 credits/mo, enough for ~200 leads).
- Store each lead as a row in a local PostgreSQL database (running in Docker).
- Trigger a Python script that pulls the next 20 leads, builds a prompt, and calls Ollama’s HTTP API.
- The prompt asks the model to produce a personalized opening line based on the lead’s job title and recent post.
- Script inserts the line into a templated email, logs the output, and moves the lead to a “done” table.
Sample prompt (Python f‑string):
prompt = f"""
You are a sales copywriter. Write one short, friendly opening line for a cold email to {first_name} who works as a {job_title} at {company}. Reference their recent LinkedIn post about {post_topic}. Keep it under 20 words.
"""
Running this on a $6/mo VPS (1 vCPU, 2GB RAM) costs about $0.02 per 1000 tokens. For 200 leads a month, the LLM cost is under $0.50. The rest of the stack (Apify, Docker, PostgreSQL) stays within the free tier or a few dollars.
Price opinion: $6/mo for the VPS is fair; you get full data ownership and unlimited runs. Paying $49/mo for a SaaS email‑personalization tool feels ridiculous when the same work runs locally for pennies.
