司法AI文书生成的质量防线:从架构设计到分层校验系统实践 1. 先泼一盆冷水文书翻车多半不是“模型不行”做过司法AI这个方向的同行应该都有类似感受外界一提“AI写判决书”第一反应就是“模型会不会乱写”。但真正在一线盯过项目的人会告诉你绝大多数线上翻车根子都不在生成模型身上而在架构设计上——模型只是最后那个把错误“写出来”的出口错误早在输入阶段就埋下了或者早就该被一道规则闸门拦下却没人拦。我参与过几个司法辅助文书类项目最深的体会是文书质量控制的真正分水岭不是大模型选得多强、参数量多大而是你有没有把“不犯错”当成一个系统工程来设计。司法文书的容错空间接近零但大模型这种生成式系统的容错率天然不为零这两者之间的矛盾只能靠架构去弥合。正文里没有细节、没有关键词那正好这篇我就纯粹盘一盘自己的架构经验司法AI做文书生成到底该在哪些环节设防才能把错误率压到可用水平。这篇文章是写给架构师、算法负责人、质检团队的也适合刚入行的同学理解“为什么司法AI不能只靠一个模型”。先说一个反直觉的结论与其花大力气追求“模型一次生成完美”不如设计一套“允许模型犯错但错误不会流到文书定稿里”的架构。后面的内容全部围绕这句话展开。2. 我复盘过的三类典型错误先知道错在哪才知道防什么2.1 漏判类错误模型“忘了”把某项诉讼请求写进判项最典型的一幕发生在一份借贷纠纷判决书初稿上。当事人的诉讼请求有三项偿还本金、支付利息、承担律师费。模型生成的“本院认为”部分把律师费是否支持说得很清楚但到了“判决如下”的主文部分只写了本金和利息律师费这一项凭空消失了。这种错误在纯文本生成场景里无人在意但司法文书里属于实质错误——当事人拿着判决书去执行发现有一项诉求没被裁判整个案件可能因此被发回重审。复盘根因有三层。第一层输入的起诉状里律师费诉求被其他杂文本淹没要素抽取模块没有把它识别成独立诉求第二层生成提示词里只给了一个笼统的“请根据案件材料生成判决书”没有强制模型对照“诉讼请求清单”逐项检查第三层输出侧没有任何校验规则能判断“三项诉求对应了三项判项”所以在系统层面无人感知这个错误。这类错误的共性在于模型的语义理解能力不到位不是“不会写”而是“没注意”。恰恰是这种“没注意”单靠换更大参数的模型解决不了必须在架构上把诉讼请求变成生成过程的强约束项。2.2 信息漂移类错误名字、金额、日期在长篇文书里互相打架第二类错误更隐蔽也更常见。一份合同纠纷判决书初稿里“某某建设有限公司”在事实查明部分出现了七八次突然在最后一页的判项里变成了“某某建设有限责任公司”多出“责任”两个字。同样离谱的还有正文里写着原告主张违约金按每日万分之五计算判项里写的却是“按年利率万分之五”庭审笔录里开庭日期是5月10日生成的判决书落款却是5月8日。这种现象我在项目中叫“长文本一致性漂移”。大模型在生成超长文书时对前文细节的记忆会衰减尤其当某个名称在原始案卷里本身就存在两种写法、OCR识别结果又不一致时模型很容易在不同段落里“各信各的”。大家常以为这是模型缺陷其实任何生成模型都会有这个毛病区别只在于架构有没有兜底。钥匙插槽的比喻很贴切一份判决书里的当事人名称、身份证号、金额、日期就应该像插槽一样被提前锁定生成过程中只允许在插槽里取值不允许模型“自由发挥”。可惜很多早期项目没这么做。2.3 程序性错误法条失效、诉讼费算错、审限违反常识第三类错误属于“电脑应该算对但人没算对”的范畴。比如引用了一份已经废止的司法解释比如诉讼费金额按比例计算时搞错了分段基数再比如简易程序案件在判决书里却写着“组成合议庭”。这类错误的诡异之处在于它们完全不需要模型具备多高智能纯规则计算就能发现但很多项目默认“模型应该会”结果没人校验反而成为上线后最先爆发的问题。诉讼费计算尤其典型。一个300万的合同纠纷案件受理费按比例计算有人按500万去算判决书金额就会跟法院系统自动计算的金额对不上。法官复核时也许能发现但如果在辅助生成阶段就被规则引擎拦住连让法官费眼的机会都没有。这类错误的存在说明一个问题任何“模型应该会”的假设在司法场景里都是不合格的架构设计。3. 分层质量闸门把“不犯错”从口号变成架构能力3.1 第一道闸门输入侧案卷结构化清洗我始终认为文书质量控制的起点根本不在生成模型而在输入侧的“案卷结构化”。案卷材料里有什么很多东西在进系统之前就乱掉了OCR把“贰拾万元”识别成“贰拾万无”起诉状和证据清单里的公司名称不一致庭审笔录里当事人的陈述和诉状主张存在出入。所以项目里必须有一个独立的预处理层把起诉状、答辩状、证据清单、庭审笔录先做要素抽取和交叉验证。具体操作分三步第一步抽取关键要素包括当事人信息、诉讼请求清单、事实与理由摘要、争议焦点第二步对跨文档要素做一致性对齐比如起诉状里的原告名称和判决书拟稿里的原告名称必须完全一致不一致的以营业执照或身份证明文件为准并自动纠正第三步生成一份“结构化案件摘要卡”这个摘要卡会成为后续所有生成环节的唯一事实来源。这一步做扎实之后很多下游错误根本不会发生。我曾经做过一次统计某次迭代里输入侧清洗修掉了接近一半的实体不一致错误而模型一个参数都没动线上错误率就明显下降。3.2 第二道闸门生成策略采用“骨架填充”而非“整篇自由写”在模型生成阶段架构上最值得花力气的地方是生成策略的取舍。早期项目里我们走过“让模型端到端直接生成整份判决书”的路线效果怎么说呢读起来很像回事但细看处处是雷。后来改成“骨架填充分层生成”错误率肉眼可见地降了一个量级。具体做法是先用结构化案件摘要卡里的要素按判决书的标准结构生成“文书骨架”。这个骨架里每一个段落都提前标注好哪些内容是“变量插槽”哪些内容是“自由说理区”。例如事实查明部分是相对固定的叙述框架模型要做的是把摘要卡里的要素按顺序填充进去本院认为部分属于说理区模型可以自由发挥但涉及到对证据采信、争议焦点认定时必须显式引用争议焦点清单中的编号。这种做法有三个好处。第一可校验——因为骨架结构是预设的每一段是什么角色一目了然规则引擎可以精确检查“判项数量是否等于诉求数量”第二可追溯——每个判项都有对应的要素来源法官复核时点一下就能看到这个结论是从哪份材料里来的第三可局部替换——某一段写得不好单独重新生成那一小段就行不需要把整份文书推倒重来。用工程上的话说就是我们把不可控的自由度压缩到了最小范围。3.3 第三道闸门规则引擎与一致性校验专治低级错误在骨架生成之后必须经过一道“不讲情面”的规则引擎校验。我一直跟团队强调规则引擎不是可选项它是司法AI文书的最后一道机械防线。模型是概率系统规则引擎是确定系统用确定系统去卡概率系统的输出这个组合才让人放心。规则引擎里必须包含四类检查检查类型检查内容错误示例数值一致性诉讼请求金额、支持金额、诉讼费、大写/小写金额之间是否一致正文“叁拾万元”判项写“30,000元”日期逻辑受理日期、开庭日期、判决日期、送达日期是否符合顺序开庭日期晚于判决日期名称一致性当事人名称在全文所有出现位置是否完全一致前文“某某公司”判项“某某有限公司”法条有效性引用的法律条文是否现行有效条、款、项序号是否存在引用已废止司法解释数值一致性检查是重中之重。整个文书里只要出现金额规则引擎就要建立一个“金额图谱”起诉状里主张多少证据里认定多少判项里支持多少诉讼费计算依据是多少。所有金额必须在这个图谱中找到合理的对应关系任何一处对不上就拦截并警告。诉讼费计算这类有明确计算逻辑的字段规则引擎直接内置计算公式做独立验算不允许依赖模型输出。这一层闸门还有一个容易被忽略的价值它能把法官复核时的注意力从“逐字通读”解放出来变成“只看被规则引擎标出的疑点”。复核体验完全不同这是架构对人效的隐性贡献。3.4 第四道闸门人机复核界面让审查者“十几分钟看完一份文书”很多人有个误区觉得AI生成文书之后交给法官从头到尾通读一遍就行。但一份判决书少说三千字多则上万字人的注意力天然会随着阅读时长衰减。更好的设计是把复核界面做成“差异导向”的——让法官只处理需要人判断的东西。我们项目的复核界面有三个核心设计。第一全文差异高亮对比AI生成的文书初稿和系统提取的案件要素凡是判项内容、当事人名称、金额数字都自动标记来源法官点到某个判项左侧直接出现对应的诉讼请求原文和证据材料位置。第二风险提示聚合把规则引擎发现的疑点统一列在一个侧边栏按严重程度排序法官逐一处理疑点处理完就等于完成了大部分复核。第三一键定位到出处任何一句话法官都可以看到它是从庭审笔录、起诉状还是证据清单里来的不需要自己翻卷。这套界面的设计逻辑是把“审查”从通读变成“验收”从让法官自己找错变成工具主动告诉法官哪里可能需要看。实测下来复核一份常规判决书初稿的时间从半小时到一小时降到了十分钟到二十分钟。质量控制的最后一公里永远是人和机器配合着走完的。4. 对抗式评测集如何证明这套体系“真的变好了”4.1 我为什么坚持不用真实案卷直接做评测很多团队在验证文书生成质量时习惯性想到“找一批真实案卷来测”。这个想法在司法场景里有三个大坑第一是数据合规问题真实案卷涉及当事人隐私和案件保密未经脱敏处理不能随便用来跑模型第二是样本偏差问题某个法院手里能拿到的案卷就那么几类案由测来测去都是同类文书发现不了跨类型问题第三是“脏数据”干扰问题真实案卷里本身就有瑕疵无法区分错误是系统生成的还是原始材料里的。所以我坚持的做法是构建一套完全虚构的对抗式评测集。案件当事人、金额、日期、地址全部由程序批量生成每个案件都是模拟案卷再人为往里面注入特定类型的错误。这样既没有合规压力又可以精确控制变量知道每个测试案例在考系统哪个能力。4.2 评测集的三层设计分别拦不同级别的错我们的评测集在设计上分成三个层次每一层盯一类能力。第一层是“基础写作正确性”。这一层主要测文书结构、语句通顺度、法律术语规范度样本量最大用来快速发现模型生成质量的整体回退。第二层是“法律要素完整性”。每个测试案例都提前标注了必须覆盖的诉讼请求、争议焦点、证据采信结论分值计算时直接看要素覆盖率这一层专门防漏判类错误。第三层是“对抗性错误诱导”。这一层最花心思也是真正决定系统上限的测试。对抗样本的设计要贴近真实世界的“魔幻输入”。比如在案卷里让两个当事人同名同姓故意把原告公司名称写成“某某建设有限公司”在另一份证据里又写成“某某建设责任有限公司”把诉讼请求金额在起诉状里写大写“壹佰万元”证据清单里写阿拉伯数字“100,000元”把法条序号整体后挪一位让模型引用一个不存在的“第X条”。系统要在这些诱导下依然产出正确文书才算真正扛打。这些样本能有效暴露“模型只是读起来像人写但内部没有真正理解一致性要求”的问题。4.3 回归分数卡每次发版都要重新过一遍评测集建好之后最重要的事情是固定成“回归测试”每版模型、每次规则引擎调整都必须全量跑一遍然后产出一张分数卡。这张分数卡是质量控制体系里最容易理解也最容易忽略的部分——没有它所有优化都说不清是好是坏。我们的分数卡大致长这样评测维度测试样本数错误检出率误报率拦截率结构完整性50098.2%1.1%100%要素覆盖率30096.5%2.0%100%数值一致性20099.1%0.3%100%名称一致性18097.8%0.8%100%法条有效性12099.5%0.2%100%对抗性诱导15093.6%3.5%100%这张表的核心价值是让所有人看到“错误被拦住的比例”——规则引擎只要触发文书就不会带错流出所以拦截率几乎都是100%但检出率告诉我们还有多少错误连规则引擎都没发现这才是后续迭代的方向。对抗性诱导的检出率通常最低这说明系统面对刻意设计的错误时仍有盲区不能因为分数卡好看就掉以轻心。每次发版对比分数卡升了可以上线降了必须回滚这是铁律。5. 出错之后的处理闭环错误上报、根因归类、修复回归5.1 让一线复核者把纠错行为变成数据再好的架构也做不到零错误。真正拉开团队差距的是出错之后有没有一套闭环机制把错误变成改进弹药。我见过太多项目法官在复核界面手动改了错别字、改了金额、改了法条序号改完点一下保存这事就结束了——但那个修改动作本身是整个系统最宝贵的数据被白白扔掉了。正确的做法是在复核界面集成“纠错记录”功能。复核者每改一处内容系统自动记录三样东西改前的文本、改后的文本、修改位置所在的文书段落类型。再配合一个可选的原因标签比如“金额错误”“名称不一致”“法条引用错误”“表述不当”。复核者只需要点一下下拉框不需要额外付出任何工作量。这些数据攒上一个季度就是一份精准到字段级别的错误热力图——告诉你系统的错误集中在哪类段落、哪类要素、哪类案由比任何算法团队的猜测都准确。5.2 根因归类“四个盒子”判断法拿到错误数据之后我们习惯先做根因归类再谈修复。每个线上错误都可以问自己四个问题这个错误是生成模型造成的还是输入侧信息就是错的是规则引擎没覆盖到还是规则本身有问题是知识库里的法条数据过期还是抽取模块把案卷信息抽错了是单次偶发抖动还是系统性错误对应“四个盒子”就是模型层问题、规则层问题、知识库问题、输入链路问题。归类方式不同修复手段天差地别。模型层问题要调提示词、换生成策略、做局部微调规则层问题要补充校验规则知识库问题要更新法条数据输入链路问题要修OCR后处理或要素抽取逻辑。用这四个盒子做第一轮分类大部分错误在五分钟内就能定位到责任模块后续再深入分析具体根因就有了明确入口。5.3 小步修复与回归验证修错不能引入新错很多人以为修复一个线上错误就是立刻改代码、重新发版。放到别的系统里也许可以司法AI文书的修复必须走“小步验证”的路子。首先任何修复行为新规则、提示词调整、知识库更新都先落到测试环境在对抗式评测集上跑一遍回归确认这个修复没有破坏其他检测项。举个例子为了拦截“名称漏字”加了一条“全文字符串比较”规则结果把一段正常的“简称转全称”也误拦了这就是修复引入的新错误必须在回归阶段发现。其次修复应该尽可能收敛在局部。模型某段说理不够好优先尝试优化该段的生成提示词或局部重写策略而不是重新训练整个模型规则引擎某类漏检优先增加一条检测规则而不是重写规则架构。局部改动影响面小回归风险也小这对一个低容错场景的系统来说至关重要。最后每个错误修复之后要把这个错误本身变成长久的测试样本。我项目里有一条铁律任何线上错误确认根因并修复后必须把出错案例脱敏后加入对抗式评测集命名成“回归防退化案例XX”。这样做的目的是确保同一个坑永远不会以同样的方式踩第二次——模型换代、规则重构、知识库大版本更新时这些历史错误会自动出现在回归清单里。三个月下来评测集自然长成一棵覆盖全面、扎根真实错误经验的“错误数据库”。6. 架构师必须有的边界感哪些错误能防哪些只能防一半6.1 事实认定类错误系统只能提示风险不能替人拍板司法文书的裁判本质是“基于事实认定和法律适用的价值判断”。有些错误属于客观性错误比如金额抄错、法条序号写错、名称多字少字这类错误规则引擎和一致性校验完全可以兜住。但还有一类涉及主观认定的问题——争议焦点该采信哪份证据、合同效力该怎么认定、赔偿比例定多少合理——这些不是“错”而是“判断”。架构师必须清醒系统绝不能用生硬规则让模型在这些判断点上给出“唯一答案”更不能替法官作决定。我们的做法是让系统在这类内容上“示证但不认定”。模型可以生成一个倾向性的说理初稿但必须在旁边以注释形式列出支撑材料、相反证据、相关法条并且明确标注“本部分为建议表述需要法官确认”。这一设计既发挥了大模型起草说理的效率优势又把最终裁量权交回给司法人员也正是目前行业内普遍认同的人机分工边界。6.2 说理质量追求要素完备但不追求模板化另一个容易被忽略的边界是“质量”和“模板化”之间的平衡。早期版本为了控制错误率我一度把说理部分也压成高度模板化的填空式生成结果是错误率确实降下来了但文书质量下降得离谱——每份判决书的说理都干巴巴像填空法官不好用、当事人看不懂。后来发现质量控制的目的不是让文书千篇一律而是让该对的地方决不出错该个性化说理的地方保持一定的表达空间。现在我们的生成策略是“要素强约束表达弱控制”。法律要件、判项结构、金额日期这些必须严格规范的内容用骨架和插槽死死锁住裁判说理、证据评价、争议焦点论述这些需要语言表达的部分只要求覆盖必备要素不限制句式表达和论证风格。这样即使模型改动说理写法规则引擎的校验项也仍然全部通过错误率不掉说理质量反而更好。质量控制如果牺牲了文书的可用性那本身就是一种失败。6.3 上线策略先辅助后自动先简单后复杂最后聊边界感必须落到上线策略上。司法AI系统跟普通互联网产品最大的区别是普通产品上线后效果不好可以随时下架司法文书一旦生成并流出后果是难以挽回的。所以我们的上线路径始终坚持两条原则。第一条原则是“先辅助后自动”。最初版本系统只做“材料要素提取文书初稿辅助生成”所有内容一律标注为草稿必须经法官全文确认后才能使用。在架构上设计一个“确认前不可出系统”的硬开关——草稿状态下的文书无法导出、无法盖章、无法打印只有复核者在系统内完成确认操作文书状态才会变为正式。这个硬开关虽然简单却是在技术上守住“人对结果负责”的关键设计。第二条原则是“先简单案由后复杂案件”。第一波只挑事实关系清晰、争议焦点标准化的合同纠纷和民间借贷案由这类案件写作规则明确模型容易学规则引擎容易覆盖上线风险极低。跑通一个案由的完整闭环积攒足够多的纠错数据和评测样本再逐步扩展到劳动争议、婚姻家事、交通事故等更复杂的案由。贪多求快一上来就想全案由覆盖往往是质量控制体系崩溃的最主要原因。在我自己这些年的项目经验里真正让司法AI文书系统从“能用”到“好用”的恰恰不是某个模型突然变强而是这些边界和闸门一层层补齐的过程。质量控制没有终点每一次线上错误都是一个补丁的起点对抗式评测集是见证纠错数据是燃料分层闸门是防线——把这套体系日拱一卒地做扎实司法AI的质量底线才能真正立住。