| name | baml-integration |
| description | SagebaseでのBAML (Boundary ML) 統合に関する知識とベストプラクティス。BAML定義ファイルの作成、クライアント再生成、Factory Pattern実装、ハイブリッドアプローチの設計を支援します。 |
BAML Integration
Purpose
SagebaseにおけるBAML (Boundary ML) 統合の正しい知識と実装パターンを提供します。
When to Activate
This skill activates automatically when:
- Creating or modifying
.bamlfiles inbaml_src/directory - Implementing BAML-based services
- Working with
baml_client/generated code - User mentions "BAML", "Boundary ML", or "structured output"
- Creating factory classes for BAML implementations
BAML Overview
What is BAML?
BAML (Boundary ML) は、LLMの構造化出力を型安全に扱うためのドメイン特化言語(DSL)です。
Key Benefits
- 型安全性: Pydanticモデルと完全に互換性のある型定義
- トークン効率: 最適化されたプロンプト生成により、従来のPydantic実装よりトークン使用量を5-15%削減
- パース精度: LLMの出力を確実に構造化データに変換
- プロンプト一元管理:
.bamlファイルにプロンプトとスキーマを集約
BAML File Structure
Directory Layout
sagebase/
├── baml_src/ # BAML定義ファイル(.baml)
│ ├── clients.baml # LLMクライアント設定
│ ├── generators.baml # コード生成設定
│ ├── minutes_divider.baml # 議事録分割
│ ├── speaker_matching.baml # 話者マッチング
│ └── politician_matching.baml # 政治家マッチング
└── baml_client/ # 自動生成されたPythonコード
├── async_client.py # 非同期クライアント
├── sync_client.py # 同期クライアント
└── types.py # 型定義
BAML File Components
1. Class Definitions
// Pydanticモデルに対応する型定義
class SpeakerMatch {
matched bool @description("マッチングが成功したか")
speaker_id int? @description("マッチした発言者のID")
speaker_name string? @description("マッチした発言者の名前")
confidence float @description("マッチングの信頼度(0.0-1.0)")
reason string @description("マッチング判定の理由")
}
重要:
- クラス名はPydanticモデル名と一致させる
- オプショナルフィールドは
?を付ける @descriptionで各フィールドの意味を明示
2. Function Definitions
function MatchSpeaker(
speaker_name: string,
available_speakers: string
) -> SpeakerMatch {
client Gemini2Flash // 使用するLLMクライアント
prompt #"
あなたは議事録の発言者名マッチング専門家です。
# 抽出された発言者名
{{ speaker_name }}
# 既存の発言者リスト
{{ available_speakers }}
# マッチング基準
1. 完全一致を最優先
2. 括弧内の名前との一致
3. 信頼度は 0.8 以上の場合のみマッチ
"#
}
ベストプラクティス:
- プロンプトは
#"..."#で囲む(ヒアドキュメント) - 変数は
{{ variable_name }}で展開 - マッチング基準や出力要件を明確に記述
BAML Client Generation
Generating Client Code
コマンド:
uv run python -c "import sys; sys.argv = ['baml', 'generate', '--from', 'baml_src']; from baml_py import invoke_runtime_cli; invoke_runtime_cli()"
いつ実行するか:
- 新しい
.bamlファイルを作成した後 - 既存の
.bamlファイルを修正した後 baml_client/のコードが古い場合
確認方法:
# 新しい関数が生成されたか確認
grep -n "MatchSpeaker" baml_client/async_client.py
# 型チェックでエラーがないか確認
uv run --frozen pyright src/domain/services/baml_speaker_matching_service.py
Generated Code Usage
from baml_client.async_client import b
# BAML関数を呼び出し
baml_result = await b.MatchSpeaker(
speaker_name="山田太郎",
available_speakers="ID: 1, 名前: 山田太郎\nID: 2, 名前: 佐藤花子"
)
# 結果をPydanticモデルに変換
match_result = SpeakerMatch(
matched=baml_result.matched,
speaker_id=baml_result.speaker_id,
speaker_name=baml_result.speaker_name,
confidence=baml_result.confidence,
reason=baml_result.reason,
)
Implementation Patterns
1. Hybrid Approach (推奨パターン)
コンセプト: ルールベースマッチング(高速パス)+ BAML(複雑ケース)
class BAMLSpeakerMatchingService:
async def find_best_match(self, speaker_name: str) -> SpeakerMatch:
# 1. 高速パス: ルールベースマッチング
rule_based_match = self._rule_based_matching(speaker_name, available_speakers)
if rule_based_match.matched and rule_based_match.confidence >= 0.9:
return rule_based_match # LLMをスキップ
# 2. BAML: 複雑なケースのみ
baml_result = await b.MatchSpeaker(
speaker_name=speaker_name,
available_speakers=self._format_speakers_for_llm(filtered_speakers)
)
return SpeakerMatch(**baml_result.__dict__)
利点:
- トークン使用量削減(完全一致は即座に判定)
- レイテンシ削減(LLM呼び出しを最小化)
- コスト削減
2. Factory Pattern
環境変数で実装を切り替え:
# src/domain/services/factories/speaker_matching_factory.py
import os
from src.domain.services.interfaces.llm_service import ILLMService
from src.domain.repositories.speaker_repository import SpeakerRepository
class SpeakerMatchingServiceFactory:
@staticmethod
def create(
llm_service: ILLMService,
speaker_repository: SpeakerRepository,
):
use_baml = os.getenv("USE_BAML_SPEAKER_MATCHING", "false").lower() == "true"
if use_baml:
from src.domain.services.baml_speaker_matching_service import (
BAMLSpeakerMatchingService,
)
return BAMLSpeakerMatchingService(llm_service, speaker_repository)
from src.domain.services.speaker_matching_service import (
SpeakerMatchingService,
)
return SpeakerMatchingService(llm_service, speaker_repository)
環境変数設定 (.env):
USE_BAML_SPEAKER_MATCHING=false # デフォルトはfalse
USE_BAML_POLITICIAN_MATCHING=false
3. Service Implementation Structure
class BAMLSpeakerMatchingService:
def __init__(
self,
llm_service: ILLMService, # 互換性のため保持(BAML使用時は不要)
speaker_repository: SpeakerRepository,
):
self.llm_service = llm_service
self.speaker_repository = speaker_repository
async def find_best_match(self, speaker_name: str) -> SpeakerMatch:
# 実装
pass
def _rule_based_matching(self, speaker_name: str, available_speakers) -> SpeakerMatch:
# ルールベースマッチング(高速パス)
pass
def _filter_candidates(self, speaker_name: str, available_speakers) -> list:
# 候補絞り込み(トークン削減)
pass
def _format_speakers_for_llm(self, speakers: list) -> str:
# LLM用フォーマット
pass
Testing BAML Services
Mock BAML Client
import pytest
from unittest.mock import AsyncMock, MagicMock
@pytest.fixture
def mock_baml_client(monkeypatch):
"""BAML clientをモック化"""
mock_b = MagicMock()
mock_match_speaker = AsyncMock()
mock_match_speaker.return_value = MagicMock(
matched=True,
speaker_id=1,
speaker_name="山田太郎",
confidence=0.95,
reason="完全一致"
)
mock_b.MatchSpeaker = mock_match_speaker
monkeypatch.setattr("baml_client.async_client.b", mock_b)
return mock_b
@pytest.mark.asyncio
async def test_baml_speaker_matching(mock_baml_client):
"""BAMLマッチングのテスト"""
service = BAMLSpeakerMatchingService(mock_llm, mock_repo)
result = await service.find_best_match("山田太郎")
assert result.matched is True
assert result.speaker_id == 1
mock_baml_client.MatchSpeaker.assert_awaited_once()
Token Optimization Tips
1. Candidate Filtering
def _filter_candidates(self, speaker_name: str, available_speakers: list) -> list:
"""候補を絞り込んでトークン削減"""
candidates = []
for speaker in available_speakers:
score = 0
# 部分一致スコア
if speaker["name"] in speaker_name:
score += 3
# 会議体所属ボーナス
if speaker["id"] in affiliated_speaker_ids:
score += 10
if score > 0:
candidates.append({**speaker, "score": score})
# 上位10件のみLLMに渡す
candidates.sort(key=lambda x: x["score"], reverse=True)
return candidates[:10] # トークン削減!
2. Compact Formatting
def _format_speakers_for_llm(self, speakers: list) -> str:
"""簡潔なフォーマットでトークン削減"""
formatted = []
for speaker in speakers:
# 必要最小限の情報のみ
entry = f"ID: {speaker['id']}, 名前: {speaker['name']}"
if speaker['id'] in affiliated_speaker_ids:
entry += " ★" # 短い記号で会議体所属を示す
formatted.append(entry)
return "\n".join(formatted)
3. Prompt Optimization
// ❌ 冗長なプロンプト
prompt #"
あなたは議事録の発言者名マッチング専門家です。
以下の詳細な手順に従って、慎重にマッチングを行ってください。
...(長い説明)
"#
// ✅ 簡潔なプロンプト
prompt #"
発言者名と既存リストから最適マッチを見つけてください。
# 基準
1. 完全一致優先
2. ★付きを優先
3. 信頼度0.8以上のみマッチ
"#
Common Pitfalls
❌ Don't: Forget to Regenerate Client
# speaker_matching.bamlを作成
# ❌ すぐに使おうとする
result = await b.MatchSpeaker(...) # AttributeError!
✅ Do: Regenerate After Changes
# 1. BAMLファイル作成/修正
# 2. クライアント再生成
uv run python -c "import sys; sys.argv = ['baml', 'generate', '--from', 'baml_src']; from baml_py import invoke_runtime_cli; invoke_runtime_cli()"
# 3. 使用
result = await b.MatchSpeaker(...) # OK!
❌ Don't: Skip Type Conversion
# ❌ BAML結果をそのまま返す
return baml_result # 型が不明確
✅ Do: Convert to Pydantic Model
# ✅ Pydanticモデルに変換
return SpeakerMatch(
matched=baml_result.matched,
speaker_id=baml_result.speaker_id,
...
)
Environment Variables
BAML Feature Flags in Sagebase
Note: 議事録分割、議員団メンバー抽出、政党メンバー抽出は現在BAML専用です。 Pydantic実装は削除されており、環境変数による切り替えはできません。
# .env または .env.example
# 以下の機能はBAML専用(環境変数不要、Pydantic実装削除済み):
# - 議事録分割(Minutes Divider)
# - 会議体メンバー抽出(Conference Member Extractor)
# - 議員団メンバー抽出(Parliamentary Group Member Extractor)
# - 政党メンバー抽出(Party Member Extractor)
USE_BAML_SPEAKER_MATCHING=false # 話者マッチング(デフォルト: false)
USE_BAML_POLITICIAN_MATCHING=false # 政治家マッチング(デフォルト: false)
Debugging
Check Generated Code
# 関数が生成されたか確認
grep -n "MatchSpeaker" baml_client/async_client.py
# 型定義を確認
cat baml_client/types.py | grep -A 10 "class SpeakerMatch"
Type Check
# 型エラーを確認
uv run --frozen pyright src/domain/services/baml_speaker_matching_service.py
Run Tests
# BAML実装のテストを実行
uv run pytest tests/domain/services/test_baml_speaker_matching_service.py -v
Migration Checklist
新しいBAML実装を追加する際のチェックリスト:
-
baml_src/に.bamlファイルを作成- クラス定義(Pydanticモデルと一致)
- 関数定義(プロンプト含む)
- BAMLクライアント再生成
- BAML実装サービスを作成
- ハイブリッドアプローチ(ルールベース + BAML)
- 候補フィルタリング
- トークン最適化
- ファクトリークラスを作成
- 環境変数で切り替え
- デフォルト値を設定
-
.env.exampleに環境変数を追加 - テストを作成
- BAMLクライアントをモック
- ルールベースマッチングのテスト
- BAML統合テスト
-
CLAUDE.mdを更新- BAML対応機能リストに追加
- トークン削減効果を記載
- ドキュメント更新
- 使い方を説明
- 環境変数の説明
References
Examples
実装例は以下のファイルを参照:
baml_src/speaker_matching.baml- 話者マッチングBAML定義src/domain/services/baml_speaker_matching_service.py- BAML実装サービスsrc/domain/services/factories/speaker_matching_factory.py- ファクトリーtests/domain/services/test_baml_speaker_matching_service.py- テスト