MENU

手元で動いた製造記録のVLM読み取りを、GPUサーバーに移して現場から呼び出す

前編では、紙の製造記録をローカルの生成AIで読み取り、Excelに文字起こしする仕組みを作りました。手書きの欄まで含めて、社外にデータを出さず、手元のパソコンで読み取るところまで動いています。

ただ、それは開発用のパソコンで動かしていただけです。現場で使うには、読み取りを担うパソコンを、誰もが呼び出せる場所に置き直す必要があります。そこで今回は、読み取りの本体をサーバーに移し、現場のパソコンからは呼び出すだけ、という構成に変えます。これは「機能を足す」話ではなく、動かす場所を変える要求変更です。思いつきで配線せず、何を変えるかを要求として書き直し、設計・テストの仕様まで作り直した記録として残していきます。

砂糖さん

サーバーに移すって言われても、そもそもGPUとかサーバーって何なのか、そこがあいまいなまま読み進めるのが少し不安で……。

MATSU

そこは飛ばさずにそろえておきましょう。移す話に入る前に、GPU・サーバー・クライアントサーバシステムの三つだけ、短く整理します。

今回はプロンプト仕様駆動開発v6を使用しています。CLAUDE.mdやプロンプトの詳細は以下の記事を参照してください。

目次

サーバーに移す前に、GPU・サーバー・クライアントサーバシステムの前提をそろえる

この記事はサーバーへの移行が主題なので、そこで繰り返し出てくる三つの言葉を先に地ならしします。どれも一度分かれば単純な話で、逆にここがあいまいだと、以降の作業がすべて宙に浮いて読めてしまいます。

GPUは、たくさんの計算を同時にこなす部品

GPUは、もとは画面の描画に使われていた部品で、たくさんの計算を一度にまとめてこなすのが得意です。前編で使った、画像を読んで文字を起こす生成AI(VLM)は、内部で大量の計算を並行して走らせます。この計算はパソコンの頭脳(CPU)だけでも動きますが、実用に耐える速さは出ません。読み取りにGPUが要るのは、この速さを確保するためです。GPUを積んでいない事務用パソコンでは、読み取りが現実的な時間で終わらない。だから、GPUを積んだ機械を別に用意して読み取りを任せる、という発想になります。

サーバーは処理を任せる機械で、今回は中規模のワークステーションを使う

サーバーは、ここでは「処理を任せるための一台の機械」くらいの意味です。今回使うのはワークステーションと呼ばれる中規模のもので、価格はおおよそ60〜100万円ほど(最近はAIトレンドで関連パーツが高騰していて、この値段では買えないかもしれませんが……)。規模に応じてスケールできるのが利点で、軽い処理ならより小さな機械で足り、扱う量が増えればGPUを足すなどして上げていけます。

クライアントサーバシステムは、頼む側と担う側に役割を分ける仕組み

クライアントサーバシステムは、処理を「頼む側(クライアント)」と「担う側(サーバー)」に分ける仕組みです。今回は、現場のパソコンがクライアントで読み取りを頼む役、GPUを積んだサーバーがそれをこなす役。重い部品(GPU)を一台にまとめられるので、現場の一台ずつにGPUを積む必要はなく、サーバー一台を全員で共有できます。現場のパソコンは、PDFを渡してExcelを受け取るだけの軽い役割に徹します。

この記事の前提となる3つの言葉
  • GPU
    たくさんの計算を同時にこなす部品。生成AIの読み取りは、これがあって初めて実用的な速さになる。
  • サーバー(ワークステーション)
    処理を任せるための機械。今回は60〜100万円ほどの中規模のもので、必要な規模に応じてスペックを上げ下げできる。
  • クライアントサーバシステム
    処理を頼む側(クライアント=現場のパソコン)と、担う側(サーバー)に役割を分ける仕組み。

手元で完結していた読み取りを、サーバーに置いて呼び出す構成に変える

前提がそろったので、今回の変更が要求のレベルで何を書き換えたのかを整理します。ここを飛ばして設計や実装に入ると、あとで「なぜこう作ったのか」が追えなくなります。まず要求を確定させ、設計・テストへ順に下ろしていきます。

読み取りをサーバーに集約し、現場のパソコンは呼び出すだけにする

変える中心は一つです。手元のパソコンの中で完結していた読み取りを、GPUを積んだサーバーに移し、現場のパソコンからは呼び出すだけにする。読み取りという重い処理を一台に集め、現場側はPDFを渡してExcelを受け取るだけに徹する分担です。この一つの変更が、芋づる式に他の要求も動かします。サーバーに移すなら現場からどう接続するかを決めねばならず、これまで画面上で結果を直していた仕組みも、サーバー越しには合わなくなります。

要求仕様を、場所・接続・確認方法の3点で書き換えた

要求仕様書に手を入れたのは、大きく3か所です。読み取りを動かす場所、現場からの接続方法、そして読み取り結果の確認方法。変更前と変更後を並べます。

スクロールできます
観点変更前変更後
読み取りを動かす場所手元のパソコンの中で完結GPUを積んだサーバー側で実行
現場からの接続手元で動くため接続は不要クライアントから2つの経路(paramiko/TeraTerm)で接続
読み取り結果の確認画面に赤く表示し、その場で直す出力Excelの該当セルをハイライトして残し、人が後で見て直す

接続の2経路に出てくるparamikoとTeraTermは、どちらも「手元のパソコンからサーバーに入り込む道具」です。paramikoはPythonからサーバーへ接続する部品、TeraTermは接続用の市販ソフト。なぜ2つ用意し、何をどこまで自動でやるかは設計・実装の節で見ます。ここでは「接続の道を2本、要求として用意した」までにします。

確認方法の変更には補足が要ります。もともとは、結果を画面に一覧表示し、あやしいセルを赤く出してその場で直す要求でした。しかしサーバーに移すと、画面でリアルタイムに直す仕組みは重く、構成に噛み合いません。そこで画面での対話的な編集を要求から外し、代わりに出力されたExcel自体に、自信の低いセルの印を残す形にしました。人はそのExcelを見て、印のついたところを確認すればよい、という流れです。

この変更版の要求仕様書は、下からダウンロードできます。どの項目を残し、外し、足したかが変更の記録として残してあります。

読み取り環境をコンテナにまとめ、2つの接続経路を設計する

要求が固まったので、それをどう作るかの設計に移ります。決めることは大きく2つ。読み取りの環境をサーバーにどう載せるか、現場からその環境にどうつなぐか。前者はDockerコンテナ、後者は2つの接続経路として設計しました。

読み取り一式をコンテナに詰めて、サーバーで動かす

読み取りには、生成AIを動かす土台(Ollama)、画像を読むモデル、読み取りの段取りを書いたスクリプトが要ります。これらをサーバーに一つずつ入れて設定すると、手元とサーバーで環境が食い違い、「手元では動いたのにサーバーでは動かない」が起きやすい。そこで一式をまとめてDockerコンテナという箱に詰めます。コンテナは必要なものを一式詰めた、持ち運べる箱で、箱ごと運べば手元と同じ環境がサーバーでも再現されます。なおサーバー側では、このコンテナを常駐させず、接続のたびにSSHで起動し、読み取りを実行して止める、というバッチ的な使い方にしています。

接続は2経路にし、同じ起動コマンドを両方から投げる

現場からの接続は、paramikoとTeraTermの2経路を設計しました。設計上、この2つは役割の重さが違います。paramikoが本命で、tkinterの薄い画面から入力フォルダと出力先を指定すると、SSH接続→PDF転送→読み取り起動→Excel回収までを一連で担います。TeraTermは画面を持たず、ターミナル上でマクロ(connect.ttl)を動かしてparamikoと同じ起動コマンドを投げるだけの代替。実行ファイルを置けない現場でも、TeraTermさえあれば同じ読み取りを起動できます。

2経路の狙いは、同じ処理を別の手段から起動でき、両者を見比べられることです。設計では、両経路が投げる起動コマンドの文字列を、設定ファイルを唯一の拠りどころにして完全に同じになるよう固定しました。どちらから呼んでも、サーバーで走る読み取りは同一、という担保です。

スクロールできます
観点paramiko経路(本命)TeraTerm経路(代替)
操作画面tkinterの薄い画面で指定する画面を持たず、ターミナルで操作する
PDFの転送・Excelの回収この経路が担う持たず、同じ起動コマンドを投げるだけ
最終的にはマクロでUIを実装
サーバーで走る読み取り同一(同じ起動コマンドを、設定ファイルを拠りどころに生成)
想定する現場実行ファイルを置ける/画面で操作したい実行ファイルを置けない/端末で足りる

読み取り環境はコンテナに詰めて持ち運べるようにし、接続のたびに起動する。接続は2経路で、paramikoが転送・回収まで担う本命、TeraTermは同じ起動コマンドを投げる代替最終的にはparamiko経由と同じようにマクロでUIを実装しました)。サーバーで走る読み取りは、どちらの経路でも同一になるよう設計で固定した。

これらを含む設計仕様書は、下からダウンロードできます。コンテナの構成と、2経路それぞれの処理の段取りを、契約(何を渡すと何が返るか)の形で残してあります。

サーバーへ配置すると、自動テストで守れる範囲が変わる

設計ができたら、次は「正しく動くか」をどう確かめるかです。今回はサーバーへプログラムを配置する移行が伴うので、ここが一段ややこしくなります。手元で自動テストが通っていても、そのままサーバー上の正しさを保証するわけではないからです。自動テストで守れる範囲はどこまでか、移行で何をし、どこが自動テストの外に出て、それをどう評価するのか——を順に切り分けます。

自動テストで守れているのは、読み取りコアのロジックとコマンドの組み立て

自動テストが得意なのは、入力に対して答えが一意に決まる部分です。読み取ったあとの処理——値の解析、製造日での並べ替え、31列のExcelへの組み立て、自信の低いセルにハイライトを付ける仕組み——は全部これに当たり、前編から自動テストで固めてあります。サーバー化で足した部分にも守れるところがあり、サーバーに投げる起動コマンドの文字列を組み立てる部分がそれです。同じ入力なら文字列は毎回同じになるはずで、その一致は機械が判定できます。

今回のサーバー化で、追加で自動テストを通した項目は次の9件(ここまでは実機に繋がなくても確認済み)。

検証できたこと
  • 手書き×低自信の判定
    手書きで自信が低いセルだけを対象と判定し、印字のセルは対象にしない(V-46)
  • ハイライトの境界
    自信度がしきい値ちょうどのセルは塗らない(未満だけを塗る)(V-47)
  • ハイライトの書き込み
    手書きの低自信セルに色を付け、値そのものは変えない(V-48)
  • 印字は塗らない
    印字項目は、自信度がいくら低くても塗らない(V-49)
  • 読取失敗の行
    失敗した記録は、手書き項目が空でも全て塗り、印字は塗らない(行全体の確認を促す)(V-50)
  • 1枚失敗で止めない
    壊れたPDFがあっても失敗として数えつつ処理を続け、成功・失敗の件数を返す(V-51)
  • 0件のとき
    対象PDFが無ければ、見出しだけのExcelを出し件数はゼロを返す(V-52)
  • 起動コマンドの生成
    指定した入力・出力が反映され、コンテナ起動と読み取り実行を含むコマンドが組める(V-53)
  • 2経路の同一性
    同じ入力なら、paramikoとTeraTermが投げるコマンド文字列が完全に一致する(V-54)

とくに最後の2件(V-53・V-54)が、2経路の評価に直結します。paramikoとTeraTermが「同じ処理を起動する」ことを、実際に繋いで見比べるのではなく、「両方が同じコマンド文字列を組み立てる」一点を自動テストで固める。文字列が一致すれば、サーバー側で走る読み取りは同一だと設計で決めてあるからです。環境で揺れる要素を持ち込まず、経路の同一性だけを先に確かめられます。

サーバーへ配置して初めて動く部分は、自動テストの外に出る

問題は、サーバーへ配置して初めて動く部分です。移行で新しく行うのは、読み取りコアをコンテナごとサーバーに載せ、SSHで接続し、PDFを転送し、コンテナを起動し、Excelを回収する一連。実際の通信・ファイル転送・GPUの割り当てが絡み、答えが一意に決まらない(実環境で結果が変わる)ため、自動テストにできません。次の3つが外に出ます。

自動テストの対象外一覧
  • paramiko経路の実挙動
    SSHで接続し、PDFを転送し、Excelを回収する一連が、実際に通るか(V-55)
  • TeraTerm経路の実挙動
    マクロが実際にサーバーで同じ起動コマンドを投げ、処理が走るか(V-56)
  • Docker・GPUの実挙動
    コンテナが立ち上がってGPU(RTX3090×2)を掴み、読み取りが走るか。都度起動にかかる時間も含む(V-57)

自動テストの外に出た部分は、実機で人が確かめる

外に出た部分は、放置せず、実機で人が確かめる評価に切り替えます。実際にサーバーへ配置し、テスト用のPDFを1枚流して、接続・転送・起動・回収が最後まで通るかを目で見る。読み取りにかかる時間もその場で測ります。読み取り精度もここに含まれ、正しいかは機械的に◯×を出せず、別の生成AIに判定させても同じ種類の誤りを一緒に見逃す(相関誤差)ので独立した検証になりません。だから人が用意した正解データと突き合わせます。ハイライトが実際に薄いピンクで表示されているか、といった見た目も、同じく人の目で確かめます。

自動テストで守れるのは、読み取りコアのロジックと、起動コマンド文字列の組み立て・2経路の同一性(追加9件は確認済み)。サーバーへ配置して初めて動く部分(paramiko・TeraTermの実挙動、Docker・GPU、読み取り精度、ハイライトの見た目)は自動テストの外に出るので、実機で人が確かめる、と評価方法を切り替える。

どの要求が、どの設計・どのテストに対応し、それが自動なのか実機・手動なのか——その対応をひとつの表にまとめたものが、トレーサビリティマトリクスです。要求の取りこぼしがないか、「何を自動で確かめ、何を人が確かめるか」の切り分けが、一覧で追えます。

仕様どおりにコンテナを組み立て、サーバー上に構築する

ここからが今回の主役です。設計で決めた構成を、実際のサーバーの上に建てます。やることは3段階。コンテナからGPUを使える土台を作り、読み取り環境をコンテナに組み立て、クライアントから繋ぐ設定を入れる。その途中で実機でしか分からなかったことがいくつも出てきました。そこが記事として一番の中身なので、あわせて残します。

コンテナからGPUが見えるように、Dockerとその周辺を入れる

最初の土台づくりです。サーバー(Ubuntu Server、GPUはRTX3090×2)に、コンテナを動かすDockerと、コンテナからGPUを使うためのNVIDIA Container Toolkitを、次の順で入れます。

STEP
Docker Engineを入れる

コンテナを作って動かす本体です。公式のリポジトリから入れます(snap版ではなく公式apt版を使うのが要点。理由は後述)。「hello-world」が動けば完了です。

STEP
sudoなしでdockerを使えるようにする

自分のユーザーをdockerグループに加えます。この変更は再ログインで有効になるので、VS CodeでSSH接続しているなら、いったん切って繋ぎ直します。

STEP
NVIDIA Container Toolkitを入れて、Dockerに認識させる

コンテナの中からGPUを使うための橋渡しです。設定を書き込んでDockerを再起動すると、コンテナがGPUを掴めるようになります。

STEP
コンテナからGPUが見えるか通し確認する

試しのコンテナを起動して、ホストと同じくRTX3090×2が表示されれば、土台は完成です。ここが今回いちばん重要な確認点で、GPUを使う指定が実際に効くと分かります。

コンテナからGPUが見えるかは、次のコマンドで確かめます。

この土台づくりで、実機で3つつまずきました。どれも設定ミスではなく、順序やタグの問題です。「実機あるある」として残します。

実機でつまずいた点
  • 権限エラー(permission denied)
    dockerグループに加えた直後、接続を繋ぎ直す前にGPUテストへ進んだのが原因。再ログイン(または現在のシェルでの再読み込み)で解消する。
  • snap版DockerではGPUが使えない
    snap版は隔離が強く、GPUデバイスへのアクセスが制限されて権限エラーになる。だから公式apt版を使う。
  • コンテナのタグ違い
    指定したイメージのタグが存在せず取得に失敗。実在するタグに直して解消。

読み取り環境をコンテナに組み立て、モデルを取り込む

土台ができたら、読み取り環境そのものをコンテナに組み立てます。生成AIを動かすOllama、読み取りのスクリプト、必要な部品を一つの箱にまとめてビルドします。途中、Pythonライブラリーのビルドエラーなどがインストールの行に「–break-system-packages」を足すとビルドが通りました。この作業はClaudeと相談しながら、やると実装まで持っていきやすいです。

コンテナが組めたら、読み取りに使うモデル(qwen2.5vl:7b、約6GB)を、ネットワークのあるうちに一度だけ取得します。運用時はこのモデルがサーバーの中にあるので、外部に出ずローカルで完結します。あわせて、転送されたPDFの置き場(work/in)と出力Excelの場所(work/out.xlsx)になる作業ディレクトリを用意します。

この段階で接続の要素をいったん切り離し、サーバー上で手動で一度通します。work/inにPDFを置いて実行し、work/out.xlsxが出来上がることを確かめる。ここまでが「読み取りの本体」の確認で、これが通れば、あとは接続だけの問題に切り分けられます。

実際に動かして、2経路それぞれで結果まで通す

構築ができたら、現場の使い方で動かします。ここで確かめるのは、自動テストの外に出た部分——接続して、PDFを送り、読み取りを起こし、Excelを回収するまでが実際に通るか、です。進め方には順序があり、いきなり自動経路から通さず、先に「読み取りの本体」だけを固めてから接続をつなぎます。

先に読み取りの本体を通し、接続を後から重ねる

先ほどの構築で、サーバー上で手動で一度、読み取りを通してあります。work/inにPDFを置いて実行し、out.xlsxが出来上がることを確認済みです。先に本体を固めておくと、自動経路でつまずいたとき「本体の問題か、接続の問題か」を切り分けられるからです。接続・転送・回収と読み取りを一度に走らせると、失敗したときにどこが原因か分からなくなります。本体は通ったので、ここで確かめるのは接続・転送・回収の自動化部分だけ。これを2つの経路それぞれで通します。

paramiko経路:ボタン2つで、転送から回収まで通す

paramiko経路は、手元の薄い画面(ui.py)から動かします。シンプルすぎる画面です。

手順はこうです。

STEP
画面を起動する

クライアントのパソコンで、薄い操作画面を立ち上げます。

STEP
入力フォルダを選ぶ

「入力フォルダを選択」で、テスト用のPDFが入ったフォルダを指定します。

STEP
出力先を指定して実行する

「出力先を指定して実行」で保存先を決めると、SSH接続→PDF転送→サーバーで読み取り→Excel回収までが一気に走ります。完了すると件数が表示され、指定した場所にExcelが戻ってきます。

動かす前に見ておくのは、鍵の受け渡し1点だけです。この経路は鍵を明示せず、パソコンの決まった置き場から自動で探して使います。ふだんVS Codeで鍵認証(パスワードなし)で繋げているなら、その鍵が決まった置き場にあるはずなので、そのまま繋がります。標準以外の名前の鍵を使っている場合だけ、自動探索が外れて接続で止まるので、鍵の場所を明示する1行を足して対処します。

MATSU

鍵の配置が課題になるかもしれませんが、クライアント側のアプリをPyInstallerでexe化したら配布でき、どのユーザーでもサーバにアクセスできるようになりますよ。

TeraTerm経路:マクロを拡張して、同じ一連を通す

TeraTerm経路も動かします。前提として、マクロ(.ttl)はダブルクリックでは実行できず、TeraTermに付属するマクロ実行用のプログラムから呼ぶか、メニューから読み込んで実行します。

設計の段では、TeraTerm経路は「接続して同じ起動コマンドを投げるだけ」で、転送と回収は別作業でした。ただ実際に動かすと手元に何も戻らず、物足りません。そこで実装では、TeraTermのマクロが持つ命令——フォルダ選択のダイアログ、ファイル転送、フォルダ内のPDF列挙——を組み合わせて、ダイアログでフォルダを選べば転送・実行・回収まで一通りやる拡張版のマクロを別に作りました。作ったときのダイアログがこちら。

これで、paramikoとほぼ同じ一連を、TeraTermだけでも通せます(ちょっとクラシカルな感じになってしまいますが)。ただしこの拡張は設計・テストの仕様に反映しきれておらず、設計時点の記述と実装の実態がずれた状態です。この食い違いは次の節で正直に取り上げます。

先に本体を通してから接続を重ねると、失敗の切り分けが利く。2経路はどちらも、フォルダを選べば結果のExcelが戻るところまで通った。

アプリの改善は、観察して直してから、どこまで仕様に戻すかを判断する

動くものはできましたが、サーバーへ移したことで新しい課題も見えました。大事なのは、これを思いつきで片端から直さないことです。開発しっぱなしにも、毎回すべてを要求仕様書に戻して書き直すことにも寄らずに、課題と向き合う流れを持っておく。今回はその考え方までを示し、実際に自走で直す実例は別の記事で扱います。

まず観察して課題を洗い出し、影響の大きさで並べる

出発点は観察です。作ったものを壊さないように動かしながら、生成されるファイルの出方、フォルダの挙動、処理にかかる時間などを見て、気になる点を挙げていきます。今回のサーバー化では、次のような課題が見えました。

観察で見えた課題
  • ハイライトが安定して出ない
    自信の低い手書きセルに付くはずの色が、思ったように出ないことがある。
  • 速度がほとんど変わらない
    手元で処理していた頃と、サーバーに移した後で、かかる時間がほぼ同じだった。
  • 蓄積したデータが消えない
    サーバー側に溜まっていく作業データが、そのまま残り続ける。
  • 仕様に反映しきれていない拡張
    TeraTerm経路を転送・回収まで一括する形に拡張したが、設計・テストの仕様がそのままになっている。

洗い出したら、影響の大きさで並べ替えます。表示や見た目のように間違っていても目で気づける「外側」の課題と、計算・判定・接続のように狂うと結果そのものが変わる「内側」の課題を分け、影響の大きいものから順に手を付けるためです。

課題起点でエージェントに相談し、直した差分を乖離診断にかける

並べたら上から対処します。いきなり要求仕様書を書き直すのではなく、課題を「どんな症状で・どう再現し・期待と実際がどう違うか」の形にして、直し方をエージェントに相談する、というやり方が取れます。実測した日や観察で分かった現象が、そのまま相談の入力になります。対処したら差分をそのままにせず、乖離診断にかけます。テストを実行して乖離レポートを出し、この変更を要求仕様まで戻して反映すべきか、どこまで遡るべきかを判断します。アプリを編集しつつ、要求仕様とのギャップを補って開発内容が孤立しないようにしていきます。

サーバークライアントシステムでは、こまめな検証に時間を割くのが必須になる

この流れの土台にあるのは、検証を省かないことです。サーバークライアント構成は、自動テストが届く層と届かない層の差が大きいぶん、直したら人が見て確かめる工程を毎回はさむ必要があります。手数はかかりますが、その手数こそが「動く」を「正しい」に近づけます。この自走の流れを複雑なサーバークライアントで実際に回す様子は、別の記事で扱います。

課題は、観察して洗い出し、影響度で並べ、課題起点で直し、乖離診断でどこまで仕様に戻すかを判断する。サーバークライアントでは、こまめな検証に時間を割くのが必須になる。

まとめ

手元で動いていた製造記録の読み取りを、GPUを積んだサーバーに移し、現場から呼び出す構成に作り替えました。この一連を、思いつきの配線ではなく、要求→設計→テストの仕様から作り直した記録として残しています。

この記事のまとめ
  • サーバーへの移行は機能追加ではなく、動かす場所を変える要求変更として扱った。
  • 読み取り環境はDockerコンテナにまとめ、接続はparamiko(本命)とTeraTerm(代替)の2経路にした。
  • 自動テストで守れるのはロジックとコマンド生成まで。接続・転送・GPU・読み取り精度は、サーバー配置で自動テストの外に出るので、実機で人が確かめる。
  • 構築は仕様どおりでも、実機ではグループ反映の順序・snap版の制約・PEP 668・アドレスの「/24」で止まった。
  • 残った課題は、観察→影響度で並べる→課題起点で直す→乖離診断でどこまで仕様に戻すか判断、という流れで向き合う。こまめな検証が必須になる。

同じ構成は、社内だけの問い合わせ窓口、文書の取り出し、音声・動画の文字起こしへと開けます。重い処理をサーバーに集め、機密は外に出さず、現場から呼び出す——この骨組みが、いろいろな社内業務の土台になります。

※本記事は仮想の事例です。使用している品名・数値・記録はすべて架空のものです。

よかったらシェアしてね!
  • URLをコピーしました!
  • URLをコピーしました!

コメント

コメントする

CAPTCHA


目次