How to Create Automated Legal Document Assembly
Assemble legal documents automatically from clause libraries and data.
Jay Banlasan
The AI Systems Guy
Automated legal document assembly pulls clauses from a library, fills in client data, and produces complete documents in minutes. I built this for a firm that was copying and pasting between Word documents to assemble custom agreements. One wrong paste and a clause from the wrong client ends up in the wrong contract. This system eliminates that risk.
What You Need Before Starting
- Python 3.8+
- Anthropic API key for clause customization
- A clause library organized by category
- SQLite for the clause database
Step 1: Build the Clause Library
import sqlite3
def init_clause_db(db_path="clause_library.db"):
conn = sqlite3.connect(db_path)
conn.execute("""
CREATE TABLE IF NOT EXISTS clauses (
id INTEGER PRIMARY KEY AUTOINCREMENT,
category TEXT,
name TEXT,
version TEXT DEFAULT 'standard',
text TEXT,
variables TEXT,
notes TEXT
)
""")
conn.commit()
conn.close()
def seed_clauses(db_path="clause_library.db"):
clauses = [
("confidentiality", "standard_nda", "standard",
"Both parties agree to maintain the confidentiality of all proprietary information disclosed during the term of this Agreement and for a period of {{conf_years}} years thereafter.",
"conf_years", "Standard mutual NDA clause"),
("confidentiality", "one_way_nda", "standard",
"{{receiving_party}} agrees to maintain the confidentiality of all proprietary information disclosed by {{disclosing_party}} for a period of {{conf_years}} years.",
"receiving_party,disclosing_party,conf_years", "One-way NDA"),
("termination", "mutual_termination", "standard",
"Either party may terminate this Agreement upon {{notice_days}} days written notice to the other party.",
"notice_days", "Standard mutual termination"),
("termination", "for_cause", "standard",
"Either party may terminate this Agreement immediately upon written notice if the other party materially breaches any provision and fails to cure such breach within {{cure_days}} days.",
"cure_days", "Termination for cause with cure period"),
("liability", "limitation_of_liability", "standard",
"In no event shall either party's total liability exceed {{liability_cap}}.",
"liability_cap", "Standard liability cap"),
("indemnification", "mutual_indemnification", "standard",
"Each party shall indemnify and hold harmless the other party from any claims arising from the indemnifying party's breach of this Agreement or negligent acts.",
"", "Mutual indemnification"),
]
conn = sqlite3.connect(db_path)
for c in clauses:
conn.execute("INSERT INTO clauses (category, name, version, text, variables, notes) VALUES (?,?,?,?,?,?)", c)
conn.commit()
conn.close()
Step 2: Build the Document Assembler
import re
def get_clauses_by_category(category, version="standard", db_path="clause_library.db"):
conn = sqlite3.connect(db_path)
rows = conn.execute(
"SELECT name, text, variables FROM clauses WHERE category = ? AND version = ?",
(category, version)
).fetchall()
conn.close()
return [{"name": r[0], "text": r[1], "variables": r[2].split(",") if r[2] else []} for r in rows]
def assemble_document(doc_config, variables):
sections = []
section_number = 1
for section in doc_config["sections"]:
conn = sqlite3.connect("clause_library.db")
clause = conn.execute(
"SELECT text FROM clauses WHERE name = ? AND version = ?",
(section["clause"], section.get("version", "standard"))
).fetchone()
conn.close()
if clause:
text = clause[0]
for key, value in variables.items():
text = text.replace(f"{{{{{key}}}}}", str(value))
heading = section.get("heading", section["clause"].replace("_", " ").title())
sections.append(f"{section_number}. {heading.upper()}\n{text}")
section_number += 1
header = doc_config.get("header", "AGREEMENT")
preamble = doc_config.get("preamble", "").format(**variables) if doc_config.get("preamble") else ""
document = f"{header}\n\n{preamble}\n\n" + "\n\n".join(sections)
missing = re.findall(r"\{\{(\w+)\}\}", document)
return {"document": document, "missing_variables": missing}
Step 3: Define Document Templates
DOCUMENT_CONFIGS = {
"service_agreement": {
"header": "SERVICE AGREEMENT",
"preamble": "This Agreement is entered into as of {effective_date} between {provider_name} (\"Provider\") and {client_name} (\"Client\").",
"sections": [
{"clause": "standard_nda", "heading": "Confidentiality"},
{"clause": "mutual_termination", "heading": "Termination"},
{"clause": "for_cause", "heading": "Termination for Cause"},
{"clause": "limitation_of_liability", "heading": "Limitation of Liability"},
{"clause": "mutual_indemnification", "heading": "Indemnification"},
]
},
"nda": {
"header": "NON-DISCLOSURE AGREEMENT",
"preamble": "This NDA is entered into as of {effective_date} between {party_a} and {party_b}.",
"sections": [
{"clause": "standard_nda", "heading": "Confidentiality Obligations"},
]
}
}
Step 4: AI-Powered Clause Customization
import anthropic
from dotenv import load_dotenv
load_dotenv()
def customize_clause(clause_text, customization_request):
client = anthropic.Anthropic()
response = client.messages.create(
model="claude-sonnet-4-20250514",
max_tokens=1024,
system="""Modify the legal clause as requested. Keep legal precision. Maintain the same structure and formality. Preserve any {{variable}} placeholders. Only change what is specifically requested.""",
messages=[{
"role": "user",
"content": f"Original clause:\n{clause_text}\n\nRequested change:\n{customization_request}"
}]
)
return response.content[0].text
Step 5: Full Assembly Workflow
def build_document(doc_type, variables):
config = DOCUMENT_CONFIGS.get(doc_type)
if not config:
return {"error": f"Unknown document type: {doc_type}"}
result = assemble_document(config, variables)
if result["missing_variables"]:
return {"error": f"Missing: {', '.join(result['missing_variables'])}"}
output_file = f"assembled/{doc_type}_{variables.get('client_name', 'draft').replace(' ', '_')}.txt"
import os
os.makedirs("assembled", exist_ok=True)
with open(output_file, "w") as f:
f.write(result["document"])
return {"document": result["document"], "file": output_file}
if __name__ == "__main__":
result = build_document("service_agreement", {
"effective_date": "January 1, 2026",
"provider_name": "ABC Consulting LLC",
"client_name": "XYZ Corporation",
"conf_years": "3",
"notice_days": "30",
"cure_days": "15",
"liability_cap": "$100,000"
})
print(result.get("document", result.get("error")))
What to Build Next
Add clause comparison. When assembling a document for a repeat client, highlight any clauses that differ from their previous agreement. That catches unintentional deviations from negotiated terms.
Related Reading
- AI for Proposal and Document Creation - document generation
- AI in Legal and Compliance - legal AI
- AI for Workflow Optimization - workflow automation
Want this system built for your business?
Get a free assessment. We will map every system your business needs and show you the ROI.
Get Your Free Assessment