Engineering reference: the booking assistant architecture
Same system as the rest of the series, drawn purely for engineers. Service names, resource identifiers, region, Bedrock model IDs, Google Calendar API choices, and the actual flow operations — everything you’d need to recreate this in your own AWS account.
Posts 1–6 walk through the system in plain language. This page is the dense version — nothing softened, just the architecture as you’d sketch it on a whiteboard during a design review.
Read this top-down, then column-by-column
Top row is the three external surfaces. Below it, the AWS account contains five subsystems: Build & Deploy across the top, then Config Sync, then three runtime columns (Reader & Parser, Scheduler, Confirmer), with a Cross-cutting strip at the bottom. A request arrives either via the form (POST to fn-intake-form) or via email (SES → S3 → fn-intake-email), is parsed if needed, and is written as a structured row into tbl-requests. The DynamoDB stream then triggers fn-scheduler, which queries Google Calendar’s freebusy API and applies the five filters from part 4. The proposal goes back to the customer with a one-click confirm link; tapping it invokes fn-confirmer, which runs the four-step claim/write/confirm/remind sequence from part 5.
Naming conventions used in the diagram
- Lambda functions:
fn-<purpose>—fn-intake-form,fn-intake-email,fn-parser,fn-scheduler,fn-confirmer,fn-config-sync,fn-archive. - DynamoDB tables:
tbl-requests(every inbound request, with parsed shape, slots, and outcome),tbl-claims(the atomic-claim ledger keyed by(resource_id, slot_start_iso), with TTL),tbl-audit(every action ever taken). - SNS topics:
t-alarmsfor general failures,t-draftsfor parser-low-confidence drafts that need a human approval. - S3 layout: single bucket
booking-assistant-datawith prefixesraw/{date}/,config/,archive/. - S3 Vectors index:
vec-services— chunked + embedded service catalogue for parser retrieval. The index lives inside an S3 vector bucket; the bucket is the parent resource, the index is what you query.
Region, model access, and Google Workspace auth
Everything runs in ap-southeast-1 (Singapore) for low latency from the Philippines. Bedrock model invocations use the Global cross-Region inference profile (model IDs prefixed with global.) — data at rest stays in Singapore; inference may route to other regions for capacity. Pricing is the same as on-demand Singapore pricing.
Google Workspace authentication uses a service account with domain-wide delegation, granted exactly three scopes: https://www.googleapis.com/auth/calendar.events to write events, https://www.googleapis.com/auth/calendar.events.freebusy to read freebusy on those calendars (the broader calendar.events scope alone is not authorised for freebusy lookups), and https://www.googleapis.com/auth/drive.readonly for the rules file. The service account’s private key is stored in AWS Secrets Manager and never leaves the runtime; rotation is a single CloudFormation parameter update.
The parser uses strict tool_use: three tool definitions (extract_service, extract_window, extract_contact) with required parameter schemas including a confidence_score in [0, 1] per field. The model can only emit structured tool calls — not a free-text reply. Free text would let it invent service IDs or hallucinate phone numbers; tool_use makes that mathematically impossible.
What’s deliberately not on the diagram
- IAM policy details — per-Lambda execution role inline policies are minimal (one bucket prefix, one or two tables, SES on a single sending identity, Bedrock invoke on one model, Secrets Manager read on the Google service-account secret).
- Per-business rules schema —
rules.ymlis a single Drive doc with sections for services, working hours, blackouts, capacity caps, ranking preferences, and tone. Updating sections updates the assistant’s behaviour without a deploy. - X-Ray tracing — on for
fn-parser,fn-scheduler, andfn-confirmer, sampling 100% during tuning, 10% in steady state. - Microsoft 365 swap-in — if you’re on Outlook/Exchange, replace the Google Calendar v3 calls with Microsoft Graph
getScheduleandevents.create. Same shape, different SDK, same idempotency story. - Bedrock Knowledge Bases — managed retrieval that can replace the explicit S3 Vectors + Titan Embeddings setup with a single connector. Worth picking when the bring-your-own-rules-file pattern isn’t strictly required; the catalogue is small enough that the explicit path is also fine.
- Bedrock Guardrails contextual grounding check — managed grounding-and-relevance scoring. The custom
confidence_scoreper field infn-parseris roughly the same idea hand-rolled; swapping to Guardrails moves the thresholds into console configuration and adds PII redaction on every model call. Worth turning on once the in-code thresholds are stable. - SMS lane — an SMS surface (Twilio number → Lambda Function URL) slots in next to the email and form intakes with no other architectural change. Keeping it off the default diagram so the per-request cost stays in the always-free band.
If you’re recreating this
Start with Build & Deploy alone (a single Lambda, no triggers). Once git push reliably updates an empty stack, get a Google service-account credential into Secrets Manager and verify a hard-coded freebusy call from a Lambda. Then the form intake and a stub scheduler that always returns three made-up slots. Then the real five-filter scheduler. Then the claim/write/confirm/remind confirmer (one step at a time — the claim’s atomic write is the part most worth integration-testing with parallel invocations). Then the email lane and the parser. Cross-cutting (audit, logs, alarms, budget, archive) goes in from day one.