🔍

AIによる契約書の自動レビュー機能を作ってみた

2024/09/24に公開

3行まとめ

  • 契約書のレビュー、契約書のバージョン管理を効率的に行いかた向け。
  • AIを活用した契約書レビューのワークフローを構築。
  • GitHubのPull Request機能を使ってレビューするので複数人でのレビューがしやすく議論の結果が残る。

概要

こんな感じで契約書をレビューしてくれます。

ソースコード(基本はこの1ファイルのみ)
https://github.com/matsubo/legal-document/blob/main/.github/workflows/ai-pr-reviewer.yml

背景

社内のAI技術勉強会にて、リーガルチェックにLLMを利用してみたときの事例を見ました。
その際に、契約書の1次チェックをLLMを使って行うと一定数の時短効果があるという考察がありました。

悩んだポイントや使い方のノウハウが
CoderabbitPR-Agentを使ったLLMによるPull Requestのレビューと似ていると思いまいした。

そこで、ソースコード管理のメタファをと契約書のレビューに適用してみようと考えました。

目的

AIによるソースコードレビューを行うのと同じように、AIによる契約書のレビューをするワークフローを作ります。

アプローチ

契約書などの法務関連のドキュメントも基本的にはテキスト情報なのでソースコードと似ているのでGitHubなどのバージョン管理ツールを使ったワークフローに乗せてみる。

ワークフローのスコープ定義

印鑑を利用した契約書に関するユースケースを整理してみます。

  • ドラフトの作成
  • ドラフトの確認、修正
  • Aが捺印
  • Bが捺印
  • 双方で保管

電子化されたとしても上記の捺印が電子捺印に変わるだけなので同様に考えられます。

今回は、契約締結ワークフローすべてを考えるとビジネス要件、法務要件などが絡んでくるので上野2つのドラフトの作成、確認、修正という点をLLMを使って処理できるようにしたいと思います。

ドキュメントのフォーマットの扱い

契約書は変更可能なドラフトの状態ではdocx, Google Docsで流通し、内容が合意できたあとはPDFで流通します。
docsやGoogle Docsの形式ではテキストエディタとの相性が悪いのでMarkdownに変換して扱います。

ワークフロー設計

  • 契約書のドラフトを作成
  • Markdownに変換
  • pull requestを作成
  • pull request上に行ごと、全体に対してレビューコメントをいれる
  • 満足が行くまで修正を繰り返す。
  • mainブランチにmergeしPDFを生成

実装

今回は契約書のサンプルのために、Wor-Qの提供する契約書の雛形を利用させてもらいます。
今回は単発の業務委託契約書のテンプレを使います。(ikkai-keiyaku-jyuninin.docx)

ファイルの変換

上記のサイトでは契約書の雛形はWord形式(docx)で提供されているのでまずは扱いやすいようにMarkdownに変換します。
ドキュメント形式のファイル変換にはpandocを使うのが一般的です。

pandocをゼロからインストールするのは面倒なのでdocker imageを探してきて利用します。

コマンド実行例

% docker run --rm \
             --volume "$(pwd):/data" \
             --user $(id -u):$(id -g) pandoc/core jtuc/ikkai-keiyaku-jyuninin.docx -o jtuc/ikkai-keiyaku-jyuninin.md

出力されたMarkdownのファイルはこちらになります。

**業務委託契約書**

○○(以下「委託者」という。)と△△(以下「受託者」という。)は、委託者が受託者に対し、□□を委託するにあたって、次のとおり業務委託契約(以下「本契約」という。)を締結する。

第1条(委託業務の内容)

委託者は、受託者に対し、以下の内容で業務を委託する(以下「本業務」という。)。

業務内容:

業務期間:

委託料:

第2条(支払方法)

委託者は、本業務の委託料を、  年 月 日までに受託者の指定する銀行口座に振り込む方法により支払う。ただし、振込手数料は委託者の負担とする。

第3条(報告)

受託者は、本業務の完了後、速やかに委託者に対し、業務完了報告書を交付する。

2.委託者は、前項の業務完了報告書を受領した日の翌日から起算して5営業日以内に、その内容を確認した旨の通知しなければならない。

3.前項に基づく委託者から受託者への通知があったときに本業務は完了したものとし、前項の期限内に前項の通知が受託者に到達しない場合も同様とする。

第4条(秘密保持)

受託者は、委託者の承諾なくして、本契約に関連して委託者から秘密であることを明示して開示された営業上又は技術上の秘密情報(以下「秘密情報」という。)を、第三者に対して開示、漏洩してはならず、本契約の履行以外の目的で使用してはならない。ただし、以下のいずれかに該当する情報は秘密情報には含まれない。

(1)開示された時点において、既に公知であった情報

(2)開示された後に受託者の責任によらないで公知になった情報

(3)開示された時点において、受託者が既に了知していた情報

(4)正当な権限を有する第三者から、受託者が秘密保持義務を負うことなく適法に取得した情報

予期していた内容とは異なります。
理想としては、第x条のところは見出し1でマークアップされて、第x項のところは見出し2でマークアップされてきてほしいですが、すべてがただのテキストとして出されてしまっています。

もし、元のdocxにWordの「見出し」を使ってマークアップされていたらMarkdownでも見出し情報が維持されるのではないかと思って
テンプレのdocxファイルに対して見出しをいくつか使ってマークアップして変換してみました。

その際の出力は以下のようになり、見出しとして出力されました。

# 第1条(委託業務の内容)

委託者は、受託者に対し、以下の内容で業務を委託する(以下「本業務」という。)。

業務内容:

業務期間:

委託料:

# 第2条(支払方法)

委託者は、本業務の委託料を、  年 月 日までに受託者の指定する銀行口座に振り込む方法により支払う。ただし、振込手数料は委託者の負担とする。

# 第3条(報告)

受託者は、本業務の完了後、速やかに委託者に対し、業務完了報告書を交付する。

2.委託者は、前項の業務完了報告書を受領した日の翌日から起算して5営業日以内に、その内容を確認した旨の通知しなければならない。

3.前項に基づく委託者から受託者への通知があったときに本業務は完了したものとし、前項の期限内に前項の通知が受託者に到達しない場合も同様とする。

# 第4条(秘密保持)

手動でこのようなマークアップを行うのも手間なので自動でやってみます。
以下のようなコードを書いて、LLMを利用してMarkdownファイルに見出し情報を付加してみました。

import sys
import os
from openai import OpenAI

client = OpenAI(
    # This is the default and can be omitted
    api_key=os.environ.get("OPENAI_API_KEY"),
)

def convert_markdown(input_file):
    # 入力ファイルを読み込む
    with open(input_file, 'r', encoding='utf-8') as file:
        content = file.read()

    # OpenAI APIにリクエストを送信
    response = chat_completion = client.chat.completions.create(
        messages=[
            {"role": "system", "content": "このファイルは契約書で法務文書です。タイトルや見出しをmarkdownで表現するようにフォーマットしてください。契約書のタイトルは見出し1,条項は見出し2を使うようにしてください。重要な文書なので文章の内容は変更しないでください。見出しやタイトルのマークアップだけを行ってください。また、見出しと同時に箇条書きに関しても可能ならばMarkdownで箇条書きにしてください。"},
            {"role": "user", "content": content}
        ],
        model="gpt-4o",
    )

    # 変換された内容を取得
    converted_content = response.choices[0].message.content

    # 出力ファイルに書き込む
    with open('out.md', 'w', encoding='utf-8') as file:
        file.write(converted_content)

if __name__ == "__main__":
    if len(sys.argv) != 2:
        print("使用方法: OPENAI_API_KEY=xxx python convert.py input_file.md")
        sys.exit(1)

    input_file = sys.argv[1]
    convert_markdown(input_file)
    print(f"{input_file} を変換し、結果を out.md に保存しました。")

以下のようなコマンドで実行すると、out.mdというファイルに出力されます。

OPENAI_API_KEY=sk-proj-xxxxx python3 convert.py jtuc/ikkai-keiyaku-jyuninin.md

vimdiff で差分を表示してみました。文章も変更されてしまっていますが、とりあえず今回はこれで進めます。

# 業務委託契約書

○○(以下「委託者」という。)と△△(以下「受託者」という。)は、委託者が受託者に対し、□□を委託するにあたって、次のとおり業務委託契約(以下「本契約」という。)を締結する。

## 第1条(委託業務の内容)

委託者は、受託者に対し、以下の内容で業務を委託する(以下「本業務」という。)。

- 業務内容:
- 業務期間:
- 委託料:

## 第2条(支払方法)

委託者は、本業務の委託料を、  年 月 日までに受託者の指定する銀行口座に振り込む方法により支払う。ただし、振込手数料は委託者の負担とする。

## 第3条(報告)

受託者は、本業務の完了後、速やかに委託者に対し、業務完了報告書を交付する。

1. 委託者は、前項の業務完了報告書を受領した日の翌日から起算して5営業日以内に、その内容を確認した旨の通知しなければならない。
2. 前項に基づく委託者から受託者への通知があったときに本業務は完了したものとし、前項の期限内に前項の通知が受託者に到達しない場合も同様とする。

## 第4条(秘密保持)

受託者は、委託者の承諾なくして、本契約に関連して委託者から秘密であることを明示して開示された営業上又は技術上の秘密情報(以下「秘密情報」という。)を、第三者に対して開示、漏洩してはならず、本契約の履行以外の目的で使用してはならない。ただし、以下のいずれかに該当する情報は秘密情報には含まれない。

- 開示された時点において、既に公知であった情報
- 開示された後に受託者の責任によらないで公知になった情報
- 開示された時点において、受託者が既に了知していた情報
- 正当な権限を有する第三者から、受託者が秘密保持義務を負うことなく適法に取得した情報

## 第5条(再委託の禁止)

受託者は、あらかじめ書面により委託者の承諾を得た場合、又は、正当な理由がある場合を除き、本業務の全部又は一部を、第三者に再委託することができない。

1. 受託者は、前項の規定により第三者に再委託する場合も、本契約に規定する受託者の義務を免れず、かつ第三者に対しても本契約上の義務を遵守させる義務を負う。

## 第6条(権利義務の譲渡等の禁止)

委託者及び受託者は、あらかじめ書面により相手方の承諾を得なければ、本契約上の権利義務ならびに本契約上の地位を、第三者に譲渡、移転その他の方法により処分してはならない。

## 第7条(損害賠償)

本契約に違反し、相手方に損害を負わせた当事者は、本契約の委託料を上限として当該違反に起因して発生した損害を賠償しなければならない。

## 第8条(不可抗力)

委託者及び受託者は、天災地変、戦争、内乱、暴動、疫病、感染症の流行等、当事者の合理的支配を超える事由により、義務の履行の全部又は一部が妨げられる範囲において、本契約に基づく義務の履行を免除され、一切の責任を負わない。

## 第9条(解除)

委託者及び受託者は、相手方が本契約に違反したときは、相当の期間を定めた催告をし、催告期間が終了しても違反が是正されない場合、本契約を解除できる。

1. 委託者及び受託者は、相手方に次の各号いずれかに該当する事由が生じたときは、何らの催告を要することなく、直ちに本契約を解除することができる。
    - 本契約の違反が重大なとき
    - 破産手続開始、民事再生手続開始、会社更生手続開始の申立てがあったとき
    - 差押え、仮差押え等の強制執行、または公租公課の滞納処分を受けたとき
    - 支払停止、または支払い不能に陥ったとき、若しくは手形が不渡となったとき
2. 前二項の定めにより本契約が解除された場合でも、解除権を行使した当事者は損害賠償の請求を妨げられない。

## 第10条(契約の中途終了の場合の報酬請求)

本契約が解除その他の事由により途中で終了したときは、委託者は受託者に対して、終了までになされた履行割合に応じた額の委託料を支払うものとする。

## 第11条(反社会的勢力の排除)

委託者及び受託者は、現在、暴力団、暴力団員、暴力団準構成員、暴力団関係企業、総会屋、社会運動等標榜ゴロ、その他これに準ずる者(以下「反社会的勢力」という。)のいずれにも該当しないことを表明し、かつ将来にわたっても該当しないことを確約する。

1. 委託者及び受託者は、相手方が次の各号のいずれかに該当する場合、ただちに本契約を解除することができ、解除により相手方に損害が生じてもこれを賠償することを要しない。
    - 相手方または相手方の役員が反社会的勢力に該当すると認められるとき
    - 相手方の経営に反社会的勢力が実質的に関与していると認められるとき
    - 相手方が反社会的勢力を利用していると認められるとき
    - 相手方が反社会的勢力に対して資金等を提供し、または便宜を供与するなどの関与をしていると認められるとき
    - 相手方または相手方の役員もしくは相手方の経営に実質的に関与している者が反社会的勢力と社会的に非難されるべき関係を有しているとき
    - 自らまたは第三者を利用して、暴力的な要求行為、法的な責任を超えた不当な要求行為、脅迫的な言動、暴力および風説の流布・偽計・威力を用いた信用毀損・業務妨害その他これらに準ずる行為に及んだとき
2. 委託者及び受託者は、自己が前項各号に該当したため相手方が本契約を解除した場合、相手方に生じた損害を賠償しなければならない。

## 第12条(存続条項)

第4条(秘密保持)、第6条(損害賠償)、第12条(管轄)の規定は、本契約の終了後も有効に存続する。

## 第13条(管轄)

本契約に関する一切の紛争は、○○地方裁判所を第一審の専属的合意管轄裁判所とする。

## 第14条(協議事項)

本契約に定めのない事項、ならびに本契約の解釈について疑義を生じたときは、当事者間で誠実に協議のうえ解決する。

本契約の締結を証するため、本書を2通作成し、委託者及び受託者記名押印の上、それぞれ1通を保有する。

20  年  月  日

(委託者)

(受託者)⏎

Pull requestをレビューするコードの作成

Markdown形式の契約書を含むドキュメントがpull requestに上がってきたらレビューするワークフローを作ります。

ゼロから作るのは大変なので、ソースコードをレビューする既存のOSSを改良してみます。

ベースとしてai-pr-reviewerを使ってみます。
今はpublic archiveになっています。運営元は有料の別サービスを開始してそちらに本腰を入れているみたいです。
https://github.com/coderabbitai/ai-pr-reviewer?tab=readme-ov-file

また、 PR-Agentも見てみましたが、ソースコードレビューに特化した機能が沢山入っていて取捨選択するのが大変なので、
作りがシンプルなai-pr-reviewerを応用してみたいと思います。

応用といっても、LLMにわたすプロンプト自体がGitHub Actionsのワークフローにかかれているので変更するだけです。

permissions:
  contents: read
  pull-requests: write

on:
  pull_request:
  pull_request_review_comment:
    types: [created]
  workflow_dispatch:


concurrency:
  group: ${{ github.workflow }}-${{ github.ref }}
  cancel-in-progress: true

jobs:
  review:
    runs-on: ubuntu-latest
    if: (github.event_name == 'workflow_dispatch') || (github.event_name == 'pull_request' && !contains(github.event.pull_request.title, 'release'))
    timeout-minutes: 15
    steps:
      - uses: coderabbitai/openai-pr-reviewer@latest
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
          OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
        with:
          debug: false
          review_simple_changes: false
          review_comment_lgtm: false
          openai_light_model: gpt-4
          openai_heavy_model: gpt-4
          openai_timeout_ms: 900000
          language: ja-JP
          path_filters: |
            !db/**
            !**/*.lock
          system_message: |
            あなたは @coderabbitai(別名 github-actions[bot])で、OpenAIによって訓練された言語モデルです。
            あなたの目的は、非常に経験豊富な法務専門家として機能し、契約書を徹底的にレビューし、
            以下のようなキーエリアを改善するためのアドバイスを提供することです:
              - 法的リスク
              - 契約条件の明確さ
              - 法的遵守
              - 紛争解決条項
              - 守秘義務
              - 知的財産権
              - 責任の制限
              - 契約の終了条件

            些細な文法の問題や、スタイルの欠落についてはコメントしないでください。
            重要な問題を特定し、解決して全体的な契約書の品質を向上させることを目指してくださいが、細かい問題は意図的に無視してください。
          summarize: |
            次の内容でmarkdownフォーマットを使用して、最終的な回答を提供してください。

              - *ウォークスルー*: 特定の条項ではなく、全体の契約書に関する高レベルの要約を80語以内で。
              - *変更点*: 条項とその要約のテーブル。スペースを節約するために、同様の変更を持つ条項を1行にまとめることができます。

            GitHubのプルリクエストにコメントとして追加されるこの要約には、追加のコメントを避けてください。
          summarize_release_notes: |
            このプルリクエストのために、その目的と契約書のユーザーストーリーに焦点を当てて、markdownフォーマットで簡潔なサマリーを作成してください。
            変更は次のように分類し箇条書きにすること:
              "概要", "自社にとってのリスク", "契約相手にとってのリスク", "表記揺れ"
            例えば:, "Correction"
            ```
            - 概要: 守秘義務条項が追加されました
            ```            ⏎

実行

あとは、pull requestを作れば自動的にレビューしてくれます。

全体に対するレビュー

以下のような指摘を上げてくれました。

  • 概要: このプルリクエストでは、新たに業務委託契約書が追加されました。主な内容としては、業務の内容、支払方法、報告義務、秘密保持、再委託の禁止、権利義務の譲渡禁止、損害賠償、不可抗力、契約解除、反社会的勢力の排除等が含まれています。また、紛争解決のための管轄裁判所や協議事項についても規定されています。
  • 自社にとってのリスク: 再委託の禁止や権利義務の譲渡禁止など、自社の柔軟性を制限する可能性があります。また、損害賠償条項により、特定の状況下での責任が増大する可能性があります。
  • 契約相手にとってのリスク: 報告義務や秘密保持など、契約相手の負担を増やす可能性があります。また、契約解除条件が厳格である場合、契約相手が契約を終了する自由度が制限される可能性があります。
  • 表記揺れ: なし

ウォークスルー:
新規に追加された業務委託契約書は、業務内容、支払方法、報告義務、秘密保持、再委託の禁止、権利義務の譲渡禁止、損害賠償、不可抗力、契約解除、反社会的勢力の排除などを含む全面的な条項をカバーしています。

変更提案してくれた箇所と視点を一覧にまとめてくれています。

個別に対するレビュー

行ごとに指摘を入れてくれるのでレビュー内容が見やすいです。
議論が必要だったらそこで議論も進められるし履歴も残るので非常に見やすいUIです。

内容が抜けているところを指摘してくれています。テンプレなので当たり前ですが、本番ではチェックしてくれるべき内容なので素晴らしいです。

なんと、契約書テンプレのtypoを発見してくれてます。条項の番号参照がズレています。ファイルを変換する過程で入り込んだという可能性があるので差分を確認していったのですが、大元のdocxファイルに元から入っているミスです。

損害賠償の上限について設けるべきという提案をしてくれています。確かによく見る契約書では入っている気がします。

ソースコード

こちらにおいておきます。mainブランチをforkしてAPIキーを設定すれば使えると思います。
https://github.com/matsubo/legal-document

今回のレビューの結果はこちらに置いておきます。
https://github.com/matsubo/legal-document/pull/1

考察

契約書をMarkdownで管理する副次的な効果

  • 差分が把握しやすい
  • 時系列、ファイル同士の関係性が追いやすい。
  • プログラムで扱いやすい。

Markdownで不足している点は以下

  • テキスト情報の意味付けがちょっと貧弱なのでXMLや独自のマークアップ記法が必要になりそうです。
    • タイトル、日付、契約主体、契約期間などはほとんど共通の事項なのでマークアップされていて欲しいです。

余談

契約締結業務をGitやGitHubを使って構築するのも可能かなと思いました。

  • Gitによるソースコードのバージョン管理は、ブロックチェーンのProof of Worksのモデルに似ているところがあります。
  • Gitのリビジョンツリーに契約対象者それぞれがサインする対象の名前を書いてコミットすれば同意とみなしても良いなと思いました。
  • Gitの分散レポジトリという点も契約書の双方保管という意図には良さそうです。用途が違うので耐改ざん性などの問題はありそうですが9割型の要素はそのまま使えそうな気がしました。

まとめ

  • AIによる契約書内容のチェックを実装してみました。
  • プロンプトを改良したりすれば一定数業務でも使えるレベルかなと思いました。
  • 法務担当の居ない会社や個人の方はアドバイザ的な役割ですぐに使っても良いレベルかなと思います。
株式会社マインディア テックブログ

Discussion