Agents SDKを使った判例検索コード( database_query.py )解析の6回目。前回に引き続き「 本文取得ツール 」のコードについて、1行ずつ、その働きを見ていく。
header = (
f"doc_id: {row['doc_id']}\n"
f"court: {row['court']}\n"
f"date: {row['date']}\n"
f"case_number: {row['case_number']}\n"
f"case_name: {row['case_name']}\n"
)
データベースから取り出した1件の判例データ(row)を、AIが読みやすい判例のプロフィール(ヘッダー情報)として整形する処理。判決の本文という長い文章に入る前に、その判例の基本スペックを整理して提示する役割を担っている。
情報の構造化
ここでは、Pythonの f-string(フォーマット済み文字列) を使って、バラバラだったデータを1つのテキストにまとめている。
- 項目のラベル付け:
doc_id:やcourt:といったラベルを付けることで、AIが、この値が何を意味するのかを正確に理解できるようにしている。 - 改行(
\n)の活用:各項目の末尾に改行を入れることで、人間が読む名刺やカルテのように、1行1項目で整理されたリスト形式にしている。
なぜこのヘッダーが重要なのか
AIにとって、このヘッダー情報は以下のようなメリットを生む。
- コンテキスト(文脈)の把握:長い本文を読み始める前に「これは東京地裁の、令和○年の事件である」という前提知識をAIに与える。これにより、要約や分析の精度が向上する。
- 引用の正確性:AIがユーザーに回答する際、「ID: {doc_id} の事件では〜」と正確に引用できるようになる。
各項目の内訳
| 項目名 | 内容 | AIにとっての価値 |
| doc_id | 固有の管理番号 | 他の判例と混同しないための絶対的な指標。 |
| court | 裁判所名 | 裁判所のレベル(地裁・高裁など)を判断する材料。 |
| date | 判決日 | その判例が最新のものか、当時の法体系での判断かを知る鍵。 |
| case_number | 事件番号 | 正式な文献として参照・引用する際の必須情報。 |
| case_name | 事件名 | どんな争点(不当利得、損害賠償など)の事件かを一目で把握。 |
body = row["main_text"] or ""
判例の核心部分である主文などを変数に格納する際、データが空っぽ(None)だったとしてもプログラムを停止させないための処理。
Pythonの「or」の賢い使い方
row["main_text"]:データベースのmain_textカラムから、判決の主文を取り出そうとしている。or "":もしデータベースの該当項目が空(None)だった場合、Pythonはorの右側にある空の文字列""を採用する。
なぜこれが必要なのか?
Pythonでは、文字列(String)と None を連結しようとすると、「型が違うので計算できません」というエラー(TypeError)が発生してプログラムが止まってしまう。この1行で、最低でも空の文字列であることを保証することで、この後のテキスト結合処理を安全に行えるようになる。
AIエージェントへの配慮
AIに情報を渡す際、この空文字への変換は地味ながら大きな効果を発揮する。
- 処理の継続:一部のデータが欠損している判例があったとしても、エラーで検索全体が失敗するのを防ぎ、「この項目は空ですが、他の情報はあります」とAIが回答できる余地を残す。
- ノイズの除去:AIに None というプログラム用語をそのまま渡すと、AIが回答の中で「主文はNoneです」と不自然な言い方をしてしまうことがある。””(空)にしておくことで、AIは自然に「主文の記載はありません」と判断しやすくなる。
reasons = row["facts_and_reasons"] or ""
判例の中でも特に重要な事実及び理由(判決に至るまでの詳しい経緯や裁判所の考え方)をデータベースから取り出す処理。body(主文)と同様に、データが空っぽだった場合の備え(or "")が含まれている。
「事実及び理由」とは何か?
データベースの facts_and_reasons カラムには、その裁判のドラマのすべてが詰まっている。
- 争点:何が争われているのか。
- 当事者の主張:原告と被告がそれぞれ何を言っているのか。
- 裁判所の判断:なぜそのような結論(主文)に至ったのかという論理的な説明。
AIエージェントが「この判例はなぜ損害賠償を認めたの?」といった高度な質問に答えるためには、この部分のデータが不可欠となる。
「or “”」による二重の安全策
仕組みは前回の body と同じだが、このフィールドにおいて or "" が特に重要な理由が2つある。
- 古い判例データの欠損への対応:古い判例や簡略化されたデータでは、この「理由」部分が未入力だったり、別の形式で保存されていたりすることがある。その際、None ではなく「空の文字」として扱うことで、プログラム全体がクラッシュするのを防ぐ。
- 結合の準備:この後の工程で、この reasons を他のテキストと合体させる。Pythonでは「文字 + 何もない(None)」はエラーになるが、「文字 + 空の文字(“”)」ならエラーにならず、スムーズに処理を続けられる。
if q.include_facts_and_reasons and reasons.strip():
return header + "\n[main_text]\n" + body + "\n\n[facts_and_reasons]\n" + reasons
ユーザー(またはAI)の希望に合わせて、詳細な理由(フルコース)を含めた最終的なレポートを組み立てる条件分岐と、その合体処理を行っている部分。
二重のチェック
if q.include_facts_and_reasons
- 役割:
CaseGetモデルで定義した「理由部分も返すか」というスイッチを確認している。 - 意味: ユーザーが「詳しい理由まで知りたい」と指示した(デフォルトはTrue)場合にのみ、次のステップに進む。
and reasons.strip()
- 役割:
reasons(事実及び理由)の中身が、空白や改行だけでないかをチェックしている。 - 意味: たとえスイッチがONでも、データベース側に理由の記載がなければ、無駄に空の項目を付け足さないようにする。
レポートの組み立て図
条件をクリアした場合、これまでに準備した具材を一つの長い文字列に合体させる。
| 構成要素 | 役割 |
header | 事件番号や裁判所名などの表紙情報。 |
"\n[main_text]\n" | ここからが「主文」であることを示すラベル。 |
body | 判決の結論部分。 |
"\n\n[facts_and_reasons]\n" | ここからが「理由」であることを示す目立つ見出し。 |
reasons | 裁判のドラマが詰まった「理由」の全文。 |
なぜ「ラベル」を付けるのか?
[main_text] や [facts_and_reasons] といった括弧付きのラベルをわざわざ挿入しているのには、以下の理由がある。
- AIの構造理解:AIは、情報の塊が「どこからどこまでが何なのか」を明示されることで、要約や分析の精度が劇的に向上する。
- ハルシネーションの防止:「理由」と「結論(主文)」が混ざってしまうと、AIが結論を誤読するリスクがある。境界線を引くことで、AIは「これは結論ではなく、当事者の主張部分だな」と正しくコンテキストを把握できる。
return header + "\n[main_text]\n" + body
関数の最後を締めくくる標準(ライト版)レポートの返却処理。前のステップ(if 文)で「詳細な理由」を含める条件に当てはまらなかった場合、最終的にこの「基本情報 + 主文」というコンパクトな形でAIエージェントにデータが渡される。
シンプル・イズ・ベスト
この処理は、いわば「情報の最小ユニット」を構築している。
- ヘッダー(
header):裁判所名や事件番号などの「誰が・いつ・どこで」という基本情報。 - ラベル(
\n[main_text]\n):AIに対して「ここから下が判決の核心(結論)ですよ」という目印。 - ボディ(
body):判決の結論(主文)。
なぜこの控えめな返却が必要なのか
すべてを詰め込むフル版(理由付き)だけでなく、このライト版があることで、システム全体に以下のようなメリットが生まれる。
- トークン(コストと速度)の節約:判決の理由は数万文字に及ぶこともある。もしユーザーが「結論だけ知りたい」と思っているなら、この短いレポートを返すだけで済むため、AIの処理が速くなり、APIの利用料金も安く抑えられる。
- 情報の整理:AIエージェントが複数の判例を比較する場合、最初から全文を読み込ませるよりも、まずはこのライト版で要点を確認させるほうが、AIが混乱しにくくなる。
これで「本文取得ツール」の解析は終わり。次回からは「参照解決ツール」の解析に移る。