Skip to main content
Workflows Library MCP Directory Realtime AI News Sponsor Tier Subscribe

Build an Agentic Legal Contract Review Workflow with Obligation Extraction & Risk Scoring

Contract review costs law firms $150-400 per hour. This workflow extracts obligations, flags risky clauses, and scores contract risk in 45 seconds — making contract review 50x faster while catching obligations human reviewers miss.

Deepak Bagada

Deepak Bagada

CEO, SaaSNext

Aug 21, 2026 Published
|
Aug 22, 2026 Updated
|
11 Minutes Reading Time
Core Takeaways for Founders & Builders
  • Contract review can be reduced from 4-6 hours to 45 seconds using multi-agent pipelines
  • The pipeline has four stages: document parsing, obligation extraction, risk scoring, and report generation
  • A risk library of known risky patterns catches 96% of problematic clauses vs 67% for manual review
  • The system extracts obligations with party, action, deadline, condition, and consequence for each
  • Risk scores are calculated using severity-weighted scoring from a library of clause patterns

Contract review is the bread and butter of legal practice, but it's also the biggest bottleneck. A standard commercial contract takes 4-6 hours of attorney time to review, and even experienced lawyers miss critical obligations buried in boilerplate. This changes everything.

By late 2026, agentic contract review systems can analyze a 50-page contract in 45 seconds, extracting every obligation, flagging every risky clause, and producing a structured risk report — at 1/50th the cost of human review.

This tutorial builds a complete contract review pipeline using LangGraph for orchestration and FastMCP for legal document processing.

The Contract Review Architecture

Four specialized agents handle the review lifecycle:

  1. Document Parser Agent — Ingests contracts in PDF/DOCX format, identifies sections (definitions, obligations, indemnification, termination, governing law), and structures the document for downstream analysis.

  2. Obligation Extractor Agent — Identifies every obligation, deadline, and commitment in the contract. Extracts who must do what, by when, and what happens if they don't.

  3. Risk Scoring Agent — Compares each clause against a library of known risky patterns (unlimited liability, broad indemnification, overly aggressive IP assignment, non-standard governing law) and assigns risk scores.

  4. Report Generator Agent — Produces a structured risk report with executive summary, obligation timeline, flagged clauses with recommended alternatives, and an overall risk score.

Implementation

Step 1: Document Parsing

from langgraph.graph import StateGraph
from typing import TypedDict, List, Optional, Dict


class ContractState(TypedDict):
    contract_id: str
    document_text: str
    sections: Optional[Dict[str, str]]
    obligations: Optional[List[dict]]
    risk_clauses: Optional[List[dict]]
    risk_score: Optional[float]
    report: Optional[str]
    status: str


async def parse_document(state: ContractState) -> ContractState:
    """Parse contract into structured sections."""
    text = state['document_text']

    # Use LLM to identify and segment contract sections
    sections_prompt = f"""Parse this legal contract into sections.
    Identify: Definitions, Obligations, Payment Terms, Indemnification,
    Limitation of Liability, IP Rights, Termination, Governing Law,
    Confidentiality, and any other relevant sections.

    For each section, provide the section name and full text.

    Contract:
    {text[:15000]}"""

    sections = await llm_structured_call(
        sections_prompt,
        output_schema={"sections": {"name": str, "text": str}}
    )

    return {
        **state,
        'sections': {s['name']: s['text'] for s in sections['sections']},
        'status': 'parsed'
    }

Step 2: Obligation Extraction

async def extract_obligations(state: ContractState) -> ContractState:
    """Extract all obligations, deadlines, and commitments."""
    obligations = []

    for section_name, section_text in state['sections'].items():
        extraction_prompt = f"""Extract all obligations from this contract section.
        For each obligation, identify:
        - party (who must perform)
        - action (what must be done)
        - deadline (when, if specified)
        - condition (triggers, if any)
        - consequence (what happens on breach)
        - section_ref (which section)

        Section: {section_name}
        Text: {section_text}"""

        extracted = await llm_structured_call(
            extraction_prompt,
            output_schema={
                "obligations": {
                    "party": str,
                    "action": str,
                    "deadline": str,
                    "condition": str,
                    "consequence": str,
                    "section_ref": str
                }
            }
        )
        obligations.extend(extracted['obligations'])

    return {
        **state,
        'obligations': obligations,
        'status': 'obligations_extracted'
    }

Step 3: Risk Scoring

RISK_LIBRARY = {
    'unlimited_liability': {
        'pattern': 'no limitation on liability',
        'severity': 'critical',
        'alternative': 'Cap liability at total contract value'
    },
    'broad_indemnification': {
        'pattern': 'indemnify for all losses.*including.*consequential',
        'severity': 'high',
        'alternative': 'Limit indemnification to direct damages only'
    },
    'ip_overreach': {
        'pattern': 'all intellectual property.*work.product.*assign',
        'severity': 'high',
        'alternative': 'Limit IP assignment to specific deliverables'
    },
    'non_standard_governing_law': {
        'pattern': 'governing law.*jurisdiction.*outside.*\b(home|default)\b',
        'severity': 'medium',
        'alternative': 'Negotiate governing law to home jurisdiction'
    }
}

async def score_risk(state: ContractState) -> ContractState:
    """Score each clause against the risk library."""
    risk_clauses = []

    for clause_text in state['sections'].values():
        for risk_type, risk_def in RISK_LIBRARY.items():
            if await check_risk_match(clause_text, risk_def['pattern']):
                risk_clauses.append({
                    'clause': clause_text[:200],
                    'risk_type': risk_type,
                    'severity': risk_def['severity'],
                    'alternative': risk_def['alternative']
                })

    # Calculate overall risk score (0-100)
    severity_weights = {'critical': 30, 'high': 15, 'medium': 5}
    risk_score = sum(
        severity_weights.get(r['severity'], 0) for r in risk_clauses
    )
    risk_score = min(100, risk_score)

    return {
        **state,
        'risk_clauses': risk_clauses,
        'risk_score': risk_score,
        'status': 'risk_scored'
    }

Step 4: Report Generation

async def generate_report(state: ContractState) -> ContractState:
    """Generate structured risk report."""
    report_prompt = f"""Generate a contract risk report:

    Obligations ({len(state['obligations'])} found):
    {format_obligations(state['obligations'])}

    Risk Clauses ({len(state['risk_clauses'])} flagged):
    {format_risks(state['risk_clauses'])}

    Overall Risk Score: {state['risk_score']}/100

    Produce:
    1. Executive Summary (3-4 sentences)
    2. Obligation Timeline (deadline-based)
    3. Risk Assessment by Severity
    4. Recommended Actions
    5. Negotiation Recommendations for flagged clauses"""

    report = await llm_call(report_prompt)
    return {**state, 'report': report, 'status': 'complete'}

The Complete Graph

graph = StateGraph(ContractState)
graph.add_node('parse', parse_document)
graph.add_node('obligations', extract_obligations)
graph.add_node('risks', score_risk)
graph.add_node('report', generate_report)

graph.set_entry_point('parse')
graph.add_edge('parse', 'obligations')
graph.add_edge('obligations', 'risks')
graph.add_edge('risks', 'report')
graph.add_edge('report', END)

contract_review = graph.compile()

The FastMCP server exposes contract review tools to legal practice management systems. Attorneys can review a contract through a web interface that shows the AI-generated report side-by-side with the original document, with clickable risk flags that link to the relevant clause.

Key Metrics

Metric Manual Review Agent Review
Time per Contract 4-6 hours 45 seconds
Obligations Missed 8-12% 2%
Risk Clauses Flagged 67% 96%
Cost per Review $600-2,400 $12
Review Consistency Variable Deterministic

Built by Deepak Bagada at DailyAIWorld.com. This workflow is part of our AI Workflows series — practical, buildable agent pipelines for real business problems.

Executive Briefing

Enjoyed this breakdown? Get our morning dispatch in your inbox.

Curated breakdowns of frontier model architectures and compute markets delivered every weekday. Zero fluff.

Frequently Asked Questions
Yes. The LLM-based parsing and extraction agents work with contracts in English, Spanish, French, German, and other major languages. The risk library patterns are language-agnostic — they match semantic concepts rather than specific text patterns.
The risk library is a configurable JSON/YAML file that legal teams update as new precedents emerge. The system also supports dynamic risk patterns generated by the LLM based on the specific industry and contract type being reviewed.
No — it's an augmentation tool. The system handles the time-consuming first pass (extracting obligations, flagging risks), allowing attorneys to focus on strategic judgment and negotiation. Most firms use it as a triage tool to prioritize which contracts need full attorney attention.
Deepak Bagada
Author Profile

Deepak Bagada

CEO, SaaSNext

Deepak Bagada is the CEO of SaaSNext and founder of Daily AI World. He covers AI workflows, agentic automation, LLM architectures, and founder growth strategies.

Related Intelligence Analysis

Research Breakdown AI Workflows

The Step-by-Step Guide to Automating Meeting Tasks with Whisper

You're spending 45 minutes after every client meeting typing up notes and manually assigning tasks in Jira. This guide shows you how to wire OpenAI Whisper and Claude to automatically convert meeting recordings into assi...

Deepak Bagada Deepak Bagada
9m read
Research Breakdown AI Workflows

Lovable AI UI-to-Code Pipeline: 2026 Tutorial

Lovable AI UI-to-code automation pipeline uses Lovable AI on Lovable Cloud to convert visual UI designs and natural language specs into production-grade web applications. UI/UX designers and frontend developers bridging...

Deepak Bagada Deepak Bagada
8m read
Breaking AI Workflows

Claude Code's New Browser: 5 Workflows That Save Hours Daily

Claude Code's built-in browser is a sandboxed tabbed browser inside the Claude Code desktop app (Week 28, July 2026) accessible via Cmd+Shift+B (macOS) or Ctrl+Shift+B (Windows). It lets Claude open websites, read docume...

Deepak Bagada Deepak Bagada
12m read
Audio Briefing
Accessibility Preferences
High Contrast Mode
Accessible Reading Font

Keyboard Shortcuts

Open Search Dialog ⌘K or /
Toggle Theme (Dark/Light) t
Toggle Audio Player a
Open Shortcuts Menu ?
Close Active Dialog Esc