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
Copy
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,
});
Step 2: Create Agent Tools
Copy
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";
},
},
};
Step 3: Run Your Agent
Copy
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.`,
});
Step 4: Download the Result
Copy
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));
Full Code Example
Copy
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));
Next Steps
- View pricing to scale your document automation
- Learn about tracked changes for collaborative workflows
- Explore image-based edits for signatures and stamps

