はじめに
拙著 MLflowで実践するLLMOps――生成AIアプリケーションの実験管理と品質保証 のサポートリポジトリを大きく拡充しました。
本書はMLflowの機能を体系的に解説する構成で、紙面の都合で「現場のどんな課題に効くか」「本文と公開コードの差分の意図」までは厚く書ききれていない箇所がありました。リポジトリ側でその文脈を補完しつつ、MLflow のバージョンアップにも追従する仕組みを整えています。本記事では追加した4つのドキュメントとCIを紹介します。
1. 全章に CHAPTER_NOTES.md を整備
各章のディレクトリに CHAPTER_NOTES.md を配置し、本文の各リスト (コード片) と公開コードの対応・差分・未収録セクションの意図を記載しました。
たとえば第5章では「リスト5.2 → evaluation/02_standard_scorers.py、本書では手動でtrace IDをコピーする説明だが、リポジトリでは get_latest_traces() で最新トレースを自動取得」のように、対応ファイル・実行コマンド・本書との差分・本書通りに再現したい場合の手順をリスト単位で書いています。
未収録の機能 (Experimental APIや別途依存追加が必要なもの等) についても、未収録の理由と試したい場合の手順を明示しています。「載っていない=不備」という第一印象を「意図して載せていない」に変える狙いです。
2. docs/why/ で章別「なぜ嬉しいのか」サマリ
本書の機能解説と並行して、各章の機能が 現場のどんな課題に効くのか を200〜400字で言語化したドキュメントを docs/why/ch3.md〜ch9.md に追加しました。
3観点で書いています。
- どんな現場課題に効くか
- 使わないとどうなるか
- 本書の該当節と一緒に読むと良い箇所
たとえば第7章 (AI Gateway) であれば、「コスト管理・レート制限・プロバイダ切り替えがアプリ側に散らばる問題をゲートウェイに集約する」「これがないとSDKごとに書き分け / コストが見えない」といった現場価値を明示しました。読み始める前にこのサマリに目を通すと、本書の機能解説に「自分の文脈」が乗りやすくなります。
3. docs/external-refs.md で本文脚注URLの要約
本書の脚注で MLflow 公式ドキュメントの URL のみを示している箇所について、章別にURLを分類し、それぞれに2〜3行の要約と「本書のどの手順の前に読むと迷わないか」のヒントを併記しました。
本文を読み進めながら脚注URLに飛んだ際に、いきなり英語の長文ドキュメントに当たって手が止まる、という事態を減らす目的です。MLflow Tracing、Prompt Registry、AI Gateway、サードパーティ評価ライブラリ連携など、本書で扱う主要トピックをカバーしています。
4. GitHub Actions で CI 継続検証
.github/workflows/ci.yml で次の2系統のジョブを定義しました。
static-checks (毎push): ruff の構文チェック・compileall・nbformat 検証・pyproject.toml の妥当性チェック。30〜60秒で完了。
uv-sync (週次・手動・[deps]コミット時): 各章のディレクトリで uv sync してクリーン環境で依存解決できるかを検証。並列で6章ぶん回しても3〜5分。
MLflow は更新が速いライブラリなので、自分が何も変更していなくても上流の変更で依存解決が壊れることがあります。週次の自動ランで早期検知し、本書の読者が「動かないものが多い」という体験をしないようにする狙いです。
第9章だけノートブック形式である理由を明記
地味ですが、第9章のサンプルコードだけ .ipynb 形式を採用している理由を ch9/CHAPTER_NOTES.md の冒頭に明記しました。対話的な試行錯誤に向いた章であること、MLflow UIと並べて見る前提であること、%pip install セルで依存を完結できること、の3点です。「9章だけノートブックなのが謎」というご指摘をいただいていたので、設計判断として明文化しました。
今後の運用
差分の発見やご要望は GitHub Issues に errata ラベルでお寄せください。「なぜ嬉しいのか」への補足や反論は why-discussion ラベルで議論に上げていただけると助かります。本リポジトリは読者からのフィードバックを取り込んで継続的に改訂していきます。
後日追記: 本書本文へのアライメント (52% → 85%)
上記の整備で「なぜ違うかが書いてある」状態は作れたのですが、「そもそも書籍とリポジトリのコードを可能な限り揃える」ところまで踏み込めるとさらに良い、というご意見を受けて、後日アライメント作業を行いました。
何をやったか
本書の原稿から、CHAPTER_NOTES.md で言及している全リストの正確なコード片を抽出し、リポジトリのコードを1件ずつ照合して、寄せられるところは本書本文に合わせる、という作業です。
原稿の参照は別プロジェクトで行い、抽出結果のドキュメント を介してリポジトリ側に反映しました。原稿そのものは触らず、リポジトリのコードのみを変更する方針です。
結果
抽出された60リストに対して、本書本文と一致するコードの割合が次のように変化しました。
| 指標 | アライン前 | アライン後 | 変化 |
|---|---|---|---|
| 一致しているリスト数 | 31 / 60 | 51 / 60 | +20 |
| 一致率 | 52% | 85% | +33pt |
| 残る差分の数 | 29 | 9 | -20 |
| 残る差分の割合 | 48% | 15% | -33pt |
章別では次のとおりです。
| 章 | 抽出リスト | 前 | 後 | 変化 |
|---|---|---|---|---|
| 第5章 | 16 | 44% | 88% | +44pt |
| 第6章 | 11 | 18% | 73% | +55pt |
| 第7章 | 6 | 0% | 83% | +83pt |
| 第8章 | 10 | 50% | 80% | +30pt |
| 第9章 | 17 | 100% | 100% | ±0 |
第9章はノートブック形式で元々全リストが収録されていたため変化なし、それ以外の章はすべて大きく改善しました。
寄せた具体例
代表的な変更点をいくつか挙げます。
変数名の統一: 第6章で initial_prompt → initial_template、improved_prompt_v2 → improved_template のように、本書記載の変数名に揃えました。
正解データの文言: 第5章リスト5.3 の expected_response (LangGraphエージェントのトークン使用量に関する長文) を本書と完全一致させました。第7章リスト7.4 の各 expected_response も同様です。
import スタイル: 第5章リスト5.9 で mlflow.genai.get_scorer(name=...) の名前空間アクセス → from mlflow.genai import get_scorer の直接インポートに変更し、本書記載と一致させました。
コメント・docstring: 第7章 serving/agent.py の docstring を「プロンプトレジストリ」→「Prompt Registry」に統一、第8章 01_tracing_setup.py の関数名を handle_request → handle_chat_request に変更し、標準タグ + カスタムタグの構成も本書通りに揃えました。
寄せなかった9件
「アライン後も残る差分」は、すべて理由を CHAPTER_NOTES.md に明記しました。読者にとっては「違うことに気付いてから理由を探す」のではなく、「なぜ違うかが先に書いてある」状態です。
| カテゴリ | 件数 | 例 |
|---|---|---|
| リポジトリの動作上の都合 | 3 | リスト5.2 (get_latest_traces で自動化) |
| 意図的な独立構成 | 1 | リスト6.8 (Prompt Registry 集中学習用) |
| 統合された複数リスト | 3 | リスト6.4 / 6.9 / 6.10 |
| プラットフォーム制約 | 1 | リスト8.12 (Databricks 専用) |
| 本書 errata (リポジトリは正しい形を維持) | 1 | リスト7.7 のモデル名タイポ |
副産物: 自著で見つけた誤り
照合作業の副産物として、本書本文の誤りに5箇所気付きました。リポジトリ側はそれぞれ正しい形を維持しているので、書籍を読みながらリポジトリのコードを写経している方には影響が小さいはずです。次の増刷・改訂時に反映する想定で、CHAPTER_NOTES.md にも記録しています。
-
リスト5.9: コメント「最新バージョンを取得」と引数
version=1の明示が矛盾しています。最新を取りたい場合はversion引数を省略するのが正しい形です。 -
リスト7.7:
EMBEDDING_MODEL=text-embedding-smallと書いていますが、OpenAIのモデル名としてはtext-embedding-3-smallが正しく、同じ章のリスト7.8では正しく記載しています。 -
リスト9.6:
# コストを計算(例: GPT-5.2の単価)というコメントを書いていますが、GPT-5.2 というモデル名は存在しません。執筆時点での具体的なモデル名 (gpt-4o-mini等) で読み替えてください。 -
リスト9.6: トークン使用量を
t.data.attributes.get("input_tokens", 0)で取得する書き方をしていますが、MLflow 3.1 以降ではtrace.info.token_usage.get("input_tokens")が正規のAPIです (本書リスト8.9 では正しい形で記載しています)。 -
リスト9.7:
mlflow.genai.optimize_prompt(単数形) と書いていますが、実際のAPI名はmlflow.genai.optimize_prompts(複数形) です。リスト6.11 では正しい形で記載しています。
照合作業をしていなければ気付かなかった部分なので、リポジトリと本書を並べて読み直すことの副次的な効果を実感しました。書籍として手元にある以上、訂正の機会は限られていますが、せめてリポジトリ側で「正しい形」が読めるようにしておきます。
振り返り
「CHAPTER_NOTES.md があれば差分の説明は十分」と思っていましたが、実際にやってみると、写経して動かす読者の体験は「なぜ違うかの説明がある」より「そもそも違わない」のほうが圧倒的に良いです。CHAPTER_NOTES.md は今回スリムになり、「本当に寄せられない・寄せるべきでない理由のあるもの」だけが残りました。
レビューで指摘された「github で公開されているコードと、本文中で引用されているコードが違いすぎ」という第一印象に対して、リポジトリ側でやれることはほぼやりきった状態です。残る改善は本書側 (改訂版での反映) になります。
作業のしくみ
ワークフローを再現可能にしておくと、次の改訂時にも再利用できます。
- 原稿プロジェクトで、CHAPTER_NOTES.md に言及されているリストごとに「原稿のコード」「前後の本文」「教育的ポイント」「アライメント判断補助」を構造化したドキュメントを生成
- そのドキュメントをリポジトリ側に持ち込み、各リストごとに「寄せる/寄せない/部分的に寄せる」を判断
- 寄せる項目をリポジトリのコードに反映
- CHAPTER_NOTES.md を更新し、寄せた項目を削除、残った差分のみを記録
書籍が改訂されたとき、もしくは MLflow の上流が大きく変わったときに、同じ手順で再アライメントできます。