Skip to main content
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

Founder & Editor-in-Chief

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.

🎉 Thank You for Subscribing!

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

Founder & Editor-in-Chief

Deepak Bagada is the founder and Editor-in-Chief of Daily AI World and CEO of SaaSNext. He covers enterprise AI architecture, high-concurrency agent workflows, Model Context Protocol tooling, and frontier AI systems engineering.

Related Intelligence Analysis

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

Cookie & Privacy Preferences

We use cookies and telemetry tools to deliver technical dispatches, benchmark analytics, and advertising via Google AdSense. Review our Privacy Policy.