🌯

MCPクライアントに古典的な言語処理であるTF-IDFを組み込んだ話

に公開

Claudeによる記事の要約

この記事の要約を読む

MCPクライアントアプリでは、MCPサーバーからの大量応答がLLMのトークン消費を増加させ、課金額の増大を招く問題がある。この問題の解決策として、1970年代から存在するTF-IDF(Term Frequency-Inverse Document Frequency)を活用した要約手法を提案している。TF-IDFを使用することで、重要な文を機械的に選別し、情報損失を抑えながら大幅なサイズ削減を実現できるが、文脈の連続性や構造化データの処理には課題が残る。

MCPクライアントを作る上での課題

生成AI系のアプリを作ろうとすると、多くの場合で「トークン数が増えて課金額も増える」という問題に遭遇します。MCPクライアントアプリを作成する場合も例外ではありません。

例えば、以前紹介したこちらの記事では MCPツール定義のトークン数多すぎ問題 について記載しました。
https://zenn.dev/ncdc/articles/26165a6fedd7e4
このときはPrompt Cachingを使うことでコストを削減しました。

しかし、MCPクライアントで発生するトークン絡みの問題は他にもあり、Cacheだけでは解決できないものもあります。

MCPサーバーの応答でかすぎ問題

MCPサーバーの応答が大きすぎる場合があるという問題があります。MCPサーバーからの応答は通常そのままLLMに渡しますので、すなわち入力トークン数が多くなって課金額が増えます。
もちろんお行儀の良いMCPサーバーであれば大量のデータを送りつけてくることはありません。しかし、MCPは生成AI界のUSB-Cを名乗るほどの汎用性をアピールしているのですから、多少お行儀の悪いMCPサーバー相手でも使えるようにするべきでしょう。

この問題は単純であるが故に解決の難易度が高いです。例えば、こちらは5ヶ月も前に書いた記事になりますが、当時の環境ではあのGitHub Copilotですら、MCPサーバーから大量のデータを送りつけられることで、無限ループというトークンの消費量以前の問題が発生しました。
https://zenn.dev/ncdc/articles/a11b1a38eaaf6e

解決策を考える

この問題の対策として、まず単純なところだと以下が思い浮かびました。

  1. 生成AIに要約させる
  2. 一定文字数で強制的に応答を切り捨てる

しかし、1に関しては、そもそもの目的が生成AIのトークン数を減らすことなのに、要約のためにトークンが必要になるので本末転倒です。安いモデルを使ったり工夫の余地もありますが、そうすると処理が複雑になるのが好ましくありませんし、処理時間が長くなる問題もあります。
2に関しては、生成AIのトークン数を減らすことには成功しますが、MCPサーバーの応答内容を大きく損なうことになります。どうしようもない時の最終手段とするべきでしょう。

というあたりで対応に詰まったので、Claudeに解決策を相談したところ「TF-IDFを使ったらどうだろうか?」という提案があったため導入してみました。

TF-IDFとは?

TF-IDF(Term Frequency-Inverse Document Frequency)は、文書内の単語の重要度を数値化する手法です。自然言語処理の分野で古くから使われており、検索エンジンや文書分類などで活用されています。

TF-IDFは2つの要素から成り立っています。

  • TF(単語頻度)
    • 文書内でよく出現する単語ほど重要
  • IDF(逆文書頻度)
    • 他の文書にはあまり出現しない単語ほど重要

この2つを掛け合わせることで、「その文書を特徴づける重要な単語」を特定できます。

TF-IDFをMCPサーバーの応答の要約へ適用する

MCPサーバーからの大量応答に対して、TF-IDFを適用する処理をざっくり説明すると以下のようになります。

  1. 応答を文単位で分割
  2. 各文のTF-IDFスコアを計算し、全体における重要度を判定
  3. 重要度の高い文を優先的に選択
  4. 目標文字数以内に収めて再構成

TF-IDFを使った要約は、古典的な手法であるが故に、次のようなメリットがあります。

  • 簡単に実装できるライブラリがある
  • 動作が早い(0.1秒未満)
  • 無料

TF-IDFによる要約の実装

TF-IDFを使った要約は前述の通り簡単に書くことができます。
今回はscikit-learnというライブラリを使って実装しました。具体的には以下のようなコードになります。

サンプルコードはこちら

このサンプルを動かすには、scikit-learnnumpyが必要です。

pip install scikit-learn numpy
python tfidf_sample.py

実際にサンプルを動かしてみると、一瞬で文章が要約されるのが分かると思います。

実行結果

=== TF-IDFテキスト圧縮デモ ===

元のテキスト:
機械学習は人工知能の一分野であり、データから学習する技術です。現代のAI技術の基盤となっています。
この技術により、明示的にプログラムされることなく、パターンを発見できます。統計的手法を用いて予測を行います。
TF-IDFは文書検索や自然言語処理でよく使われる手法です。単語の重要度を数値化する代表的なアルゴリズムです。
Term Frequency-Inverse Document Frequencyの略で、文書内頻度と逆文書頻度を組み合わせます。情報検索の精度向上に貢献します。
文書内での単語の出現頻度と、全文書での希少性を計算に使用します。重要な特徴語を自動的に抽出できます。
これにより、文書の特徴を効果的に表現することができます。ベクトル空間モデルの基礎技術として活用されています。
機械学習のアルゴリズムには教師あり学習、教師なし学習、強化学習があります。それぞれ異なる学習パラダイムを持ちます。
教師あり学習では正解データを使って学習を行います。分類や回帰問題に適用されます。
教師なし学習は正解なしでパターンを発見します。クラスタリングや次元削減に使われます。
強化学習は環境との相互作用から学習します。ゲームやロボット制御で成功を収めています。
実際のプロジェクトでは、問題の性質に応じて適切な手法を選択することが重要です。データの特性と目的を考慮する必要があります。

各文のTF-IDFスコア:

  1. [0.522] 機械学習は人工知能の一分野であり、データから学習する技術です...
  2. [0.302] 現代のAI技術の基盤となっています...
  3. [0.674] この技術により、明示的にプログラムされることなく、パターンを発見できます...
  4. [0.302] 統計的手法を用いて予測を行います...
  5. [0.522] TF-IDFは文書検索や自然言語処理でよく使われる手法です...
  6. [0.302] 単語の重要度を数値化する代表的なアルゴリズムです...
  7. [1.000] Term Frequency-Inverse Document Frequencyの略で、文書内頻度...
  8. [0.302] 情報検索の精度向上に貢献します...
  9. [0.522] 文書内での単語の出現頻度と、全文書での希少性を計算に使用します...
  10. [0.302] 重要な特徴語を自動的に抽出できます...
  11. [0.522] これにより、文書の特徴を効果的に表現することができます...
  12. [0.302] ベクトル空間モデルの基礎技術として活用されています...
  13. [0.674] 機械学習のアルゴリズムには教師あり学習、教師なし学習、強化学習があります...
  14. [0.302] それぞれ異なる学習パラダイムを持ちます...
  15. [0.302] 教師あり学習では正解データを使って学習を行います...
  16. [0.302] 分類や回帰問題に適用されます...
  17. [0.302] 教師なし学習は正解なしでパターンを発見します...
  18. [0.302] クラスタリングや次元削減に使われます...
  19. [0.302] 強化学習は環境との相互作用から学習します...
  20. [0.302] ゲームやロボット制御で成功を収めています...
  21. [0.522] 実際のプロジェクトでは、問題の性質に応じて適切な手法を選択することが重要です...
  22. [0.302] データの特性と目的を考慮する必要があります...

元の文字数: 597
圧縮後文字数: 298
圧縮率: 49.9%
処理時間: 0.001秒

圧縮されたテキスト:
機械学習は人工知能の一分野であり、データから学習する技術です この技術により、明示的にプログラムされることなく、パターンを発見できます TF-IDFは文書検索や自然言語処理でよく使われる手法です Term Frequency-Inverse Document Frequencyの略で、文書内頻度と逆文書頻度を組み合わせます 文書内での単語の出現頻度と、全文書での希少性を計算に使用します これにより、文書の特徴を効果的に表現することができます 機械学習のアルゴリズムには教師あり学習、教師なし学習、強化学習があります 実際のプロジェクトでは、問題の性質に応じて適切な手法を選択することが重要です

TF-IDFは重要な部分を機械的に判断できますので、単純な切り捨てと違って、情報の損失を抑えながら、素早くサイズ削減が可能です。

課題

当然ですが、この方法にも問題はあります。

  • 文脈は一切考慮されない
    • 文単位で重要そうな項目を機械的に選択されるので、連続性がなくなり文脈が飛ぶことがあります
  • 構造化データに弱い
    • JSONなどの構造化データに対しては、うまく要約できません
    • 例えば、slackのユーザー一覧取得などは要約処理によって完全に応答が破壊されてしまったので、要約から除外する機能を追加したりもしました

そもそもが要約なので元の情報を完全に維持するのはできませんが、今後はより情報の損失が少ない手法も探っていきたいです。

MCPサーバーの作成者さまへのお願い

今の世の中は便利なMCPサーバーが多数公開されていてありがたい限りです。公式/非公式問わず作っていただいた作成者の皆様ありがとうございます。
しかし、以下のようなことも考慮してもらえると、利用者側の負荷が減るので、より助かります。

  • ツールの応答のサイズは抑えて欲しいです。多くても数千文字程度に抑えてもらえると助かります。
  • ツールの説明はLLMが理解しやすいプロンプトにしていただけると助かります。できれば利用者側で説明文をカスタマイズできると嬉しいです。

MCPサーバーを作るとき、まずはAPIを単純に変換することも多いんじゃないかなと思います。まずは公開されること自体がありがたいので、最初はそれでいいと思っています。しかし、本来はMCPサーバーとAPIは全く別なものです。作って公開しただけで満足せずに、より使いやすいMCPサーバーにブラッシュアップしてもらえるとありがたいです。
APIの単純変換では、応答サイズ以外にも精度悪化やコスト増に繋がるポイントがあるので、参考記事を貼っておきます。
https://memo.tyoshida.me/power-platform/copilot-studio/mcp-is-not-api/

また、最近Anthropicから凄くいい記事が出ましたので、そちらも貼っておきます。
https://www.anthropic.com/engineering/writing-tools-for-agents

さいごに

MCPはその汎用性の高さ故に非常に便利な存在ですが、一方でお行儀の悪いMCPサーバーも数多く存在します。MCPサーバーの応答が大きすぎると、LLMが消費するトークン数が増え、コスト増に繋がります。
最新技術である生成AIやMCPに関するこのような課題解決に、1970年代から存在する古典的な技術であるTF-IDFが使えたのは、なかなか面白い話ではないでしょうか。

NCDCエンジニアブログ

Discussion