サービス
Lase が提供するサービスクラスの一覧です。すべて CodebaseJp\Lase\Services 名前空間に属します。
SubscriptionService
サブスクリプションの作成・更新を担当します。
createSubscription(Tenant $tenant, Plan $plan, Collection $options)
テナントに対してサブスクリプションを作成します。
- 悲観的ロックでテナントをロックし、競合状態を防止
- 現在のプランよりダウングレードする場合、利用量が新しい上限を超えていないかチェック
SubscriptionQuota・SubscriptionFeature・GrantedQuota・GrantedFeatureを自動同期- DB トランザクション内で実行
AuthService
認証周りのロジックを担当します。アカウントの有効化・パスワードリセット・トークン発行などを処理します。
AnalyticsService
管理画面向けの統計データを集計します。
- テナント数・ユーザー数・お問い合わせ数のサマリー
- 日次・月次の時系列データ生成
RecaptchaService
Google reCAPTCHA v3 のトークン検証を行います。
$recaptchaService->verify(string $token): boolSenterService
外部課金サービス「Senter」との API 通信を担当します。
- サービスプラン・オプション・アドオン情報の取得
- 請求書の取得
- 未請求利用量の取得
- Stripe 連携(顧客作成・SetupIntent・支払方法管理)
ActivityLogService
操作ログの記録を担当します。
record(BackedEnum $type, int $userId, ?int $tenantId, ?Request $request = null)
アクティビティログを1件記録します。
$type: ログの種別(ActivityLogTypeEnum の値)$userId: 操作を行ったユーザーの ID$tenantId: テナント ID(テナントコンテキスト外ではnull)$request: HTTP リクエスト(省略時は IP・UA が記録されない。スケジューラー・キュー等の非 HTTP コンテキストから呼ぶ場合は省略可)
AiEmbeddingService
ドキュメントをベクトル化してデータベースに保存し、AIチャットの RAG(検索拡張生成)を実現するサービスです。
インデックスの作成は php artisan lase:index-docs コマンドで実行します。
主なメソッド
| メソッド | 説明 |
|---|---|
chunkText() | Markdown をチャンクに分割する。Embeddings API に依存しない純粋関数 |
isIndexed() | 同一内容で既にインデックス済みかを返す |
indexDocument() | チャンク分割 → Embedding 生成 → DB 保存を行う |
findRelevantChunks() | クエリに近いチャンクを類似度の高い順に返す |
pruneExcept() | 指定ソース一覧に含まれない孤立チャンクを削除する |
チャンク分割の方針
- 先頭の YAML フロントマターは除去し、本文のみを対象にする
- 見出し(
#〜###)単位に分割する。コードブロック(```/~~~)内の#は見出しとして扱わない - 各チャンクの先頭に見出しパス(例:
ログイン > パスワードを忘れた場合)を付与し、チャンク単体でも文脈が分かるようにする - 上限文字数を超えるセクションは段落単位で分割し、チャンク間に
chunk_overlap文字分の重複を持たせる
検索の仕様(ハイブリッド検索)
ベクトル検索と全文検索の結果を RRF(Reciprocal Rank Fusion) で融合します。
score = Σ 1 / (60 + 各検索での順位)ベクトル検索は意味的な近さに強い一方、製品名・エラーコードのような固有語の完全一致に弱いという弱点があります。全文検索(MySQL の ngram パーサ)を併用することで、たとえば「E4021」のような語がベクトル的に一致しないチャンクも拾えるようになります。hybrid_enabled を false にするとベクトル検索のみになります。
ベクトルは正規化(ノルム1)して保存するため、類似度計算は内積のみで済みます。similarity_threshold を下回るチャンクは無関係とみなして除外し、無関係な文書がプロンプトへ混入するのを防ぎます。クエリ側の Embedding は query_cache_ttl 秒間キャッシュされます。
WARNING
InnoDB の全文検索インデックスは未コミットの行を参照できません。インデックス作成と同一トランザクション内で検索した場合、全文検索側は0件となりベクトル検索のみに縮退します(テストで RefreshDatabase を使う場合が該当します)。
WARNING
Embedding 生成に失敗した場合は例外を投げます(ゼロベクトルを保存してインデックスを黙って壊さないため)。indexDocument() は Embedding 生成を完了させてから既存チャンクを差し替えるため、API 失敗時に既存インデックスを失うことはありません。
ベクトルの保存形式
embedding は正規化済みベクトルを float32 で並べた BLOB です。JSON 文字列に比べて次の効果があります(実測値)。
| 1536次元 JSON | 512次元 float32 | |
|---|---|---|
| 1チャンクあたり | 32,794 B | 2,048 B |
| デコード(1000件) | 77.4 ms | 8.2 ms |
次元数の削減は text-embedding-3-* の Matryoshka 表現を利用しており、1536→512 程度では検索精度の低下はごく僅かです。合わせて 約1/16のサイズ・約9.5倍のデコード速度になります。
embedding_model / dimensions を各行に保持しているため、モデルや次元数を変更すると:
- 既存チャンクは SQL の段階で検索対象から除外されます(PHP 側で0点にするのではなく)
isIndexed()がfalseを返すため、--forceなしで自動的に再インデックスされます
設定
config/lase.php の ai セクションで調整できます。
| キー | 既定値 | 説明 |
|---|---|---|
embedding_model | text-embedding-3-small | Embedding 生成に使うモデル |
embedding_dimensions | 512 | ベクトルの次元数。0 でモデル既定値(次元数指定に非対応のモデル向け) |
embedding_batch_size | 50 | 1リクエストにまとめるチャンク数 |
chunk_max_length | 1500 | チャンクの上限文字数 |
chunk_min_length | 20 | これ以下のチャンクは破棄する |
chunk_overlap | 100 | 分割時に重複させる文字数 |
top_k | 5 | 検索で取得するチャンク数 |
similarity_threshold | 0.3 | この類似度を下回るチャンクは除外する |
hybrid_enabled | true | 全文検索を併用し RRF で融合する |
candidate_multiplier | 4 | 融合前に各検索から取得する候補数の倍率(top_k × この値) |
query_cache_ttl | 3600 | クエリ Embedding のキャッシュ秒数(0 で無効) |