Prompt Engineering for an Automated Email System

Aug 2025

Blog Image 07
Blog Image 07
Blog Image 07

This project showcases how I prepare contextual information for an LLM before coding with an LLM. The below features system prompts, rules, and code scaffolding for an automated email reply tool built for a jewelry studio. These documents are used to provide context to the LLM while coding the project.

The goal of the project is to cut down the time spent answering repetitive enquiries by generating warm, consistent drafts based on operator commands, anonymised past examples, and pre-approved "message blocks." These email drafts would then be revised if necessary and sent by a staff member.

System Brief

Used to guide code generation. Defines goals, guiding principles, CLI workflow, vector DB setup, etc.

Goal: Ship a CLI tool that:
  • Searches Gmail, lets you select one or multiple threads for a customer, and merges them into one context.
  • Uses a vector database of anonymised email examples for style/tone/structure.
  • Inserts fixed, approved text from a local messageblocks file only when explicitly requested.
  • Obeys strict system instructions (tone + quote formatting).
  • Parses a short operator command into a plan.json.
  • Generates a draft reply via OpenAI and prints it to the terminal (optionally creates a Gmail draft).
  • We’ll later move from CLI → UI with the same core pipeline.

Core Principles
  1. Tone is always warm, professional, and clear (learned from vector examples).
  2. messageblocks are inserted only when listed in the plan, and exactly as written.
  3. Vector retrieval is for style/structure, not to trigger messageblocks.
  4. Operator sets the facts; the model expands them in brand voice.
  5. Separation of concerns:
    • Vector DB → style/tone/example replies
    • messageblocks.json → fixed phrases (reviews, social-share, etc.)
    • System prompt → rules, tone, quote format

CLI Workflow
  1. Search Gmail with --search (e.g., from:addr@example.com newerthan:90d) or specify --threads.
  2. CLI lists matching threads.
  3. Select threads.
  4. Tool merges them into one transcript.
  5. Operator passes --cmd (e.g., yes; custom quote 1000; eta 2 weeks; include reviewslink).
  6. planbuilder parses to plan.json.
  7. retriever queries vector DB with context + plan.
  8. composer builds prompt with system instructions + examples + plan + messageblocks.
  9. Model returns draft; postprocessor enforces formatting and block insertion.
  10. CLI prints draft, optional Gmail draft creation.

Gmail API - CLI Integration
  • Scopes: `gmail.readonly`, `gmail.modify` (for labels), `gmail.send` (optional for drafts).
  • Search examples:
    • All recent from a customer: `from:customer@example.com newer_than:30d`
      • Add keywords: `from:customer@example.com subject:(signet OR engraving) newer_than:90d`
    • Functions:
      • `search_messages(query) → [thread_ids]`
      • `get_thread(thread_id) → {messages...}`
      • `create_draft(thread_id, body) → draft_id`
    • Merging: flatten chosen threads into one timeline; include recent *customer* prompts prominently; include past *our* replies as context when relevant.

Anticipated Codebase Layout:
/project-root
├── config/
├── message_blocks.json        # Fixed inserts by key
├── pricing_presets.json       # Optional common SKU quotes
├── system_prompt.txt          # Core system prompt
├── mappings.json              # Operator keywords message_block keys
├── openai.json                # Model + vector store IDs/settings
├── data/
├── test_emails/               # Sample email JSON for offline tests
├── retrieved_examples/        # Debug dumps from vector store
└── logs/                      # Input/output logs of drafts
├── src/
├── cli.py                     # CLI entry
├── gmail_api.py               # Auth, search, fetch, create draft
├── plan_builder.py            # Operator command plan JSON
├── retriever.py               # Vector store query + filters
├── composer.py                # Build prompts, call OpenAI
├── postprocessor.py           # Quote formatting + block insertion
├── utils.py                   # Config, logging, helpers
├── requirements.txt
└── README.md

Vector Database Record Format:
{
  "text": "Customer: [Customer enquiry here]\n\nOur Reply: [Our response here]",
  "metadata": {
    "type": ["quote_request", "custom_order"],
    "response_type": ["quote_reply"],
    "customer_status": "new"
  }
}

Vector Database Type Enumerations:
type: quote_request, custom_order, order_confirmation, order_status_update, repair_request, appointment_booking, info_request, complaint, thank_you, bulk_order, shipping_question
response_type: quote_reply, custom_process_explanation, confirmation_reply, order_status_reply, repair_reply, booking_reply, faq_reply, apology_reply, testimonial_acknowledgement, bulk_quote_reply
customer_status: new, repeat, unknown

Example: message_blocks.json
{
  "reviews_link": "If you get a moment, we’d be so grateful for a quick Google review: https://g.page/example",
  "social_share_request": "Would you be happy for us to share a photo of your piece on our socials once it’s finished? We can credit you if you’d like."
}

Example: mappings.json
{
  "reviews link": "reviews_link",
  "google review": "reviews_link",
  "social share request": "social_share_request",
  "share on socials": "social_share_request"
}

Example: plan.json
{
  "quote": {
    "total": 1000,
    "currency": "NZD",
    "eta": "2 weeks",
    "line_items": [
      {"label": "Custom 9ct gold signet ring (black finish)", "price": 1000}
    ]
  },
  "notes": [
    "Black finish is a surface treatment and will soften with wear over time."
  ],
  "message_blocks_to_include": ["reviews_link"]
}

System Roadmap

A phased roadmap for building the project. It covers repo setup, Gmail API integration, retrieval pipeline, and CLI workflow.

Phase 1 — Project scaffolding
  • Init repo + env: .env, requirements.txt, pre-commit hooks (black/ruff).
  • Folders & configs: create /config, /data, /src exactly as in the brief.
  • Configs: add systemprompt.txt, messageblocks.json, openai.json, mappings.json.
  • Done when: repo boots, configs load via utils.py, simple python -m src.cli --help works.

Phase 2 — Gmail API foundation
  • OAuth + token cache: implement auth flow and token persistence.
  • Search & fetch: searchmessages(query) → thread IDs; getthread(threadid) → flattened timeline.
  • Draft creation: createdraft(threadid, body).
  • Done when: CLI can search by query, list threads, fetch thread(s), and dump JSON to /data/logs.

Phase 3 — Plan Builder
  • Operator cmd parser: parse “yes; custom quote 1000; eta 2 weeks; include reviewslink” into plan.json.
  • Support: yes/no, quote (with line items), ETA, message block inclusion, notes.
  • Mappings.json: operator keywords → block keys.
  • Done when: CLI builds valid plan.json from cmd string.

Phase 4 — Retriever
  • Vector DB integration: build /data/testemails/ JSON dataset → embed → Pinecone/FAISS.
  • Retriever: query with customer context + plan summary, return 3–5 similar examples.
  • Done when: retriever.py returns JSON array of {text, metadata}.

Phase 5 — Composer
  • Build prompt: system instructions + retrieved examples + plan.json + required messageblocks.
  • Model call: send prompt to OpenAI, capture raw output.
  • Done when: CLI prints draft email to stdout.

Phase 6 — Post-processor
  • Quote formatter: enforce $X,XXX format, section headings.
  • Block enforcement: insert only blocks in plan.json; copy verbatim.
  • Sign-off: always “Warmly,\nAmy”.
  • Done when: no drift in pricing or blocks.

Phase 7 — CLI polish
  • Add flags: --search, --threads, --cmd.
  • Interactive thread select, plan.json preview, regeneration option.
  • Optional Gmail draft creation.
  • Done when: python cli.py end-to-end works.

Phase 8 — CLI → UI
  • Lightweight web app or Gmail add-on:
    • Search & select threads
    • Operator cmd input
    • Quote/ETA fields
    • message_block checkboxes
    • Approve & Send / Regenerate buttons
    • Sources panel for retrieved examples


Email → JSON Extraction

Markdown rules for preparing email data and ensuring consistent structure when stored in the vector DB.

Formatting Instructions
  • You are preparing real customer emails and responses from a custom jewelry studio to be stored in a vector database.
  • These examples will later be used to retrieve similar emails and generate accurate draft replies with an AI assistant.

STEP 1: Anonymize Sensitive Information
  • Replace customer names → [CustomerName]
  • Replace team member names → [TeamMembmer]
  • Studio name → [OurStudioName]
  • Email addresses → [email@example.com]
  • Phone numbers → [PhoneNumber]
  • Locations → [Location]

STEP 2: JSON Format - Each record must be JSON with:
{
  "text": "Customer: ...\n\nOur Reply: ...",
  "metadata": {
    "type": "...",
    "responsetype": "...",
    "customerstatus": "..."
  }
}

STEP 3: Metadata
- type: quoterequest, customorder, orderstatus, repairrequest, etc.
- responsetype: quotereply, confirmationreply, apologyreply, etc.
- customerstatus: new, repeat, unknown

STEP 4: Preserve Style
  • Keep emails warm, concise, professional.
  • Maintain line breaks and spacing.
  • Use exactly the tone shown in training examples.


Runtime Prompt Example

Defines the agent prompt used for every model run. Enforces tone, formatting, and correct handling of message blocks. This prompt is designed to work with OpenAI’s Assistants API or RAG pipeline.

You are a helpful, professional, and warm email assistant for a high-end jewelry studio.
This business operates two brands:
  • Custom Signet Rings – specializing in engraved signet rings and pendants.
  • Bespoke Jewelery – bespoke fine jewelry.

You will receive:
  1. Latest customer message(s) + thread context
  2. Retrieved example replies from vector DB (tone/style reference only)
  3. A plan.json with explicit details
  4. messageblocks.json with approved snippets

Rules:
  • Match tone from examples (warm, clear, professional).
  • Insert only blocks listed in plan.messageblockstoinclude.
  • Copy block text verbatim.
  • Do not insert similar text unless explicitly in plan.
  • Never mention AI/assistant.
  • Always sign off:


This project showcases how I prepare contextual information for an LLM before coding with an LLM. The below features system prompts, rules, and code scaffolding for an automated email reply tool built for a jewelry studio. These documents are used to provide context to the LLM while coding the project.

The goal of the project is to cut down the time spent answering repetitive enquiries by generating warm, consistent drafts based on operator commands, anonymised past examples, and pre-approved "message blocks." These email drafts would then be revised if necessary and sent by a staff member.

Contact

Blending creativity and workflow efficiency.

This portfolio includes a mix of professional and personal projects. All trademarks and copyrights are the property of their respective owners and are shown here for demonstration purposes only.

Contact

Blending creativity and workflow efficiency.

This portfolio includes a mix of professional and personal projects. All trademarks and copyrights are the property of their respective owners and are shown here for demonstration purposes only.

Contact

Blending creativity with workflow efficiency.

This portfolio includes a mix of professional and personal projects. All trademarks and copyrights are the property of their respective owners and are shown here for demonstration purposes only.