平时做技术实践时,很多问题不是概念不会,而是细节没串起来。拿“基于.NET开发一个AI合同智能体的实践教程”来说,它看着像小点,放到项目里常会牵出环境、配置、兼容性和维护成本。下面按实际采用顺序,把思路、关键写法和容易踩坑的地方讲清楚,便于大家直接对照操作。

结合项目来看,每周一早上,采购团队打开同一个收件箱,看到同一堆东西:供应商协议。主服务协议、保密协议(NDA)、采购订单、补充协议——一二十份新文档,每一份都是对方以 PDF 或 Word 形式发过来的,格式跟你团队见过的任何模板都对不上。
每份协议在这周结束之前,都要走完下面这套流程:
- 实际处理时,有人通读协议,把关键条款拎出来——当事方是谁、合同金额多少、什么时候生效、怎么终止;
- 同样的条款被手工敲进审核跟踪表;
- 协议对照公司正策做检查,借助或打回;
- 在这个场景下,而每一家借助的供应商,都要产出一份全新的合同——打开标准模板、替换十几个字段、导出 PDF——每家供应商都要重复一遍。
在大多数公司,这套流程是手工的、重复的、容易出错的。而这恰好是 AI 文档智能体(AI document agent) 在这个场景下,最擅长解决的活儿。这篇文章会讲清楚为什么,随后用 .NET 构建一个智能体:批量审核一批协议、套用审批规则、为每一家借助的供应商生成一份可直接签署的合同。

一、人工流程为什么累
把人工流程剥开来看,它的形状是这样的:
PDF 合同 → 逐份通读 → 把关键条款敲进 Excel → 人工审批 → 手工填合同模板 → 每家供应商导出一份 PDF
这是一个每一环都有人插手的流水线,正是这种形状让它变得昂贵:
- 同一份文档要被反复读。 实际处理时,审核、登记、起草合同,每一步都要再打开一次协议。同一段文字,被同一批人读三遍。
- 数据全靠手工复制。 从实现思路看,供应商名称、合同金额、付款条款,从 PDF 一格一格搬到电子表格里。抄错、漏项时有发生,没人发现,直到合同发出去出了问题。
- 每家借助的供应商都意味着一个新合同。 打开模板、替换十几个
{{Placeholder}}字段、导出 PDF——每家重复一遍,而法务和财务在意的格式,取决于你能不能每次都做得一模一样。 - 量一上来,计划就崩。 新供应商入库、季度末冲量、一次审计——积压翻倍,团队只能加班。
这些活儿没有一件是难的。它们只是数量多、形态统一,而这恰恰是软件应该吸收的活。
二、思路:一个能"读、判、写"文档的智能体
若有一个组件能做三件事,这套流程就能被压缩掉:
- 读(Understand) 结合项目来看,—— 读懂一份文档。不管供应商发来的是什么格式的协议,都能把当事方、金额、日期、义务拎出来。
- 判(Decide) —— 用你公司的审批规则,对提取出来的信息做判断。
- 写(Manipulate) 结合项目来看,—— 拿一份合同模板加一份过审供应商名单,为每家产出一份格式正确、排版完好的合同。
这就是 AI 聊天机器人(Chatbot)(读文本、回话)与 AI 智能体(Agent)(读文档、动手干活、把结果做成一份新文档)的区别。流水线变成:
文档 → AI 审核 → 结构化数据 → 业务规则 → 过审供应商 → 合同生成
在这个场景下,原来在文件之间复制字段的人,现在只负责审批例外、复核输出。中间环节全部自动化。
三、用 Spire.Agent.Office 在 .NET 中落地
在 .NET 应用里,这套流程能够用 Spire.Agent.Office 实际处理时,来实现——一个文档 AI 智能体 SDK,对 Word、Excel、PowerPoint、PDF 四种文档统一暴露一个 AI() 处理器。设计很轻松:用普通的 Spire API 加载文档,挂上智能体,再用一句自然语言指令描述你要做的事,剩下的一切交给智能体。
安装只需一个 NuGet 包加一个密钥:
dotnet add package Spire.Agent.Office
Spire.Agent.Office 用 SpireToken 实际处理时,激活 AI 功能(官网提供免费版和商业版),兼容 .NET 10,可在 Windows、macOS、Linux 和 Docker 上运行。
第 1 步:一次性设置智能体
实际处理时,四种格式共用同一份设置。新建 AIOptions,设置密钥,把 WorkDir 指向会话和生成文件的落盘目录:
using Spire.Agent.Office.AI;
using Spire.Agent.Office.Extensions;
AIOptions agent = new AIOptions
{
SpireToken = spireToken, // 从你的 Spire.Agent.Office 账户获取
WorkDir = @"C:legal-opswork",
TimeoutMs = 300_000
};
WorkDir 很关键:智能体的会话文件夹都放在这里——稍后我们会进去看看它到底留下了什么。
第 2 步:批量审核收件箱里的每一份协议
核心调用是 ExecuteInstruction(document, instruction, outputPath, attachments):传入文档对象、用自然语言描述需求,得到一个 AIResult。下面把本周到达的每一份 PDF 都加载进来,让智能体生成一份结构化的审核简报:
using Spire.Pdf;
string reviewInstruction =
"审核这份供应商协议,写出 Markdown 简报:用一张单行表格列出当事方、" +
"生效日期、合同金额、付款条款、责任上限和终止通知期,再用项目符号列出" +
"任何对标准供应商协议来说异常的条款。";
foreach (string file in Directory.GetFiles(inboxPath, "*.pdf"))
{
string briefPath = Path.Combine(
briefsPath, Path.GetFileNameWithoutExtension(file) + ".md");
using (PdfDocument agreement = new PdfDocument())
{
agreement.LoadFromFile(file);
AIResult result = agreement.AI(agent).ExecuteInstruction(
agreement, reviewInstruction, briefPath, Array.Empty<string>());
if (result is null || !result.Success)
throw new InvalidOperationException(
$"审核失败 {Path.GetFileName(file)}:{result?.ErrorMessage}");
}
}
在这个场景下,智能体以 PDF 原生格式直接读取文档——不需事先做文本抽取,也不用针对每种排版写规则。几分钟的运行,替代掉一周里"读 + 抄"的部分。
第 3 步:审批决策交给业务规则
这一步是很多人会跳过的,但恰恰是让 AI 工作流"经得起追问"的关键。哪家供应商能过审——这个判断不该交给大语言模型,而应该放在确定性的、可审计的代码里:
// 智能体负责提取,你的代码负责判断。
var approved = new List<ReviewResult>();
foreach (ReviewResult r in ParseBriefs(briefsPath)) // 来自第 2 步
{
bool passes = r.PartyCount >= 2
&& r.LiabilityCap >= 1_000_000m // 我们的责任上限下限
&& r.TerminationNotice <= 60; // 天
if (passes) approved.Add(r);
}
WriteVendorList(@"C:legal-opsdataapproved-vendors.xlsx", approved);
(ParseBriefs 和 WriteVendorList 用你现有的读跟踪表、写表格的方式即可——也能够让智能体把提取结果直接写成 .xlsx、.json 或 .csv。重要的是判断放在哪里:放在代码里,才能被评审、被版本化管理、被审计时引述。)
只有借助的供应商才进入最后一步。
第 4 步:为每一家过审的供应商生成合同
实际处理时,这一步,智能体要做的正是确定性 SDK 需用几十行 Replace 才能做完的事:一份模板加一份过审名单,为每一行产出一份合同。
using Spire.Doc;
string[] attachments = { @"C:legal-opsdataapproved-vendors.xlsx" };
using (Document contract = new Document())
{
contract.LoadFromFile(@"C:legal-opstemplatessupplier-contract.docx");
AIResult result = contract.AI(agent).ExecuteInstruction(
contract,
"为每一位过审的供应商签发一份采购合同:逐行读取 approved-vendors.xlsx," +
"用每家供应商的数据填充本模板中的 {{Placeholder}} 占位符,保持模板的排版与样式," +
"并把每份合同另存为工作目录下的独立 PDF 文件。",
null, // 一家一份,由智能体写入工作目录
attachments);
if (result is null || !result.Success)
throw new InvalidOperationException(
$"合同生成失败:{result?.ErrorMessage}");
}
在这个场景下,模板承载占位符,表格承载数据,一条指令驱动所有合同。条款编号、表格和字体都得以保留——因为输出由确定性的文档引擎生成,而不是让大语言模型即兴发挥去编一个文件格式。再注意这里的 null 输出路径:没有单个得到文件,智能体就会改为每家供应商写一份 PDF——放进 WorkDir 下它自己的会话文件夹里。
四、机制探秘:智能体在磁盘上留下了什么
在这个场景下,这是我最想讲的部分,也是这类智能体能被法务工作流信任的原因。每次调用 ExecuteInstruction,智能体都会运行一个会话(session)从实现思路看,,而每个会话都会以文件的形式留在 WorkDir 下:
C:legal-opswork
└─ .office_use_tmp
└─ Word
└─ 0812093540_c6825d69dd ← 每次 ExecuteInstruction 调用一个会话
├─ process.csx ← 智能体生成的 C# 脚本
├─ input.docx ← 它处理的合同模板
├─ approved-vendors.xlsx ← 它读取的过审供应商名单
├─ output_Brightpath_Analytics_Ltd.pdf
├─ output_Evercrest_Digital_Solutions_Inc.pdf
└─ output_Novalune_Systems_LLC.pdf
会话文件夹是平铺且自包含的:输入文档、生成的脚本、以及每家过审供应商一份的输出 PDF——以供应商命名——直接放在一起。会话 ID 是"时间戳 + 短哈希",批量运行时不会互相覆盖。
智能体并不是靠"魔法"改动你的文件。它会先规划任务,写出一段真实的 C# 脚本(process.csx),用 Spire 文档 API 执行这些操作结合项目来看,,再借助 .NET 脚本运行时运行它,随后把输入和输出都放进会话文件夹。脚本是自包含的:它在开头用 #r "nuget: ..." 指令自行拉取所需的 Spire 包,按自己声明的版本来运行,而不依赖你的应用装了哪些包。
理解这一步时,这一点值得落到实地上看,因为 process.csx 正是你期望一个 .NET 开发者手写的那段代码:
// process.csx —— 智能体生成的脚本(略有删减)
var workDir = Args[0];
using (var wb = new Workbook())
{
wb.LoadFromFile(Path.Combine(workDir, "approved-vendors.xlsx"));
// ... 逐行读出每家供应商的数据,存入 vendors
}
foreach (var v in vendors)
{
var doc = new Document();
doc.LoadFromFile(Path.Combine(workDir, "input.docx"));
doc.Replace("{{Vendor Name}}", v["Vendor Name"]);
doc.Replace("{{Payment Terms}}", v["Payment Terms"]);
doc.Replace("{{Contract Term}}", v["Contract Term"]);
var fileName = "output_" + v["Vendor Name"].Replace(' ', '_') + ".pdf";
doc.SaveToFile(Path.Combine(workDir, fileName), Spire.Doc.FileFormat.PDF);
doc.Dispose();
}
结合项目来看,智能体读取了审批工作簿、为每一行加载模板、逐个替换占位符、每家导出 PDF——随后把脚本留在磁盘上供你阅读。这就是"确定性文档层"在起作用:模型决定填什么,脚本就是完成这件事的普通 Spire API 代码。
这带来三个实实在在的好处:
- 可检视。 打开
process.csx,就能看到对你的模板执行了哪些操作——替换了哪些占位符、PDF 是怎么导出的。没有需凭信的黑盒。 - 可审计。 在这个场景下,会话文件夹就是一份完整记录:输入的文档、脚本、输出的文档,一应俱全。对合同而言,这份纸面记录是"能被辩护的自动化"与"只能祈祷的自动化"之间的分水岭。
- 输出是真正的文档。 落到代码里,因为脚本驱动的是确定性引擎,产出的是格式正确的
.docx或.pdf,而不是一段文本。模型决定做什么,引擎保证文件是怎么构建出来的。
结合项目来看,这正是"文档智能体"与"AI 摘要 PDF"的区别:它会留下可复现、可检视的痕迹,同时把能够直接签署的文件交回到你手上。
五、为什么不用"裸"大模型?
在这个场景下,若手上有大模型 API,很容易忍不住跳过文档层,想用一句提示词"生成合同"。但在合同场景下,这个做法会在三处碰壁:
- 大模型不能可靠地读写 Office 文件。 它看到的是文本,不是
.docx、.pdf的结构。读取 Word 模板、保持表格完整、产出有效 PDF,意味着你得自己搭一套提取与重建的管线。 - 格式就是交付物。 在这个场景下,合同模板承载着条款编号、表格和字体,法务和财务都在意这些。裸大模型只得到文本,丢掉的格式恰恰是接收方最在意的。
- 整个编排都要自己维护。 理解这一步时,提示词设计、解析、文件 I/O、错误处理,全都要变成你的代码,还得扛着大模型的不可确定性。
落到代码里,智能体把模型的语义理解与确定性的文档层结合起来:模型决定提取和填充什么,文档层保证文件本身。这就是"演示"与"能上线的流程"的区别——而且正如我们刚看到的,这个区别是实实在在地写在磁盘上的。
六、这套模式不止能处理合同
落到代码里,注意,这条流水线——理解 → 判断 → 操作 → 批量——本身没有任何合同特有的东西。同一个智能体,换一条指令、换一份模板,就能处理团队下一步想自动化的活儿:
- 简历筛选:从一文件夹 PDF/DOCX 简历里提取候选人信息,套用招聘规则,生成录用通知。
- 发票处理:从 PDF 里抽出发票号、供应商、金额、税额,标出异常,生成付款报告。
- RFP 响应:读懂需求文档,对照公司能力库匹配信息,起草提案。
- 供应商报价对比:把 PDF 和 Excel 里的报价标准化,逐行对比,生成采购分析报告。
每一类都是同一套架构:一条指令进、一份真实文档出,磁盘上留着一个会话文件夹,记录刚才发生了什么。
七、总结
实际处理时,合同审核与生成,是最快能从 AI 文档智能体拿到回报的场景之一:一批 PDF 进来,一份由代码决定的过审名单,随后每一家过审的供应商一份可直接签署的合同——每份文件背后,还有一个能够审计的 process.csx 会话。
从实现思路看,我们构建的这套流程每份文档只要几秒,替代掉法务分析师一周里"读 + 抄"的部分,同时把两件不该交给 AI 的事牢牢留在循环之外:二选一的审批决策放在代码里,最后文档保持为真实、格式正确的文件。
理解这一步时,以上就是基于.NET开发一个AI合同智能体的完整教程的详细内容,更多关于.NET开发AI合同智能体的资料请关注脚本之家其它相关文章!
- C# 原生编码智能体运行时 SharpClawCode详解
- 解锁.NET 11 中 Microsoft.Extensions.AI 在智能后端开发的深度实战指南
- 采用C#在.NET中实现高效提取PDF文档中的图片
- C# .NET实现为PDF文档添加水印