Inspiration

Contract review is one of the most time-consuming and error-prone tasks in legal and business workflows. A single missed indemnification clause or an "automatic renewal" buried in page 12 can cost companies thousands. I wanted to build an agent that actually reads contracts the way a senior lawyer would — not just keyword matching, but reasoning about risk.

What it does

Legal Risk Compass is a multi-step AI agent that analyzes contract clauses and rates their legal risk:

  • 🔴 Strict — High exposure clauses (indemnification, unlimited liability, perpetual terms)
  • 🟡 Moderate — Clauses worth negotiating (auto-renewal, exclusivity, confidentiality)
  • 🟢 Standard — Balanced, common commercial terms

The agent follows a Search → Retrieve → Reason → Advise pipeline:

  1. Search — Uses platform.core.search tool to run hybrid search across the legal document corpus in Elasticsearch
  2. Retrieve — Uses platform.core.get_document_by_id to fetch full clause text
  3. Reason — The LLM reasons about legal risk using the retrieved context
  4. Advise — Returns a risk rating + actionable recommendation

How I built it

  • Elastic Agent Builder — Configured a custom agent named "Legal Risk Compass" with 5 active tools: platform.core.search, platform.core.get_document_by_id, platform.core.get_index_mapping, and platform.core.list_indices
  • Elasticsearch (serverless) — Indexed legal documents (NDAs, SAFEs, service agreements) using the attachment pipeline for PDF/text extraction into the search-obog index
  • Next.js 16 + React 19 — Built a chat-style web UI that calls the agent via a secure server-side API route (API key never exposed to browser)
  • Tailwind CSS 4 — Color-coded risk cards (red/amber/green) with skeleton loading states

Challenges

  • Agent Builder API endpoint discovery — The Kibana Agent Builder chat API path is not publicly documented. I had to probe multiple endpoint patterns (/api/, /internal/) to find the correct one, while maintaining a direct Elasticsearch search fallback so the UI never breaks.
  • Elasticsearch v9 client — The @elastic/elasticsearch v9 SDK dropped the body wrapper; all query parameters now go at the top level. The TypeScript types also changed significantly.
  • Structured output from unstructured PDFs — Getting consistent risk-relevant snippets from attachment-pipeline-indexed PDFs required careful field mapping across attachment.content, attachment.title, and fallback fields.

What I learned

  • How to configure multi-tool agents in Elastic Agent Builder with custom system prompts that enforce a reasoning chain
  • How Elasticsearch's hybrid search (BM25 + vector) retrieves more relevant legal clauses than keyword search alone
  • How to build a production Next.js app that securely proxies AI agent calls server-side

Built With

Share this project:

Updates