LayerX エンジニアブログ

LayerX の エンジニアブログです。

LLMを用いて長文ドキュメントを速く・安く・安全に構造化する試み

こんにちは。Ai Workforce事業部 FDEグループ エンジニアのkoseiと申します。

この記事はLayerX Tech Advent Calendar 2025 16日目の記事です。

本日は私たちの事業部が開発しているプロダクト「Ai Workforce」が扱うような長いドキュメントを構造化する方法として少し試していることについてお話しできればと思います。

背景

Ai Workforceではこれまで、エンタープライズのお客様にドキュメントワークを中心とした業務を効率化するユースケースを幅広く扱ってきました。

例えば、契約書のような200P以上に及ぶ長大なドキュメント同士の比較や、見積書からの全ての商品情報の抽出・分類など、様々な種類のドキュメントやユースケースを扱っています。

多くのドキュメント、特に文章量が多いドキュメントや、扱う問題が複雑になる場合に共通して、ユースケース実現のために扱うドキュメントの特徴を利用したくなることがあります。例えば、契約書から特定の条文を抽出するタスクにおいては、「章」という大きな意味的まとまりを単位として区切ってからLLMに渡すことで抽出精度を向上させることができる、など。

ドキュメントの特徴を利用した意味的な分割だけでなく、より一般に、ドキュメント全体を構造化できているとユースケースも広がります。例えば契約書において、条番号と該当条文の全文と対応がついていれば、「2つの異なる契約書に記載された対応する条文同士を比較する」ことに繋がります。

一方で、長文ドキュメント全体に対してそのような構造化を行うのは精度・コスト的なハードルも高いです。試しに契約書の全条文の構造化のユースケースを考えてみましょう。

素朴な案としては、LLMで以下のような構造化データを全ての条文について抽出するような方法が考えられます。


    {"clause_number": "第1条", "title": "定義", "content": "本契約書において、..."}
  

しかし、この方法では以下の問題が生じます。

  • LLMで条文を抽出することによる精度懸念

    存在しない条文の記述をLLMが出力してしまったり、微妙な表現をLLMが修正してしまったり、特定の記述の抽出を漏れてしまうなどのリスクがあります。

  • 速度が遅い・コストが高い

    LLMの速度とコストは、出力トークンの量が大きく影響します。契約書全文は10万トークンを超えることも多く、全てをLLMに出力させるのは高くつきます。

    出力トークン量に関する実務上の課題に関しては以下の記事でもTIPSを紹介しています。

    tech.layerx.co.jp

取り組む課題

上記を踏まえると、

(1)ドキュメント全体を構造的に分解したい

(2)LLMがドキュメントの記述や表現を変更できないようにしたい

(3)特定の文章が出力から漏れてしまうことを防ぎたい

(4)LLMが生成するトークン数は減らしたい

これらを満たすような方法があると良さそうです。

もちろんこれら全てを実現するのは難しいかもしれませんが、面白そうなアプローチとして以下の方法を考えてみたので紹介します。

アプローチ:ドキュメント全体を修正していき、構造化ファイルに変換する

ドキュメントの構造化を「テキストの分割・抽出」ではなく、「ファイルの編集」のように捉えると、取り回しが良くなるのではと考えました。

具体的には、テキストをLLMを用いて編集し、所望のyaml形式にすることで、所望の構造化データにする形です。

ここでLLMが出力するのは「どの行にどんなyamlキーを挿入すべきか」です。 例えば上記の変換を行うためには、LLMは以下の出力を行います。


    [{"row": 1, "insert": "clause_title: "}, {"row": 2, "insert": "content: |"}]
  

これを用いて変換前のテキストの1行目の手前に”clause_title: ”を追加し、2行目の手前に”content: |”を行追加すれば、変換後のyaml形式になります。yaml形式を使用した理由は、jsonは括弧閉じが煩雑な一方で、yamlはキーを追加するだけで構造化データにできるのでLLMと併せて扱いやすいためです。

このアプローチについて、先ほど挙げた(1)〜(4)の観点で見てみると、

(1)ドキュメントの構造化:精度は要検証ですが、yaml形式なので階層構造や配列など、拡張性はありそうです。また、ドキュメントの変換後の構造をpydantic等で検証すれば、様々な制約をもとにLLMに再度考えさせることが可能です。(例:深さが3階層以上であることを必須とするなど)

(2)誤抽出の懸念:LLMはyamlの文法上のキーを出力するのみでドキュメントの文章は変えないため、誤った記述が追加されるリスクは少なくなります。

(3)抽出漏れの懸念:行番号で全て管理しているので、ルールベースでのフォールバック処理を追加しやすく、何らかの形で出力に漏れなく含めることが可能です。

(4)出力トークン数:1条のみでも、条文すべてを抽出すると少なくとも100トークンは容易に超えますが、上記の例では30トークンほどなので、大幅に削減できます。

 

少し試す

とはいえ、精度的に使えないと元も子もないので、軽く試してみます。

以下のような契約書をOCRしたテキストを構造化するタスクを解かせてみます。

出典:農林水産省 契約書雛形:https://www.maff.go.jp/j/kanbo/tizai/brand/attach/pdf/keiyaku-31.pdf

左画像が契約書PDFで、右画像がOCRの出力テキストの抜粋です。

変換先の構造は以下のpydanticモデルで定義しています。ドキュメントがこの形式になるように、LLMに変換を考えさせます。

pydanticモデルの定義と変換後の構造の定義とドキュメントのテキストを丸ごとGPT-4.1に入力する設定で試してみると、以下の結果となりました。(一部抜粋)

  • 第一条、第二条などの大きな粒度での分割はできているが、項番2が項番1のtitleに含まれたままで、修正できていない
  • subclausesの形式が正しくない・インデントや改行など構造をミスしている箇所が多い

ことがわかります。前者は入力を分割すれば細かい階層まで認識させる、後者はyamlの形式が間違っている時のエラーメッセージを返して再処理させる・yamlが崩れていても修正する処理を追加する等である程度改善するかと思います。

結論、今回試したシンプルな設定では細かい構造化が難しく、上述のような追加の工夫を行う必要がありそうです。

まとめ

  • 精度的にはまだまだ検証・改善せねばですが、LLMの処理速度・コストや不確実性を減らしながらドキュメント全体の構造化を行うアプローチの一つとして、ありうるかもしれません。
  • 対象のテキストやデータをそのままLLMで扱うのではなく、idや行番号を付与してLLMに操作させる、というのは一般によく使える手法なので参考になれば幸いです。

最後に

ここまで読んでいただいてありがとうございました。

少しでも面白いと思っていただけたら幸いです!

Ai Workforce事業部では技術的・ビジネス的に面白い課題がゴロゴロ転がっています!

ぜひ一緒に解いていきましょう!以下リンクから気軽に話せます!

jobs.layerx.co.jp