おはようございます。マイクです。
時刻は朝七時を回りました。二千二十六年九月六日、日曜日の朝、いかがお過ごしでしょうか。
ここからの時間は「zenncast」、きょうもZennで話題になっているトレンド記事を、ゆるっと楽しくご紹介していきます。
きょうは全部で五本の記事をピックアップしてきました。
ターミナルの話、Goのちょっとディープなトレーシングの話、ローカルLLMチューニング、S三ストレージのコスト最適化、それからPDFカタログからの商品情報抽出と、かなりバラエティ豊かなラインナップになってます。
まず一つ目。
WindowsでWSLツーをよく使ってる方に向けて、「noctty」というターミナルを強くおすすめしている記事です。
nocttyは、あのGhosttyのエンジンを使いながら、Windows向けにかなり作り込まれているターミナルなんですね。
特徴としては、既定ターミナルへの登録ができたり、エクスプローラの右クリックメニューからすぐ開けたり、セッション復元や名前付きレイアウトなど、ふだんの開発で効いてくる便利機能がかなり充実しています。
で、みんな気になるのが「速さ」なんですが、ここで出てくるのがConPTYという仕組みです。
よく「ConPTY経由は遅いよね」っていうイメージがあるんですけど、この記事いわく、それは古い世代のConPTY、バージョンワンだけの話なんですね。
nocttyが同梱できるConPTYバージョンツーを使えば、WSLとのvsock直結と同じレベルまで高速になる、というのを実測で示しています。
さらに、古いConPTYバージョンワンは、kitty graphicsみたいな一部の制御シーケンスを勝手に削ったり書き換えちゃう問題もあって、画像表示ができない、みたいなトラブルも起こることが説明されています。
なので、新しいConPTYをちゃんと使う、というのがポイントになってきます。
ただし、ここに落とし穴があって、自分でビルドしたnocttyだと、その同梱のConPTYが自動で配置されない場合があるんですね。
そうすると、気づかないうちに遅いバージョンワンのConPTYを使ってしまうことになる。
そこでこの記事では、`noctty +version` というコマンドで、今どのConPTYを使っているか確認して、必要なら公式パッケージからバージョンツーをexeファイルの隣に置くべきだ、と注意喚起しています。
筆者の方は、このあたりを改善する過程で四本のプルリクエストを送ったそうなんですが、そのときに印象的だったのが、プロジェクト側の方針。
AIを使ったコード支援は歓迎するけど、その内容を自分で説明できない、検証できない変更は受け入れない、というスタンスなんですね。
その方針のもとで、再現手順や測定データをきちんと示すこと、それから既存のバグと自分の変更による影響をちゃんと切り分けることの大事さを学んだ、というふうに振り返っています。
ターミナルをより快適にしたい方と、OSSへのコントリビュートに興味がある方、両方に刺さる内容になっています。
。.。.。.
続いて二つ目。
Go言語のOpenTelemetry向けツール「otelc」が持っている、GLSという機能について解説している記事です。
GLSというのは「goroutine local storage」を略したような概念で、面白いのが、`context.Context` をちゃんと渡してないコードでも、同じゴルーチンの中にあるspanを、親子関係でつなげてくれる仕組みなんですね。
通常、トレースの世界だと、「親のspanから子のspanへ、コンテキストをちゃんと渡そうね」というのが基本なんですが、現実のコードって、必ずしも全部がそうなってないですよね。
特に `database/sql` みたいなライブラリだと、`context.Background()` でspanが作られちゃって、「あれ、このクエリのspan、HTTPリクエストのトレースにくっついてないじゃん」ということが起こりがちです。
そこでGLSです。
GLSは、「今このゴルーチンでアクティブなspan」のスタックを、ゴルーチンごとにローカルに持っておいて、親子関係が途切れないようにしてくれます。
この記事で紹介されているotelcは、コンパイル時にソースコードを書き換えるツールなんですが、Goのruntimeパッケージにあるゴルーチンの構造体 `g` に、spanスタック用のフィールドを追加しちゃうんですね。
で、spanの開始と終了のタイミングでpush、popを行う。
さらに、`trace.SpanFromContext` が呼ばれたときに、「コンテキストの中にspanがなければ、GLS側から読む」というフックを差し込むことで、親子関係を復元していきます。
これ、実装としてかなり攻めていて、Goには本来「ゴルーチンローカルストレージ」という仕組みは存在しないので、runtime自体を書き換えて、それをエミュレートしているのがユニークなポイントです。
実験では、`context.Background()` から作られてしまう `database/sql` のspanも、HTTPリクエストのspanの子として、ちゃんと同じトレースに入ってくることが確認されています。
現時点のバージョン、バージョン一・一・ゼロでは、GLSを有効にするために、`go.opentelemetry.io/otel/sdk/trace` をブランクインポート、つまり `_ "..."` の形でインポートしておく必要があるという注意点も書かれています。
トレースをちゃんとつなげたいけど、既存コードやライブラリが素直にコンテキストを受け渡してくれない……そんな悩みを持っているGoエンジニアには、とても勉強になる一記事です。
。.。.。.
三つ目の記事は、Tips系。
テーマは「RTX五千六十Ti・十六ギガ環境で、Qwenスリー・エイトの二十七B系モデルをどう使うか」です。
筆者の方がいろんなパターンを試した結果、もっとも速度と精度のバランスがよかったのが、「soyaakinohara版の qwen三八−十二gb−mtp」とOllamaの組み合わせだった、というレポートになっています。
他にも、Flash−NextやvLLM、AWQやNVFPフォーといった方式も試してみたそうなんですが、こちらはメインメモリやSSDの帯域にかなり高い要求があったり、長文チャットでKVキャッシュがビデオRAMを圧迫して、メモリ不足エラーになりやすい、ということで、現環境では実用をあきらめたとのことです。
Qwenスリー・エイト二十七BをOllamaで使うときの具体的な設定も紹介されていて、Modelfileの `PARAMETER num_ctx` でコンテキスト長を伸ばしていく話が出てきます。
デフォルトの六万四千トークンから、九万六千、さらに十一万六千と伸ばしても動作はしてくれて、そのときのVRAM消費も計測されています。
六万四千から比べて、九万六千ではプラスおよそ五百四十メビバイト程度、十一万六千にしても、まだ余裕を持って動かせた、という結果です。
ただし、コンテキストを十一万六千まで伸ばすと、生成が著しく遅くなってしまった。
原因を追っていくと、Ollamaの自動VRAM見積もりがかなり保守的で、全六十六層あるうちの五十八層しかGPUに載っていなかったことが判明します。
残り八層はCPUで計算されていて、ここがボトルネックになっていたわけですね。
そこで `options.num_gpu` という設定で、GPUに載せたいレイヤー数を明示的に指定。
全六十六層をGPUに固定してみたところ、VRAMは残り五百メビバイトくらいを残して安定動作し、生成速度は三・七トークン毎秒から、約三十トークン毎秒まで、一気におよそ八倍改善したそうです。
この設定は手動で毎回やるのは面倒なので、Modelfileに `PARAMETER num_gpu 66` と書き込んでおいて、「起動したら自動で全層GPU+長コンテキストで立ち上がるモデル」として運用している、という工夫も紹介されています。
ローカルで大きめのモデルを回している方、特に「なんか遅いな……」と感じている方には、かなり参考になる実測ベースのTipsになっています。
。.。.。.
四つ目。
これは大規模な画像データをS三に置いている方には、耳が痛くもあり、ありがたくもあるコスト削減の話です。
題材になっているのは、数百テラバイト、数億件という、ものすごい数のレシート画像。
これを全部S三スタンダードに置いていたため、ほぼアクセスされない古い画像にも、高い保存料金を払い続けてしまっていた、という状況からスタートします。
最初に検討したのは、「バケット全体をIntelligent−Tieringにしちゃえばいいんじゃないか」という案。
ところが、オブジェクト数があまりに多すぎて、Intelligent−Tiering用の監視料金がかなり大きくなってしまい、トータルで見ると割に合わないことがわかったそうです。
そこで方針転換。
バケットの中をフォルダ、つまりプレフィックスごとに見直して、「ほとんど再参照されない画像のフォルダ」と、「アプリから時々見返される画像のフォルダ」に分けました。
前者の、ほぼ読まれない画像フォルダについては、ライフサイクル設定で一定日数が経ったらGlacier Instant Retrievalへ移動。
読み出しはほぼしない前提にすることで、保存料金を大きく削減しています。
一方で、アプリから検索されたり、ユーザーが時々見に来る画像フォルダについては、Intelligent−Tieringを採用。
監視の対象をここだけに絞ることで、ユーザー体験をあまり犠牲にせず、コストだけしっかり抑える構成にしています。
さらに安いDeep Archiveという選択肢もあるんですが、こちらは取り出しに最大で半日から二日ほどかかることもあって、問い合わせ対応や分析で過去の画像をすぐ見たいケースには向かない、という判断で採用を見送ったとのことです。
運用上の落とし穴としては、ストレージクラスを変更するその操作自体に、大量のリクエスト料金が一時的にかかる、というポイント。
ここを最初は見落としていて、「あれ、思ったよりお金かかったぞ」となった経験から、今後はオブジェクト数と単価をちゃんと掛け算して、事前に見積もるべきだと反省している、というエピソードも書かれています。
結果として、もともとS三スタンダード単体で払っていたストレージ料金を、フォルダごとにクラスを使い分けることで、合計およそ四十五パーセント、つまり約五十五パーセント削減することに成功。
筆者は同じような削減を狙う場合、オブジェクト数や平均サイズ、フォルダごとのアクセス頻度をちゃんと調べたうえで、「ここはほとんど読まれない場所なのか」「ここはいつ読まれるかわからない場所なのか」を切り分けて、それぞれに合ったストレージクラスを選ぶのが大事だ、とおすすめしています。
。.。.。.
そして五つ目。
こちらもTips系で、テーマは「LLMでPDFカタログから商品情報を抜き出すとき、どう設計すれば安定するか」です。
ポイントになっているのは、「LLMは決定的な関数じゃない」、つまり、どれだけプロンプトを工夫しても完全に同じ出力にはならないし、必ずブレや誤りが残る、という前提に立つこと。
だからこそ、「あとで直す」「あとで守る」ための後処理を前提にした設計が重要だ、と説明されています。
まずプロンプト側の工夫としては、「など」といった曖昧な言葉を避けて、禁止項目はちゃんと全部列挙しておくこと。
カテゴリのフィールドは、あらかじめ候補リストを用意して、その中から選ばせる形にして、「その他」という逃げ道も用意する。
説明文を生成させるときには、必ず元のPDF本文を参照させるように指示して、勝手な想像で書かせない、などなど。
こうした工夫で、出力のブレを少しでも減らしていきます。
フィールド設計の話も面白くて、たとえば価格。
「オープン価格」と、本当にその商品だけ情報が抜き出せなかった「ヌル」とでは、同じヌルでも意味がまったく違いますよね。
そこで、「このヌルはオープン価格かどうか」を表す真偽値のカラムを追加して、意味をはっきり区別できるようにしています。
また、カタログ全体で共通な会社名や季節といった情報は、LLM任せにはせず、後処理で強制的に上書きするようにして、一貫性を保っています。
それでもやっぱり、「size」「サイズ」のような表記ゆれであったり、「検索y用説明文」みたいな微妙なノイズが多く残ってしまう。
そこはカラム名の正規化マップを用意して、「こういう名前が来たら、このカラムに正す」という形で吸収していきます。
さらに、処理のパイプラインをステージワン、ステージツーのように分けて、その間はインデックスでしっかり紐付け。
件数が合わなかったり、JSONが壊れていたりしたら、無理に進めず処理を止める、あるいは別のモデルでJSONの修復を試す、といった安全装置も用意しています。
数百ページ規模のPDFカタログを安定して処理するために、途中で止まっても再開できる仕組みや、一時ファイルを経由した安全な書き込み、それから既存の安定した処理と、新しく試している機能をきれいに分離する設計なども紹介されています。
全体を通じて徹底しているのが、「誤ったデータを出すくらいなら、出さないことを優先する」という判断基準。
LLMを本番のデータパイプラインに組み込みたい人にとって、かなり実践的な知見が詰まった内容になっています。
。.。.。.
というわけで、きょうのzenncastはこのあたりでお時間となりました。
きょうご紹介したのは、
一つ目、WSLツーユーザーにおすすめなターミナル「noctty」と、ConPTYバージョンツーでの高速化とOSSコントリビュートの話。
二つ目、GoのOpenTelemetryコンパイル時計装「otelc」のGLS機能で、コンテキストが渡っていないコードのspanもちゃんとトレースにつなぐテクニック。
三つ目、RTX五千六十Ti・十六ギガ環境でのQwenスリー・エイト二十七B運用術と、Ollamaの`num_ctx`と`num_gpu`設定で八倍速くしたTips。
四つ目、数百テラバイト規模のレシート画像をS三でコスト最適化して、ストレージ料金をおよそ五十五パーセント削減した事例。
そして五つ目、PDFカタログからのLLM商品情報抽出で、「誤るくらいなら出さない」を徹底する設計と後処理の工夫、の五本でした。
気になった記事の詳しい内容やキーワードは、番組のショーノートにまとめておきますので、あとでぜひチェックしてみてください。
zenncastでは、番組の感想や「こんなテーマを取り上げてほしい」といったリクエストも大歓迎です。
あなたの開発現場での工夫や失敗談なんかも、ぜひ教えてください。
それでは、きょうも良い一日をお過ごしください。
お相手はマイクでした。また次回のzenncastでお会いしましょう。