OpenAI のモデル選択・活用ガイドまとめ📚
OpenAIより、実運用ケースのためのモデル選択・活用ガイドが展開されていて非常に勉強になったのでまとめてみた。
🧠目的 & 対象読者
このクックブックは、GPT 4.1、o3、o4-mini など OpenAI の複数モデルをどのように使い分け、適切にプロンプトを設計し、本番環境へ展開するかを解説する実践的なガイドです。ソリューションエンジニア、テクニカルアカウントマネージャ、パートナーアーキテクト、あるいは半分技術寄りのビジネス担当者まで、幅広い層を対象としています。
本資料の特徴:
- 最新モデルの能力、業界ニーズ、ユースケース(法律、医薬、保険など)を踏まえて、実際にどのモデルをどう使うべきかを示す。
- モデル比較表とモデル進化図を使い、タスクごとにどのモデルが向いているかを素早く判断できるようにする。
- RAG (Retrieval-Augmented Generation) や マルチエージェントシステムなどの具体例を挙げ、実務に近い形でのアーキテクチャを示す。
- プロトタイプを実際に本番運用へ移行する際のチェックリストや考慮点をまとめる。
- 料金やレイテンシなどのコスト構造についてもなるべくわかりやすく解説。
重要: ここで紹介するメトリクス・推奨事項は2025年時点の実験に基づくもので、今後のモデル更新や利用環境の変化によって最適解は変化する可能性があります。
今回の CookBook の構成
-
目的 & 対象読者
誰が読むべきか、この文書のカバー範囲は何かを概観。 -
モデルガイド
- どのモデルがどんなタスクに最適かを素早く判断するための「モデルイントロ・マトリックス」
- モデルの進化図
-
ユースケース
-
3A. 法律文書の長文RAG (Legal Q&A)
長大な法律文書からのQ&Aに向けたエージェンティックRAGシステムを構築する例。 -
3B. 製薬R&D向けAI Co-Scientist
製薬分野での実験計画やアイデア創出を促進するマルチエージェントシステムの例。 -
3C. 保険請求処理
手書きの保険申請フォームをデジタル化し、正しく解析・検証する手順。
-
3A. 法律文書の長文RAG (Legal Q&A)
-
試作から本番へ
- プロトタイプで得た結果を実際の運用に移すときに必要なチェックリスト。
-
適応のための決定ツリー
- 特定要件(予算制約や推論深度など)に応じてモデルを選ぶフローチャート。
-
付録
- 料金表・レイテンシ
- プロンプトパターンの一覧
- 外部リソースへのリンク
「とりあえずどのモデルを使うべきか早く知りたい」という場合はモデルガイドを、「どう実装すればいいか詳しく知りたい」という場合は該当ユースケースのセクションを、そして実運用段階の課題は試作から本番へ・決定ツリーで確認すると効率的です。
🧭モデルガイド
モデルイントロ・マトリックス
モデル | コア強み | 理想的な用途 | 注意点 | 上位/下位モデル移行パス |
---|---|---|---|---|
GPT-4o | リアルタイムの音声/ビジョン対応 | 音声対応のマルチモーダルエージェント | テキスト処理においては GPT-4.1 よりやや劣る | 深い推論が必要→ o4-mini |
GPT-4.1 | 100万トークン対応のテキストSOTA、精度が高い | 大規模文書分析、コードレビュー | ネイティブなステップバイステップ推論力はやや弱め。高コストでもある | コスト重視→ 4.1-mini / nano |
o3 | ツール活用型の深い推論に強い | 高リスク・多段階の推論を要するタスク | 価格とレイテンシが高め | コスト/速度→ o4-mini |
o4-mini | 安価かつ素早い推論処理 | 大量・「ほどほど」な精度でいいロジック処理 | o3ほどの深い推論は難しい | 高い正確度が必要→ o3 |
6.1節で価格と詳細ユーティリティを表にまとめています。
モデルの進化概要
OpenAIのモデル群は、それぞれの特化性に合わせて発展し、以下のような2系統に分岐しています。
-
GPT系 (GPT-4.1, GPT-4oなど)
一般用途に最適化されたファミリー。GPT-4.1 は最大1Mトークンの超ロングコンテキストを扱え、GPT-4o は音声や画像(ビジョン)に対応するバリアントを含みます。 -
o系 (o3, o4-miniなど)
ステップバイステップの推論やツールコールによる複雑なタスクに強い。reasoning_effort
(low/medium/high)を調整でき、タスクごとに推論の深さを変えられます。
モデルファミリーの大きな違い
-
GPT-4.1 ファミリー
- 長文処理と高精度の生成に強い
- mini / nano バリアントで低コスト版も用意
- 大規模コンテキストを必要とするドキュメント要約や法務分析に向く
-
o3 / o4-mini ファミリー
- 段階的推論とツール連携に特化
- 計算多めだが正確度が高く、複数のステップを踏むような理由づけが要求される場面に適性
- コスト重視の場合は o4-mini の low/med 設定などを活用
本書で取り上げる例は主に GPT-4.1 シリーズ、o3、o4-mini にフォーカスしています。
📖3A. ユースケース: 法律文書のロングコンテキストRAG (Legal Q&A)
法律文書のようにページ数や規定が膨大なものから、正確かつ証拠(該当条項)の根拠を示しながら回答するシステム構築は、多くのエンタープライズで課題となっています。ここではTBMP (米国特許商標庁の商標審判部マニュアル) を例として、長大なドキュメントを事前のベクトル化なしでRAGを行うアプローチを紹介します。ミリオンサイズのコンテキストウィンドウを活かし、分割したチャンクを段階的に絞り込むという「階層的エージェント式」の手法です。
TL;DR 概要
- 長文テキスト: TBMPは1,194ページほどの法的手続マニュアル。
-
利用モデル:
- GPT-4.1-mini で関連セクションを段階抽出 → GPT-4.1で最終回答生成 → o4-miniで回答検証
-
メリット:
- 大がかりなベクトルDBや事前インデックス構築が不要
- 段階的に必要部分を深掘りしていける
- 構造化された引用(パラグラフID等)つきの回答が可能
-
トレードオフ:
- シンプルなベクトル検索に比べ、1クエリあたりの推論コストが高い
- 大量のクエリにはやや不向き
想定シナリオ
- ユーザ: 知的財産訴訟・係争を担当する弁護士やパラリーガル
-
質問例:
- 「Motion to Compel Discovery の提出様式と署名要件を TBMP に従って教えてほしい」
- 「Discovery Conference の期限はどこで規定されているか」
-
結果:
- 質問に対し、TBMPの該当箇所を引用したうえで正確に答える
- 階層的な文書探索により必要部分を絞り込む
典型アーキテクチャの流れ
-
文書を20チャンクほどに分割
ミリオン・トークンでも1,000ページ以上ある文書を全部一括提示はできるが、メモリ効率上分割するほうが扱いやすい。 -
段階的に関連チャンクをルーティング
- GPT-4.1-mini を使い、「どのチャンクが質問に関連するか」を順次絞る
- 深い階層まで細分化 (さらに20に分割など) し、最終的に関連度の高い段落だけを取り出す
-
回答生成
- GPT-4.1 が必要段落を参照しながら回答
- 段落IDを明示して引用を埋め込む(ユーザは裏付け確認がしやすい)
-
検証 (LLM-as-Judge)
- o4-miniなどで回答内容の整合性や引用の正しさをチェック
運用コスト
- 従来RAG: Embedding + データベース構築 … 初期に数百円〜
- エージェンティックRAG: 前処理なし → その分クエリごとに階層ナビゲーションの費用がかかる
- 例: gpt-4.1-mini を複数ステップ + GPT-4.1回答 + o4-mini検証
- 1クエリあたり合計 ~$0.36 程度 (2025年4月時点試算)
適用可能な他分野
-
ヘルスケア規定・医療ガイドライン
指定された部分だけ抽出したいが、法的厳密性も大事 -
金融のコンプライアンス文書
厳密な引用・担保が必要 - 大規模テクニカルマニュアル
まとめ
- 大規模な文書を対象であり、インデックス前処理なしで即時回答したい場合に有効
- 引用の厳密性や段階的な文書スキミングが必要な法務系などに向く
- 1問1答あたりの計算コストはやや高いが、メンテナンス不要のシンプル設計が魅力
🧪3B. ユースケース: 製薬R&D向けAI Co-Scientist
新薬開発や実験計画の最適化は、仮説検証を繰り返す長いプロセスを要しがちです。ここでは、AIエージェントが「共同研究者(Co-Scientist)」 として実験アイデアを出し、プロトコルを組み、コストや安全性を見ながら評価する流れを紹介します。
全体像
- 目的: 例として「XYZ-13 という化合物の合成収率を 15%以上上げる」
- チーム構成: 複数のAIエージェント(役割分担)+ 人間研究者(最終判断)
-
主なステップ:
- Ideation: o4-mini の複数エージェントが、それぞれ「仮説」「プロトコル」「リソース最適化」など役割を変えて並行にアイデアを提案
- Ranking: 候補プランを対決形式で比較し、最も有望なものを選ぶ
- Critique (Deep Review): o3が「上級研究者」としてプランを厳密査読し、調整案やリスク評価を加える
- Safetyチェック: gpt-4.1-mini等で最終安全性を簡易チェック
- 人間が承認: 実験を実施し、結果を再度AIで解析→DBに蓄積
-
使い分け:
- o4-mini は高速&安価なのでブレストや並列実行に適する
- o3 は段階推論に強く、最終的な科学的レビュー・分析に優位
想定メリット
-
実験計画のサイクルタイム短縮
手動での計画立案をAIが補助することで、数日〜数週間かかる議論を短時間で回せる。 -
コスト管理
AIが試薬コストや実験工数を加味したプランを自動生成 → 予算オーバーを防ぎやすい。 -
リスク管理
複数エージェント方式により「この条件は危険かも」「規制上問題ないか」など複数視点を自動チェック。
コスト面
- Ideation(o4-mini複数起動) → プロトコル数が増えるにつれ多少のコスト
- Ranking(o4-mini) → ペアワイズ比較
- Critique(o3) → 精密査読はトークン量多め
-
安全チェック(gpt-4.1-mini) → スクリーニング程度
トータルで高精度だが、段階ごとにモデルを切り替える設計が重要。コストと精度をバランスできる。
注意点
-
最終的な責任は人間
安全リスクや法規制関連を完全自動化するのはリスクが大きい。 -
ドメイン知識の補強
AIの提案が正しいか検証するために、社内データベースや文献検索と連携するとさらに有効。
まとめ
- 複雑な実験設計を高速化
- マルチエージェントで創造的+厳密なアプローチ
- 研究者の負担を削減しつつイノベーションを加速
🧾3C. ユースケース: 保険金請求処理
多くの業界で、手書きフォームをデジタル化して後工程(審査・データ活用など)に回したい、という需要があります。ここでは保険会社の手書き請求フォームを例に、Vision + LLM を組み合わせて自動化するパターンを示します。
シナリオ
- 課題: 手書きの住所や電話番号、メールアドレスをOCRで抽出し、欠損や不確実性があれば二次確認を行いたい
-
モデル分担:
- gpt-4.1 で画像OCR → できるだけ正確にテキスト化
-
o4-mini で補完や検証 → 例えば「メールアドレスが
l
か1
かわからない場合、外部APIで存在確認」など
- 出力: 完整合な JSON や既定フォーマットで返す
- コスト目標: 1,000ページあたり$20以下が目標など
具体的なポイント
-
高精度OCR
通常のOCRエンジンよりLLMのVision対応が有利な場合がある(細かい手書き判読など)。 -
不明箇所の対応
- 文字が曖昧な場合には「候補 A or B」と記述し、後段で検証 or 人間レビュー
-
ツール連携
- 住所なら郵便番号を補完するために「web検索API」
- メールアドレスなら「メール有効性検証API」
-
段階的推論
- 最初に抽出 → 次に検証 → 仕上げに確定値を返却
評価指標
- OCR文字単位精度: 必須情報(名前や住所など)の正確度
- 不明フィールドの埋め率: 処理可能なものは自動補完できているか
- ヒューマンレビュー率: UNKNOWNとして放置された割合があまりに高くないか
運用上の利点と留意点
- バッチ処理によるコスト削減: 多少レイテンシがあっても問題ないなら、夜間に一括処理しやすい。
- セキュリティ/プライバシー対応: 保険申請書には個人情報が含まれるため、社内環境やセキュアなAPI設計が必須。
- 拡張応用: 他の紙書類(税関連、医療関連など)にも適用可能。
🚀試作から本番へ
ここでは「PoC (概念実証) や社内向けツールとして実験的に作ったシステム」を本番運用に切り替える際の注意点をまとめます。
TL;DR チェックリスト
項目 | 概要 |
---|---|
成功基準 (KPI/SLO) の定義 | ・回答精度、処理コスト、レイテンシなど、定量的に計測可能なゴールを明確化 |
モデル選定の理由を文書化 | ・なぜ特定のモデルを使うか (コスト/性能/ユースケースの特徴)、将来バージョンアップ時の参考 |
評価とテスト | ・ゴールデンセットを用いた自動テストで回帰検証 ・ツール呼び出しや外部API失敗時のハンドリングも確認 |
可観測性とコスト管理 | ・全ステージのトークン数、呼び出し回数、レイテンシをログ化 ・コストが想定外に膨らまないようリミット設定 |
セーフティ & コンプライアンス | ・モデレーションAPIやドメイン固有チェックで危険出力を防ぐ ・法的・倫理的要求に合致させる(金融・医療・法務など必須) |
モデル更新とバージョン管理 | ・バージョン固定/ロールバック策を整備 ・A/Bテストで新モデルの精度向上 or リグレッション確認 |
1. 成功基準を定量化
- 例:
- 法務RAG: 回答の正答率95%以上、引用箇所誤り率 <2%
- 保険OCR: フィールド入力正解率98% 等
- それらを常時ログ観測・ダッシュボード化し、変動があればアラート。
2. モデル選定理由を文書化
- 後から「なぜ GPT-4.1 を使っているの?」と訊かれたときに答えられるようにする。
- 開発者やステークホルダー間の共有に役立つ。
3. テストと評価
- ファクトチェック、ツールコールが失敗したときのフォールバックを確認。
- 量産フェーズ前に最悪ケースを試す「ストレステスト」も行うと良い。
4. 可観測性 & コスト制御
- 1リクエストあたりのトークン使用量、応答時間を記録・可視化。
- 予想外の急増があった場合は、モード切り替え(Fast / Thorough)などで対応。
5. セーフティ & コンプライアンス
- 業種特有の規制(医療情報保護、金融庁ガイドラインなど)を踏まえ、必要に応じたプロンプト強化やフィルタリング実装を。
- Human in the Loop (HITL): リスクが高い回答は人間が必ずレビューするフロー。
6. モデル更新とバージョン管理
- 「o4-mini-2025-04-16」等のバージョンを指定して固定
- 新バージョンを試す際は、少数トラフィックでA/Bテスト → OKなら切り替え → 万一問題あればロールバック。
🌲適応のための決定ツリー
同じタスクでも要件が変わるとベストなモデルは変わります。以下は大まかな指針です。
-
マルチモーダル対応(画像/音声)か?
- はい → GPT-4o
- いいえ → 次へ
-
ロングコンテキスト(20万~100万トークン級)が必要か?
- はい → GPT-4.1 ファミリー
- いいえ → o系 or GPT-4o-mini 検討
-
ステップバイステップの推論がどれくらい重要か?
- 深い推論が必要 → o3 or o4-mini (reasoning_effort=high)
- そこまで複雑でない → GPT-4.1-mini / o4-mini(low/med)
-
コストを最優先したいか?
- はい → mini / nano / low 設定
- いいえ → 高精度のフルサイズ or o3(high)
ビジネスへの説明
-
ビジネス成果との関連
「法務チームが回答にかかる時間をXX%削減できる」「コールセンター対応を迅速化し満足度UP」など具体的に。 -
トレードオフ
高精度モデルほどコストとレイテンシが増すので、予算やSLAに応じて選択していることを示す。 -
サンプル比較
同じ質問を mini・nano・フルサイズモデルでどう答えが違うかビジュアル化すると理解が早い。
📎付録
以下は本クックブックの補足情報や参考資料です。
用語解説
用語 | 説明 |
---|---|
コンテキストウィンドウ (Context Window) | LLM が一度に読み込んで処理できるテキスト量(トークン数)の上限 |
ハルシネーション (Hallucination) | 実際には根拠がない内容を、あたかも真実らしくモデルが生成してしまう現象 |
Latency | 入力から応答が返るまでの待ち時間 |
LLM | Large Language Model の略称 |
プロンプトエンジニアリング | AI への入力指示(プロンプト)を最適化して、望ましいアウトプットを得るための工夫 |
RAG | Retrieval-Augmented Generation。外部の検索やデータベースでコンテキストを取得してから生成するアプローチ |
SOTA | State-of-the-Art(最先端) |
トークン | 自然言語を分割した最小単位。英語では1トークン≒0.75単語とされることが多い(日本語はもう少し変動する) |
料金表 (2025年4月時点)
Azure OpenAI Serviceも同じ価格でした
モデル | コンテキスト | 入力価格 (1Mトークンあたり) | 出力価格 (1Mトークンあたり) | 得意分野 |
---|---|---|---|---|
GPT-4.1 | 1M | $2.00 | $8.00 | ロングドキュメント、コードレビュー |
GPT-4.1 mini | 1M | $0.40 | $1.60 | コストと性能のバランス型、プロダクション運用 |
GPT-4.1 nano | 1M | $0.10 | $0.40 | 大量スケール・予算厳しめ向け |
GPT-4o | 128K | $5.00 | $15.00 | リアルタイム音声・ビジョン対応 |
GPT-4o mini | 128K | $0.15 | $0.60 | ビジョンタスクをコスト抑えつつ |
o3 (low/med/high) | 200K | $10.00* | $40.00* | 多ステップ推論・ツール活用 |
o4-mini (low/med/high) | 200K | $1.10* | $4.40* | ビジョン + 推論。高速・安価ながら一定深度の推論可 |
*low/med/highの指定で推論が増えれば追加トークンコストも増加する
プロンプトパターン早見表
パターン | 説明 | トークン増加率 | レイテンシ増加率 | 適合モデル |
---|---|---|---|---|
Self-Critique | 回答後にモデル自身で内容をチェックさせる | +20-30% | +15-25% | GPT-4.1, o3 |
Chain-of-Thought | 「ステップを踏んで考えろ」と明示的に指示し、途中思考を展開させる | +40-80% | +30-50% | o3, o4-mini (high) |
Structured Outputs | JSONなど特定構造での出力を強制し、パースをしやすくする | +5-10% | +5-10% | 全モデル |
Zero-Token Memory | 会話履歴を外部保存し、モデルには最小限のみを与える | -70-90% (会話) | -5-10% | GPT-4.1ファミリー |
Skeleton-Fill-In | ひな型を提示し、空欄に埋めるだけの形にする | -10-20% | -5-15% | o4-mini, GPT-4.1 nano |
Self-Consistency | 複数解答を生成し、その中で最も一貫性があるものを再選別する | +200-300% | +150-250% | o3 (high) |
Role-Playing | モデルに「専門家」「ユーザ」「レビュー担当」など役割を割り振る | +5-15% | ほぼ変動なし | GPT-4o, o4-mini |
Tournament Ranking | 候補をペアにして比較させ、勝者を選ぶ形 | +50-100% | +30-60% | o3, o4-mini (high) |
Tool-Calling Reflex | 不確実性があればツール呼び出しするよう誘導 | +10-30% | +20-40% | o3, GPT-4.1 |
リンク集
OpenAI Official Resources
- OpenAI Cookbook Main Repository
- Function Calling Guide
- Vision Models Guide
- Agents Documentation
- Structured Outputs Guide
RAG & Retrieval
Specialized Use Cases
Prompting & Model Selection
Evaluation & Deployment
- Getting Started with OpenAI Evals
- How to use the Usage API and Cost API to monitor your OpenAI usage
🤖AOAI での検証コード
現在、Azure OpenAI Serviceを利用すると、なぜかresponses APIがうまく動かなかったので、chat completionに置き換えて、一部試してみました。
サンプル 3A
一部変更点のコードブロックのみ記載
- Document Loading
変更なし - Improved 20-Chunk Splitter with Minimum Token Size
変更なし - Router Function with Improved Tool Schema
from openai import AzureOpenAI
import json
from typing import List, Dict, Any
# Azure OpenAI クライアントを初期化
client = AzureOpenAI(
azure_endpoint="https://<resource name>.openai.azure.com/",
api_version="2025-03-01-preview",
api_key="<your key>"
)
def route_chunks(question: str, chunks: List[Dict[str, Any]],
depth: int, scratchpad: str = "") -> Dict[str, Any]:
"""
モデルに質問し、関連するチャンクIDを選択しながら
スクラッチパッドに推論を記録します。
Chat Completions API を使用した実装版。
Args:
question: ユーザーの質問
chunks: 評価対象のテキストチャンクリスト
depth: 現在のナビゲート深さ
scratchpad: これまでのモデルの考え(空文字スタート)
Returns:
selected_ids: 選択されたチャンクIDのリスト
scratchpad: 更新後のスクラッチパッド
"""
print(f"\n==== ROUTING AT DEPTH {depth} ====")
print(f"Evaluating {len(chunks)} chunks for relevance")
# システムメッセージ
system_message = (
"You are an expert document navigator. Your task is to:\n"
"1. Identify which text chunks might contain information to answer the user's question\n"
"2. Record your reasoning in a scratchpad for later reference\n"
"3. Choose chunks that are most likely relevant. Be selective, but thorough."
)
# ユーザーメッセージに質問・スクラッチパッド・チャンクを追加
user_message = f"QUESTION: {question}\n\n"
if scratchpad:
user_message += f"CURRENT SCRATCHPAD:\n{scratchpad}\n\n"
user_message += "TEXT CHUNKS:\n\n"
for chunk in chunks:
user_message += f"CHUNK {chunk['id']}: {chunk['text']}\n\n"
# Scratchpad 更新用 function schema
update_fn = {
"name": "update_scratchpad",
"description": "Record your reasoning about why certain chunks were selected",
"parameters": {
"type": "object",
"properties": {
"text": {"type": "string", "description": "Reasoning text"}
},
"required": ["text"],
"additionalProperties": False
}
}
# 初回呼び出し: スクラッチパッド更新
messages = [
{"role": "system", "content": system_message},
{"role": "user", "content": user_message + "\nFirst, call update_scratchpad with your reasoning."}
]
response1 = client.chat.completions.create(
model="gpt-4.1-mini",
messages=messages,
functions=[update_fn],
function_call={"name": "update_scratchpad"}
)
choice1 = response1.choices[0].message
# function_call の結果をパース
args1 = json.loads(choice1.function_call.arguments)
reasoning = args1.get("text", "")
new_scratch = scratchpad + ("\n\n" if scratchpad else "") + f"DEPTH {depth} REASONING:\n{reasoning}"
# function 実行結果を会話履歴に追加
messages.append(choice1)
messages.append({
"role": "function",
"name": "update_scratchpad",
"content": "Scratchpad updated successfully."
})
# 二回目: チャンク選択用 function schema
select_fn = {
"name": "selected_chunks",
"description": "Return IDs of chunks relevant to the question",
"parameters": {
"type": "object",
"properties": {
"chunk_ids": {"type": "array", "items": {"type": "integer"}}
},
"required": ["chunk_ids"],
"additionalProperties": False
}
}
messages.append({"role": "user", "content": "Now select the chunk IDs relevant to answer the question."})
response2 = client.chat.completions.create(
model="gpt-4.1-mini",
messages=messages,
functions=[select_fn],
function_call={"name": "selected_chunks"}
)
choice2 = response2.choices[0].message
args2 = json.loads(choice2.function_call.arguments)
selected_ids = args2.get("chunk_ids", [])
print(f"Selected chunks: {selected_ids}")
print(f"Updated scratchpad:\n{new_scratch}")
return {"selected_ids": selected_ids, "scratchpad": new_scratch}
# navigate_to_paragraphs 関数の呼び出しなどは従来通り使用できます
- Recursive Navigation Function
変更なし - Run the Improved Navigation for a Sample Question
変更なし - Answer Generation
from typing import List, Dict, Any
from pydantic import BaseModel, field_validator
class LegalAnswer(BaseModel):
"""Structured response format for legal questions"""
answer: str
citations: List[str]
@field_validator('citations')
def validate_citations(cls, citations, info):
# Access valid_citations from the model_config
valid_citations = info.data.get('_valid_citations', [])
if valid_citations:
for citation in citations:
if citation not in valid_citations:
raise ValueError(f"Invalid citation: {citation}. Must be one of: {valid_citations}")
return citations
def generate_answer(question: str, paragraphs: List[Dict[str, Any]],
scratchpad: str) -> LegalAnswer:
"""Generate an answer from the retrieved paragraphs using Chat Completions API."""
print("\n==== GENERATING ANSWER ====")
# Extract valid citation IDs
valid_citations = [str(p.get("display_id", str(p["id"]))) for p in paragraphs]
if not paragraphs:
return LegalAnswer(
answer="I couldn't find relevant information to answer this question in the document.",
citations=[],
_valid_citations=[]
)
# Build context string
context = ""
for paragraph in paragraphs:
display_id = paragraph.get("display_id", str(paragraph["id"]))
context += f"PARAGRAPH {display_id}:\n{paragraph['text']}\n\n"
# System prompt
valid_citations_str = ", ".join(valid_citations)
system_prompt = f"""You are a legal research assistant answering questions about the
Trademark Trial and Appeal Board Manual of Procedure (TBMP).
Answer questions based ONLY on the provided paragraphs. Do not rely on any foundation knowledge or external information or extrapolate from the paragraphs.
Cite phrases of the paragraphs that are relevant to the answer. This will help you be more specific and accurate.
Include citations to paragraph IDs for every statement in your answer. Valid citation IDs are: {valid_citations_str}
Keep your answer clear, precise, and professional.
"""
# Prepare messages
messages = [
{"role": "system", "content": system_prompt},
{"role": "user", "content": f"QUESTION: {question}\n\nSCRATCHPAD:\n{scratchpad}\n\nPARAGRAPHS:\n{context}"}
]
# Define function schema for structured output
fn_schema = {
"name": "legal_answer",
"description": "Return a structured legal answer with citations",
"parameters": {
"type": "object",
"properties": {
"answer": {"type": "string"},
"citations": {
"type": "array",
"items": {"type": "string"}
}
},
"required": ["answer", "citations"],
"additionalProperties": False
}
}
# Call Chat Completions with function calling
response = client.chat.completions.create(
model="gpt-4.1-mini",
messages=messages,
functions=[fn_schema],
function_call={"name": "legal_answer"},
temperature=0.3
)
msg = response.choices[0].message
args = json.loads(msg.function_call.arguments)
# Instantiate Pydantic model, injecting valid_citations
args['_valid_citations'] = valid_citations
answer_obj = LegalAnswer.parse_obj(args)
print(f"\nAnswer: {answer_obj.answer}")
print(f"Citations: {answer_obj.citations}")
return answer_obj
# Generate an answer
answer = generate_answer(
question,
navigation_result["paragraphs"],
navigation_result["scratchpad"]
)
- Answer Verification
cited_paragraphs = []
for paragraph in navigation_result["paragraphs"]:
para_id = str(paragraph.get("display_id", str(paragraph["id"])))
if para_id in answer.citations:
cited_paragraphs.append(paragraph)
# Display the cited paragraphs for the audience
print("\n==== CITED PARAGRAPHS ====")
for i, paragraph in enumerate(cited_paragraphs):
display_id = paragraph.get("display_id", str(paragraph["id"]))
print(f"\nPARAGRAPH {i+1} (ID: {display_id}):")
print("-" * 40)
print(paragraph["text"])
print("-" * 40)
from typing import List, Dict, Any, Literal
from pydantic import BaseModel
import json
class VerificationResult(BaseModel):
"""Verification result format"""
is_accurate: bool
explanation: str
confidence: Literal["high", "medium", "low"]
def verify_answer(question: str, answer: LegalAnswer,
cited_paragraphs: List[Dict[str, Any]]) -> VerificationResult:
"""
Verify if the answer is grounded in the cited paragraphs using Chat Completions API.
Args:
question: The user's question
answer: The generated answer
cited_paragraphs: Paragraphs cited in the answer
Returns:
VerificationResult with accuracy, explanation, and confidence
"""
print("\n==== VERIFYING ANSWER ====")
# Build context from cited paragraphs
context = ""
for paragraph in cited_paragraphs:
display_id = paragraph.get("display_id", str(paragraph["id"]))
context += f"PARAGRAPH {display_id}:\n{paragraph['text']}\n\n"
# System prompt
system_prompt = """You are a fact-checker for legal information.
Your job is to verify if the provided answer:
1. Is factually accurate according to the source paragraphs
2. Uses citations correctly
Be critical and look for any factual errors or unsupported claims.
Assign a confidence level: high, medium, or low."""
# User prompt
user_content = f"""
QUESTION: {question}
ANSWER TO VERIFY:
{answer.answer}
CITATIONS USED: {', '.join(answer.citations)}
SOURCE PARAGRAPHS:
{context}
Is this answer accurate and properly supported by the source paragraphs?
"""
messages = [
{"role": "system", "content": system_prompt},
{"role": "user", "content": user_content}
]
# Function schema for structured output
fn_schema = {
"name": "verification",
"description": "Return verification result with accuracy, explanation, and confidence",
"parameters": {
"type": "object",
"properties": {
"is_accurate": {"type": "boolean"},
"explanation": {"type": "string"},
"confidence": {"type": "string", "enum": ["high", "medium", "low"]}
},
"required": ["is_accurate", "explanation", "confidence"],
"additionalProperties": False
}
}
# Call Chat Completions API
response = client.chat.completions.create(
model="o4-mini", # またはご自身のデプロイ名
messages=messages,
functions=[fn_schema],
function_call={"name": "verification"},
)
# Parse out function_call arguments
msg = response.choices[0].message
args = json.loads(msg.function_call.arguments)
# Validate and return
result = VerificationResult.model_validate(args)
print(f"\nPASSED: {result.is_accurate}, Confidence: {result.confidence}")
print(f"Explanation: {result.explanation}")
return result
# Verify the answer using only the cited paragraphs
verification = verify_answer(question, answer, cited_paragraphs)
# Display final result with verification
print("\n==== FINAL VERIFIED ANSWER ====")
print(f"Verification: {'PASSED' if verification.is_accurate else 'FAILED'} | Confidence: {verification.confidence}")
print("\nAnswer:")
print(answer.answer)
print("\nCitations:")
for citation in answer.citations:
print(f"- {citation}")
3B, 3C はまた時間あれば試していきたい。。
また、不備等あればぜひコメントで教えていただきたいです。
Discussion