0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

常駐型AIエージェントのLLM呼び出しコストを制御する設計パターン

0
Posted at

チャットやメールを常時監視して働くAIエージェントは、ユーザーからの質問に答えるだけのチャットボットと違い、誰も見ていない間もLLMを呼び続けてしまうという特有のコスト問題を抱えます。監視対象が増えるほど、ポーリングのたびにLLMへ投げるトークン量が積み上がり、気づいたころには「1テナントあたりの月間コストが読めない」状態になります。この記事では、常駐型エージェントを運用する中で固めた「LLM呼び出しコストを制御する設計パターン」を整理します。

なぜ普通のチャットボットより制御が難しいのか

対話型のチャットボットは、呼び出し回数がユーザーの発言回数にほぼ比例するため、コストの見積もりが立てやすいです。一方、常駐型エージェントは人間が何もしなくても定期的に動き続けるため、呼び出し回数がテナント数×監視頻度で機械的に増えていきます。しかも「念のため毎回LLMに要約させる」実装にすると、変化がない時間帯にも同じようなトークンを消費し続けることになります。

コストが問題化するのは開発中ではなく、テナント数が増えて初めてです。1テナントなら誤差でも、100テナント分が同じ頻度でポーリングすると、無駄な呼び出しがそのまま請求書に跳ね返ります。

パターン1: LLMを呼ぶ前にルールベースで「差分があるか」を先に判定する

最も効果が大きいのは、LLMを呼ぶかどうかの判断自体をLLMにやらせないことです。

async function checkAndSummarize(source: MonitoredSource) {
  const snapshot = await fetchSnapshot(source);
  const prevHash = await store.get(`hash:${source.id}`);
  const currentHash = hash(snapshot);

  if (currentHash === prevHash) {
    return; // 変化なし。LLMは呼ばない
  }
  await store.set(`hash:${source.id}`, currentHash);

  return await llm.summarize(diff(prevSnapshot, snapshot)); // 差分だけを渡す
}

ポイントは2つあります。

  • 変化検知はハッシュ比較や差分抽出などの安価な処理で済ませ、LLMは「何が変わったか意味付けする」ところだけを担当させる
  • LLMに渡すのはスナップショット全体ではなく差分のみにする。監視対象が肥大化しても、渡すトークン量は変化量に比例するので膨らみにくい

パターン2: 監視頻度を「重要度」で階層化する

すべての監視対象を同じ頻度でポーリングする必要はありません。反応速度が価値に直結するものと、そうでないものを分けます。

階層 例 ポーリング頻度
リアルタイム 商談中のチャット、緊急連絡 数十秒〜1分
準リアルタイム タスクの進捗、日中のメール 5〜15分
バッチ 週次レポート素材、アーカイブ系 1日1回

「全部リアルタイムにしておけば安心」は、コスト面では最も高くつく選択です。その情報が1時間遅れて届いても業務上困らないかを基準に階層を決め、下の階層に落とせるものは積極的に落とします。

パターン3: 同種の呼び出しをバッチにまとめる

複数の監視対象・複数テナントの要約を、個別にLLMへ投げるのではなく一定時間分をまとめて1回のプロンプトで処理すると、プロンプトの固定部分(システムプロンプトや指示文)のオーバーヘッドを分散できます。

// 悪い例: 10件の検知イベントに対して10回LLMを呼ぶ
for (const finding of findings) {
  await llm.summarize(finding);
}

// 良い例: まとめて1回、構造化出力で受け取る
const summaries = await llm.summarizeBatch(findings); // 1回の呼び出しでN件分を返させる

ただしバッチ化は同一テナント内に限定します。テナントをまたいでプロンプトに混ぜると、他社の情報が同じコンテキストに乗ってしまい、マスキング設計が複雑化するうえテナント分離の原則にも反するため避けます。

パターン4: キャッシュ可能な入力は固定し、プロンプトキャッシュを効かせる

多くのLLM APIはプロンプトの先頭部分をキャッシュして安く再利用できます。常駐エージェントはシステムプロンプトやツール定義が呼び出しのたびに変わらないケースが多いため、変化する部分(差分データ)を末尾に配置し、固定部分を先頭にまとめるだけでキャッシュ命中率が上がり、実質コストが下がります。プロンプトの組み立て順を変えるだけで効くので、実装コストの割に効果が大きい対策です。

パターン5: コストをテナント単位で可視化し、外れ値を検知する

設計で防ぎきれない無駄呼び出しは必ず残るため、テナントごとの呼び出し回数・トークン量を集計し、平均から外れたテナントを検知する運用を仕組みに組み込みます。特定のテナントだけ異常にポーリングが多い場合、監視対象の設定ミス(不要なソースまで登録されている等)が原因であることが多く、コスト削減だけでなく設定不備の早期発見にもつながります。

まとめ

  • LLM呼び出しの要否判定はルールベースで先に行い、LLMには「差分の意味付け」だけをやらせる
  • 監視対象は重要度で階層化し、リアルタイム性が不要なものは頻度を落とす
  • 同一テナント内であれば呼び出しをバッチ化し、プロンプトの固定部分を先頭に置いてキャッシュを効かせる
  • テナント単位でコストを可視化し、外れ値から設定ミスを早期発見する

常駐型エージェントは「動き続けること」自体が価値なので、コスト制御を後回しにすると値段の面でスケールできなくなります。機能を作り込む前に、この5つのパターンを一度チェックしておくと後の負債を減らせます。


筆者は Slack / Teams / Chatwork に常駐する法人向けAIスタッフ「HACH」を開発しています。テナント数が増えてもコストが線形にしか増えないよう、まさにこの設計で運用しています。同種のエージェントを開発している方の参考になれば幸いです。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?