💭

【Yozora Diff:決算版】#4 決算短信の変化をLLMに要約させよう!

に公開

はじめに

こんにちは!Rayです。普段は自然言語処理(NLP)の研究をしています。
僕たちは、Yozora Financeという学生コミュニティで、誰もが自分だけの投資エージェントを開発できる世界を目指して活動しています。
その中の基盤モジュール群のひとつとして、開示文書の差分に特化したシリーズを開発しており、それが Yozora Diff です。完成後は誰でも使えるようオープンソースで公開予定です!
前回は、新旧の決算短信の文を対応付けるアライメント処理を行いました。
この対応付けによって「どの文を比較対象にすべきか」という土台が整い、いよいよ今回から“意味の変化そのもの”を扱える段階に入ります。

ここからは、既存研究で示されているアイデアを実装として落とし込み、LLMを使って「何が・どう変わったのか?」を実際に説明させていきます。
単なる再現ではなく、論文で提案されている価値が“手元で実感できる形”になるので、かなり面白いフェーズになると思います!

👉実際に動くコードを GitHubで公開中

なぜLLMに差分を要約させるのか

金融文書の「差分検出」では、基本的にデータセットを作ってBERTなどのモデルを学習させるという流れが主流です。
しかし...

データセットを作るのに莫大なコストがかかる

文書の差分には細かい判断が必要なので、1件ずつ人手でアノテーションする必要があります。
→ 特に決算短信みたいな専門的な文章だと、そもそもアノテーションの敷居が高い。

データセットを公開できない

海外の既存研究では、米国の開示資料(10-Kなど)をもとにデータセットを作り、それをBERTなどのモデルなどで学習させる手法がよく使われています。10-Kは研究コミュニティで幅広く利用されており、そこから作られたコーパスも一般に公開されています。

一方で、日本の開示資料は著作権の扱いが厳しく、本文を含むデータセットとして公開することが難しいです。そのため、データセットを作って共有するという手法とは相性が悪く、オープンソース化や再現性の面でハードルが高くなります。

そこで本記事では、データセットを前提とせず、LLMに直接差分要約を行わせるアプローチを採用しています。この方法なら、原文を配布せずとも処理手法だけを公開でき、日本の決算短信でも再現可能になります。

LLMを用いた差分要約の設計方針

ただ、LLMに差分をそのまま入力するだけでは少し物足りません。そこで、「LLMに差分を要約させる」というアプローチに着目して関連研究を調べたところ、ピッタリな研究がありました。
Santosh et al. (2024)は、LLMへの入力設計を工夫することで差分要約の精度を高める手法を提案しており、特定のドメインに依存しないため、適時開示の差分検知にも応用可能です。

この研究では、個々の差分でなく、共通するテーマごとに編集意図を束ねて要約することで、「文章全体としてどんな変化があったか」を把握できる点が特徴です。決算短信の差分でも重要なのは細かな変化の羅列ではなく、全体としての意味変化なので、そのまま適用できます。
なお、Limitationでは英語のNLPに関する学術論文を対象としており、文書の種類や改定の範囲が限定されているため、他ジャンルへの一般化には制約がある可能性が指摘されています。ただし、決算短信のように構成が一定で変更が局所的な文書では、むしろ本手法との親和性が高く、応用が期待できます。

処理の流れ

  1. 2つの文章と差分をdiff形式にする
  2. LLMにより編集内容を整理・要約させる
    2-1. diff形式を入力し、差分ごとに「何が・なぜ変わったか」という編集意図を生成させる
    2-2. 編集意図を入力し、テーマごとにクラスタリング+要約を生成させる

1.のdiff形式入力は、モデルが存在しない変更を捏造してしまうedit-hallucinationという現象を防ぐために行います。<add><del>といったタグ付けで差分を明示的にモデルに入力することで、この問題を緩和できると報告されています。
2.のLLM要約フェーズでは、まず差分ごとに編集意図を生成し(2-1)、次に内容が近い編集意図をクラスタリングしてテーマ単位の要約を作成します(2-2)。これにより、「局所的な変更の集合」から「文章全体としての意味的な変化」を抽出できます。

実装

1. 2つの文章と差分をdiff形式にする

まずはLLMが差分箇所を正確に理解できるように、diff形式のタグ付き構造に変換します。
この研究では文を編集単位として扱うため、差分を含む文ペア全体をインデックス付きの<edit {i}> </edit {i}>で囲みます。その後、文内部の語・句レベルの変更を明示するため、削除部分を<del> </del>、追加部分を<add> </add>でマークします。

対応する文が存在しない場合、その文を単独の編集単位として扱います。旧文のみがある場合は <edit {i}><del>旧文</del></edit {i}>、新文のみがある場合は <edit {i}><add>新文</add></edit {i}>とし、どちらにも文がある場合のみ<edit {i}><del>…</del><add>…</add></edit {i}>のように差分を表現します。

こうした多層的なタグ付けによって、LLM が「どの範囲が編集対象なのか」「どのような変更があったか」を構文的に把握しやすくなります。

旧: 
売上高は1,200億円となりました。これは為替の影響によるものです。

新: 
売上高は1,300億円となりました。これは国内需要の回復によるものです。

diff形式:  
<edit 0>売上高は<del>1,200</del><add>1,300</add>億円となりました。</edit 0>
<edit 1>これは<del>為替の影響</del><add>国内需要の回復</add>によるものです。</edit 1>

実装時の工夫

  • 数値を<NUM>に変換する
    例えば上の<edit 0>では、<NUM>変換をしないと「"1,200", "1,300"」が「"1,2", "1,3"」と分割され、正しい差分になりません。
  • 短い共通部分はマージする
    短い共通文字列を分断せず、隣接する<del>/<add>と結合することで変更意図が明確になります。例えば上の<edit 1>では、「"為替", "国内需要"」+「"影響", "回復"」を差分とするより、「"為替の影響", "国内需要の回復"」を差分とした方が自然です。

数値の復元処理や、短い共通部分をマージした際に生じるタグ構造の再整形はやや複雑ですが、これを整えることでLLMに渡す入力が安定し、後続の処理の精度が良くなります。
ここまで整えば、あとはLLMに入力して「どんな編集があったのか」を要約させるだけです。

2. LLMにより編集内容を整理・要約させる

それぞれ論文のプロンプトを直訳したものが以下になります。

2-1. 編集意図の生成
*プロンプトでは<edit><Group>に代わっていました。

システムプロンプト:
あなたは、2つの文書の違いを分析し、
バージョン間の広範なテーマ的な変更の要約を提供する任務を持つ、有能なアシスタントです。

最初の段階では、各変更グループを記述します。
正確な変更内容のみを記述してください。
変更の文脈を導くために、隣接する部分または文書全体を使用してください。

あなたの入力は変更グループの記述です。
<Group i>と</Group i> のタグの間のテキストが、i番目の変更グループを表します。
グループ全体のテキストが変更されたわけではないことに注意してください。
<add>と</add>の間のテキストのみが追加された部分であり、
<del>と</del>の間のテキストのみが削除された部分です。

変更グループの形式で与えられた入力をもとに、各変更グループの記述を提供する必要があります。
正確な変更内容のみを記述してください。
変更の文脈を導くために、隣接する部分または文書全体を使用してください。

出力は次の形式で作成してください。
各グループの記述を新しい行で開始し、
行の先頭に<Group i Desc>を、行の末尾に</Group i Desc>を置いてください。
ここでiは、あなたが記述しているグループのインデックスです。

ユーザープロンプト:
以下に、2つのバージョン間の違いを示します:

2-2. テーマのクラスタリング+要約

システムプロンプト:
あなたは、2つの文書の違いを分析し、
バージョン間の広範なテーマ的な変更の要約を提供する任務を持つ、有能なアシスタントです。

この段階では、変更グループの記述をクラスタ化してトピック的なテーマを形成します。
あなたの入力は変更グループの記述です。
<Group i>と</Group i>のタグの間のテキストが、i番目の変更グループにおける変更を表します。

変更グループの記述が与えられた場合、
あなたは変更グループ内のトピックに基づくテーマを特定する必要があります。
類似したトピックを持つグループをクラスタ化し、
各変更グループのクラスタに対して簡潔な要約を提供してください。

出力は次の形式で作成してください。
各クラスタの記述を新しい行で開始し、
行の先頭に<Cluster i Desc>を、行の末尾に </Cluster i Desc> を置いてください。
ここでiは、あなたが記述しているクラスタのインデックスです。
また、各クラスタの記述の末尾に、
そのクラスタに含まれる変更グループ番号を括弧内に記載してください。
各クラスタの記述は2つの改行で区切ってください。

ユーザープロンプト:
以下に、変更グループの記述を示します:

結果

論文の方法で、決算短信の差分を要約した結果が以下になります。
モデルはgemini-2.5-proを使用しました。

<Cluster 1 Desc> 報告期間の更新(中間連結会計期間→第3四半期連結累計期間)に伴う、経営成績に関する記述と数値の変更。中間期の好調要因が削除され、第3四半期累計での好調要因が追加された。(0, 1, 2, 10)</Cluster 1 Desc>
<Cluster 2 Desc> 報告時点の更新に伴い、財政状態(資産、負債、純資産)に関する記述が変更され、総額や増減要因に関する具体的な数値が最新化された。(11, 12, 13, 14, 15, 16)</Cluster 2 Desc>
<Cluster 3 Desc> 各事業セグメント(学園、公教育、ウェルネス、ソフトエンジニアリング)について、報告期間が更新されると共に、各事業の進捗状況(新規案件の納品、大規模案件の落札、システム導入状況など)が反映された記述に変更された。(3, 4, 5, 6, 7, 8, 9)</Cluster 3 Desc>
<Cluster 4 Desc> キャッシュ・フローの状況に関する記述(見出し、導入文、各キャッシュ・フローの詳細説明など)が全面的に削除された。(17, 18, 19, 20, 21, 22, 23, 24, 25, 26)</Cluster 4 Desc>"

出力結果としては、報告期間や事業セグメントなどの変更が的確にクラスタ化されており、文書構造の更新や説明内容の差分をうまく整理できていることが分かります。

一方で、この要約は 「何が変わったか」には強いものの、「その変化が投資的にどんな意味を持つのか」 までは踏み込めていません。
つまり、あくまで構造的・説明的な変化を抽出しており、業績や将来見通しに関する解釈(投資的な意味)までは得られないという限界があります。
ただし、これは論文の仕様通りの挙動であり、想定された設計上の範囲内です。
今後は、プロンプト設計を工夫することで 投資的な意味にまで踏み込んだ要約も実現できると考えています。

テスト中ですが、stage1の要約で事実に加えて、投資家的解釈を出力させると、より役に立つ情報が得られるようになりました。
プロンプトによる挙動変化も面白そうなので、時間があれば書きたいですね。

まとめ

今回はACLの論文A Tale of Two Revisions: Summarizing Changes Across Document Versionsで提案された手法をツールに落とし込みました。
差分を取るまでの方法がほぼ一致していたため、前回までのコードを一切変更せずに導入できて、とても嬉しかったです。
今の時点で動かすことができるので、Githubで公開しました。
投資の知識はまだ素人なので、皆さんの考える最強のプロンプトで使ってみてください!
よさげなプロンプトができたらコメントなどから共有していただけると、とても嬉しいです笑
最後まで読んでいただきありがとうございました!

参考文献

GitHubで編集を提案

Discussion