01Overview
Workly is my own B2B outbound product. Instead of templates and merge fields, a team of specialised agents researches every prospect, picks a sales framework that fits the buyer, writes the email or LinkedIn message, and reviews it before anything sends. The agents are data, not code: each one is a row in a vault with its prompt, tools and output schema, so the system can add a specialist to itself, test it in a sandbox and promote it without a deploy. It runs on Django, Postgres with pgvector and Celery, with Postgres doing the jobs that usually need Redis.

02The Challenge
Most outbound tools personalise with merge fields and send on a fixed schedule. Buyers can tell, and the same sequence goes to a CTO and a VP of Sales alike. Doing it properly means researching each person, choosing an angle that fits their situation, writing on the channel they actually use and knowing when to stop, which is exactly the work that doesn't scale for a human team. Letting AI do it creates new risks: drafts that sound machine-written, invented facts and links, LinkedIn accounts pushed past their limits, and a system that never learns from its own replies. The goal was autonomy that is safe to switch on: every automatic action with a check in front of it, and a system that measurably improves as it runs.
Pain points we set out to solve
- Merge-field personalisation that buyers see through
- AI drafts that sound machine-written or invent facts
- LinkedIn accounts restricted for going past real limits
- No feedback loop from replies back into the writing
03Objectives
- 01Research every prospect and write from that research, not from a template
- 02Nothing sends on its own without passing independent quality and compliance checks
- 03Keep every LinkedIn and email account inside its real limits, enforced in code
- 04Learn from outcomes automatically, with no new prompt going live unless it beats the current one
04Approach
How it was delivered: phased, with clear checkpoints and evidence at each step.
- Foundation
Agents as data
A vault holds each agent's role, prompt, temperature, tools and output schema, plus a registry of 12 tools and 17 structured output schemas. Agents are built from those rows at runtime, checkpointed step by step and traced under one id per cycle.
- Pipeline
Research, reason, frame, write, gate
Six research streams run in parallel and are cached for as long as the data stays true. A brief, a DISC profile, the top three matching case studies and one of seven sales frameworks shape the draft, which a devil's-advocate manager scores and can send back twice.
- Channels
Email and LinkedIn in one conversation
Eight step types across both channels share one conversation history. LinkedIn actions reserve a slot before every API call, invite caps follow the account tier and acceptance rate, and new prospects are admitted only as fast as they can be contacted.
- Learning
Five loops behind an eval gate
Recurring critiques feed the next draft, every outcome becomes a lesson, a synthesizer drafts new agents into a sandbox, challenger prompts must beat the champion on the same tests, and a nightly job labels outcomes once their window closes.

05The Solution
Every autonomous action has a gate in front of it. A message sends on its own only when the manager scores it 7 or higher, a simulated sceptical prospect predicts a real reply, and a compliance check comes back clean; anything less waits for a person. A synthesized agent is promoted only after seven days in a sandbox, an eval score of 7.0, 80% compliance and a check that its tools fit the monthly research budget. Fifteen guardrails in code catch what prompts can't guarantee, from em dashes and placeholder brackets to hallucinated links and signatures.


Three checks before auto-send
Manager score of 7+, a simulated-prospect pass and a clean compliance check. Anything less is held for human review.
Seven frameworks as specs, not prompts
Challenger, JOLT, NEPQ and four more, each a structured spec the writer follows, the manager enforces and code validates, so the three can't disagree.
Self-improving, behind a gate
New agents and new prompts are tested against the current ones on the same cases, and only adopted when they score better without breaking more rules.
LinkedIn safety built into the call path
Atomic slot reservation, tier-aware ceilings, warm-up, an acceptance-rate throttle and per-account budgets shared across campaigns.
Hybrid retrieval over five memories
Dense and full-text search fused with reciprocal rank fusion, so drafts get 3 to 5 grounded chunks instead of 20.
Deliverability and compliance
Bounce suppression, opt-outs checked at send time, per-domain caps, SPF, DKIM and DMARC re-checks, and an automatic pause if complaints reach 0.08%.
06Technology stack
Picked for latency, cost, and long-term maintainability — not for novelty.
AI / Agents
- Gemini 3 Flash
- LangChain
- Structured output (17 schemas)
- Google-grounded research tools
Memory / RAG
- pgvector (768-dim)
- Postgres full-text
- Reciprocal rank fusion
- Context caching
Backend
- Python 3.13
- Django 5
- Django REST Framework
- Celery with a Postgres broker
Channels
- Nylas (email and calendar)
- Unipile (LinkedIn)
- Resend
Frontend / Ops
- React 18
- TypeScript
- TanStack Query
- Render
- 1,077 automated tests
07Results
- ~75%Fewer input tokens per draft from hybrid retrieval
- 90%Cheaper static context with model-side caching
- 3Independent checks before any message auto-sends
- 1,077Automated tests across 102 test modules

Autonomy that is safe to switch on. Every draft is grounded in research and retrieved knowledge, every automatic send has passed three independent checks, and the system improves itself without a deploy: new agents and prompts earn their place against the current ones before they go live. Running Postgres as the job broker, cache and cache registry also removed Redis from the stack entirely.
08Key takeaways
- Make agents data, not classes. A new specialist becomes a new row the system can test and promote on its own
- Put a gate in front of every autonomous action: auto-send needs three checks, promotion needs five
- Enforce platform limits before the API call, not after it
- Judge a new prompt against the current one on the same test cases, not against an absolute score
- Treat silence as feedback, but never count outreach that still has time left as a failure