Hasnain Ali

The agent backend behind Speculo's outreach

Hasnain Ali, Senior AI EngineerPublished , updated 7 min read

I built the Python backend that times every call, text and follow-up for Speculo's outreach system. Its agents make first contact with each lead and qualify it, then hand the qualified leads to the client's human agents.

In short: four LangGraph agents and one delivery service handle first contact and qualification, then hand qualified leads to the client's human agents. The hard part was state: keeping each lead in the right sequence while several agents ran at once. LangGraph's explicit state graph made every transition inspectable, and an idempotency key on every touch stopped redelivered messages from repeating it.

The problem we were solving

Speculo needed to reach thousands of new leads every week by email, text and phone. Doing that with a human team is expensive, so we built AI agents to handle first contact and qualification.

The architecture: four agents and a delivery service

The system runs on LangGraph, which handles orchestration, state management and the conditional routing between agents.

The research agent reads the lead record and builds a structured profile of the lead, including the most relevant hook for outreach.

The personalization agent takes that profile and writes an email for that lead. The subject line, opening line and CTA are generated per lead. It also decides whether email or phone is the better first touch.

The email delivery service handles SMTP routing, sending windows, rate limiting and bounce handling. Emails go out through warm domains with SPF, DKIM and DMARC set up. Bounce signals feed back into the lead scoring model.

The voice agent calls high-priority leads with a realistic ElevenLabs voice delivered through Twilio. It handles the full conversation flow: introduction, qualification questions, objection handling and calendar booking. Calls are transcribed and scored in real time.

The follow-up agent tracks engagement and runs multi-touch follow-up. Opened the email but no reply? Schedule a call. Answered the call but not ready to book? Send a follow-up email. The sequencing logic is a state machine managed by LangGraph.

State management was the hard part. Each lead had to move through the right sequence without duplicate touches, skipped steps or race conditions while several agents were active at once. Pub/Sub delivers at least once, so every touch carried an idempotency key, and a redelivered message could not send a second email or place a second call.

What surprised me

The voice agent converted better than the email agent on cold outreach. We expected the opposite. People picked up, and when the call was useful to them, they stayed on and talked. The ElevenLabs voice held up for the whole conversation, from the introduction through questions and objections.

Personalization had a bigger impact than send volume. A smaller batch of highly personalized emails outperformed a larger batch of lightly personalized ones on reply rate.

Technical decisions

We chose LangGraph over LangChain because its explicit state graph makes multi-step agent flows predictable and easier to debug. With LangChain chains, tracing a failure deep in a chain was painful. With LangGraph, every state transition is logged and can be inspected.

Lead scoring uses pgvector: we embed lead profiles and score each new lead by cosine similarity against past leads that converted.

Every agent runs async, and research, personalization and delivery run in parallel batches with backpressure.

Message me

What is this about?

I read and answer these myself.

Or email codingwithhasnain@gmail.com