BanditDB について
BanditDB は、Rust 製データベースを基盤とする Python SDK と MCP サーバーで、コンテキストに基づく永続的な意思決定メモリーを扱います。適応型システムや、行動の結果を共有するエージェント群を構築する開発者向けに、キャンペーン管理とオフライン評価も備えています。
主な機能
- 01コンテキスト型バンディットキャンペーン
- 02行動の予測と報酬の記録
- 03エージェント間の永続メモリー共有
- 04キャンペーン診断とレポート
- 05Parquet エクスポートとオフライン評価
活用シーン
- 01コンテキストと結果に基づくタスク振り分け
- 02ユーザーごとの選択肢の最適化
- 03複数エージェント間で学習結果を共有
- 04エクスポートデータを使ったポリシー評価
MCP 対応
内蔵 MCP サーバーは、キャンペーンの作成・管理、コンテキストに基づく判断、結果の記録、診断、予算管理のツールをエージェントに提供します。BANDITDB_URL と任意の BANDITDB_API_KEY で設定した稼働中の BanditDB サーバーに接続します。
設定
ローカルサーバーの設定はどのクライアントでも共通です。コピーしてクライアントの MCP 設定ファイルに貼り付けてください。
uvx banditdb-pythonBANDITDB_URLBase URL of the BanditDB server, e.g. http://localhost:8080 or https://your-instance.api.banditdb.com. Try it against the public sandbox at https://sandbox.banditdb.com before running your own.BANDITDB_API_KEYシークレットAPI key. A writer-role key is enough for get_intuition and record_outcome; admin is needed to create or delete campaigns.ローカルサーバーにはマシン上の実行環境(Node.js 18 以上など)が必要です。設定ファイルの場所はクライアントごとに異なります。
ツール
12掲載時のツール一覧 · 2 週間前に更新
| パラメーター | 型 | 必須 | 説明 |
|---|---|---|---|
| arms | array | はい | |
| alpha | number | いいえ | |
| budgets | any | いいえ | |
| algorithm | string | いいえ | |
| campaign_id | string | はい | |
| feature_dim | integer | はい | |
| decay_half_life_hours | any | いいえ |
詳細
BanditDB Python SDK
BanditDB 用の公式 Python クライアントおよび Model Context Protocol(MCP)サーバーです。Rust で記述された、超高速でロックフリーのコンテキストバンディットデータベースです。
BanditDB は、強化学習(LinUCB、Thompson Sampling)の複雑な線形代数を、シンプルな API の背後に隠します。リアルタイムのパーソナライズ機能や動的な A/B テストを構築し、LLM エージェントに数学的に厳密な永続メモリを提供できます。
mcp-name: com.banditdb/mcp
インストール
pip install banditdb-python
BanditDB Rust サーバーが実行中である必要があります(デフォルト:http://localhost:8080)。
1. 標準的な SDK の使い方
クライアントには、自動接続プーリング、指数バックオフによる再試行、厳格なタイムアウトが備わっています。
from banditdb import Client, BanditDBError
# Connect to the BanditDB server.
# Pass api_key if BANDITDB_API_KEY is set on the server.
db = Client(
url="http://localhost:8080",
timeout=2.0,
api_key="your-secret-key", # omit if server runs without auth
)
try:
# 1. Create a campaign (run once at startup)
# algorithm defaults to "linucb"; use "thompson_sampling" for Bayesian exploration
db.create_campaign(
campaign_id="checkout_upsell",
arms=["offer_discount", "offer_free_shipping"],
features=["is_mobile", "cart_value", "is_returning_user"],
)
# or: db.create_campaign(..., algorithm="thompson_sampling")
# 2. A user arrives — ask the database what to show them.
# Scale each value to 0–1; the SDK puts them in order, adds a constant 1
# and normalises the vector.
arm_id, interaction_id = db.predict("checkout_upsell", {
"is_mobile": 1.0,
"cart_value": min(cart_total / 200, 1.0),
"is_returning_user": 0.0,
})
print(f"Showing: {arm_id}") # e.g., "offer_free_shipping"
# 3. The user clicked — send the reward
db.reward(interaction_id, reward=1.0)
except BanditDBError as e:
print(f"Database error: {e}")
名前付き特徴量またはベクトル。 features=[...] を指定すると、キャンペーンは特徴量の順序をメタデータに保存し、predict、batch_predict、interact では名前をキーとする dict を受け付けます。名前が不足している場合や誤記がある場合は、リクエストを送信する前に ValueError が発生します。BanditDB コンソールで作成したキャンペーンも dict を受け付けます。順序はキャンペーンごとに campaign_info() で一度読み込まれ、クライアントにキャッシュされます。ベクトルを自分で管理するには、代わりに feature_dim を渡してリストを送信してください。normalize_context() はベクトルを単位長に正規化します。
すべての Client メソッド
ヘルス
| メソッド | 説明 |
|---|---|
health() | サーバーに接続でき、WAL ライターが正常な場合は True を返します。 |
health_detail() | キャンペーンごとの entropy と status("ok"/"warning"/"critical")を含む、ヘルス情報の完全な辞書を返します。 |
キャンペーン
| メソッド | 説明 |
|---|---|
create_campaign(campaign_id, arms, feature_dim=None, alpha=1.0, algorithm="linucb", metadata=None, decay_half_life_hours=None, pacing=None, features=None) | 新しいキャンペーンを登録します。辞書を使って予測する場合は features(順序付きの名前)を、ベクトルを送信する場合は feature_dim を指定します。algorithm には "linucb"、"thompson_sampling"、NeuralLinUCBConfig、NeuralTSConfig、ProgressiveConfig を指定できます。metadata は任意の JSON 辞書です(≤ 64 KB)。pacing には PacingConfig を指定します。予算制約を参照してください。 |
list_campaigns() | alpha、arm_count、algorithm を含む、すべてのキャンペーン(アクティブおよびアーカイブ済み)のリストを返します。 |
campaign_info(campaign_id) | アームごとの完全な状態を返します。theta、theta_norm、予測数、報酬数が含まれます。見つからない場合は APIError(404)を発生させます。 |
report(campaign_id) | ビジネスレベルの収束レポートです。converged=True は、1 つのアームが 95% CI で統計的に有意なリードを得ており、安全に停止できることを意味します。converged=False はリードしているものの、CI がまだ重なっていることを意味します。converged=None は、まだデータが不十分(アームあたりの報酬が 30 未満)であることを意味します。 |
diagnostics(campaign_id) | オペレーター向け診断情報です。アームごとの theta ノルム、A_inv の不確実性範囲、エントロピーの健全性(selection_entropy、entropy_status、entropy_trend、likely_cause、suggested_action)、トーナメントのトラフィック、ニューラルバッファーサイズが含まれます。 |
archive_campaign(campaign_id) | ソフト削除します。予測と報酬を一時停止しますが、学習済みの重みはすべて保持します。restore_campaign() で復元できます。 |
restore_campaign(campaign_id) | 重みをすべて保持したまま、アーカイブ済みのキャンペーンをアクティブ状態に戻します。 |
delete_campaign(campaign_id) | キャンペーンを完全に削除します。見つからない場合は False を返します。 |
pacing(campaign_id) | ペーシング対象キャンペーンの予算状態を返します。リソースごとの budget、consumed、remaining、utilization、lambda(シャドウプライス)、is_exhausted が含まれます。キャンペーンにペーシングが設定されていない場合は None を返します。Server 2.3.0 以降。 |
add_arm(campaign_id, arm_id, group=None, warm_start=None, costs=None) | 稼働中のキャンペーンにアームを追加します(管理者キー)。ペーシング対象キャンペーンでは、costs にリソース名から予測ごとの消費単位への対応を指定します。 |
set_arm_status(campaign_id, arm_id, status) | "active"、"paused"(選択対象外。一時停止解除可能)、または "retired" を指定します。アームが学習した内容は保持されます。管理者キーが必要です。 |
予測と報酬
| メソッド | 説明 |
|---|---|
predict(campaign_id, context, eligible_arms=None, exclude_arms=None) | context は特徴量の値を持つ辞書(名前付きキャンペーンの場合)またはベクトルです。(arm_id, interaction_id) を返します。ループを完了するには、interaction_id を reward() に渡します。オプションのフィルターは、この呼び出しでのみ候補を絞り込みます。 |
batch_predict(predictions) | 1 回のラウンドトリップで、最大 100 件のキャンペーン/コンテキストの組み合わせを予測します。各項目は `{"campaign_id": str, "context": dict |
reward(interaction_id, reward) | 結果を記録します。reward は [0.0, 1.0] の範囲内である必要があります。インタラクションがすでに報酬済み、または有効期限切れ(デフォルト TTL:24 h)の場合は APIError を発生させます。 |
データとエクスポート
| メソッド | 説明 |
|---|---|
checkpoint() | WAL をフラッシュし、モデルのスナップショットを作成し、Parquet シャードを書き込み、ニューラル再学習とトーナメント評価を実行して、WAL をローテーションします。要約文字列を返します。 |
export() | キャンペーンごとにグループ化された Parquet エクスポートシャードの一覧を返します。{export_dir, shards} を返します。 |
予算制約
サーバー 2.3.0 以降。キャンペーンに 1 つ以上の予算対象リソースと、アームごとのコストを設定します。サーバーはリソースごとのシャドー価格を学習し、期間内に予算を使い切るよう支出を調整します。コストの高いアームを選ぶのは、その効果がコストに見合う場合に限られます。
from banditdb import Client, PacingConfig, ResourceConstraint
db.create_campaign(
"promos", ["none", "discount_10", "discount_20"], feature_dim=4,
pacing=PacingConfig(resources=[
ResourceConstraint(
name="promo_budget",
budget=5_000.0, # units available over the horizon
horizon=100_000, # decisions the budget should last
arm_costs={"discount_10": 1.0, "discount_20": 2.0}, # "none" costs 0
),
]),
)
# Arms added later declare their own costs
db.add_arm("promos", "discount_30", costs={"promo_budget": 3.0})
for r in db.pacing("promos")["resources"]:
print(f"{r['name']}: {r['utilization']:.0%} spent, lambda={r['lambda']:.3f}")
知っておくとよいこと:
- 予算は
predict時に消費され、reward時には消費されません。 実行しなかった推奨でも、予算は消費されます。 - 予算はリセットされず、支出ペースは変更できません。 一度消費された予算は、キャンペーンを再作成するまで、コストの高いアームを選択できない状態にします。
arm_costsのキーはキャンペーンのアームでなければなりません。サーバーはスペルミスのあるアームを無料として扱いますが、SDK は代わりにValueErrorを発生させます。- 支出ペースを調整するキャンペーンへの予測では、サーバーの WAL が飽和している場合に 503 が返されることがあります。
- サーバーを 2.3.0 より古いバージョンにダウングレードすると、支出ペース調整は通知なく無効になります。
2. AI「集合知」(Model Context Protocol)
標準的な LLM エージェントはステートレスです。タスクを誤ったモデルに振り分けて失敗しても、翌日には同じ間違いを繰り返します。BanditDB に組み込まれた MCP サーバーは、エージェント群全体に共有の永続メモリを提供します。
MCP サーバーの起動
# Set environment variables before starting
export BANDITDB_URL=http://localhost:8080
export BANDITDB_API_KEY=your-secret-key # omit if server runs without auth
banditdb-mcp
Claude Desktop への接続
Claude の設定ファイルに追加します。
- Mac:
~/Library/Application Support/Claude/claude_desktop_config.json - Windows:
%APPDATA%\Claude\claude_desktop_config.json
{
"mcpServers": {
"banditdb": {
"command": "banditdb-mcp",
"args": [],
"env": {
"BANDITDB_URL": "http://localhost:8080",
"BANDITDB_API_KEY": "your-secret-key"
}
}
}
}
エージェント群は、次の 12 個のツールを利用できるようになります。
| ツール | 機能 |
|---|---|
create_campaign | 新しい意思決定キャンペーンを作成します。algorithm("linucb" または "thompson_sampling")、alpha、およびコストのかかるアクション用の任意の budgets を指定できます。調整不要で自然なベイズ探索を行うには、Thompson Sampling を使用します。 |
list_campaigns | 有効なキャンペーンをすべて一覧表示します(algorithm と alpha を表示)。get_intuition の呼び出し前に、既存のキャンペーンを確認するのに便利です。 |
campaign_diagnostics | アームごとの学習状態(theta_norm、予測件数、報酬率、エントロピーの健全性)を確認します。キャンペーンが学習していないように見える場合や、特定のアームが優勢な場合に使用します。 |
campaign_report | ビジネス向けの収束レポートを表示します。キャンペーンが統計的に収束したかどうかと、信頼区間を含む勝者のアームを示します。 |
get_intuition | 指定したコンテキストに対して、BanditDB に選択するアームを問い合わせます。任意の exclude_arms / eligible_arms を指定すると、このリクエストに限って選択肢を絞り込めます。アームと、保存用の interaction_id を返します。 |
batch_get_intuition | 1 回のラウンドトリップで複数のキャンペーンの決定を取得します。{campaign_id, context} の辞書のリストを渡します。 |
record_outcome | 選択したアクションが成功(1.0)したか失敗(0.0)したかを報告します。共有モデルを更新します。 |
archive_campaign | キャンペーンを論理削除します。すべての学習済み重みは保持したまま、予測と報酬の処理を一時停止します。 |
restore_campaign | アーカイブ済みキャンペーンを、すべての重みを保持したまま有効な状態に戻します。 |
campaign_budget | リソースごとの予算支出を表示します。消費済み、残量、利用率、シャドー価格、枯渇状況を確認できます。 |
add_arm | 実行中のキャンペーンにアクションを追加します。既存のアームからのウォームスタートと、予算コストの設定は任意です。 |
set_arm_status | アクションを一時停止、廃止、または再有効化します。学習済みの内容はすべて保持されます。 |
ネットワーク内のエージェントが行うすべての意思決定は、今後のすべてのエージェントのルーティングを改善します。
3. データサイエンスとオフライン評価
BanditDB はすべての予測と報酬をイベントソース方式で Write-Ahead Log(WAL)に記録します。checkpoint() を呼び出すと、完了した予測→報酬のペアが Snappy 圧縮された Parquet ファイルにまとめられます。ファイルはキャンペーンごとに 1 つ作成され、Polars または Pandas でオフライン分析できます。
報酬が数時間後に届く場合でも、すべての予測が Parquet ファイルに確実に記録されます。BanditDB はチェックポイントごとに処理中のインタラクションを再出力するため、遅れて届いた報酬も後続のサイクルで必ず取り込まれます。
# Checkpoint: snapshot models, write Parquet, rotate the WAL.
# Call this on a schedule or after significant traffic.
summary = db.checkpoint()
print(summary)
# "Checkpoint written and WAL rotated: 2 campaigns, offset 4821 bytes,
# 150 interactions exported, 3 in-flight re-emitted"
# List which Parquet files are available
print(db.export())
# 'Parquet files in /data/exports: ["llm_routing.parquet"]'
# Load directly from the mounted volume into Polars.
# Flat schema: interaction_id | arm_id | reward | predicted_at | rewarded_at | propensity | feature_0 | ...
import polars as pl
df = pl.read_parquet("/data/exports/llm_routing.parquet")
print(df.head())
print(df.columns)
オフラインポリシー評価(OPE)
SDK には banditdb.eval の OPE 推定器が 3 つ含まれています。これらは、「ライブ実験を行わずに、別のポリシーを適用していた場合の平均報酬はどのくらいだったか」という問いに答えます。
評価用の依存関係をインストールします。
pip install "banditdb-python[eval]"
| 推定器 | 関数 | 仕組み | 使用する場面 |
|---|---|---|---|
| リプレイ | replay(df) | 各インタラクションを確率 (1/K) / propensity で受け入れます(Li et al. 2010)。一様ランダムポリシーの不偏標本です。 | ベースラインの健全性チェックに使用します。カバレッジが低いのは想定内です。使用されるインタラクションは約 1/K です。 |
| IPS / SNIPS | ips(df, clip=10.0) | すべてのインタラクションを、重要度重み (1/K) / propensity を使って評価します。分散を抑えるため、自己正規化します。重みのクリッピング(デフォルトは 10×)でバイアスと分散のトレードオフを調整します。 | 主な推定器です。十分なデータがあり、全データを使いたい場合に使用します。 |
| 二重ロバスト | doubly_robust(df, clip=10.0) | 線形報酬モデルを適合させた後、残差に IPS 補正を適用します。報酬モデルまたは傾向スコアのどちらかが正しければ、一致推定量になります。 | 統計的効率が最も高い方法です。複数のポリシーを比較する場合や、alpha を変えて評価する場合に使用します。 |
3 つの推定器はいずれも、次のとおりです。
- BanditDB の Parquet エクスポートから読み込んだ Polars または pandas の DataFrame を受け取ります
- 一様ランダムポリシーを評価対象として使用します(上回るべき不偏ベースラインです)
- Thompson Sampling のキャンペーンでは
ValueErrorを発生させます(propensity 列が null です。TS は傾向スコアを記録しません) estimate、std_error、n_used、n_total、methodを持つOPEResultを返します
import polars as pl
from banditdb.eval import replay, ips, doubly_robust
df = pl.read_parquet("/data/exports/llm_routing.parquet")
# How much reward would a uniform random policy have earned?
print(replay(df))
# OPEResult(method='replay', estimate=0.4821, std_error=0.0312, coverage=22.1% [33/149])
print(ips(df))
# OPEResult(method='ips', estimate=0.5103, std_error=0.0187, coverage=100.0% [149/149])
print(doubly_robust(df))
# OPEResult(method='doubly_robust', estimate=0.5219, std_error=0.0141, coverage=100.0% [149/149])
# Compare against the observed reward of the logging policy:
print("Observed (logging policy):", df["reward"].mean())
# If observed >> estimate, the campaign has learned something real — it outperforms random.
実用的な使い方:デプロイ前にオフラインで alpha を変えて評価します。 実際のトラフィックでキャンペーンを学習させ、チェックポイントを Parquet に保存してから、doubly_robust() で異なる alpha 値を再生し、最適な探索レベルを見つけます。ライブ実験は不要です。
注: OPE には
propensity列が必要ですが、この列が書き込まれるのは LinUCB キャンペーンのみです。Thompson Sampling キャンペーンでは、TS のアーム選択が確率的であり、傾向スコアの算出には決定論的なロギングポリシーが必要なため、propensity はnullとして記録されます。
アルゴリズムの選択
BanditDB は5つのアルゴリズムに対応しており、キャンペーンの作成時に選択します。
| アルゴリズム | algorithm の値 | 探索方法 | 使用する場面 |
|---|---|---|---|
| LinUCB | "linucb"(デフォルト) | 決定論的な UCB ボーナス:θ·x + α√(x·A⁻¹·x) | 予測しやすく、調整可能です。オフラインで alpha を試して調整できます。 |
| Linear Thompson Sampling | "thompson_sampling" | θ̃ ~ N(θ, α²·A⁻¹) からサンプリングし、θ̃·x でスコアを算出します | ベイズ事後分布を使用するため、alpha の試行は不要です。複数のユーザーが同時に利用すると、選択肢は自動的に多様化します。 |
| NeuralLinUCB | NeuralLinUCBConfig(...) | 深層 MLP の埋め込みと、埋め込み空間での LinUCB | 報酬関数が非線形の場合に適しています。報酬を N 回受け取るごとに MLP を再学習します。 |
| NeuralTS | NeuralTSConfig(...) | NeuralLinUCB と同じ MLP 埋め込みを使い、UCB 境界の代わりに埋め込み空間の事後分布からのサンプルでスコアを算出します | 初期のリグレットより長期的な性能が重要な、非線形の報酬に適しています。20K 以上のインタラクションで、さらに収束します。 |
| Progressive | ProgressiveConfig(...) | 自動調整型のトーナメント:ベースとチャレンジャーを並行して実行し、勝者にトラフィックを振り分けます | 設定不要のモデル選択に適しています。最適なアルゴリズムを自動的に選びます。 |
from banditdb import Client, NeuralLinUCBConfig, NeuralTSConfig, ProgressiveConfig
db = Client("http://localhost:8080")
# LinUCB (default)
db.create_campaign("routing", ["fast", "cheap"], feature_dim=4, alpha=1.5)
# Thompson Sampling — natural Bayesian exploration, alpha=1.0 is ideal
db.create_campaign("routing_ts", ["fast", "cheap"], feature_dim=4,
algorithm="thompson_sampling")
# NeuralLinUCB — learns a deep embedding of the context, then applies LinUCB
cfg = NeuralLinUCBConfig(
context_dim=4, # must match feature_dim
embed_dim=32, # arm matrix dimension (default 32)
hidden_dim=128, # MLP hidden layer width (default 128)
retrain_every=200, # retrain the MLP every N cumulative rewards
)
db.create_campaign("routing_neural", ["fast", "cheap"], feature_dim=4, algorithm=cfg)
# NeuralTS — same parameters as NeuralLinUCB, posterior-sampling exploration
cfg = NeuralTSConfig(context_dim=4, embed_dim=32)
db.create_campaign("routing_nts", ["fast", "cheap"], feature_dim=4, algorithm=cfg)
# Progressive — runs LinUCB vs NeuralLinUCB, shifts traffic to whoever wins SNIPS checkpoints
cfg = ProgressiveConfig(
base="linucb",
challenger=NeuralLinUCBConfig(context_dim=4, embed_dim=32),
min_obs=100, # minimum buffer entries per arm before any traffic shift
required_wins=3, # consecutive checkpoint wins to earn one traffic step
step_bps=1000, # traffic delta per win run, in basis points (1000 = 10%)
)
db.create_campaign("routing_prog", ["fast", "cheap"], feature_dim=4, algorithm=cfg)
5つすべてのアルゴリズムで、同じ predict → reward ループを使用します。どちらのニューラル設定も、Progressive の base または challenger として使用できます。
NeuralLinUCB と NeuralTS の比較
| NeuralLinUCB | NeuralTS | |
|---|---|---|
| 探索 | UCB 上限(決定論的) | 事後分布からのサンプル(確率論的)。広がりは alpha によって調整されます |
| 初期のリグレット | 低い | 高い |
| 20K 以上のインタラクションでの収束 | 遅い | 速い |
| 最適な用途 | 本番環境での安全性、初期の失敗コストが高い場合 | 長期的な性能の最大化 |
| 論文 | Xu, Wen, Zhao & Gu, ICLR 2022 (arXiv:2012.01780) | Zhang, Zhou, Li & Gu, ICLR 2021 (arXiv:2010.00827) |
どちらのニューラルアルゴリズムも、neural 機能を有効にしてビルドされたサーバーが必要です。"neural" in db.server_info()["features"] で確認してください。
エラー処理
| 例外 | 発生する場合 |
|---|---|
BanditDBError | 基底例外です。すべての SDK エラーを処理するには、これを捕捉してください。 |
ConnectionError | サーバーがオフライン、または接続できません。 |
TimeoutError | リクエストが設定されたタイムアウトを超過しました。 |
APIError | サーバーがエラーを返しました(例:キャンペーンが見つからない、認証されていない)。 |
ライセンス
Apache-2.0 — Copyright (C) 2026 Simeon Lukov and Dynamic Pricing Ltd. 詳細については、メインリポジトリを参照してください。
コメント
AI アプリケーションが外部のツールやデータソースに統一的な方法で接続するためのオープンなプロトコルです。クライアントはモデルとの対話を担い、MCP サーバーはツール・リソース・プロンプトテンプレートを提供します。標準メッセージでやり取りするため、1 つのサーバーを複数のクライアントで再利用できます。