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:
- Search — Uses
platform.core.searchtool to run hybrid search across the legal document corpus in Elasticsearch - Retrieve — Uses
platform.core.get_document_by_idto fetch full clause text - Reason — The LLM reasons about legal risk using the retrieved context
- 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, andplatform.core.list_indices - Elasticsearch (serverless) — Indexed legal documents (NDAs, SAFEs, service agreements) using the attachment pipeline for PDF/text extraction into the
search-obogindex - 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/elasticsearchv9 SDK dropped thebodywrapper; 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
- agent
- builder
- css
- elastic
- elasticsearch
- next.js
- react
- tailwind
- typescript
Log in or sign up for Devpost to join the conversation.