Commit 4766f9df by ccran

feat: 麓发标准替换成通用sheet

parent 42b23279
...@@ -11,29 +11,42 @@ if TYPE_CHECKING: ...@@ -11,29 +11,42 @@ if TYPE_CHECKING:
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
FINDING_PREPROCESSOR_SYSTEM_PROMPT = """你是合同审查结果文本处理助手。 FINDING_PREPROCESSOR_SYSTEM_PROMPT = """你是合同审查建议的文本微调助手。
你的任务是严格按照给定的处理要求修改建议文本。 必须以原始建议为底稿,只对处理要求明确涉及的局部文字做细微调整。
只允许执行明确要求的文字调整,不得补充、删减、解释或改写其他内容。 不得重写整段建议,不得补充、删减、扩写、总结或解释其他内容。
返回 JSON 对象,格式为:{"suggestion": "处理后的建议文本"}。""" 合同原文是判断现有条款内容的唯一依据。必须区分建议中引用的原文和建议修改后的目标文本:
1. “将A修改为B”“把A替换为B”“删除A”等表述中的 A 是原文定位文本,必须是合同原文中实际存在的连续文本;
2. 如果 A 与合同原文不一致,只能依据合同原文将 A 校正为对应的原文文本,不得虚构;
3. 处理要求只作用于 B 等修改后的目标文本,不得错误修改作为定位依据的 A;
4. 建议直接给出拟新增或修改后的条款时,处理要求作用于该拟定文本。
suggestion 的值必须是连续的纯文本,不得包含标题、项目符号、编号列表、加粗、
引用、代码块、链接等任何 Markdown 格式。
只返回 JSON 对象,格式为:{"suggestion": "微调后的纯文本建议"}。"""
FINDING_PREPROCESSOR_USER_PROMPT = """请处理以下合同审查建议: FINDING_PREPROCESSOR_USER_PROMPT = """请处理以下合同审查建议:
审查项:{rule_title} 审查项:{rule_title}
处理要求:{processing_requirement} 处理要求:{processing_requirement}
合同原文(仅供理解上下文,不得修改或输出):{original_text}
原始建议:{suggestion} 原始建议:{suggestion}
请仅返回 JSON 对象。""" 除处理要求涉及的局部文字外,字词、标点和语序均须保持不变。
请仅返回 JSON 对象,suggestion 中不得使用任何 Markdown 格式。"""
class FindingPreprocessor: class FindingPreprocessor:
"""在 finding 入库前按审查项执行文本处理。""" """在 finding 入库前按审查项执行文本处理。"""
_LLM_PROCESSING_REQUIREMENTS = { _LLM_PROCESSING_REQUIREMENTS = {
"安装调试审查": "将建议文本中的“指导”替换为“支持”,其他内容保持不变。", "安装调试与指导审查": (
"拟新增或修改后的目标文本不得使用“指导”,应改用“支持”。"
"如果建议采用“将A修改为B”等表达,A 必须保持为合同原文中实际存在的文本,"
"只将 B 中的“指导”调整为“支持”;其他内容保持不变。"
),
} }
_SUGGESTION_REPLACEMENTS = { _SUGGESTION_REPLACEMENTS = {
"安装调试审查": { "安装调试与指导审查": {
"指导": "支持", "指导": "支持",
}, },
} }
...@@ -41,13 +54,16 @@ class FindingPreprocessor: ...@@ -41,13 +54,16 @@ class FindingPreprocessor:
def __init__(self, llm_tool: LLMTool | None = None) -> None: def __init__(self, llm_tool: LLMTool | None = None) -> None:
self._llm_tool = llm_tool self._llm_tool = llm_tool
def process(self, finding: Finding,use_rule=True) -> Finding: def process(self, finding: Finding, use_rule: bool = False) -> Finding:
"""优先使用 LLM 处理,调用失败时回退到确定性规则。""" """优先使用 LLM 处理,调用失败时回退到确定性规则。"""
if use_rule: if use_rule:
return self.process_with_rules(finding) return self.process_with_rules(finding)
return self.process_with_llm(finding) return self.process_with_llm(finding)
def process_with_llm(self, finding: Finding) -> Finding: def process_with_llm(self, finding: Finding) -> Finding:
if finding.result != "不合格":
return finding
rule_title = (finding.rule_title or "").strip() rule_title = (finding.rule_title or "").strip()
requirement = self._LLM_PROCESSING_REQUIREMENTS.get(rule_title) requirement = self._LLM_PROCESSING_REQUIREMENTS.get(rule_title)
if not requirement or not finding.suggestion: if not requirement or not finding.suggestion:
...@@ -56,6 +72,7 @@ class FindingPreprocessor: ...@@ -56,6 +72,7 @@ class FindingPreprocessor:
user_content = FINDING_PREPROCESSOR_USER_PROMPT.format( user_content = FINDING_PREPROCESSOR_USER_PROMPT.format(
rule_title=rule_title, rule_title=rule_title,
processing_requirement=requirement, processing_requirement=requirement,
original_text=json.dumps(finding.original_text or "", ensure_ascii=False),
suggestion=json.dumps(finding.suggestion, ensure_ascii=False), suggestion=json.dumps(finding.suggestion, ensure_ascii=False),
) )
...@@ -80,6 +97,9 @@ class FindingPreprocessor: ...@@ -80,6 +97,9 @@ class FindingPreprocessor:
return self.process_with_rules(finding) return self.process_with_rules(finding)
def process_with_rules(self, finding: Finding) -> Finding: def process_with_rules(self, finding: Finding) -> Finding:
if finding.result != "不合格":
return finding
replacements = self._SUGGESTION_REPLACEMENTS.get( replacements = self._SUGGESTION_REPLACEMENTS.get(
(finding.rule_title or "").strip(), {} (finding.rule_title or "").strip(), {}
) )
......
...@@ -2,6 +2,7 @@ from __future__ import annotations ...@@ -2,6 +2,7 @@ from __future__ import annotations
import argparse import argparse
import re import re
from contextlib import redirect_stdout
from pathlib import Path from pathlib import Path
from typing import Iterable from typing import Iterable
...@@ -86,8 +87,17 @@ def extract_annotaion( ...@@ -86,8 +87,17 @@ def extract_annotaion(
def compare_annotaion(val_dir: Path, answer_dir: Path) -> None: def compare_annotaion(val_dir: Path, answer_dir: Path) -> None:
"""Run benchmark comparison on extracted annotations.""" """Run benchmark comparison on extracted annotations."""
output_excel = compare(val_dir=val_dir, answer_dir=answer_dir) log_file = val_dir.with_suffix(".log")
log_file.parent.mkdir(parents=True, exist_ok=True)
with log_file.open("w", encoding="utf-8") as log_output:
with redirect_stdout(log_output):
output_excel = compare(
val_dir=val_dir,
answer_dir=answer_dir,
print_result=True,
)
print(f"Compare result written to: {output_excel}") print(f"Compare result written to: {output_excel}")
print(f"Compare log written to: {log_file}")
def _strip_suffix_once(stem: str, suffixes: Iterable[str]) -> str: def _strip_suffix_once(stem: str, suffixes: Iterable[str]) -> str:
...@@ -121,7 +131,7 @@ def _parse_args() -> argparse.Namespace: ...@@ -121,7 +131,7 @@ def _parse_args() -> argparse.Namespace:
parser.add_argument( parser.add_argument(
"--datasets-dir", "--datasets-dir",
type=Path, type=Path,
default=base / "results" / "jp-output-lufa-20260514-182859", default=base / "results" / "jp-output-20260701-144647-280297",
help="Directory containing Word files with annotations.", help="Directory containing Word files with annotations.",
) )
parser.add_argument( parser.add_argument(
......
No preview for this file type
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or sign in to comment