Portfolio project Live demo

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.

Open the Hub →
Gemini 2.5 Flash Cohere Rerank 3.5 Neon pgvector Supermemory Cloud Run

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.