Building an Automated Email and Document Workflow
Generate an email draft and Markdown record from the same checked data without sending or accessing a real mailbox.
Overview
For learners who want to complete this practical exercise with inspectable sample data and explicit checks.
- Separate AI drafting, content checking, and artifact generation.
- Create a structured MIME email with the standard library.
- Organize output files with stable record IDs.
You will build: DEMO-1.eml and DEMO-1.md containing the same checked response.
Prerequisites
- Python 3.10+ and an editor; optionally a chat assistant for drafting.
The script uses no SMTP, OAuth, or email account. An optional assistant may have limits/fees; all example records are fictional.
Step-by-step tutorial
Step 1. Use a sample request instead of a mailbox
Use a simple request for a getting-started guide. The fictional policy only says that the guide is under review and has no confirmed completion date. Static input makes the workflow reproducible without granting mailbox access.
Step 2. Draft with AI using an explicit policy
Request: "Please send the getting-started steps."
Fictional policy: the guide is under review; no completion date is confirmed. Draft a polite 1–2 sentence response. Add no link, attachment, or promise of later delivery. Do not send anything.Read and correct the draft, then place your accepted text in approved_reply below. This field records your own exercise-level content check; it does not fabricate an external reviewer or workflow approval.
Step 3. Generate both artifacts from one source
Save this as email_workflow.py. EmailMessage handles header/body structure, and example.com addresses are for illustration only. The .eml is a local artifact; there is no email-sending operation.
import json
from email.message import EmailMessage
from pathlib import Path
RECORDS = [{"id": "DEMO-1", "subject": "Onboarding guide",
"request": "Please send the getting-started steps.",
"approved_reply": "Thank you. The guide is being reviewed; no date is confirmed."}]
def build(records, out):
out.mkdir(parents=True, exist_ok=True)
seen = set()
for record in records:
ident = record["id"]
if not ident.startswith("DEMO-") or not ident[5:].isdigit() or ident in seen:
raise ValueError("invalid or duplicate id")
seen.add(ident)
message = EmailMessage()
message["From"] = "training@example.com"
message["To"] = "recipient@example.com"
message["Subject"] = "Draft: " + record["subject"]
message.set_content(record["approved_reply"])
(out / (ident + ".eml")).write_bytes(message.as_bytes())
document = ("# " + ident + "\n\nRequest: " + record["request"] +
"\n\nApproved draft: " + record["approved_reply"] + "\n")
(out / (ident + ".md")).write_text(document, encoding="utf-8")
return len(seen)
if __name__ == "__main__":
print("Created", build(RECORDS, Path("drafts")), "draft pair; nothing sent")python3 email_workflow.pyStep 4. Inspect output and rerun
Open drafts/DEMO-1.md and inspect the .eml as text or as a draft if your mail app supports it. Compare subject/body and check that no completion date was promised. Rerunning overwrites the same ID pair instead of duplicating it. This is not transactional storage: a failure can leave a partial pair that requires a corrected rerun.
Step 5. Plan real integration one permission at a time
For a real mailbox, choose Email Trigger (IMAP) or another organization-approved connector, configure separate credentials, filter a test folder, and persist message IDs for deduplication. Keep draft creation separate from sending; do not attach SMTP merely because a model suggests it. Check retention policy before sending email content to AI.
Verification
- Both artifacts use exactly the same approved_reply and make no network call.
- Rerunning does not create additional IDs.
Troubleshooting
Email line breaks or Unicode look wrong
Cause: The MIME message was parsed by naive string splitting.
Fix: Use email.parser or a MIME-aware client and inspect the decoded body.
An old output remains after a batch fails
Cause: The exercise has no batch transaction or automatic cleanup.
Fix: Use a dedicated test directory and design validation/error handling before replacing real outputs.
Exercises & extensions
- Add two requests, preserve unique IDs, and verify the output-pair count.
- Write a test parsing .eml and comparing its decoded body with Markdown.
The exercise does not read mail, send mail, or create Google Docs. A local .eml is not evidence of email delivery; real integration requires separate checks.
