GLM-Legal 读判决书、提取争议焦点,判决要旨还要回原文核

文章导读
用 GLM-Legal 读判决书,能不能直接引用它给出的争议焦点和裁判要旨,取决于每条摘要能不能回到原文的某一句话。稳妥的做法是:先把判决书按段落切出可定位的结构位置,再让模型只做指认、不做改写,最后逐条回原文比对,把增加、遗漏、改写三类偏差分别标出来。凡是回不到原句的条目,先不要放进对外使用的摘要。
📋 目录
  1. 一 在判决书里切出当事人、诉请、争议焦点、裁判理由
  2. 二 让 GLM-Legal 按结构提取争议焦点和裁判要旨
  3. 三 回原文核对提取内容是否偏离判决表述
  4. 四 标出模型概括与原文不一致的句子
  5. 五 输出可溯源的判决摘要
A A

用 GLM-Legal 读判决书,能不能直接引用它给出的争议焦点和裁判要旨,取决于每条摘要能不能回到原文的某一句话。稳妥的做法是:先把判决书按段落切出可定位的结构位置,再让模型只做指认、不做改写,最后逐条回原文比对,把增加、遗漏、改写三类偏差分别标出来。凡是回不到原句的条目,先不要放进对外使用的摘要。

判决摘要在检索、归类、初步判断时可用,直接引用风险偏高。可执行的做法是:先按段落建立位置编号,要求 GLM-Legal 输出的每条争议焦点和裁判要旨都带段落编号与原句索引;再用脚本或人工把摘要逐条贴回原文核对,原文没有的内容标为增加,原文有而未提取的标为遗漏,换了说法的标为改写。金额、日期、姓名、案号一律以原文为准,核对状态未置为已核的条目不要进入正式摘要。

在判决书里切出当事人、诉请、争议焦点、裁判理由

结构化输入的目的不是把判决书改写成另一种文体,而是给每一段文字一个可以回指的坐标。段落编号加字符起止位置,就足以支撑后面的核对;结构标签只是让模型知道这段属于哪一类,减少它自由概括的空间。判决书常见的结构块包括首部(当事人、案号、审理经过)、诉请与答辩、经审理查明的事实、争议焦点、本院认为部分、裁判依据、判决结果,但具体文书的分段方式差异较大,规则匹配只能打底,标签仍需人工抽查修正。

下面这段 Python 只做两件事:按行切段并记录 start/end 字符位,再按关键词给段落打结构标签。把 raw_text 换成实际读取到的判决书全文即可。关键词列表要结合手上的文书样本调整,不要指望一套规则覆盖所有法院的写法。

import json

def segment(raw_text):
    blocks, cursor = [], 0
    for line in raw_text.split('\n'):
        s = line.strip()
        if not s:
            continue
        start = raw_text.find(s, cursor)
        end = start + len(s)
        cursor = end
        blocks.append({'pid': 'P%04d' % (len(blocks) + 1),
                       'start': start, 'end': end, 'text': s})
    return blocks

LABELS = [
    ('首部',       ['原告', '被告', '上诉人', '被上诉人', '第三人']),
    ('诉请与答辩', ['诉讼请求', '请求判令', '答辩称', '上诉请求']),
    ('查明事实',   ['经审理查明', '本院查明', '一审法院查明']),
    ('争议焦点',   ['争议焦点', '本案争议焦点为']),
    ('裁判理由',   ['本院认为', '本院经审查认为']),
    ('裁判依据',   ['依照', '根据《']),
    ('裁判结果',   ['判决如下', '裁定如下']),
]

def label(blocks):
    current = '首部'
    for b in blocks:
        for name, keys in LABELS:
            if any(k in b['text'] for k in keys):
                current = name
                break
        b['section'] = current
    return blocks

blocks = label(segment(raw_text))
print(len(blocks), blocks[0], blocks[-1])

运行后先看首尾两条的 start/end 能否用 raw_text[start:end] 原样切回该段文字,再抽几段人工确认结构标签。切不回去,说明分段逻辑有问题,后面的核对全部不可信。

结构块识别线索是否允许改写建议引用方式
首部当事人、案号、审理经过不允许照抄,含姓名与案号
诉请与答辩请求判令、答辩称可压缩,不可换意带段落编号
查明事实经审理查明谨慎压缩带段落编号与原句
争议焦点争议焦点、本院归纳不允许原句加段落编号
裁判理由本院认为可摘要,须能回指原句加段落编号
裁判结果判决如下、裁定如下不允许照抄

让 GLM-Legal 按结构提取争议焦点和裁判要旨

这一步的目标是拿到候选摘要,而不是拿到最终结论。关键在于把原文位置变成硬性输出字段:每条争议焦点和裁判要旨都必须给出段落编号、原句索引和字符区间,缺一项就不接收。提示词里要明确禁止扩写和同义替换,找不到原句的条目直接省略,而不是让模型自行补一个看起来合理的说法。

system:
你是判决书信息提取器,只做指认,不做改写。
用户输入为带编号的段落,每段形如 [P0012] 正文。
输出要求:
1. 只输出 JSON,不要解释文字。
2. issues 为争议焦点,holdings 为裁判要旨(本院认为部分的核心判断)。
3. 每条必须包含 para_ids、quote、source_span 三个字段。
4. quote 为原文连续截取,不超过 40 字,不得改写、不得补字。
5. 涉及金额、日期、姓名、案号,原样照抄。
6. 原文没有直接表述的内容不生成;找不到原句的条目直接省略。

输出示例:
{'issues': [{'issue_id': 'I1', 'para_ids': ['P0012'],
             'quote': '本案争议焦点为合同解除时间',
             'source_span': [1234, 1250]}],
 'holdings': [{'holding_id': 'H1', 'para_ids': ['P0031'],
               'quote': '本院认为,合同自通知到达时解除',
               'source_span': [3120, 3145]}]}

接入层可以用通用的对话补全骨架,字段名以实际使用的 SDK 或网关为准,temperature 调低以抑制改写。system 提示词和拼接后的段落文本放在同一次请求里,段落编号必须随文本一起送入,否则模型给出的原句索引无从对应。

POST {base_url}/chat/completions
Content-Type: application/json

{
  'model': 'glm-legal',
  'temperature': 0.1,
  'messages': [
    {'role': 'system', 'content': SYSTEM_PROMPT},
    {'role': 'user', 'content': '[P0001] ...\n[P0002] ...'}
  ],
  'response_format': {'type': 'json_object'}
}

返回后先做格式校验再进下一步:JSON 能否解析、每条 para_ids 是否存在于分段表、source_span 是否落在全文长度范围内、quote 是否为空。任一项不通过就重跑该段,不要带着坏数据往下走。

回原文核对提取内容是否偏离判决表述

核对是整条链路上最不能省的一步。做法很直接:把每条摘要的 quote 拿去和原文对应区间做字符串比对,能精确命中就通过,命中不了再决定是偏移问题还是概括问题。核对结果要落到一张对照表里,逐条记录增加、遗漏和改写三类情况,方便后面复核和留痕。

def check(item, raw_text):
    span = item.get('source_span')
    quote = item.get('quote', '')
    if not quote:
        return 'EMPTY_QUOTE'
    if span and quote in raw_text[span[0]:span[1]]:
        return 'OK'
    if quote in raw_text:
        return 'SPAN_OFFSET_MISMATCH'
    return 'QUOTE_NOT_FOUND'
条目模型摘要段落编号原文原句判定差异类型
I1合同解除时间存在争议P0012本案争议焦点为合同解除时间OK无
H1合同自通知到达时解除且无需催告P0031本院认为,合同自通知到达时解除QUOTE_NOT_FOUND增加(无需催告)
—未提取违约金调整的论述P0044本院认为,违约金过分高于损失应予调整—遗漏

QUOTE_NOT_FOUND 不一定是编造,也可能是模型把两段合成了一句,需要人工读原文判断是改写还是无中生有。遗漏类要靠通读裁判理由段落反向补,脚本本身发现不了。判定为增加或改写的条目,不要就地修正后静默通过,要保留原始输出。

标出模型概括与原文不一致的句子

差异记录的作用是留痕,不是给人看的热闹。每条记录同时保存模型原句、判决原句、差异类型和段落编号,加上处理人和处理时间占位,便于后续追溯是哪一版提示词、哪一次请求产生的偏差。建议用 JSONL 按行追加,方便逐条筛。

{'item_id': 'H1',
 'para_ids': ['P0031'],
 'model_text': '合同自通知到达时解除且无需催告',
 'source_text': '本院认为,合同自通知到达时解除',
 'source_span': [3120, 3145],
 'diff_type': 'ADDED',
 'reviewer': '<处理人>',
 'reviewed_at': '<时间>',
 'status': 'pending'}
  • ADDED:模型写出了原文没有的限定、条件或结论,常见于把多段观点合并。
  • MISSING:原文有明确表述但未进入摘要,多发生在裁判理由的后半段。
  • REWRITTEN:意思接近但用词被替换,金额、日期、主体名称一旦被换就必须回改。
  • SPAN_OFFSET_MISMATCH:quote 能在全文找到但字符区间对不上,通常说明分段或拼接环节有偏移。

记录不要覆盖原值。同一段落多次提取出现的差异,可以按 para_ids 聚类,反过来看是提示词问题还是这一版模型对某类表述不稳定。

输出可溯源的判决摘要

最终交付的摘要,每条都要带段落编号和核对状态,读的人能据此决定是否回查原文。核对状态建议只保留三种:已核(quote 命中且语义一致)、待确认(格式或偏移存疑、需人工判读)、已剔除(判定为增加或无法回指原文)。状态由回原文核对脚本和人工共同置定,模型自己声明的可信度不作数。

[争议焦点-1] 本案争议焦点为合同解除时间
  原文:本案争议焦点为合同解除时间
  位置:P0012  source_span [1234, 1250]
  状态:已核

[裁判要旨-1] 合同自通知到达时解除
  原文:本院认为,合同自通知到达时解除
  位置:P0031  source_span [3120, 3145]
  状态:已核

[裁判要旨-2] 违约金过分高于损失应予调整
  原文:本院认为,违约金过分高于损失应予调整
  位置:P0044  source_span [4010, 4032]
  状态:待确认

交付前做一次回查验证:随机抽几条已核摘要,用 pid 从分段表取回原文,确认文字与摘要一致;把全部待确认条目单独列一份清单交给复核人。需要把摘要用于对外文书、检索库或问答内容时,只放已核条目,待确认和已剔除的留在内部记录里。判决书版本变更或补充裁定时,重新跑一遍分段和提取,旧记录按 para_ids 标记失效,不要直接沿用上一版摘要。