| name | contract-clause-analyzer |
| title | Contract Clause Analyzer |
| description | Analyze construction contract clauses. Identify risks, obligations, and key terms using NLP. |
| author | datadrivenconstruction |
| author_url | https://github.com/datadrivenconstruction/DDC_Skills_for_AI_Agents_in_Construction/tree/main/1_DDC_Toolkit/Document-Control/contract-clause-analyzer |
| license | MIT |
| version | 0.1.0 |
| execution_mode | open |
| jurisdiction | general |
| practice | construction |
| language | en |
Contract Clause Analyzer
Business Case
Problem Statement
Contract review is time-consuming and error-prone:
- Important clauses missed
- Risk provisions overlooked
- Inconsistent interpretation
- Long review cycles
Solution
AI-assisted contract clause analysis that identifies key provisions, flags risks, and extracts critical terms.
Technical Implementation
import pandas as pd
from datetime import datetime, date
from typing import Dict, Any, List, Optional
from dataclasses import dataclass, field
from enum import Enum
import re
class ClauseType(Enum):
SCOPE = "scope"
PAYMENT = "payment"
SCHEDULE = "schedule"
CHANGE_ORDER = "change_order"
TERMINATION = "termination"
INDEMNIFICATION = "indemnification"
INSURANCE = "insurance"
WARRANTY = "warranty"
DISPUTE = "dispute"
LIABILITY = "liability"
FORCE_MAJEURE = "force_majeure"
SAFETY = "safety"
COMPLIANCE = "compliance"
OTHER = "other"
class RiskLevel(Enum):
HIGH = "high"
MEDIUM = "medium"
LOW = "low"
INFO = "info"
:
clause_id:
section:
title:
text:
clause_type: ClauseType
risk_level: RiskLevel
key_terms: [] = field(default_factory=)
obligations: [] = field(default_factory=)
deadlines: [] = field(default_factory=)
amounts: [] = field(default_factory=)
notes: =
:
contract_name:
analyzed_date: datetime
total_clauses:
clauses: [ContractClause]
risk_summary: [, ]
key_dates: [[, ]]
key_amounts: [[, ]]
:
RISK_KEYWORDS = {
: [, , , , ,
, , , ],
: [, , , , , ,
, , ],
: [, , , , , ]
}
CLAUSE_PATTERNS = {
ClauseType.PAYMENT: [, , , ],
ClauseType.SCHEDULE: [, , , ],
ClauseType.CHANGE_ORDER: [, , , ],
ClauseType.TERMINATION: [, , ],
ClauseType.INDEMNIFICATION: [, , ],
ClauseType.INSURANCE: [, , , ],
ClauseType.WARRANTY: [, , , ],
ClauseType.DISPUTE: [, , , ],
ClauseType.LIABILITY: [, , ],
ClauseType.FORCE_MAJEURE: [, , ],
}
():
.clauses: [ContractClause] = []
() -> AnalysisResult:
.clauses = []
sections = ._split_into_sections(text)
i, section (sections):
clause = ._analyze_clause(, section)
.clauses.append(clause)
risk_summary = {
: ( c .clauses c.risk_level == RiskLevel.HIGH),
: ( c .clauses c.risk_level == RiskLevel.MEDIUM),
: ( c .clauses c.risk_level == RiskLevel.LOW)
}
key_dates = []
key_amounts = []
clause .clauses:
d clause.deadlines:
key_dates.append({: clause.clause_id, : d})
a clause.amounts:
key_amounts.append({: clause.clause_id, : a})
AnalysisResult(
contract_name=contract_name,
analyzed_date=datetime.now(),
total_clauses=(.clauses),
clauses=.clauses,
risk_summary=risk_summary,
key_dates=key_dates,
key_amounts=key_amounts
)
() -> [[, ]]:
sections = []
pattern =
parts = re.split(pattern, text)
current_title =
i, part (parts):
re.(, part):
current_title = part.strip()
part.strip() current_title:
sections.append({
: current_title,
: part.strip()
})
current_title =
sections text.strip():
sections.append({: , : text.strip()})
sections
() -> ContractClause:
text = section.get(, )
title = section.get(, )
text_lower = text.lower()
clause_type = ._determine_type(text_lower)
risk_level = ._assess_risk(text_lower)
key_terms = ._extract_key_terms(text)
obligations = ._extract_obligations(text)
deadlines = ._extract_dates(text)
amounts = ._extract_amounts(text)
ContractClause(
clause_id=clause_id,
section=clause_id,
title=title,
text=text[:] + (text) > text,
clause_type=clause_type,
risk_level=risk_level,
key_terms=key_terms,
obligations=obligations,
deadlines=deadlines,
amounts=amounts
)
() -> ClauseType:
clause_type, keywords .CLAUSE_PATTERNS.items():
(kw text kw keywords):
clause_type
ClauseType.OTHER
() -> RiskLevel:
high_count = ( kw .RISK_KEYWORDS[] kw text)
medium_count = ( kw .RISK_KEYWORDS[] kw text)
high_count >= :
RiskLevel.HIGH
high_count >= medium_count >= :
RiskLevel.MEDIUM
medium_count >= :
RiskLevel.LOW
RiskLevel.INFO
() -> []:
patterns = [
,
,
]
terms = []
pattern patterns:
matches = re.findall(pattern, text)
terms.extend(matches[:])
((terms))[:]
() -> []:
patterns = [
,
,
]
obligations = []
pattern patterns:
matches = re.findall(pattern, text, re.IGNORECASE)
obligations.extend(matches[:])
obligations[:]
() -> []:
patterns = [
,
,
,
]
dates = []
pattern patterns:
matches = re.findall(pattern, text, re.IGNORECASE)
dates.extend(matches)
dates[:]
() -> []:
patterns = [
,
,
]
amounts = []
pattern patterns:
matches = re.findall(pattern, text, re.IGNORECASE)
amounts.extend(matches)
amounts[:]
() -> [ContractClause]:
[c c .clauses c.risk_level == RiskLevel.HIGH]
():
pd.ExcelWriter(output_path, engine=) writer:
summary_df = pd.DataFrame([{
: result.contract_name,
: result.analyzed_date,
: result.total_clauses,
: result.risk_summary[],
: result.risk_summary[],
: result.risk_summary[]
}])
summary_df.to_excel(writer, sheet_name=, index=)
clause_data = [{
: c.clause_id,
: c.title[:],
: c.clause_type.value,
: c.risk_level.value,
: .join(c.key_terms[:]),
: (c.obligations),
: .join(c.deadlines[:]),
: .join(c.amounts[:])
} c result.clauses]
pd.DataFrame(clause_data).to_excel(writer, sheet_name=, index=)
output_path