AI agents NDA redlining is now practical for engineering teams that need to review hundreds of mutual NDAs against a standard playbook. This post walks through building a deterministic pipeline where an agent extracts clauses, proposes edits, and emits a redlined Word document—without manual copy-paste or fragile regex.
Step 1: Ingest the source NDA and split clauses
Start by extracting text from the executed or draft NDA. PDFs are common; .docx is easier because you keep paragraph structure. Use pypdf for PDFs and python-docx for Word files.
from pypdf import PdfReader
def extract_pdf_text(path: str) -> str:
reader = PdfReader(path)
return "\n".join(page.extract_text() or "" for page in reader.pages)
text = extract_pdf_text("mutual_nda.pdf")
Clause boundaries matter. Most NDAs number clauses as 1., 1.1, or Article I. Split on a regex that captures leading numerals, then keep the clause ID as metadata.
import re
CLAUSE_RE = re.compile(r"(?m)^\s*(\d+\.\d+|\d+|Article\s+\w+)\.\s")
def split_clauses(text: str):
parts = CLAUSE_RE.split(text)
# parts: [preamble, id1, body1, id2, body2, ...]
clauses = []
for i in range(1, len(parts), 2):
cid = parts[i].strip()
body = parts[i+1].strip()
clauses.append({"id": cid, "text": body})
return clauses
clauses = split_clauses(text)
Verify success: print len(clauses) and inspect the first clause’s text. If the split merges two clauses, tighten the regex.
Step 2: Define a redlining playbook as code
The agent needs a target standard. Encode your legal team’s preferences as a JSON schema or a Python dataclass. Keep it machine-checkable.
{
"governing_law": {
"allowed": ["Delaware", "New York"],
"default": "Delaware"
},
"confidentiality_period_months": {
"max": 36,
"default": 24
},
"liability_cap": {
"forbid": ["uncapped"],
"default": "twice the fees paid"
}
}
Load it and expose it to the agent as a system prompt fragment. The playbook is the source of truth; the LLM proposes deviations only when the clause violates it.
import json
with open("playbook.json") as f:
PLAYBOOK = json.load(f)
SYSTEM_PROMPT = f"""You are a contract redlining agent.
Compare each clause to the playbook:
{json.dumps(PLAYBOOK)}
Output JSON edits where the clause diverges.
"""
Step 3: Run the AI agents NDA redlining loop with tool calls
Call an OpenAI-compatible chat endpoint. If you want one endpoint that addresses 240+ models with automatic fallback when a provider is degraded, point the client at n4n.ai’s OpenAI-compatible URL and use the same message format. The agent should return structured edits, not prose.
from openai import OpenAI
client = OpenAI(base_url="https://api.n4n.ai/v1", api_key="YOUR_KEY")
def propose_edits(clause: dict) -> list[dict]:
resp = client.chat.completions.create(
model="gpt-4o-mini",
response_format={"type": "json_object"},
messages=[
{"role": "system", "content": SYSTEM_PROMPT},
{"role": "user", "content": json.dumps(clause)}
],
temperature=0.0,
)
data = json.loads(resp.choices[0].message.content)
return data.get("edits", [])
edits = []
for c in clauses:
edits.extend(propose_edits(c))
Each edit should look like:
{
"clause_id": "7.2",
"original": "This Agreement governed by laws of California.",
"proposed": "This Agreement governed by laws of Delaware.",
"reason": "Playbook requires Delaware or New York."
}
The loop is the core of AI agents NDA redlining: it maps each clause to a delta against the playbook. Keep temperature at zero and validate the JSON schema before applying.
Step 4: Apply edits as tracked changes in Word
python-docx does not expose tracked changes natively, but you can inject w:ins and w:del XML elements. Build a helper that replaces a paragraph’s text with a deletion run and an insertion run.
from docx import Document
from docx.oxml.ns import qn
from docx.oxml import OxmlElement
def redline_paragraph(paragraph, original: str, proposed: str):
# Clear existing runs
for r in paragraph.runs:
r._element.getparent().remove(r._element)
# Deletion
del_run = paragraph.add_run(original)
del_run.font.strike = True
rpr = del_run._element.get_or_add_rPr()
del_elem = OxmlElement("w:del")
del_elem.set(qn("w:id"), "1")
rpr.append(del_elem)
# Insertion
ins_run = paragraph.add_run(proposed)
ins_run.font.underline = True
rpr2 = ins_run._element.get_or_add_rPr()
ins_elem = OxmlElement("w:ins")
ins_elem.set(qn("w:id"), "2")
rpr2.append(ins_elem)
doc = Document("mutual_nda.docx")
# Map clause text to paragraph (simplified: first match)
for edit in edits:
for p in doc.paragraphs:
if edit["original"] in p.text:
redline_paragraph(p, edit["original"], edit["proposed"])
break
doc.save("mutual_nda_redlined.docx")
For production, match on clause IDs rather than substring. The code above is minimal but shows the XML mechanics. The output document opens in Word with strikethroughs and underlines—standard redline visual language.
Step 5: Verify the redline and close the loop
Verification is engineering, not lawyering. Write a test that asserts every edit’s original no longer appears verbatim in the saved file and that proposed is present.
from docx import Document
def verify_redline(path: str, edits: list[dict]):
doc = Document(path)
full_text = "\n".join(p.text for p in doc.paragraphs)
for e in edits:
assert e["original"] not in full_text, f"Original not removed: {e['original']}"
assert e["proposed"] in full_text, f"Proposed missing: {e['proposed']}"
return True
assert verify_redline("mutual_nda_redlined.docx", edits)
Run pytest on this. If it passes, the AI agents NDA redlining pipeline produced a consistent document. For legal sign-off, route the redlined doc to a human reviewer via your existing workflow—the agent handles volume, the lawyer handles judgment.
Operational notes
Cache provider responses per clause hash. NDAs repeat boilerplate; a memoization layer cuts token spend by 40–60% on batch runs. Forward cache-control hints if your gateway honors them.
Keep the playbook in version control. When counsel updates the liability cap, the diff is reviewable and the agent inherits the change on the next run.
If a clause is ambiguous, the agent should return an edits: [] and flag needs_review: true. Do not let it guess on indemnification scope. The goal of AI agents NDA redlining is to remove mechanical drift from the playbook, not to negotiate.
What you shipped
You now have a pipeline that ingests a contract, compares it to a coded playbook, proposes precise clause edits via an LLM, and writes a Word redline with tracked-change markup. The verification step ensures the output is structurally correct before it reaches a reviewer. That is the entire loop, runnable today on a laptop.