Agents SDKを使った判例検索 (4)

Agents SDKを使った判例検索コード( database_query.py )解析の3回目。引き続き「 検索ツール 」部分のコードについて1行ずつその働きを見ていく。

    keywords = [k.strip() for k in q.keywords.split() if k.strip()]

ユーザーが入力したキーワード文字列を、プログラムが扱いやすい綺麗なリスト形式に整える処理を行っている。CaseSearch モデルで定義した keywords(スペース区切りの文字列)を、実際の検索処理に使うために整理する工程。この1行(内包表記)で、以下の3つのステップを同時に行っている。

  1. q.keywords.split():文字列を空白(スペースや全角スペース、改行など)で分割してリストにする。
    • 入力: "不法行為 損害賠償" (間にスペースが2つある場合など)
    • 結果: ['不法行為', '', '損害賠償']

      ここで使われている「q」は search_cases に渡される引数。
        @function_tool
        def search_cases(q: CaseSearch) -> str:

      中身は、例えば次のようになる。
        q = CaseSearch(
         keywords=”発信者情報開示 プロバイダ責任制限法”,
         court=”東京地裁”,
         limit=5
         )
      この例では、q.keywords は “発信者情報開示 プロバイダ責任制限法” になる。
  2. if k.strip():strip() は文字列の前後の不要な空白だけを除去するコード。分割した結果の中に空文字(何も入っていない項目)がないかをチェックしている。k.strip() が True(kの前後の空白を除いても、何か文字が残っている)なら、その要素をリスト(keywords)に追加する。この処理によりスペースが連続して入力された際に出るゴミを取り除く。
  3. k.strip() (前半部分):念のため、各キーワードの前後にある余分な空白を削除している。
    if not keywords:
        return "検索語が空です。"

検索キーワードが一つも存在しない状態(keywords が false)かどうかをチェックし、もし空であれば即座に処理を中断するバリデーション(入力チェック)を行っている。

    where_parts: List[str] = []
    params: List[object] = []

ユーザーの入力に合わせて、検索条件(SQL)を柔軟に組み立てるための準備をしている。検索機能において、キーワードだけで探す場合もあれば、裁判所名を追加する場合もあるため、最初から固定のクエリを書くのではなく、「部品」としてリストに貯めていく手法を採っている。

where_parts: List[str] = []

SQLの WHERE 句(条件文)のひな形を格納するリストを作成している。ここに、["content LIKE ?", "court LIKE ?"] のように、条件式が文字列として追加されていく。

params: List[object] = []

where_parts?(プレースホルダ)に入る実際の値を格納するリストを作成している。ここに、 ["%損害賠償%", "%東京地裁%"] のように、ユーザーが入力した具体的な検索語が入る。

    for kw in keywords:

この for 文は、前のステップで綺麗に掃除・リスト化したキーワードを1つずつ取り出して、実際の検索ルール(SQL)に変換していくエンジンの回転にあたる部分。ユーザーが複数の言葉を入力した場合(例:「不法行為 損害賠償」)、このループにより複数の言葉をすべて含むといった高度な検索が可能になる。

        where_parts.append(
            "("
            "doc_id LIKE ? OR court LIKE ? OR date LIKE ? "
            "OR case_number LIKE ? OR case_name LIKE ? "
            "OR main_text LIKE ? OR facts_and_reasons LIKE ?"
            ")"
        )

1のキーワードを裁判例データの複数の項目(カラム)から横断的に検索するためのルールを定義している。ひとことで言えば、「どこかの項目にこの言葉が含まれていればヒットさせる」という全文検索的な処理を行なっている。通常、キーワードを「主文(main_text)」から探すだけでは、事件番号や裁判所名で検索したいユーザーの要望に応えられない。そのため、このコードは、以下のすべての項目に対して「または(OR)」という条件で網を広げている。

  • doc_id: 内部管理用ID
  • court: 裁判所名
  • date: 判決日
  • case_number: 事件番号(例:令和5年(ワ)第123号)
  • case_name: 事件名(例:損害賠償請求事件)
  • main_text: 主文
  • facts_and_reasons: 事実及び理由(判決の本文)
        like = f"%{kw}%"

この1行は、SQLのあいまい検索(LIKE検索)を実行するために、キーワードを専用の形式に変換している。データベースに対して「この文字を含むものを探して」と命令するための、検索用パターンを作成している。

各要素の意味

  • f”…” (f-string):Pythonのフォーマット済み文字列リテラル。{kw} の部分に、実際の変数 kw の中身(例:「損害賠償」)が直接埋め込まれる。
  • % (パーセント記号):SQLにおけるワイルドカードと呼ばれる特殊文字。「0文字以上の任意の文字列」を意味する。
  • f”%{kw}%”:キーワードの前後を % で囲むことで、部分一致(含む)の条件になる。
パターン意味検索のイメージ
%キーワード%部分一致どこかにその言葉が入っていればヒット
キーワード%前方一致その言葉から始まっているものだけヒット
%キーワード後方一致その言葉で終わっているものだけヒット
        params.extend([like] * 7)

直前で作った7つの検索項目(カラム)に対して、キーワードを安全に流し込むためのデータ作成を行っている。

[like] * 7 (リストの複製)

like 変数の中身(例:"%損害賠償%")を 7個並べた新しいリストを作成している。

  • 中身のイメージ["%損害賠償%", "%損害賠償%", ...(合計7個)]
  • なぜ「7」なのか::直前の where_parts.append で、doc_id, court, date など合計7つの項目に対して ?(プレースホルダ)を置いたため、それに対応する値も7個必要だから。

params.extend(...)

すでに作成されている params リストの末尾に、新しいリストの要素をまとめて追加するメソッド。append だとリストの中に「リストそのもの」が入ってしまうが、extend を使うことで、中身の要素だけをバラバラにして綺麗に追加できる。この処理により、SQL文の中にある 「?」params の値が、左から順番にピタッと一致するようになる。

  1. doc_id LIKE ?paramsの1個目
  2. court LIKE ?paramsの2個目
  3. facts_and_reasons LIKE ?paramsの7個目