I go into a lot of meetings. Client calls, partner intros, job interviews. The prep ritual is always the same: open LinkedIn, Google the person, scan a few headlines, try to remember something useful before they say hello. It's inefficient and often incomplete. Je participe à beaucoup de réunions. Appels clients, présentations de partenaires, entretiens. Le rituel de préparation est toujours le même : ouvrir LinkedIn, chercher la personne, parcourir quelques titres. C'est inefficace et souvent incomplet.
So I automated it. The result is a tool that takes a name and company, searches the web in three directions, and uses an LLM to synthesize a clean briefing. The whole thing runs serverless on AWS and costs less than a cent per use. J'ai donc automatisé ça. Un outil qui prend un nom et une entreprise, effectue trois types de recherches web, et utilise un LLM pour synthétiser une fiche propre. Le tout tourne sans serveur sur AWS pour moins d'un cent par utilisation.
→ Try the tool first, then come back and read how it was built. → Essayez l'outil d'abord, puis revenez lire comment il a été construit.
Architecture overviewVue d'ensemble de l'architecture
Why Lambda as a container image?Pourquoi Lambda avec une image conteneur ?
Lambda supports two deployment models: a zip file or a container image stored in ECR. I chose the container path intentionally — you learn Docker, ECR, and Lambda container support in one shot, at near-zero cost. The free tier covers this image (~190MB) and the first 1M Lambda invocations per month. Lambda supporte deux modèles de déploiement : un fichier zip ou une image conteneur stockée dans ECR. J'ai choisi le conteneur intentionnellement — on apprend Docker, ECR et Lambda en une seule fois, à coût quasi nul. Le niveau gratuit couvre cette image (~190 Mo) et le premier million d'invocations Lambda par mois.
What the Lambda actually doesCe que fait réellement le Lambda
1 — Validate input1 — Valider les entrées
Name, company, and context are trimmed and capped at 100/100/300 characters before touching any external API. User input goes into a clearly-labelled data block, never directly into the system prompt — this is the prompt injection guard. Le nom, l'entreprise et le contexte sont nettoyés et limités à 100/100/300 caractères avant de toucher une API externe. Les entrées utilisateur vont dans un bloc de données clairement étiqueté, jamais directement dans le système de prompt — c'est la protection contre l'injection.
2 — Run three SerpAPI searches2 — Effectuer trois recherches SerpAPI
General search, LinkedIn-targeted search, and a news search. All three run sequentially — Lambda's 30s timeout is not a bottleneck. Each failed search is caught and logged. Recherche générale, recherche ciblée LinkedIn, et actualités. Les trois s'exécutent séquentiellement — le délai Lambda de 30s n'est pas un goulot d'étranglement. Chaque recherche échouée est capturée et journalisée.
3 — Deduplicate and pack results3 — Dédupliquer et regrouper les résultats
Results from three searches overlap. A Python set handles O(1) deduplication by title. Up to 15 unique results are packed into a structured research block before being sent to Bedrock. Les résultats des trois recherches se chevauchent. Un set Python gère la déduplication O(1) par titre. Jusqu'à 15 résultats uniques sont regroupés dans un bloc de recherche structuré avant d'être envoyés à Bedrock.
4 — Call Claude Haiku on Bedrock4 — Appeler Claude Haiku sur Bedrock
The research block is sent to us.anthropic.claude-haiku-4-5-20251001-v1:0 — the cross-region inference profile ID. The us. prefix enables routing from ca-central-1 to Bedrock in us-east-1. Without it, you get a ValidationException.
Le bloc est envoyé à us.anthropic.claude-haiku-4-5-20251001-v1:0 — l'ID du profil d'inférence inter-région. Le préfixe us. permet le routage depuis ca-central-1 vers Bedrock en us-east-1. Sans lui, Bedrock retourne une ValidationException.
def call_bedrock(system: str, user_message: str) -> str:
body = json.dumps({
"anthropic_version": "bedrock-2023-05-31",
"max_tokens": 1500,
"system": system,
"messages": [{"role": "user", "content": user_message}],
})
response = bedrock.invoke_model(
modelId=MODEL_ID, # us.anthropic.claude-haiku-4-5-20251001-v1:0
body=body,
contentType="application/json",
accept="application/json",
)
return json.loads(response["body"].read())["content"][0]["text"]
The Docker build gotcha that will cost you an hourLe piège Docker qui vous coûtera une heure
Building on Windows with Docker Desktop and pushing to Lambda? By default, Docker BuildKit creates an OCI image index (a multi-platform manifest list) even when you target a single platform. Lambda needs a single-platform manifest and rejects the image. The fix: Vous construisez sur Windows avec Docker Desktop et poussez vers Lambda ? Par défaut, Docker BuildKit crée un index d'images OCI même pour une seule plateforme. Lambda a besoin d'un manifeste à plateforme unique et rejette l'image. La solution :
docker buildx build \ --platform linux/amd64 \ --provenance=false # ← this is the fix --load \ -t meeting-prep .
CORS: Lambda headers aren't enoughCORS : les en-têtes Lambda ne suffisent pas
When you add an API Gateway trigger to a Lambda, API Gateway gets its own CORS configuration — separate from whatever headers your Lambda returns. Configure CORS in two places: Lambda response headers (for the actual request) and API Gateway CORS settings (for the preflight). Both must allow the same origins. Quand vous ajoutez un déclencheur API Gateway, il obtient sa propre configuration CORS — distincte des en-têtes Lambda. Configurez CORS en deux endroits : les en-têtes Lambda (pour la vraie requête) et les paramètres CORS d'API Gateway (pour le preflight). Les deux doivent autoriser les mêmes origines.
What it costsCe que ça coûte
| ServiceService | UsageUtilisation | Cost / briefCoût / fiche |
|---|---|---|
| Lambda | ~5s · 256MB | $0.000021 |
| API Gateway | 1 request1 requête | $0.000001 |
| Bedrock (Haiku 4.5) | ~3K in · ~1K out | $0.0037 |
| SerpAPI | 3 searches3 recherches | $0.003 |
| Total | ~$0.007 |
What I'd do differentlyCe que je ferais différemment
- Rate limitingLimitation de débit — an API Gateway usage plan with per-IP throttle would prevent abuse. un plan d'utilisation API Gateway avec plafond par IP préviendrait les abus.
-
Parallel SerpAPI callsAppels SerpAPI parallèles —
running searches concurrently with
asynciowould cut ~2s of latency. les exécuter avecasyncioréduirait la latence d'environ 2s. - CachingCache — a DynamoDB TTL cache would eliminate redundant SerpAPI calls for the same person within the same day. un cache DynamoDB avec TTL éliminerait les appels SerpAPI redondants pour la même personne dans la même journée.
- StreamingStreaming — streaming the Bedrock response to the browser would make the brief appear instantly instead of after a 3s wait. streamer la réponse Bedrock vers le navigateur ferait apparaître la fiche instantanément au lieu d'attendre 3s.
→ Try the meeting prep tool → Essayer l'outil de préparation