はい、承知いたしました。llama-cpp-python の内部構造や開発に関心のある方向けに、開発者ドキュメントを作成します。
llama-cpp-python は、C++ で実装された高性能な LLM 推論ライブラリ llama.cpp の Python バインディングです。主な目的は、llama.cpp の持つ高速な CPU/GPU 推論能力、メモリ効率(特に量子化モデル)、そして豊富な機能を、Python 開発者が容易に利用できるようにすることです。
主な設計目標:
- Pythonic なインターフェース: C++ API をラップし、直感的で使いやすい Python クラスとメソッドを提供します。
- パフォーマンス:
llama.cppのパフォーマンスを可能な限り引き出せるように、ctypesを介した効率的な連携を目指します。 - 機能網羅: テキスト生成(補完、チャット)、埋め込み、トークナイズ、KV キャッシュ管理、グラマー制約、マルチモーダル (LLaVA)、LoRA 適用など、
llama.cppの主要機能をサポートします。 - OpenAI 互換性: 特にサーバー機能において、OpenAI API と互換性のあるインターフェースを提供し、既存のエコシステムとの連携を容易にします。
- 拡張性: 新しいチャットフォーマットやカスタムトークナイザなどを追加しやすい構造を目指します。
llama-cpp-python は、いくつかのレイヤーで構成されています。
graph TD
A[User Python Code: examples/high_level_api] --> B[Llama class: llama.py]
B --> C[Llama Internals: LlamaModel, LlamaContext, LlamaBatch, LlamaSampler _internals.py]
C --> D[ctypes Bindings: llama_cpp.py]
D --> E[libllama Compiled C++ Library]
F[User Python Code: FastAPI Client] --> G[FastAPI Endpoints: server/app.py]
G --> H[LlamaProxy: server/model.py]
H --> B
I[Chat Formatting Logic] --> J[llama_chat_format.py]
B --> J
G --> J
K[Tokenizer Logic] --> L[llama_tokenizer.py]
B --> L
M[Grammar Logic] --> N[llama_grammar.py]
B --> N
J --> N
O[LLaVA Logic] --> P[ctypes Bindings: llava_cpp.py]
P --> Q[libllava Compiled C++ Library]
J --> Pz
libllama/libllava(C++ Core):llama.cpp(およびllava.cpp) をコンパイルして生成される共有ライブラリ (.so,.dylib,.dll)。- 実際のモデル読み込み、推論計算 (Transformer レイヤー、KV キャッシュ管理)、トークナイズ、サンプリングなどの低レベルな処理を担当します。
- ハードウェアアクセラレーション (BLAS, Metal, CUDA, ROCm) を活用します。
llama_cpp.py/llava_cpp.py(CTypes Bindings):- Python の標準ライブラリ
ctypesを使用して、libllama/libllavaが公開している C API (関数、構造体、定数) を Python から呼び出せるようにします。 - C のデータ型と Python のデータ型間の変換を行います。
- 共有ライブラリのロード (
_ctypes_extensions.py) も担当します。
- Python の標準ライブラリ
_internals.py(Low-level Python Wrapper):- C API を直接呼び出すよりも少し扱いやすい Python クラス (
LlamaModel,LlamaContext,LlamaBatch,LlamaSamplerなど) を提供します。 - C ポインタや構造体のライフサイクル管理(例:
contextlib.closingを使ったリソース解放)を部分的に行います。 - 主に
Llamaクラスの内部実装で使用されますが、より低レベルな制御が必要な場合に直接利用することも可能です。
- C API を直接呼び出すよりも少し扱いやすい Python クラス (
llama.py(High-level API -Llamaclass):- ユーザーが主に直接利用するクラスです。
- モデルのロード、コンテキストの作成、トークナイズ/デトークナイズ、テキスト生成 (
__call__,create_completion,create_chat_completion)、埋め込み生成 (create_embedding)、状態管理 (save_state,load_state) などを抽象化されたメソッドとして提供します。 - 内部で
_internals.pyのクラスやllama_cpp.pyの関数を呼び出します。 - KV キャッシュの管理(プレフィックスマッチングなど)も行います。
- Supporting Modules (
llama_chat_format.py,llama_tokenizer.py,llama_grammar.pyなど):llama_chat_format.py: 様々なチャットテンプレートを処理し、OpenAI 形式のチャット補完を実現します (Jinja2 使用)。llama_tokenizer.py: トークナイザの抽象インターフェースと、llama.cpp組み込みトークナイザや Hugging Face Tokenizers を使うための実装を提供します。llama_grammar.py: GBNF (GGML BNF) 形式のグラマーをパースし、制約付き生成を可能にします。JSON スキーマからの変換機能も含まれます。
server/(FastAPI Server Layer):- FastAPI を使用して Web サーバーを構築し、OpenAI 互換の API エンドポイントを提供します。
model.py: 複数のモデル設定を管理し、リクエストに応じてLlamaインスタンスをロード/アンロードするLlamaProxyを提供します。settings.py: Pydantic を使ってサーバーとモデルの設定を管理します。app.py: FastAPI アプリケーションのセットアップ、ルーティング、非同期処理 (AnyIO + Threadpool)、SSE ストリーミングなどを担当します。errors.py: エラーハンドリングを行い、OpenAI 形式のエラーレスポンスを返します。
-
llama_cpp.py:ctypesの関数 (ctypes_function) と構造体定義が中心です。libllamaの C ヘッダーファイル (llama.h) に対応します。- 定数 (例:
LLAMA_SPLIT_MODE_LAYER,LLAMA_FTYPE_MOSTLY_Q4_0) もここで定義されます。 - ライブラリのロード処理は
_ctypes_extensions.pyのload_shared_libraryで行われ、環境変数 (LLAMA_CPP_LIB_PATH) によるオーバーライドもサポートされます。
-
_internals.py:LlamaModel: GGUF ファイルをロードし、モデル構造 (レイヤー数、埋め込み次元など) やメタデータへのアクセスを提供します。LlamaContext: 推論コンテキスト (KV キャッシュなど) を保持します。llama_decodeの呼び出しを担当します。LlamaBatch:llama_decodeに渡すためのトークン、位置、シーケンス ID などの情報をまとめたバッチ構造を管理します。LlamaSampler: Top-K, Top-P, Temperature, Mirostat, Repetition Penalty などのサンプリングパイプラインを構築・実行します。
-
llama.py(Llamaclass):__init__: パラメータを解釈し、LlamaModel,LlamaContext,LlamaBatchを初期化します。チャットハンドラやトークナイザもここで設定されます。eval(): トークンを受け取り、バッチに分割してLlamaContext.decode()を呼び出し、KV キャッシュを更新します。sample(): 現在の logits に対してLlamaSampler.sample()を呼び出し、次のトークンを取得します。generate():eval()とsample()を組み合わせたコアな生成ループを提供するジェネレータです。KV キャッシュの管理 (プレフィックスマッチ、シフト) もここで行われます。create_completion()/create_chat_completion():generate()をラップし、OpenAI 互換のインターフェースとレスポンスを提供します。チャットの場合はllama_chat_formatを利用します。
-
server/ディレクトリ:- FastAPI の標準的な構造に従います。
app.pyがエントリーポイントで、ルーティング、ミドルウェア、依存性注入 (DI) を設定します。 LlamaProxyが DI を通じて各エンドポイントに提供され、モデルへのアクセスを抽象化します。- 非同期 (
async def) エンドポイント内で、同期的なllama-cpp-pythonのメソッド (Llama.__call__など) をrun_in_threadpoolを使って呼び出すことで、イベントループのブロッキングを防いでいます。 - ストリーミングは
EventSourceResponseとanyioのメモリチャネルを使って実現されています。get_event_publisher関数がバックグラウンドで生成結果をチャネルに送り、FastAPI がそれをクライアントに SSE として送信します。 llama_outer_lockとllama_inner_lock: サーバーが複数のリクエストを処理する際、同時にllama_decodeを呼び出さないようにするための排他制御です。また、ストリーミング中に新しいリクエストが来た場合に既存のストリームを中断する (interrupt_requests) 機能にも関連しています。
- FastAPI の標準的な構造に従います。
pip install llama-cpp-pythonを実行すると、setup.py(またはpyproject.tomlのビルドバックエンド設定) がCMakeを呼び出します。- CMake は
vendor/llama.cppディレクトリ内のソースコードをコンパイルし、共有ライブラリ (libllama) を生成します。 - 生成された
libllamaは Python パッケージ内に配置されます。 - カスタマイズ:
- GPU サポート: 環境変数
CMAKE_ARGSまたはpip install --config-settings="cmake.args=..."を使って CMake にフラグ (例:-DLLAMA_CUBLAS=ON,-DLLAMA_METAL=ON) を渡すことで、GPU サポートを有効にしてビルドできます。 - 外部ライブラリ: 環境変数
LLAMA_CPP_LIB_PATHを設定すると、実行時に指定されたディレクトリ内のlibllamaがロードされます(ビルド済みのライブラリを使用する場合)。 - ソース変更: リポジトリをクローンし、
CMakeLists.txtやvendor/llama.cppのコードを直接変更してpip install .でビルドすることも可能です(上級者向け)。
- GPU サポート: 環境変数
- チャットフォーマット:
llama_chat_format.pyの@register_chat_completion_handlerデコレータを使うか、LlamaChatCompletionHandlerRegistryを直接操作して、新しいチャットフォーマットハンドラを登録できます。- 多くの場合は
Jinja2ChatFormatterを使ってテンプレートと特殊トークンを指定するだけで対応可能です。
- トークナイザ:
llama_tokenizer.pyのBaseLlamaTokenizerを継承し、tokenizeとdetokenizeメソッドを実装することで、カスタムトークナイザを作成できます。Llamaの__init__でtokenizer引数にカスタムトークナイザインスタンスを渡します。
- Verbose 出力:
Llama(..., verbose=True)やサーバー設定でverbose: trueを指定すると、llama.cppからの詳細なログ (モデルロード、推論タイミングなど) が標準エラー出力に表示されます。 - CTypes エラー: ライブラリのロード失敗 (
FileNotFoundError,OSError) は、共有ライブラリが見つからないか、依存関係が満たされていない場合に発生します。ビルドログやldd(Linux),otool -L(macOS), Dependency Walker (Windows) などで確認します。関数が見つからない (AttributeError) 場合は、libllamaとllama-cpp-pythonの C API バージョン間の不整合が考えられます。 - 推論結果の問題: プロンプトのフォーマット、トークナイズ、サンプリングパラメータ、KV キャッシュの状態などを確認します。低レベル API (
_internals) を使ってステップごとにデバッグすることも有効です。 - サーバー関連: FastAPI や Uvicorn のログ、リクエスト/レスポンスの内容、非同期/同期処理の境界、ロックの状態などを確認します。
- コーディングスタイルに従ってください (主に
black,flake8)。 - 新しい機能にはテストを追加してください (
tests/)。 - ドキュメント (README, docstrings) を更新してください。
- GitHub リポジトリで Issue を立てて議論し、Pull Request を送ってください。
このドキュメントが llama-cpp-python の内部構造の理解や開発に役立つことを願っています。