建設コンサル向け「ローカルAIナレッジ検索・報告書支援」を実データで試す - その3 -
Ubuntu 24.04+GPU環境にPDFを取り込み、実務で使えるか検証する
はじめに
前回は、Ubuntu Server 24.04上に次の構成でローカルRAG環境を作るための設計をまとめました。
- Ollama
- Open WebUI
- Qdrant
- FastAPI
- Docker Compose
- NVIDIA GPU
設計としてはひととおり形になりましたが、実際に使えるかどうかは、手元にあるPDFを入れてみなければ分かりません。
今回は、過去の業務報告書や技術資料などのPDFを使い、ローカルRAGが実務でどこまで役に立つのかを確認していきます。
最初から報告書を丸ごと書かせるのではなく、まずは次の3点を確認します。
- PDFの内容を正しく読み取れるか
- 質問に関係するページを探せるか
- 回答と一緒に、参照した資料名やページ番号を示せるか
この3点が安定してから、要約や報告書ドラフトの作成へ進む方針です。
今回のゴール
今回の段階では、本番システムを完成させることを目的にはしません。
まずは3~4件程度のPDFを対象に、次のような質問へ答えられる状態を目指します。
- 過去に同じような地形条件で検討した業務はあるか
- 設計条件として採用した降雨量はいくつか
- 使用した解析手法やソフトウェアは何か
- 検討結果として残された課題は何か
- 類似する対策工法を採用した事例はあるか
- この数値は、どの報告書の何ページに書かれているか
Open WebUIのKnowledge機能は、大量の文書から関連部分を検索し、回答の参照箇所を引用として表示できます。文書全体を毎回モデルへ渡すのではなく、質問に関係する部分を検索して利用する仕組みです。
ただし、PDFをアップロードしただけで実務に使える精度になるとは限りません。
特に建設コンサルタントの報告書には、表、図、数式、縦書き、段組み、スキャン画像などが含まれます。PDFからどのように文章を取り出すかが、検索精度を大きく左右します。
全体の処理イメージ
PDFそのものは保存しておき、検索用にはPDFから取り出した文章とメタデータを使います。
元のPDFを残しておく理由は、AIの回答が正しいか、最後に人が原本を確認できるようにするためです。
Step 1:GPUがDockerから使えるか確認する
最初に、Ubuntu側でGPUが認識されていることを確認します。
nvidia-smi
続いて、DockerとDocker Composeを確認します。
docker --version
docker compose version
DockerコンテナからGPUが見えるかも確認します。
docker run --rm --gpus all \
nvidia/cuda:12.9.0-base-ubuntu22.04 \
nvidia-smi
Docker Composeでは、サービスごとにGPUを割り当てられます。GPUを利用するサービスには、capabilities: [gpu]などのデバイス予約を設定します。
OllamaをDockerで動かす場合も、NVIDIA Container Toolkitを設定したうえで、コンテナへGPUを渡します。
ここで確認したいのは、単にnvidia-smiが表示されることだけではありません。
Ollamaでモデルを起動した後、次のコマンドも確認します。
docker exec -it ollama ollama ps
PROCESSORが100% GPUになっていれば、モデル全体がGPUへ載っています。
GPUメモリが足りない場合は、一部がCPUへ移されることがあります。その場合は、モデルサイズやコンテキスト長を小さくして調整します。Ollamaでは、コンテキスト長を大きくすると必要なGPUメモリも増えます。
Step 2:PDFをいきなり全部入れない
手元のPDFをすべて登録したくなりますが、最初は種類の異なる資料を少数だけ選びます。
例えば、次のような組み合わせです。
- テキストを選択できる一般的な報告書
- スキャンした古い報告書
- 表が多い資料
- 図面やグラフが多い資料
- 100ページを超える報告書
- 複数年度の類似業務報告書
最初にPDFを分類しておくと、どの種類の資料で問題が起きているのか分かりやすくなります。
PDFごとに、少なくとも次の項目を管理します。
document_id
ファイル名
資料名
業務名
業務年度
発注者
資料区分
分野
作成日
ページ数
機密区分
ファイルハッシュ
登録日時
更新日時
ファイル名だけで管理すると、後から同名資料や改訂版が出てきたときに区別できなくなります。
ファイルのSHA-256ハッシュを記録しておけば、同じPDFの二重登録や更新も判定できます。
Step 3:PDF解析は別処理として用意する
Open WebUIにもPDFやスキャン文書を扱うための文書抽出機能があります。
ただし、実務資料を扱う場合は、PDF解析処理をFastAPI側へ分離しておいた方が調整しやすくなります。
今回の候補としては、Doclingを使います。
DoclingはPDFの文章だけでなく、ページレイアウト、文章の読み順、表構造、数式、画像などを解析し、MarkdownやJSONへ変換できます。スキャンPDF向けのOCRにも対応しており、ローカル環境だけで処理できます。
処理後のデータは、次のように保存します。
data/
├── documents/
│ └── original/
│ └── sample_report.pdf
├── extracted/
│ ├── markdown/
│ │ └── sample_report.md
│ └── json/
│ └── sample_report.json
├── page_images/
│ └── sample_report/
│ ├── page_0001.png
│ └── page_0002.png
└── metadata/
└── rag_metadata.duckdb
元のPDF、抽出したMarkdown、構造情報を持つJSON、必要に応じてページ画像を残します。
DuckDBには、文書情報、変換状態、エラー内容、Qdrantへの登録状態などを保存します。
Qdrantには検索用のベクトルと、資料名、ページ番号、見出しなどのメタデータを保存します。
Step 4:文章を固定文字数だけで分割しない
RAGでは、PDFから取り出した文章を小さな単位へ分割します。この単位は一般にチャンクと呼ばれます。
単純に500文字ごとに切る方法でも動きますが、報告書の途中で文章や表が分断されることがあります。
実務資料では、できるだけ次の単位を使います。
- 章
- 節
- 項
- 段落
- 表
- 箇条書き
- ページ
例えば、次のようなメタデータをチャンクへ付けます。
{
"document_id": "REPORT_2025_001",
"title": "○○川河川改修概略設計業務報告書",
"fiscal_year": 2025,
"category": "河川",
"page": 42,
"section": "4.3 計画高水流量の設定",
"chunk_index": 128
}
この情報があると、回答時に「どの資料の何ページか」を表示できます。
また、「2024年度の河川業務だけ」「特定の発注者の資料だけ」といった絞り込みも可能になります。Qdrantは、ベクトル検索とメタデータ条件を組み合わせた検索に対応しています。
Step 5:Embeddingモデルを決める
Embeddingモデルは、文章を検索用の数値へ変換するモデルです。
最初の検証では、Ollamaから利用できる次のモデルを候補にします。
ollama pull qwen3-embedding:0.6b
OllamaはRAG向けのEmbeddingモデルとして、qwen3-embedding、embeddinggemma、all-minilmなどを案内しています。qwen3-embeddingには複数のモデルサイズがあります。
最初から大型モデルを使う必要はありません。
まず小型モデルで一連の処理を動かし、検索精度が不足する場合に、Embeddingモデルの変更を検討します。
Embeddingモデルを変更すると、原則として登録済み文書をすべて再計算する必要があります。そのため、使用したモデル名やバージョンもDuckDBへ記録します。
Step 6:回答用モデルを決める
回答生成には、まず日本語を扱いやすい小~中規模モデルを使います。
一例としては、次のような構成から始めます。
回答生成 qwen3:8b
Embedding qwen3-embedding:0.6b
GPUメモリに余裕がある場合は、14B以上のモデルも比較します。
ただし、RAGではモデルを大きくする前に、次の項目を確認した方が効果的です。
- 正しいページが検索されているか
- PDFから文章を正しく抽出できているか
- チャンクが細かすぎないか
- 見出しやページ番号が残っているか
- 質問と関係のない資料が混ざっていないか
回答が間違っていると、すぐにLLMの性能を疑いたくなります。
実際には、その前段階であるPDF解析や検索結果に原因があることも少なくありません。Open WebUIのRAGトラブルシューティングでも、回答の問題がモデルではなく、検索されたコンテキストに起因する場合があると説明されています。
Step 7:ベクトル検索だけで終わらせない
意味の近い文章を探すベクトル検索は便利ですが、工事名、河川名、基準名、数値、型番などの検索では、普通のキーワード検索が強い場合もあります。
そのため、実務版では次の2種類を組み合わせます。
ベクトル検索
意味が近い文章を探す
キーワード検索
固有名詞、数値、基準名などを探す
Qdrantでは、密ベクトルと疎ベクトルの検索結果をRRFなどで統合するハイブリッド検索を構成できます。また、候補を広めに検索してから再評価する多段検索にも対応しています。
ただし、最初から複雑な構成にする必要はありません。
次の順番で比較します。
- ベクトル検索のみ
- キーワード検索のみ
- ハイブリッド検索
- ハイブリッド検索+再ランキング
同じ質問を使い、どの方法が正しいページを上位に出せるか確認します。
Step 8:AIへ守らせる回答ルールを決める
実務利用では、回答内容だけでなく、回答方法を固定することが重要です。
例えば、システムプロンプトへ次のようなルールを設定します。
あなたは社内技術資料を検索する支援システムです。
回答は、検索された資料に記載されている内容だけを使用してください。
資料に書かれていない内容は推測せず、
「登録資料からは確認できません」と回答してください。
回答には、必ず次の情報を付けてください。
- 資料名
- ページ番号
- 章または節
- 根拠となる記述の要約
数値、年月日、基準名、固有名詞については、
検索結果を再確認してから回答してください。
特に「資料にない内容を推測しない」という条件が重要です。
回答が自然に見えても、根拠資料に書かれていなければ実務では使えません。
Step 9:検証用の質問集を作る
PDFを入れて、何となく質問して終わりでは精度を判断できません。
あらかじめ、人が答えを確認した質問集を作ります。
最初は30~50問程度で構いません。
質問は次のように分けます。
単純検索
対象流域の面積はいくつか。
計画規模は何年確率か。
使用した粗度係数はいくつか。
条件検索
2023年度の業務で採用した解析手法は何か。
○○川を対象とした報告書だけから回答してください。
比較
2022年度と2024年度で計画流量は変更されているか。
複数の対策案について、長所と短所を比較してください。
資料に答えがない質問
この業務の最終的な工事費はいくらか。
答えが資料にない場合、無理に回答せず「確認できない」と返せるかを確認します。
表や図を参照する質問
表4.2に記載されたケース2の最大水位はいくつか。
表や図の質問は難易度が高いため、通常の文章検索とは分けて評価します。
評価項目
検証結果はDuckDBへ保存します。
質問
正解資料
正解ページ
検索上位の資料
AIの回答
引用資料
引用ページ
回答時間
人による評価
備考
最低限、次の点を確認します。
- 正解のページが検索上位に入ったか
- 回答が資料の内容と一致しているか
- ページ番号が正しいか
- 資料にない内容を追加していないか
- 数値や単位を間違えていないか
- 回答までにかかった時間は許容範囲か
正答率だけでなく、根拠を正しく示せたかも評価します。
実務では何に使うか
精度が確認できたら、最初は次の用途から使います。
過去資料の検索
同じような地質条件の業務を探す。
過去に使用した解析条件を確認する。
採用した対策工法の事例を探す。
報告書の要約
業務目的を300文字程度にまとめる。
検討結果と残された課題を整理する。
章ごとの要点を一覧にする。
報告書ドラフトの作成
過去資料を参考に、調査目的の文章案を作る。
検討条件を箇条書きで整理する。
複数案の比較表のたたき台を作る。
照査支援
本文中で使用している計画規模が統一されているか。
同じ施設名の表記が揃っているか。
本文と表で数値が異なっていないか。
ただし、AIが作った文章や照査結果を、そのまま最終成果品には使いません。
数値、基準、法令、設計条件、解析結果については、必ず元資料と照合します。
いきなり本番運用にはしない
今回の構成は社内LAN内で動かしますが、ローカル環境だから自動的に安全になるわけではありません。
Open WebUIは、社内ファイアウォール、VPN、認証付きリバースプロキシなどの内側へ配置することが推奨されています。運用側がネットワーク公開範囲、認証、秘密情報、ログなどを管理する必要があります。
実務運用へ進む前に、次の点も整理します。
- 利用者ごとのログイン
- 部署や業務ごとの閲覧権限
- 機密資料の登録ルール
- PDFの削除と再登録
- 改訂版の管理
- Qdrantのバックアップ
- DuckDBのバックアップ
- 操作ログ
- 回答履歴
- モデル更新時の再評価
特に、閲覧権限が異なる資料を同じKnowledge Baseへ入れないようにします。
今後の進め方
このシリーズは、次のように分けると整理しやすそうです。
その3
Ubuntu 24.04+GPU環境で、基本構成を起動する。
- NVIDIA GPUの確認
- DockerからのGPU利用確認
- Ollamaの起動
- Open WebUIの起動
- Qdrantの起動
- FastAPIの起動
- 少数のPDFを登録
- 基本的な質問応答を確認
その4
PDF処理と検索精度を改善する。
- DoclingによるPDF解析
- OCR対応
- Markdown・JSON保存
- 見出し単位のチャンク分割
- ページ番号の保持
- メタデータ検索
- ハイブリッド検索
- 評価用質問集
その5
報告書支援へ広げる。
- 報告書の要約
- 類似業務検索
- 条件比較
- 報告書ドラフト
- 表記ゆれ確認
- 数値整合性チェック
- WordやExcelへの出力
その6
社内運用へ向けて整理する。
- 利用者管理
- 権限管理
- バックアップ
- ログ管理
- HTTPS
- 監視
- Ansible化
- 更新手順
- 障害復旧手順
まとめ
今回の次のステップは、単にPDFをアップロードしてチャットすることではありません。
実務で使うためには、次の流れをひとつずつ確認する必要があります。
PDFを正しく読み取る
↓
見出しやページ番号を残す
↓
質問に関係する箇所を検索する
↓
根拠資料だけを使って回答する
↓
人が元のPDFを確認する
↓
検証結果を記録する
まずは少数のPDFで動かし、どのような資料で読み取りに失敗するのかを確認します。
検索精度と引用箇所が安定した後に、要約や報告書ドラフトへ進む方が、遠回りに見えて結果的には安全です。
次は、Ubuntu 24.04のGPU環境でDocker Compose一式を起動し、実際のPDFを登録できるところまで実装して、社内の余っているPCにUbuntu server 24.04を載せて動かしてみます。
