构建法律AI评估框架:从InsufficiencyBench看LLM处理模糊查询的工程实践
2026/8/24 2:03:10 网站建设 项目流程

在实际的法律咨询场景中,用户提出的问题往往是模糊、不完整或缺乏关键细节的。例如,“我签的合同有效吗?”或“公司能开除我吗?”。这类问题被称为“欠明确查询”。大型语言模型在处理这类问题时,面临着巨大挑战:它可能因信息不足而给出过于笼统、不具操作性的建议,也可能在未澄清事实的情况下做出不准确甚至错误的假设性判断。这种“幻觉”或“过度自信”在严肃的法律领域是致命的。

InsufficiencyBench 正是为了解决这一问题而诞生的基准测试框架。它不是一个简单的问答集,而是一个系统化的评估工具,旨在衡量 LLM 在面对法律领域的欠明确查询时,其回答的充分性、谨慎性和实用性。对于从事法律科技、AI产品开发或对LLM能力边界感兴趣的研究者和工程师而言,理解并应用此类基准,是确保AI辅助工具可靠性的关键一步。

本文将从工程实践的角度,带你理解 InsufficiencyBench 的核心设计,并模拟如何构建一个类似的、用于评估LLM法律建议充分性的本地化测试环境。我们将不涉及复杂的模型训练,而是聚焦于如何设计测试用例、构建评估管道、量化模型表现,并最终将评估结果转化为可指导产品改进的洞察。

1. 理解 InsufficiencyBench 的核心评估维度

在开始动手之前,必须明确我们要评估什么。一个优秀的法律AI助手,在面对信息不足的问题时,其回答不应是终点,而应是高质量对话的起点。InsufficiencyBench 主要从以下几个维度进行考量:

1.1 识别信息缺口的能力

这是最基础的一层。模型能否准确识别出用户查询中缺失的、对法律判断至关重要的信息?例如,对于“合同有效吗?”这个问题,关键缺失信息可能包括:合同当事人的行为能力、合同标的物是否合法、是否履行了法定形式(如登记)、是否存在欺诈或胁迫情形等。模型不应直接回答有效或无效,而应首先指出需要这些信息。

1.2 提出澄清性问题的质量

识别出缺口后,模型如何引导用户补充信息?评估点在于:

  • 相关性:提出的问题是否直接针对法律要件?
  • 具体性:问题是笼统的(“能说说具体情况吗?”)还是具体的(“合同中关于争议解决条款是如何约定的?”)?
  • 结构化:问题是否清晰、有条理,便于用户逐一回答?
  • 中立性:提问是否带有诱导性或预设立场?

1.3 有条件回答的谨慎性

在必须给出一些指引时,模型是否会提供“有条件”的回答?例如,“如果合同双方都具有完全民事行为能力,且合同内容不违反法律强制性规定,那么通常情况下合同是有效的。但是,合同的生效还可能涉及……等因素。” 这种回答明确了前提假设,并指出了其他可能性。

1.4 避免不当假设与幻觉

这是评估的核心负面指标。模型是否在缺乏依据的情况下自行“脑补”了事实?例如,用户仅说“被公司辞退”,模型就断言“公司属于违法解除,你可以要求赔偿金”,而忽略了用户可能严重违纪、试用期不合格等多种合法解雇情形。这种幻觉是法律AI应用中的高风险行为。

2. 构建本地评估环境与数据准备

我们不需要完全复现原论文的完整数据集,但可以构建一个小型的、针对特定法律领域(如劳动合同)的测试集,来模拟评估流程。

2.1 环境与依赖配置

本项目主要使用 Python。建议创建一个干净的虚拟环境。

# 创建并激活虚拟环境 python -m venv venv_insufficiency_bench source venv_insufficiency_bench/bin/activate # Linux/macOS # venv_insufficiency_bench\Scripts\activate # Windows # 安装核心依赖 pip install openai==1.12.0 # 或其他LLM API客户端,如 anthropic, litellm pip install pandas==2.0.3 pip install numpy==1.24.3 pip install scikit-learn==1.3.0 # 用于一些评估指标计算 pip install tqdm==4.66.1 # 进度条 pip install python-dotenv==1.0.0 # 管理API密钥

创建一个.env文件来管理敏感信息(切勿提交至版本库):

# .env OPENAI_API_KEY=your_openai_api_key_here # ANTHROPIC_API_KEY=your_anthropic_api_key_here

2.2 设计测试用例集

我们以JSON格式来定义测试用例,每个用例包含一个欠明确的用户查询,以及与之对应的“理想回答”应包含的要素。

创建一个文件test_cases.json

[ { "id": "labor_001", "domain": "劳动合同", "underspecified_query": "公司今天把我开除了,我能拿到赔偿吗?", "missing_information": [ "解除劳动合同的具体理由(如:严重违纪、不能胜任工作、经济性裁员等)", "你在该公司的入职时间(用于计算经济补偿或赔偿金的年限)", "你的月平均工资数额", "公司解除流程是否合规(如是否提前通知、是否支付代通知金)", "你是否有证据证明公司解除理由不成立" ], "expected_clarifying_questions": [ "公司是以什么理由解除劳动合同的?例如,是认为您严重违纪、不能胜任工作,还是公司进行经济性裁员?", "您在这家公司工作了多久?", "您离职前12个月的平均工资是多少?", "公司是突然通知您离职,还是提前30天书面通知或支付了代通知金?" ], "conditional_answer_template": "根据《劳动合同法》,员工能否获得赔偿(此处指违法解除赔偿金)或经济补偿,关键取决于解除的性质。\n1. 如果公司是违法解除(如无合法理由或程序严重违法),您有权要求支付赔偿金(标准为经济补偿金的双倍)。\n2. 如果公司是合法解除,但符合支付经济补偿金的情形(如协商一致、不能胜任工作经培训调岗后仍不能胜任、经济性裁员等),您有权获得经济补偿金。\n3. 如果公司是合法解除且无需支付补偿的情形(如试用期不符合录用条件、严重违纪等),则无法获得赔偿或补偿。\n因此,需要根据上述提到的具体理由、工作年限和工资情况才能进行准确判断。" }, { "id": "contract_001", "domain": "合同效力", "underspecified_query": "我签的电子合同有效吗?", "missing_information": [ "合同当事人的身份及行为能力(如是否为未成年人)", "合同标的物是否合法", "电子签名的可靠性与认证方式", "合同内容是否违反法律、行政法规的强制性规定或公序良俗", "是否存在欺诈、胁迫或重大误解情形" ], "expected_clarifying_questions": [ "合同双方是什么主体?是个人与个人,个人与公司,还是公司与公司?", "这份电子合同是通过什么平台或方式签署的?是否使用了可靠的电子签名?", "合同的主要内容是关于什么的?", "在签署过程中,您是否完全理解合同条款,是否存在被误导或被迫签署的情况?" ], "conditional_answer_template": "根据《民法典》和《电子签名法》,依法成立的电子合同与纸质合同具有同等的法律效力。但其有效性需满足以下一般要件:\n1. 当事人具有相应的民事行为能力。\n2. 意思表示真实(无欺诈、胁迫等)。\n3. 不违反法律、行政法规的强制性规定,不违背公序良俗。\n此外,可靠的电子签名是保障电子合同效力的关键。需要审查您使用的电子签名是否满足“专有性”、“控制性”和“不可篡改性”等要求。在您补充上述信息后,才能对这份特定电子合同的有效性进行更具体的分析。" } ]

这个结构定义了评估的“标准答案”。missing_informationexpected_clarifying_questions将用于后续的自动和人工评估。

3. 实现LLM调用与回答生成管道

接下来,我们编写一个Python脚本,读取测试用例,调用LLM API获取回答,并保存结果。

创建run_benchmark.py

import os import json import pandas as pd from openai import OpenAI from dotenv import load_dotenv import time # 加载环境变量 load_dotenv() # 初始化OpenAI客户端 client = OpenAI(api_key=os.getenv('OPENAI_API_KEY')) def get_llm_response(query, model="gpt-4-turbo-preview", temperature=0.3): """ 调用LLM API获取对用户查询的回答。 temperature设置较低,使输出更稳定、更少随机性。 """ try: response = client.chat.completions.create( model=model, messages=[ {"role": "system", "content": "你是一个严谨的中国法律AI助手。当用户的法律问题信息不足时,你必须首先识别缺失的关键信息,并提出具体、相关的澄清问题。在必要时,可以给出基于不同假设的有条件分析,但必须明确指出分析的局限性,并避免做出没有事实依据的确定性结论。"}, {"role": "user", "content": query} ], temperature=temperature, max_tokens=1500 ) return response.choices[0].message.content.strip() except Exception as e: print(f"调用API出错: {e}") return f"ERROR: {e}" def main(): # 1. 加载测试用例 with open('test_cases.json', 'r', encoding='utf-8') as f: test_cases = json.load(f) results = [] # 2. 遍历每个用例,调用LLM for case in test_cases: print(f"处理用例 {case['id']}: {case['underspecified_query']}") llm_response = get_llm_response(case['underspecified_query']) # 3. 保存结果 result_record = { "case_id": case["id"], "domain": case["domain"], "query": case["underspecified_query"], "llm_response": llm_response, "missing_info_ground_truth": case["missing_information"], "expected_questions_ground_truth": case["expected_clarifying_questions"] } results.append(result_record) # 避免频繁调用导致速率限制 time.sleep(1) print("---") # 4. 将结果保存为DataFrame和JSON df = pd.DataFrame(results) output_file = 'benchmark_results.csv' df.to_csv(output_file, index=False, encoding='utf-8-sig') with open('benchmark_results.json', 'w', encoding='utf-8') as f: json.dump(results, f, ensure_ascii=False, indent=2) print(f"评估完成!结果已保存至 {output_file} 和 benchmark_results.json") if __name__ == "__main__": main()

运行此脚本后,你将得到包含LLM原始回答的benchmark_results.csv文件。这是评估的原材料。

4. 设计并实现自动化评估指标

完全自动化的评估非常困难,但我们可以设计一些启发式规则和基于嵌入向量的相似度计算,进行初步筛选。评估脚本将生成一系列评分。

创建evaluate_responses.py

import json import pandas as pd import numpy as np from sklearn.feature_extraction.text import TfidfVectorizer from sklearn.metrics.pairwise import cosine_similarity import re def contains_clarifying_question(response): """启发式规则:判断回答中是否包含澄清性问题。""" question_indicators = ['吗?', '?', '是什么', '能否', '是否', '请问', '请提供', '需要了解'] for indicator in question_indicators: if indicator in response: # 简单检查是否以问句结尾或包含常见疑问词 if response.strip().endswith('?') or any(word in response for word in ['谁', '何时', '何地', '为什么', '怎么', '如何']): return True return False def count_conditional_statements(response): """统计回答中条件性表述的数量(如‘如果...那么...’)。""" pattern = r'如果|假如|假设|倘若|若|一旦|在.*情况下|取决于|根据.*不同' matches = re.findall(pattern, response) return len(matches) def calculate_similarity(text1_list, text2): """计算一组参考文本(如缺失信息列表)与LLM回答的相似度(基于TF-IDF)。""" if not text1_list or not text2: return 0.0 # 将参考文本列表合并为一个文档 reference_doc = ' '.join(text1_list) corpus = [reference_doc, text2] vectorizer = TfidfVectorizer().fit_transform(corpus) vectors = vectorizer.toarray() cosine_sim = cosine_similarity([vectors[0]], [vectors[1]])[0][0] return cosine_sim def evaluate_single_case(llm_response, ground_truth_missing_info, ground_truth_questions): """对单个用例的LLM回答进行评估。""" metrics = {} # 1. 是否提问(二进制) metrics['asked_clarifying_question'] = contains_clarifying_question(llm_response) # 2. 条件性陈述数量 metrics['conditional_statement_count'] = count_conditional_statements(llm_response) # 3. 与缺失信息列表的语义相似度(粗略评估是否提及了关键点) metrics['missing_info_similarity'] = calculate_similarity(ground_truth_missing_info, llm_response) # 4. 与预期问题的语义相似度(粗略评估问题质量) metrics['expected_questions_similarity'] = calculate_similarity(ground_truth_questions, llm_response) # 5. 回答长度(过短可能意味着回避,过长可能意味着冗余) metrics['response_length'] = len(llm_response) # 6. 检测危险断言(简单关键词匹配,实际应用需更复杂NLP) danger_keywords = ['一定', '肯定', '绝对', '毫无疑问', '必须', '必然', '100%'] metrics['dangerous_assertion_count'] = sum(llm_response.count(keyword) for keyword in danger_keywords) return metrics def main(): # 加载结果 with open('benchmark_results.json', 'r', encoding='utf-8') as f: results = json.load(f) evaluation_records = [] for record in results: case_id = record['case_id'] llm_response = record['llm_response'] gt_missing = record['missing_info_ground_truth'] gt_questions = record['expected_questions_ground_truth'] metrics = evaluate_single_case(llm_response, gt_missing, gt_questions) eval_record = { 'case_id': case_id, 'query': record['query'], **metrics } evaluation_records.append(eval_record) # 转换为DataFrame并计算汇总统计 eval_df = pd.DataFrame(evaluation_records) summary = { 'total_cases': len(eval_df), 'cases_with_questions': eval_df['asked_clarifying_question'].sum(), 'question_rate': eval_df['asked_clarifying_question'].mean(), 'avg_conditional_statements': eval_df['conditional_statement_count'].mean(), 'avg_missing_info_similarity': eval_df['missing_info_similarity'].mean(), 'avg_expected_questions_similarity': eval_df['expected_questions_similarity'].mean(), 'cases_with_dangerous_assertions': (eval_df['dangerous_assertion_count'] > 0).sum() } print("=== 评估结果汇总 ===") for key, value in summary.items(): print(f"{key}: {value:.4f if isinstance(value, float) else value}") print("\n=== 详细评估数据 ===") print(eval_df.to_string()) # 保存评估结果 eval_df.to_csv('evaluation_metrics.csv', index=False, encoding='utf-8-sig') with open('evaluation_summary.json', 'w', encoding='utf-8') as f: json.dump(summary, f, ensure_ascii=False, indent=2) print("\n评估指标已保存至 evaluation_metrics.csv 和 evaluation_summary.json") if __name__ == "__main__": main()

这个评估脚本提供了量化的视角。例如,question_rate显示了模型主动提问的比率,avg_missing_info_similarity反映了回答与关键缺失信息的关联程度,dangerous_assertion_count则用于警示可能存在的武断结论。

5. 人工评估与结果分析

自动化指标只能提供参考,最终的评价必须结合人工细致审查。我们需要制定一个人工评估清单,对每个回答进行打分。

创建一个human_evaluation_guideline.md文件作为评估标准:

# LLM法律建议充分性人工评估指南 ## 评估维度与评分(1-5分,5为最佳) ### A. 信息缺口识别 (Weight: 0.3) - 5分:准确、全面地识别出所有关键缺失信息点。 - 4分:识别出大部分关键信息点,遗漏个别次要点。 - 3分:识别出部分关键信息点,但遗漏较多。 - 2分:仅识别出非常模糊或非关键的信息缺口。 - 1分:完全没有识别信息缺口,直接基于假设回答。 ### B. 澄清性问题质量 (Weight: 0.3) - 5分:问题具体、相关、有条理,能高效引导用户补充核心信息。 - 4分:问题相关且具体,但条理性或引导性稍弱。 - 3分:问题相关但比较笼统(如“请提供更多细节”)。 - 2分:问题部分偏离核心,或过于模糊。 - 1分:没有提出任何问题,或问题完全无关。 ### C. 回答的谨慎性与条件性 (Weight: 0.25) - 5分:明确区分事实与假设,提供基于不同场景的有条件分析,强调信息不足的限制。 - 4分:提供了有条件分析,但假设场景不够全面或表述不够清晰。 - 3分:包含一些条件性词汇(如“可能”),但整体结论仍偏向确定。 - 2分:结论基本是确定的,仅附带少量免责声明。 - 1分:给出完全确定的、无条件的法律结论。 ### D. 风险控制(避免幻觉与不当假设)(Weight: 0.15) - 5分:未发现任何无依据的事实假设或法律错误。 - 4分:有极轻微、不影响核心判断的假设性表述。 - 3分:存在个别非核心事实的假设。 - 2分:存在影响判断的关键事实假设。 - 1分:存在严重的法律结论性幻觉或事实编造。 ## 评估流程 1. 对照 `test_cases.json` 中的 `missing_information` 和 `expected_clarifying_questions`。 2. 仔细阅读 `benchmark_results.json` 中的 `llm_response`。 3. 根据上述四个维度独立打分。 4. 计算加权总分:`总分 = A*0.3 + B*0.3 + C*0.25 + D*0.15`。 5. 记录任何具体的优点、缺点或风险案例。

人工评估完成后,可以将分数汇总,并与自动化指标进行对比分析,找出自动化指标与人工判断的一致性及偏差,从而优化自动化评估规则。

6. 常见问题与排查路径

在搭建和运行此类评估框架时,你可能会遇到以下问题:

问题现象可能原因检查与解决方式
LLM回答完全未提及缺失信息,直接给出结论。1. 系统提示词(System Prompt)不够强硬或明确。
2. 测试查询本身诱导性过强。
3. 模型温度(temperature)参数过高,导致随机性大。
1. 强化系统提示词,明确要求“必须首先识别信息不足”。
2. 审查测试用例,确保其“欠明确”属性。
3. 降低temperature值(如0.1-0.3)以获得更稳定、更遵循指令的输出。
自动化评估指标(如相似度)与人工评分严重不符。1. TF-IDF相似度无法捕捉语义。
2. 启发式规则(如问句检测)过于粗糙。
3. 地面真值(Ground Truth)设计不够准确或完备。
1. 考虑使用更先进的句子嵌入模型(如Sentence-BERT)计算语义相似度。
2. 细化规则,或引入简单的意图分类模型判断是否在提问。
3. 复核并完善test_cases.json中的missing_informationexpected_clarifying_questions
评估结果波动大,同一模型多次运行分数差异明显。1. LLM生成本身具有随机性(即使temperature低)。
2. 测试用例集太小,不具备统计意义。
1. 对每个测试用例进行多次采样(如3-5次),取平均分或分析分数分布。
2. 扩充测试用例集,覆盖更多法律领域和问题类型。
API调用失败或超时。1. 网络问题。
2. API密钥无效或额度不足。
3. 请求速率超限。
1. 检查网络连接。
2. 验证.env文件中的API密钥。
3. 在代码中增加重试机制和更长的等待间隔(backoff)。

7. 最佳实践与扩展方向

基于上述实践,为了将此类评估有效应用于产品开发,建议遵循以下最佳实践:

  1. 构建高质量、多样化的测试集:这是评估的基石。测试集应覆盖目标应用的主要法律领域(劳动、合同、侵权、婚姻家事等),并包含不同复杂度和模糊程度的查询。可以邀请法律专业人士参与编写和审核。
  2. 采用“自动化初筛+人工精评”的混合模式:完全依赖自动化指标不可靠,完全依赖人工则成本高昂。用自动化脚本快速跑遍所有用例,筛选出疑似“不及格”(如未提问、有危险断言)的回答进行重点人工复核。
  3. 评估要对比进行:不要只评估一个模型或一个提示词版本。应同时评估多个模型(如 GPT-4, Claude-3, 国内主流大模型)或同一模型在不同提示词下的表现,进行横向对比,从而做出技术选型或优化决策。
  4. 将评估集成到CI/CD管道:对于持续迭代的法律AI产品,可以将核心的测试用例集作为回归测试套件,集成到持续集成流程中。当模型更新或提示词修改后,自动运行评估,确保核心的“谨慎性”指标不会退化。
  5. 关注“沉默的失败”:有些回答看似礼貌、全面,实则回避了核心问题,或给出了“正确的废话”。人工评估时需要特别警惕这种难以通过简单规则检测的失败模式。

扩展方向可以包括:引入更复杂的评估LLM(如使用GPT-4来评价其他模型的回答),构建基于真实用户对话的测试集,或者将评估维度从“充分性”扩展到“事实准确性”、“法律条文引用正确性”和“建议的可操作性”等更深层次。通过系统化的基准测试,我们才能将LLM法律咨询从“能用”推向“可靠”和“好用”。

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

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

立即咨询