ACE is an autonomous cold outreach pipeline built on LangGraph and Gemini. It reads leads from a Google Sheet, researches each company, drafts a personalized email grounded in your resume, and either sends it after your approval or saves it as a Gmail draft for later review. Follow-up sequences, A/B subject line testing, email validation, and campaign analytics are all handled automatically.
- LangGraph workflow with a stateful, multi-node execution graph (fetch, validate, research, generate, review, refine, send, update).
- Two execution modes: Interactive (human-in-the-loop, review each email before sending) and Automatic (bulk-draft all emails to Gmail Drafts).
- Gemini-powered research: uses Google Search via Gemini to gather company and recipient context before writing.
- Resume-aware generation: reads your
resume.mdand dynamically selects the most relevant achievements for each recipient. - A/B subject line testing: generates 3 subject line variants per email and lets you pick one.
- Email validation: checks RFC syntax and MX records before wasting LLM calls on unreachable addresses.
- Follow-up sequences: supports 2-stage threaded follow-ups that reply in the original Gmail thread.
- Resume attachment: attaches your
resume.pdfto outgoing emails. - Refinement loop: provide free-text feedback in Interactive mode to have the AI rewrite the draft (capped at 5 iterations).
- Batch draft sending: send queued Gmail drafts at timed intervals via
--send-drafts. - Campaign analytics: tracks sent/drafted/skipped/failed counts, skip reasons, A/B variant choices, and prints a summary report.
- Google Sheets sync: reads leads from and writes status back to your spreadsheet automatically, preventing duplicate outreach.
- Internship and job outreach at scale, with personalized, research-backed emails.
- Startup founder outreach for partnerships, sales, or investor conversations.
- Any scenario where you need to send many individualized cold emails grounded in public research about the recipient.
- Python 3.10+
- A Google Cloud project with the following APIs enabled: Gmail API, Google Sheets API, Vertex AI API
- OAuth 2.0 Desktop credentials (
credentials.json) - A Google API key (from Google AI Studio or Cloud Console)
git clone https://github.com/indenigrate/ACE.git
cd ACE
# Install uv if you do not have it
pip install uv
# Install dependencies
uv sync- Go to the Google Cloud Console.
- Create a new project (or select an existing one).
- Enable these APIs: Gmail API, Google Sheets API, Vertex AI API.
- Go to APIs & Services > OAuth consent screen.
- Choose "External" user type.
- Fill in the required fields.
- Under "Test users", add the Gmail address you will send from.
- Go to Credentials > Create Credentials > OAuth client ID.
- Application type: Desktop app.
- Download the JSON file, rename it to
credentials.json, and place it in theconfig/directory.
cp .env.example .envOpen .env and fill in:
| Variable | Description |
|---|---|
GOOGLE_API_KEY |
Your API key from Google AI Studio or Cloud Console. |
GOOGLE_PROJECT_ID |
The ID of your Google Cloud project. |
GOOGLE_SHEET_NAME |
The name of your spreadsheet tab (e.g. Internship_Leads). |
GOOGLE_SHEET_ID |
The ID from your Google Sheet URL (the long string between /d/ and /edit). |
-
Create a Google Sheet with these exact column headers in the first row:
Name|Company|Position|Email|LinkedIn|Status -
Fill in rows with your leads. Leave the
Statuscolumn empty. -
Open
resume.mdin the project root and paste your resume content. The AI uses this to write emails. -
Place your resume PDF as
resume.pdfin the project root. It will be attached to outgoing emails.
Open src/tools_gmail.py, find the signature variable inside the send_email function, and replace the placeholder name, title, phone number, and links with your own.
uv run main.pyThe system processes one lead at a time. For each lead it will:
- Fetch the next unprocessed row from your Google Sheet.
- Validate the email address (syntax + MX record check).
- Research the company and recipient via Google Search.
- Generate a draft with 3 A/B subject line variants.
- Display the draft in your terminal and wait for your input.
At the review prompt you can:
- Type
yto approve and send. - Type
sto skip this lead. - Type any feedback (e.g. "make it shorter", "remove the second bullet") to have the AI refine the draft.
uv run main.py
# Then select option 2 at the mode promptDrafts all emails to Gmail without pausing for review. Useful for batch runs where you want to review drafts inside Gmail before sending.
uv run main.py --follow-ups 1 # Stage 1 follow-up
uv run main.py --follow-ups 2 # Stage 2 (final) follow-upFollow-ups are sent as threaded replies to the original email. The system reads the Thread ID stored in your Google Sheet from the initial send.
uv run main.py --send-drafts # Send all drafts, 20s interval
uv run main.py --send-drafts 10 # Send at most 10 draftsFetches recent Gmail drafts and sends them one by one with a 20-second gap between each send. Press Ctrl+C to stop early.
ACE/
├── main.py # CLI entry point and execution loop
├── config/
│ ├── settings.py # Environment loading, path constants, validation
│ └── credentials.json # OAuth credentials (not committed)
├── src/
│ ├── graph.py # LangGraph workflow definition and routing
│ ├── nodes.py # All graph node functions (fetch, research, generate, send, etc.)
│ ├── state.py # AgentState TypedDict (shared state schema)
│ ├── prompts.py # All LLM prompt templates
│ ├── analytics.py # Event logging and campaign summary reporting
│ ├── tools_gmail.py # Gmail API helpers (send, draft, list, attach)
│ ├── tools_sheets.py # Google Sheets read/write helpers
│ ├── tools_followup.py # Follow-up column setup and thread sync
│ ├── google_auth.py # OAuth token management
│ └── utils.py # Shared utilities
├── resume.md # Your resume in Markdown (not committed)
├── resume.pdf # Your resume PDF for attachment (not committed)
├── pyproject.toml # Project metadata and dependencies
├── .env.example # Template for environment variables
└── .gitignore
resume.md-- replace with your own resume content.resume.pdf-- replace with your own resume PDF.src/tools_gmail.py-- update the email signature inside thesend_emailfunction.src/prompts.py-- the prompts reference "IIT Kharagpur" and "Devansh Soni" in a few places. Update these to match your own background..env-- fill in your own API keys, project ID, and sheet details.config/credentials.json-- your own OAuth credentials from Google Cloud.- Google Sheet -- create your own sheet with the required headers and your leads.
This project is provided as-is for personal use.