☰
DeepSeek法律文书摘要全流程:从446页PDF到精简文本的工程实践
2026/10/8 2:27:46 网站建设 项目流程

简介:这是一份面向法律科技从业者与NLP算法工程师的DeepSeek法律文档智能摘要技术方案,核心目标是借助抽象式文本生成,在压缩篇幅的同时保留法律效力,适用于诉讼材料归档、合同审查与合规摘要等场景。资源为单个PDF文件,大小12.47MB,共446页、50个章节,支持目录跳转、书签大纲与章节快速定位。内容从法律文本结构化解析、术语图谱构建、数据清洗,到预训练模型选型、混合损失函数设计、超参数调优与分布式训练,形成完整技术链路;同时覆盖小样本标注、数据增强、法律效力要素标注体系、实体关系事件抽取、损失曲线监控等工程化细节。文档结构清晰,知识点密度高,目前已有141人学习下载,适合用于理解法律AI落地方案并快速定位相关技术模块。

1. 一份446页PDF到两页精简文书:DeepSeek法律摘要方案到底解决谁的痛点

446页的法律文书放在桌面上,人工摘要通常是三个律师助理一周的工时;换用DeepSeek加一条抽象式文本生成链路,解析、分块、摘要、效力校验四步走完,得到一份两三页、关键效力字段原样保留的精简文书。这套方案的核心不是把PDF整本丢给大模型“通读一遍”,而是先把PDF解析成结构化的文本块,再让DeepSeek按法律文书的分节逻辑逐段抽象摘要,最后用硬字段核对守住法律效力的底线。适合三类人:被合同、判决书、监管答复淹没的法务;需要批量处理历史文书的合规团队;以及在做法律工具产品的开发者——他们真正缺的不是大模型,而是把“摘要结果还能不能拿去用”这件事量化出来的工程手段。

2. 抽象式摘要 vs 抽取式摘要:法律文书的“改写”为什么必须由模型来完成

2.1 “抽取式”在法律长句面前的两个硬伤

法律文书摘要的第一道选择题是:用抽取式(extractive)还是抽象式(abstractive)。抽取式的思路是从原文里挑句子拼接,好处是结果一定忠于原文,坏处是法律判决书里的长句根本没法直接用。一份判决书的“本院认为”段落里,经常出现一个长达一百多个字的长句,里面嵌套了“虽……但……故……”的多层转折,抽取式会把整个长句原样搬进摘要,结果“摘要”和原文一样长。抽象式文本生成则不同,它让模型理解语义后重新组织语言,把长句里的并列理由拆成独立要点,把“原告主张……被告辩称……本院经审理认为……”这类程序性套话压缩成一行。

另一个硬伤是信息密度。抽取式按句子为单位保留,而法律文书的实质信息往往分散在句子的从句里。一个合同纠纷判决里,真正关键的可能只有“合同于2023年3月1日解除”和“被告应支付违约金人民币12万元”两个事实,但这两个事实各自藏在两个长句的从句里。抽取式要么把整句全摘出来,要么漏掉从句。抽象式可以直接生成“合同解除日:2023年3月1日;违约金:人民币12万元”这样的高密度表达,这也是标题里“要点快速提取”的来源——本质上不是提取,而是让模型生成更紧凑的复述。

2.2 法律文书摘要的真正难点:效力字段的忠实度

抽象式摘要最被人诟病的风险是幻觉——模型在改写过程中可能“顺手”把金额四舍五入,把当事人名称简写,甚至丢掉“判决如下”之后的某一条主文。在法律文书场景里,这个问题不能用“反正最后有人工复核”来搪塞,因为效力字段一旦被改写,文书的可用性就没了。我一般把法律文书里的信息分成三类:

一是效力硬字段,包括案号、当事人全称、诉讼标的金额、判决主文条款、日期、管辖依据。这些字段必须与原文逐字一致,哪怕一个逗号都不能动。

二是效力软字段,包括“本院认为”部分的裁判理由、事实认定逻辑。这些内容可以压缩改写,但改写后不能改变因果关系和否定关系。

三是纯程序性表述,比如“如不服本判决,可在判决书送达之日起十五日内上诉”“本案案件受理费由被告负担”。这些句子要不要保留取决于输出篇幅,如果精简文书的使用场景是内部速览,可以整体压缩成一句“程序性事项完整保留在原文第X页”。

后面所有工程的展开都围绕第一条展开:先定义哪些是硬字段,然后让摘要链路对这些字段做“绕行处理”,而不是寄希望于模型自觉。

2.3 定义不可改写的效力硬字段:代码先于提示词

提示词里写“请保持金额准确”是远远不够的。模型在长文本生成中一旦进入“概括模式”,对数字的注意力会下降,十次里有三四次会把“人民币1,234,567元”改写成“约123万元”。我的做法是在进入提示词之前,先用规则引擎把硬字段从原文里抽出来独立存放,生成摘要后拿硬字段清单做强制比对,发现缺失就回填或触发重新生成。

# hard_fields.py # 效力硬字段的预抽取:这一步先于任何模型调用 import re def extract_hard_fields(text: str) -> dict: fields = {} # 案号,兼容“(2024)京01民终1234号”和“〔2024〕浙民初88号” m = re.search(r"[((]?\d{4}[))]?\s*[省市京沪津粤苏浙皖闽赣鲁豫鄂湘琼渝川贵云陕甘青宁蒙新藏吉辽黑]\S*?(?:民初|民终|民再|执|刑初|行初)\s*\d+号", text) fields["case_no"] = m.group(0) if m else None # 金额:把中文大写金额也纳进来,常见于判决主文 amounts = re.findall(r"(?:人民币)?[\d,,]+\.?\d*\s*元", text) cny_words = re.findall(r"人民币[\u4e00-\u9fa5]+(?:元|圆)", text) fields["amounts"] = amounts + cny_words # 日期,统一成xxxx年x月x日 dates = re.findall(r"\d{4}年\d{1,2}月\d{1,2}日", text) fields["dates"] = dates # 判决主文:取“判决如下/裁定如下”之后到“本判决为终审判决”之前的段落 m = re.search(r"(?:判决如下|裁定如下)[\s\S]*?(?:本判决为终审判决|本裁定为终审裁定)", text) fields["main_claim"] = m.group(0) if m else None return fields

这段代码的逻辑是先于模型把效力字段“锁定”。amounts用的正则覆盖了阿拉伯数字和中文大写两种写法,因为判决主文里“人民币壹拾贰万元整”这种写法很常见,只匹配阿拉伯数字会漏。main_claim的抽取尤其重要——判决主文部分在后面生成摘要时直接走“保留原文”分支,不进改写链路。参数方面需要注意正则的容错:案号部分不同法院的括号写法不统一,所以用了“((数字化))”这种兼容写法;如果实际文书里还有“字第”这类表述,要按语料情况补正则分支。

2.4 DeepSeek作为生成底座的中文长文本优势

硬字段抽完之后才轮到模型。选DeepSeek做抽象式摘要底座,主要有三个现实原因。第一是中文长文本的指令遵循能力:法律文书摘要的prompt往往要写很长,既要有角色设定、输出格式约束,又要嵌入原文片段,DeepSeek在这类长上下文指令下的稳定性比同量级模型好一些。第二是API成本,446页的PDF拆成上百个文本块逐块摘要,token消耗不小,DeepSeek的定价让批量处理成为可能。第三是可以本地化部署,数据不出内网的场景下用vllm把DeepSeek跑起来,摘要任务不需要特别高的并发,单卡就能应付。如果你的场景不需要本地部署,直接用官方API配合OpenAI SDK调用即可,代码差异只在一个base_url。

3. 446页PDF的解析与分片:扫描件、表格和跨页条款先整理成可喂给DeepSeek的语料

3.1 先判断PDF是文本型还是图片型:两行代码做体检

拿到446页PDF,第一件事不是急着抽取文本,而是体检:这份PDF到底是文本型还是图片型。文本型PDF可以直接抽取文字层,图片型PDF本质上是扫描件,必须走OCR。判断方法很简单——抽取一页文本,如果返回空字符串或只有几个孤立的词,那这一页基本就是图片。

# pdf_probe.py import pdfplumber def probe_pdf(path: str, sample_pages: int = 10): text_page_count = 0 with pdfplumber.open(path) as pdf: total = len(pdf.pages) for i in range(min(sample_pages, total)): page = pdf.pages[i] txt = page.extract_text() or "" if len(txt.strip()) > 20: # 超过20个字符视为有文本层 text_page_count += 1 ratio = text_page_count / min(sample_pages, total) if ratio > 0.8: print("文本型PDF:直接进入文本抽取") elif ratio < 0.2: print("图片型PDF:需要OCR,参考3.3节") else: print("混合型PDF:逐页判断,文本页抽文本,图片页走OCR")

probe_pdf的作用是避免在一条链路上浪费算力。混合型PDF在法律文书中很常见,尤其是“正文是电子排版、最后一页盖章扫描”的情况。参数sample_pages取10页足够判断整体类型,如果PDF前10页是封面目录、后436页是扫描正文,抽样会误判——所以更稳妥的做法是把sample_pages提到30,或者先按页码段抽样。我的经验是法律文书的PDF往往封面是图片、正文是文本,混合型的概率比想象的高,不要跳过这一步。

3.2 文本抽取与表格转写:pdfplumber的一个参数坑

文本型页面用pdfplumber的extract_text()就能拿到文本,但法律文书里经常有表格——赔偿计算表、费用明细表、当事人信息对照表。extract_text()对表格的处理是“按阅读顺序拼文字”,结果表格的单元格边界信息全部丢失,表头和数据行被揉成一团。这种情况需要用extract_tables()把表格结构显式提取出来,再转写成带分隔符的文本块,否则后续分片会把一张表的表头和第一行数据切成两段。

# pdf_to_text.py import pdfplumber def page_to_text(page) -> str: text = page.extract_text() or "" tables = page.extract_tables() if not tables: return text table_blocks = [] for idx, table in enumerate(tables, 1): lines = [] for row in table: cleaned = [str(c).replace("\n", " ").strip() if c else "" for c in row] lines.append(" | ".join(cleaned)) table_blocks.append("[表格{} 共{}行]\n{}".format(idx, len(table), "\n".join(lines))) return text + "\n\n" + "\n\n".join(table_blocks)

这里有个坑:extract_tables()默认的参数是{"vertical_strategy": "lines", "horizontal_strategy": "lines"},它只按页面里的线条来切分表格。很多法律文书扫描进PDF后表格线是模糊的,检测不到线条就会返回空列表。遇到这种情况要把策略改成"text",按文本间隙推断单元格边界,代价是可能把同一列里间距较大的两段文本误判成两个单元格。我的做法是先用默认参数抽一遍,如果发现表格行数明显偏少,再改用text策略重抽。表格转写成文本时用竖线做分隔符,是因为竖线在后续提示词里不容易和正文混淆,模型看到竖线就知道这是表格数据,不能随意压缩。

3.3 OCR兜底:pdf图片中文设置与扫描件的“水印污染”

图片型PDF只能走OCR。常见的做法是用PaddleOCR跑中文识别,它自带中英文模型,但有两个设置直接影响结果质量。第一个是“pdf图片中文设置”——如果你直接把PDF页面转成图片再送进OCR,要注意PaddleOCR识别中文时有一个默认参数rec_img_shape,中文和英文的输入尺寸不一样,处理混合文本时用默认的"3, 48, 320"通常没问题,但扫描件里的中文如果字号偏大,建议把det_limit_side_len调大,防止检测框把“合同”截成“同”。

第二个坑是水印污染。很多法律文书PDF是扫描的复印件,页面上带着“仅供内部参考”“副本”这类深色水印。OCR会把水印当成正文一起识别出来,混进文本层之后,模型摘要时可能把“副本”两个字也当成文书内容。解决方式是在OCR之前先做人眼都看得出的预处理:把页面转成灰度图,再用阈值过滤掉颜色较浅的像素。这一步不是必须的,但遇到带水印的扫描件,它能省掉后面大量的清洗时间。OCR的结果会有识别错误,最典型的是“第(一)”被识别成“第(一)”——括号全半角不一致。这类错误在硬字段二次抽取时会造成正则匹配失败,所以OCR之后要加一道全半角统一清洗,把中文括号统一转英文括号,再进分片链路。

3.4 分片按文书结构走:固定字数切分会把“第八条”切成两半

文本抽出来之后,接下来是分片。很多做文本摘要的人习惯按固定字符数切分——每2000字一片——但这个做法在法律文书上是灾难。判决书里“第八条”和“第九条”之间可能只隔着三行,固定字符切分随时会把“第八条”两个字切到上一片末尾,把“的约定如下”切到下一片开头。模型摘要时看不到完整条款,生成的要点自然是残缺的。

正确的分片策略是按文书结构边界切。法律文书的结构标志非常稳定:章、条、款、判决主文、附则。我一般用正则先标记所有结构边界,再在边界之间做合并,保证每一片要么是一个完整的条,要么是几个完整的条。单条太长时再按句子边界二次切分,但绝不从条款中间硬切。

# chunk_by_structure.py import re STRUCT_BOUNDARY = re.compile( r"(第[一二三四五六七八九十百千0-9]+[章条节]|判决如下|裁定如下|本院认为|依照.*?的规定,判决)" ) def chunk_document(text: str, max_chars: int = 3000): # 先按结构边界切出“边界段” segments = [] current = [] last_end = 0 for m in STRUCT_BOUNDARY.finditer(text): if m.start() > last_end: current.append(text[last_end:m.start()]) current.append(m.group(0)) last_end = m.end() # 当前累计长度达到阈值,或遇到“判决如下”这种强边界,就封片 joined = "".join(current) if len(joined) >= max_chars or m.group(0) in ("判决如下", "裁定如下"): segments.append(joined) current = [] if current: segments.append("".join(current)) return segments

chunk_document的核心逻辑是“只允许在边界处封片”。max_chars=3000是给DeepSeek摘要留的余量:如果按3000字一片,加上提示词模板和硬字段清单,单次请求的输入token大约在4500到5500之间,这个长度既不会撑爆上下文窗口,也不会因为文本太长导致模型摘要时丢失尾部信息。遇到“判决如下”强制封片,是为了保证判决主文单独成片,后面生成摘要时可以直接整段保留。分片之后建议顺手给每片记录页码范围——正则匹配时用pdfplumber拿到每个结构边界所在的页码,后面生成摘要时让模型输出“原文页码”,这个页码在最终精简文书里就是可追溯性的来源。

4. 用DeepSeek生成保留法律效力的精简版文书:提示词模板与温度参数的落地配置

4.1 标准提示词模板:效力约束写在系统指令里

分片完成之后进入生成阶段。我在生产环境里用的提示词分两层:系统指令固定不变,用户消息里动态嵌入“页码+原文+硬字段清单”。系统指令里必须写清三条禁令:不得补充外部知识、不得改写硬字段、输出必须带页码。不要在用户消息里才写这些约束,模型对系统指令遵循度更高,尤其是DeepSeek这类经过RLHF的对话模型,把“禁止”类约束放在system角色里效果明显更好。

# summarize.py # 用OpenAI SDK调用DeepSeek官方API,base_url按官方文档配置 from openai import OpenAI import os client = OpenAI( base_url="https://api.deepseek.com", api_key=os.environ["DEEPSEEK_API_KEY"], ) SYSTEM_PROMPT = ( "你是资深法律文书摘要助手。你的任务是把用户提供的法律文书片段压缩成要点。" "硬性规则:1)案号、当事人全称、金额、日期、判决主文必须与原文逐字一致,禁止简写、禁止四舍五入;" "2)不得添加原文不存在的法律依据或事实;" "3)输出格式为三行一组:原文页码 / 原句要点 / 改写后要点;" "4)原文中没有的内容一律不写。" ) def summarize_chunk(chunk_text: str, page_label: str, hard_fields: dict, model: str = "deepseek-chat"): user_prompt = ( f"【原文页码】{page_label}\n" f"【效力硬字段】{hard_fields}\n" f"【原文】\n{chunk_text}\n" ) resp = client.chat.completions.create( model=model, messages=[ {"role": "system", "content": SYSTEM_PROMPT}, {"role": "user", "content": user_prompt}, ], temperature=0.2, max_tokens=1500, top_p=0.5, ) return resp.choices[0].message.content

这段代码里的关键参数有三个。temperature=0.2是摘要任务的基准值,允许模型在组织语言时有少量灵活性,但远低于0.7的通用对话值。top_p=0.5是配合低温度用的核采样参数,进一步截断低概率词,防止模型在改写时“灵光一现”换掉某个法律术语。max_tokens=1500是单片的输出上限,按每片文本3000字计算,改写成要点一般在800到1200字之间,1500足够但不会给模型留“自由发挥”的空间。

需要留意的是user_prompt里把hard_fields显式传给了模型。这一步的作用是提醒模型:这些字段是清单项,生成时如果某个字段在正文里出现,必须原样保留。但不要依赖这个提醒,真正的保底是第2.3节里那段正则会单独抓这些字段,生成完成后做回填核对。

4.2 生成摘要的四项参数:temperature 0.2不是玄学

很多第一次做文本摘要的人会把temperature调高,认为这样生成结果更有“多样性”。这个想法在法律文书场景里是反的。摘要任务需要的是忠实度和确定性,不是创造性。我把常用参数列成一张表,按场景选值:

参数推荐值作用与调整建议
temperature0.1~0.30.1适合判决主文摘要,0.3适合“本院认为”部分的理由改写
top_p0.5固定0.5,与temperature联动,避免高概率词被完全锁死
max_tokens1200~2000单片输入越长,输出上限要同步调大;多留buffer但别超过2500
presence_penalty0摘要任务开启presence_penalty会让模型刻意换词,反而伤忠实度

presence_penalty是大多数人忽略的参数。通用对话里把它设为0.3可以防止模型车轱辘话来回说,但摘要任务里我们恰恰希望模型把同一术语原样重复,“违约金”就是“违约金”,不能为了表达多样性换成“违约赔偿款”。所以presence_penalty在摘要任务里一律设0。frequency_penalty同理,保持默认0即可。

4.3 分层生成:章节摘要→文书级摘要的两遍法

446页的PDF分片后可能有150到200片,每一片单独摘要之后,还需要一层全局归纳——把所有片段的摘要合并成一份精简版文书。这个过程我用的map-reduce两遍法:map阶段用上一节的summarize_chunk处理每个分片,reduce阶段把所有分片的摘要按页码顺序拼接,再让DeepSeek做第二轮抽象。reduce阶段的提示词和map阶段完全不同:

# reduce.py def summarize_document(chunk_summaries: list[dict], model: str = "deepseek-chat"): joined = "\n\n".join( f"[第{i+1}部分,原文第{s['page']}页]\n{s['summary']}" for i, s in enumerate(chunk_summaries) ) reduce_prompt = ( "以下是446页法律文书的逐部分摘要。请把所有部分合并成一份精简版文书," "要求:按争议焦点归类,同一焦点的证据和理由合并;" "判决主文部分逐条保留,放在文书末尾;" "所有案号、当事人、金额、日期不得改动;" "每个要点末尾标注其来源页码,格式为【原文第X页】。" ) resp = client.chat.completions.create( model=model, messages=[ {"role": "system", "content": SYSTEM_PROMPT}, {"role": "user", "content": reduce_prompt + "\n\n" + joined}, ], temperature=0.1, max_tokens=4000, ) return resp.choices[0].message.content

reduce阶段把temperature降到0.1,因为这一轮要做的是“合并”而不是“改写”,自由度更低。max_tokens提到4000,因为全文摘要的输出比单摘要长得多。有一个细节:map阶段的摘要列表里保留了页码信息,reduce阶段把页码作为文本输入再传给模型,这样最终输出的精简文书里每个要点都能溯源到原文位置。如果reduce阶段发现某段摘要的页码丢了,不要硬猜,回到map阶段重新生成那一片。

4.4 本地部署DeepSeek时的采样参数调整

官方API适合快速验证,但法律文书往往涉及数据合规,文本不能出内网。这种情况下常见做法是用vllm在内部服务器上部署DeepSeek,代码层只需要把base_url改成vllm服务暴露的地址,OpenAI SDK的兼容层可以直接对接。本地部署时的采样参数和官方API有一定差别:vllm服务默认的temperature是0,如果你通过vllm的API访问时不显式传temperature,所有请求都会变成贪心解码,摘要质量会显得“过于死板”——所有内容都按最高概率词生成,长句读起来像机器翻译。所以本地部署时反而要把temperature显式传0.2到0.3,让模型在组织语言时保留一点浮动空间。vllm部署的命令参数里建议设置max-model-len为32768以上,否则长分片会被截断;换模型版本时也要同步检查tokenizer的上下文长度配置,这个参数不一致会直接导致长文档摘要缺失尾部内容。

5. 避坑:摘要结果在法律效力校验时最容易翻车的5类现象

5.1 金额被四舍五入:让数值绕开生成链路

现象:原文写“人民币1,234,567元”,摘要输出变成“人民币123万余元”。原因:抽象式文本生成模型在压缩长文本时,对数字的心理表征是“近似值”,不是精确值。这是一个系统性问题,不是提示词能彻底解决的。解决:金额字段在进模型之前已经由第2.3节的extract_hard_fields抽出,生成后做字符串比对,发现摘要里的金额数字不在原始金额列表里就直接报警并触发重新生成。如果重试三次还是不一致,就让金额字段绕过生成链路——摘要文本里留一个占位符【金额见原文第X页】,由脚本把原始金额回填进去。这是唯一稳妥的做法。

5.2 当事人名称被“简称化”:白名单与后处理核对

现象:原文反复出现“北京华信科技有限公司”,摘要里变成“华信公司”或“北京华信”。原因:模型的压缩本能会优先压缩高频名词,当事人全称是最常被压缩的对象。解决:在extract_hard_fields里把“原告/被告/第三人/上诉人/被上诉人”后面跟的单位全称全部抽出来,生成后逐一对比。注意一个细节:如果原文里同时出现了全称和简称(比如首次出现全称,后面写“以下简称华信公司”),要让模型按首次出现的全称统一回填,否则一份精简文书里两种叫法混用,在法律语境里会造成主体混淆。

5.3 “驳回其他诉讼请求”被摘要逻辑吞掉

现象:原文“判决如下:一、被告支付……;二、驳回原告其他诉讼请求”,摘要把第二句删了。原因:模型在抽象压缩时倾向于保留“有内容”的条款,而“驳回其他诉讼请求”“驳回上诉,维持原判”这类否定性、程序性条款在模型看来信息量低,容易被当作套话滤掉。但这恰恰是判决主文的效力要件。解决:判决主文部分(从“判决如下”到“本判决为终审判决”)在分片阶段就单独切出,摘要时完全不进改写链路,而是用文本压缩的方式原样保留,只去掉空行和重复标点。法律文书摘要不是所有内容都要“摘要”,该原样保留的段落不要省。

5.4 条款序号从“第八条”变成“第8条”

现象:原文“第八条”,摘要变成“第8条”。原因:模型对中文数字和阿拉伯数字的转换没有一致性约束,会在一次生成里混用两种写法。这在法律文书里会引起引用混乱,“根据第8条”和“根据第八条”在条款指向上没有歧义,但作为正式精简文书拿出去用,格式上就不严谨。解决:在reduce阶段的提示词里加一条强制规则:“条款序号必须沿用原文写法,中文数字保持中文数字,阿拉伯数字保持阿拉伯数字”。同时在硬字段核对时对比序号写法,肉眼检查随机抽取的段落里有没有混用——这一项靠正则很难完全覆盖,因为数字写法转换后语义不变,只能靠规则加上抽查双保险。

5.5 OCR把印章读成“仅供参考”混进正文

现象:扫描件PDF经过OCR后,文本里出现“仅供参考”“副本”等字样,摘要模型把这些当成正文一起压缩进要点。原因:OCR识别的是图像里的所有文字,包括水印和印章,而且印章文字通常颜色浅、笔画粘连,识别结果经常是断词残句,比水印更隐蔽——比如“合同专用章”可能被识别成“合同专 用 章”,中间带空格,清洗时容易被忽略。解决:OCR处理后做一轮关键词过滤,把“仅供参考”“副本”“复印件”“盖章无效”这类标记词连同所在行一起删除。但要注意不能删得太激进,有些文书正文里真的有“副本”两个字,比如“本件与原件核对无异”是效力表述,不能删。我一般维护一个黑名单词表,只删“水印词+语气词”的组合,比如“仅供参考”单独出现才删,“本件与原件核对无异”保留。

6. 输出前的复核技巧:十页抽样与双模型对照,别再信第一次生成的摘要

6.1 十页抽样复核法

全部自动生成之后,人工复核不要从第1页开始翻,而是按页码做分层抽样。我的习惯是每50页抽2页,重点看三类内容:判决主文所在页、合同里带金额的页、以及OCR识别过的扫描页。抽样页要对着原文逐句比对摘要要点,确认硬字段没有遗漏。446页的文书抽18页左右,人工耗时约40分钟,这个成本远低于全文复核。

6.2 让第二个模型当校对员

两遍法生成完的精简文书,我还会跑一个双模型对照校验:用另一个低温度实例对同一份摘要做“找茬”,让它逐条核对摘要里的硬字段是否与原文一致。第二个模型不需要很高的temperature,反而要把temperature设为0,让它以挑错为唯一任务。脚本拿到校验结果后,把所有“不一致”的条目输出成一个清单,人工只需要看清单,不用再全文比对。

# cross_check.py def cross_check(original_text: str, summary_text: str, model: str = "deepseek-chat"): check_prompt = ( "你是一名文书质检员。下面给出了法律文书原文和精简摘要。" "请逐项核对:案号、当事人全称、金额、日期、判决主文条款。" "只要发现不一致,就按格式输出:字段名 / 原文内容 / 摘要内容。" "没有不一致时输出:全部一致。" ) resp = client.chat.completions.create( model=model, messages=[ {"role": "system", "content": "只输出核对结果,不做任何解释。"}, {"role": "user", "content": f"{check_prompt}\n\n【原文】\n{original_text}\n\n【摘要】\n{summary_text}"}, ], temperature=0, max_tokens=1000, ) return resp.choices[0].message.content

cross_check函数的输入建议不要传446页全文,而是按分片逐一核对,否则上下文窗口塞不下。核对结果里的“不一致”清单如果超过十条,说明生成链路有问题,优先检查分片逻辑而不是提示词——我踩过最惨的一次就是分片脚本把“第八条”和后续内容切成两片,结果两片摘要里各缺一半条款,双模型校验一口气报了13条不一致,全是同一个原因。从那以后我养成了习惯:先跑一次分片统计,把每一片的开头前20个字打印出来看一眼,确认所有条款边界完整,再进生成链路。这个动作只需要一分钟,能省掉后面两小时的返工。希望帮到你。

本文还有配套的精品资源,点击获取

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询