Building a Contract Review Agent
Let’s build an AI agent that reviews and edits NDAs. If you don’t have an API key yet, sign up for free.Step 1: Upload the Document
import { AgentOffice } from "agent-office-sdk";
import fs from "fs";
const client = new AgentOffice({
apiKey: "YOUR_API_KEY",
});
const fileBuffer = fs.readFileSync("nda.docx");
const file = new File([fileBuffer], "nda.docx", {
type: "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
});
const { docId, markdown } = await client.documents.create({
file: file,
returnMarkdown: true,
});
from agent_office import AgentOffice
client = AgentOffice(api_key="YOUR_API_KEY")
doc = client.documents.create(
file="nda.docx",
return_markdown=True
)
doc_id = doc.doc_id
markdown = doc.markdown
Step 2: Create Agent Tools
import { z } from "zod";
import { randomUUID } from "crypto";
const tools = {
readFile: {
description:
"Use this tool to read the current state of the document as markdown.",
parameters: z.object({}),
execute: async () => {
const result = await client.read({
docId: docId,
readUid: randomUUID(),
});
return result.markdown;
},
},
editFile: {
description:
"Use this tool to directly apply an edit to a small section or paragraph of the document. All changes will be applied immediately, this tool should not be used for adding tracked changes or comments.",
parameters: z.object({
editInstructions: z
.string()
.describe(
"Give clear instructions of the edit to be made. Use first-person, clear descriptions."
),
lookupText: z
.string()
.optional()
.describe(
"A unique text snippet from the section to edit. Provide the exact wording without markdown formatting. Matching uses only alphanumeric characters, so choose distinctive words that uniquely identify the location."
),
}),
execute: async ({ editInstructions, lookupText }) => {
const result = await client.edit({
docId: docId,
editUid: randomUUID(),
editInstructions,
lookupText,
});
return result.editApplied ? "Edit applied" : "Edit failed";
},
},
};
from langchain.tools import tool
from langchain_anthropic import ChatAnthropic
from langchain.agents import create_react_agent, AgentExecutor
from uuid import uuid4
@tool
def read_file() -> str:
"""Use this tool to read the current state of the document as markdown."""
result = client.read(doc_id=doc_id)
return result.markdown
@tool
def edit_file(edit_instructions: str, lookup_text: str = None) -> str:
"""Use this tool to directly apply an edit to a small section or paragraph of the document. All changes will be applied immediately, this tool should not be used for adding tracked changes or comments.
Args:
edit_instructions: Give clear instructions of the edit to be made. Use first-person, clear descriptions.
lookup_text: A unique text snippet from the section to edit. Provide the exact wording without markdown formatting. Matching uses only alphanumeric characters, so choose distinctive words that uniquely identify the location.
"""
result = client.edit(
doc_id=doc_id,
edit_uid=str(uuid4()),
edit_instructions=edit_instructions,
lookup_text=lookup_text,
)
return "Edit applied" if result.edit_applied else "Edit failed"
tools = [read_file, edit_file]
Step 3: Run Your Agent
import { generateText } from "ai";
import { anthropic } from "@ai-sdk/anthropic";
const result = await generateText({
model: anthropic("claude-sonnet-4"),
tools,
prompt: `You are an expert legal assistant.
You represent Quantum Dynamics LLC (the recipient) who have just been sent an NDA by NovaTech Industries.
This NDA has landed on your desk and you must fill in the placeholders with dummy values and make edits to the document so that Quantum Dynamics LLC is protected.
The document content is:
${markdown}
Use the editFile tool to make edits to the document.
After making a large amount of edits, use the readFile tool to read the document and make sure the edits are applied correctly.`,
});
llm = ChatAnthropic(model="claude-sonnet-4")
agent = create_react_agent(llm, tools, prompt_template)
agent_executor = AgentExecutor(agent=agent, tools=tools)
agent_executor.invoke({
"input": f"""You are an expert legal assistant.
You represent Quantum Dynamics LLC (the recipient) who have just been sent an NDA by NovaTech Industries.
This NDA has landed on your desk and you must fill in the placeholders with dummy values and make edits to the document so that Quantum Dynamics LLC is protected.
The document content is:
{markdown}
Use the edit_file tool to make edits to the document.
After making a large amount of edits, use the read_file tool to read the document and make sure the edits are applied correctly."""
})
Step 4: Download the Result
const result = await client.documents.download({ docId: docId });
// Download file from the presigned URL
const fileResponse = await fetch(result.downloadUrl);
const fileBlob = await fileResponse.blob();
// Save to disk
const arrayBuffer = await fileBlob.arrayBuffer();
fs.writeFileSync("edited_nda.docx", Buffer.from(arrayBuffer));
import requests
result = client.documents.download(doc_id=doc_id)
# Download the file
file_response = requests.get(result.download_url)
with open("edited_nda.docx", "wb") as f:
f.write(file_response.content)
Full Code Example
import { AgentOffice } from "agent-office-sdk";
import { generateText } from "ai";
import { anthropic } from "@ai-sdk/anthropic";
import { z } from "zod";
import { randomUUID } from "crypto";
import fs from "fs";
// Initialize client
const client = new AgentOffice({
apiKey: "YOUR_API_KEY",
});
// Upload document
const fileBuffer = fs.readFileSync("nda.docx");
const file = new File([fileBuffer], "nda.docx", {
type: "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
});
const { docId, markdown } = await client.documents.create({
file: file,
returnMarkdown: true,
});
// Define tools
const tools = {
readFile: {
description:
"Use this tool to read the current state of the document as markdown.",
parameters: z.object({}),
execute: async () => {
const result = await client.read({
docId: docId,
readUid: randomUUID(),
});
return result.markdown;
},
},
editFile: {
description:
"Use this tool to directly apply an edit to a small section or paragraph of the document. All changes will be applied immediately, this tool should not be used for adding tracked changes or comments.",
parameters: z.object({
editInstructions: z
.string()
.describe(
"Give clear instructions of the edit to be made. Use first-person, clear descriptions."
),
lookupText: z
.string()
.optional()
.describe(
"A unique text snippet from the section to edit. Provide the exact wording without markdown formatting. Matching uses only alphanumeric characters, so choose distinctive words that uniquely identify the location."
),
}),
execute: async ({ editInstructions, lookupText }) => {
const result = await client.edit({
docId: docId,
editUid: randomUUID(),
editInstructions,
lookupText,
});
return result.editApplied ? "Edit applied" : "Edit failed";
},
},
};
// Run agent
const result = await generateText({
model: anthropic("claude-sonnet-4"),
tools,
prompt: `You are an expert legal assistant.
You represent Quantum Dynamics LLC (the recipient) who have just been sent an NDA by NovaTech Industries.
This NDA has landed on your desk and you must fill in the placeholders with dummy values and make edits to the document so that Quantum Dynamics LLC is protected.
The document content is:
${markdown}
Use the editFile tool to make edits to the document.
After making a large amount of edits, use the readFile tool to read the document and make sure the edits are applied correctly.`,
});
// Download result
const downloadResult = await client.documents.download({ docId: docId });
const fileResponse = await fetch(downloadResult.downloadUrl);
const fileBlob = await fileResponse.blob();
const arrayBuffer = await fileBlob.arrayBuffer();
fs.writeFileSync("edited_nda.docx", Buffer.from(arrayBuffer));
from agent_office import AgentOffice
from uuid import uuid4
from langchain.tools import tool
from langchain_anthropic import ChatAnthropic
from langchain.agents import create_react_agent, AgentExecutor
from langchain import hub
import requests
# Initialize client
client = AgentOffice(api_key="YOUR_API_KEY")
# Upload document
doc = client.documents.create(
file="nda.docx",
return_markdown=True
)
doc_id = doc.doc_id
markdown = doc.markdown
# Define tools
@tool
def read_file() -> str:
"""Use this tool to read the current state of the document as markdown."""
result = client.read(doc_id=doc_id)
return result.markdown
@tool
def edit_file(edit_instructions: str, lookup_text: str = None) -> str:
"""Use this tool to directly apply an edit to a small section or paragraph of the document. All changes will be applied immediately, this tool should not be used for adding tracked changes or comments.
Args:
edit_instructions: Give clear instructions of the edit to be made. Use first-person, clear descriptions.
lookup_text: A unique text snippet from the section to edit. Provide the exact wording without markdown formatting. Matching uses only alphanumeric characters, so choose distinctive words that uniquely identify the location.
"""
result = client.edit(
doc_id=doc_id,
edit_uid=str(uuid4()),
edit_instructions=edit_instructions,
lookup_text=lookup_text,
)
return "Edit applied" if result.edit_applied else "Edit failed"
tools = [read_file, edit_file]
# Run agent
llm = ChatAnthropic(model="claude-sonnet-4")
prompt = hub.pull("hwchase17/react")
agent = create_react_agent(llm, tools, prompt)
agent_executor = AgentExecutor(agent=agent, tools=tools)
agent_executor.invoke({
"input": f"""You are an expert legal assistant.
You represent Quantum Dynamics LLC (the recipient) who have just been sent an NDA by NovaTech Industries.
This NDA has landed on your desk and you must fill in the placeholders with dummy values and make edits to the document so that Quantum Dynamics LLC is protected.
The document content is:
{markdown}
Use the edit_file tool to make edits to the document.
After making a large amount of edits, use the read_file tool to read the document and make sure the edits are applied correctly."""
})
# Download result
result = client.documents.download(doc_id=doc_id)
file_response = requests.get(result.download_url)
with open("edited_nda.docx", "wb") as f:
f.write(file_response.content)
Next Steps
- View pricing to scale your document automation
- Learn about tracked changes for collaborative workflows
- Explore image-based edits for signatures and stamps

