LawAgent
An M&A diligence assistant: Corrective RAG on a curated legal corpus, plus a Drafting & Review Hub for generating, revising, and reviewing contracts.
Live demo
Drafting & Review Hub
Generate a contract from a prompt, revise your draft, or review an existing agreement — with AI-powered issue spotting, missing-clause proposals, and per-change accept/reject controls.
The problem
M&A diligence is repetitive, high-stakes, and document-heavy. Junior associates spend hours issue-spotting NDAs and SPAs against playbooks that rarely change. LawAgent asks: what if a retrieval pipeline could do the first pass — flagging weak clauses, proposing missing provisions, and citing its sources — so lawyers spend their time on judgment, not pattern-matching?
What I built
LawAgent has two surfaces. The demo is a Flask app on Cloud Run that runs a Corrective RAG pipeline against a corpus of M&A agreements (MAUD + CUAD + EDGAR filings). The Drafting & Review Hub is the main product: a two-pane editor where you can generate a contract from a prompt, revise an uploaded draft, or review an existing agreement — with per-clause accept/reject/edit controls, anchored chat, and four downloadable artifacts.
The anonymizer runs first on everything: a Flash-Lite pseudonymization layer that maintains a per-session two-way map (24h in-memory TTL, never persisted). Only anonymized summaries ever reach Supermemory, and a PII firewall re-screens before every write.
Pipeline
┌─────────────────────────────────────────────────────────────────────┐
│ LawAgent CRAG Pipeline │
└─────────────────────────────────────────────────────────────────────┘
Input (query / draft / prompt)
│
▼
┌──────────────────┐
│ Flash-Lite │ ← always-on anonymizer (per-session two-way map)
│ Anonymizer │
└──────────┬───────┘
│ anonymized text
▼
┌──────────────────┐
│ Cohere Embed v4 │ ← embed query → retrieve from Neon pgvector
└──────────┬───────┘
│ top-k candidates
▼
┌──────────────────┐
│ Cohere Rerank │ ← relevance score per candidate
│ 3.5 │
└──────────┬───────┘
│
┌────────┴────────┐
│ │
score≥0.72 0.45–0.72
│ │
│ GRADER_LLM_ENABLED?
│ │
▼ ▼
generate Flash-Lite grader → pass→generate / fail→fallback
│
▼
┌──────────────────┐
│ Gemini 2.5 │ ← issue spotter + missing-clause proposer
│ Flash │ (structured JSON output, ≤14 changes)
└──────────┬───────┘
│
▼
┌──────────────────┐
│ Editing Hub │ ← user accepts / rejects / rewrites per change
└──────────┬───────┘
│
▼
redline.docx · clean.docx · memo.docx · register.json
│
▼
Supermemory write (anonymized review_summary, per-session TTL 24h)
Stack
Generation
- Gemini 2.5 Flash
- Flash-Lite (anonymizer)
Retrieval
- Cohere Embed v4
- Cohere Rerank 3.5
- Neon pgvector
Memory
- Supermemory
- Per-session 24h TTL
Backend
- Flask
- Cloud Run
- Neon Postgres
Export
- python-docx
- docx-revisions
- GCS
Infra
- GCP
- Cloudflare DNS
- Secret Manager
What I learned
The hardest part was the anonymizer-first data flow. Every piece of text that leaves the user's browser needs to be pseudonymized before it hits the LLM or Supermemory, but the two-way map must stay in process memory only — you can't persist the mapping or you've just created a different PII problem. Getting the session lifecycle right (24h hard TTL, 2h idle TTL, GCS lifecycle rule, Cloud Scheduler sweep) took more iterations than the retrieval pipeline did.
Corrective RAG with Cohere Rerank is genuinely better than vanilla vector search for legal text. The reranker understands that "indemnification cap" and "liability ceiling" are the same concept in a way that cosine distance often doesn't.