BAML を採用する前に確認すべきこと: 型付き LLM 呼び出し言語の実像
The programming language for agents
ひと目でわかる
- これは何?
- BAML は LLM の出力を型で縛るために作られた DSL で、Rust 実装のコンパイラと各言語向けのクライアントを配布している。README から読み取れる設計と、そこから見える制約を整理する。
- 誰に向いている?
- 既存の TypeScript や Python のコードベースから LLM 呼び出し部分だけを切り出し、出力の型とエラーを静的に扱いたいチームに向く。逆に、LLM 呼び出しが数か所しかない小規模なコード、あるいは新しい DSL とビルド手順を増やしたくないプロジェクトでは、既存言語のクライアントライブラリで足りる可能性が高い。
- 商用利用できる?
- できます。Apache-2.0 は寛容なライセンスで、著作権表示とライセンス表示を残せば、使用・改変・販売が可能です。
- 今もメンテナンスされている?
- されています。直近 1 日以内に新しいコミットがあります。
- 何の言語で書かれている?
- 主に Rust です(GitHub の言語統計による)。
回答はプロジェクトの GitHub データ(最終同期:2026年9月15日)と当サイトの分析に基づくもので、法的助言ではありません。
オープンソース詳細解説
BAML が埋めようとしている穴はどこにあるのか
LLM をアプリケーションに組み込むとき、多くの言語では応答が文字列か、緩い辞書として返ってくる。型注釈を書いても実行時には消えるため、モデルが想定外のキーを返したり、数値であるべき場所に文章を返したりしても、それは呼び出し側の実行時エラーとして初めて表面化する。BAML はこの層を専用の言語として切り出し、スキーマをコンパイル時に検査する。README は「Types persist at runtime. There is no any nor casting dangerously to any type.」と述べており、型が実行時にも残ることを設計の中心に置いている。対象読者は、LLM 呼び出しをプロダクトの一部として多数抱え、出力の形が壊れたときに静的に気づきたい開発者だ。単発のプロトタイプではなく、呼び出しの数と種類が増えていく段階のチームを想定していると読める。
TypeScript 風の構文と Rust 風の型をどう組み合わせているか
README の説明では、BAML の見た目は TypeScript に近いが、型システムは Rust 的だとされる。ここで重要なのは、型が単なる注釈ではなく、モデルへの指示と応答の検証の両方に使われる点だ。型が実行時に残るということは、モデルから返ってきたデータを検証する段階で同じ定義を再利用できるという意味になる。加えて「Errors are typed and statically analyzed.」とあり、失敗の種類も型として扱われる。LLM の失敗はネットワークエラーだけではなく、パース不能な出力やスキーマ違反も含むため、これらを型で表現できるかどうかは呼び出し側の分岐の書きやすさに直結する。ファイルシステムがモジュールや名前空間を記述するという方針も README に明記されており、設定ファイルでモジュール構成を宣言するのではなく、ディレクトリ構造そのものを言語仕様に取り込む設計だ。
並行処理とテストを言語機能として持つ意味
README は green threads と colorless concurrency を挙げ、Go に近い並行モデルだと説明している。LLM 呼び出しは待ち時間が支配的な処理なので、複数の呼び出しを並行に走らせる必要が出やすい。言語側にその仕組みがあれば、呼び出し側の言語ごとに非同期の書き方を揃える手間が減る。さらに組み込みのテストと eval フレームワークを持つとされており、プロンプトの変更が出力に与える影響を同じ言語内で検証できる建て付けになっている。ここは評価が分かれる点だ。テスト基盤を言語に同梱するということは、既存の CI やテストランナーとは別にもう一つ実行系を持つことでもある。README には統合方法の詳細までは書かれていないため、既存のパイプラインにどう組み込むかは導入時に自分で確かめる必要がある。
導入コマンドと、そこで起きること
README が示す手順は短い。まず `brew install baml` で CLI を入れ、`baml agent install`、`baml init`、`baml ide install --code` の順に実行する。`baml init` がプロジェクトの雛形を作り、`baml ide install --code` がエディタ側の支援を設定する、という流れだ。README はこれ以上の説明を置いていないので、各コマンドが実際にどのファイルを書き換えるかは実行して確認するしかない。注目したいのは、README が「Can be run standalone or adopt incrementally (you can call a BAML function from TS, Py, Go, C#, Java, etc).」と書いている部分で、既存のアプリケーションを丸ごと置き換えるのではなく、LLM を呼ぶ関数だけを BAML 側に置いて他言語から呼ぶ使い方を想定している。Rust で書かれたコンパイラが各言語向けのクライアントを生成する構成だと読めるが、生成物の詳細は README には記述がない。
nightly が既定であるという配布上の事実
リポジトリ情報で確認できる直近のリリースは baml-language-0.18.1-nightly.20260908.a、同 20260906.a、同 20260905.a の 3 件で、いずれも nightly 表記を含む。既定ブランチも canary だ。これは活発な開発の裏返しであり、同時に、バージョンを固定せずに追従すると破壊的変更を拾う可能性があることを意味する。README のインストール手順にはバージョン指定が含まれていないため、`brew install baml` で何が入るかは導入時点の配布状況に依存する。プロダクションで使うなら、nightly 以外の安定版が存在するか、存在するならどの番号かをリリース一覧で確認し、CI ではそのバージョンを明示的に固定する運用を検討することになる。ここは README だけでは判断できない領域だ。
向かない場面と、既存言語で足りる場面
BAML が効くのは、LLM 呼び出しのスキーマが増え、呼び出し側の言語が複数にまたがる場合だ。逆に、LLM を呼ぶ箇所が 1 つか 2 つで、出力も単純な文字列で足りるなら、新しい言語と CLI とエディタ拡張を導入する理由は薄い。既存言語のクライアントライブラリと JSON スキーマ検証の組み合わせで同じことができる。もう一つの向かない場面は、ビルド手順を増やせない環境だ。BAML はコンパイル段階を持つため、CI に `baml` のインストールと生成ステップが加わる。README はこの生成物をどうバージョン管理するかについて触れていない。生成コードをコミットするのか、ビルド時に都度生成するのかで、レビューの負荷もキャッシュ戦略も変わる。この判断は導入前に決めておかないと後から効いてくる。
代替としての Instructor 系アプローチとの違い
同じ問題、つまり LLM の出力を構造化して型で受け取る問題に対して、Python では Pydantic モデルをそのままスキーマとして渡す手法が広く使われている。Instructor はその代表で、既存のクラス定義を関数デコレータ経由で LLM 呼び出しに結びつける。違いは導入の粒度だ。Instructor は既存の Python コードの中に数行で組み込めるが、型の検証は実行時のバリデーションに留まり、コンパイル時にスキーマの不整合を検出する仕組みは持たない。BAML は専用言語とコンパイラを要求する代わりに、型とエラーを静的に解析し、複数言語のクライアントを同じ定義から生成する。どちらが優れているかではなく、スキーマの数と言語の広がりがどれだけあるかで選ぶべきものだ。単一言語の単一サービスなら Instructor 側のコストの方が低い。
ライセンスと保守コストの見取り図
ライセンスは Apache-2.0 で、リポジトリ情報から確認できる。Apache-2.0 は商用利用や改変、再配布を許容し、特許条項を含む一般的な許容型ライセンスだ。ただし、生成されたクライアントコードや同梱物に別の条件がかかっていないかは README からは分からない。ライセンスの解釈は法務の領域なので、ここでは条件の所在だけを示しておく。保守コストの面では、nightly リリースが継続している以上、アップグレードの頻度と検証の手間は相応にかかる。README には後方互換性の方針やサポート期間の記述がない。導入を検討するなら、まず `baml init` で生成されるファイル一式を確認し、次にリリース一覧で nightly 以外のバージョンが存在するかを確かめ、最後に `baml ide install --code` が既存のエディタ設定に何を追加するかを見ておく。この 3 点が確認できて初めて、チームの CI に載せられるかどうかの判断ができる。
編集部の結論
既存の TypeScript や Python のコードベースから LLM 呼び出し部分だけを切り出し、出力の型とエラーを静的に扱いたいチームに向く。逆に、LLM 呼び出しが数か所しかない小規模なコード、あるいは新しい DSL とビルド手順を増やしたくないプロジェクトでは、既存言語のクライアントライブラリで足りる可能性が高い。採用を決める前に、baml init で生成されるファイル構成と、baml agent install および baml ide install --code が何を書き込むかを自分の環境で確認し、nightly ビルドに依存しないバージョンを固定できるかをリリース一覧で確かめておきたい。
コミュニティノート