LLMに社内APIをちゃんと使わせるには — Toolformer論文を手がかりに“ツール利用データ”を自動で増やす
はじめに
ルミナイR&Dチームの栗原です。
「電卓も検索エンジンもあるのに、LLM がぜんぶ自分で考えようとしてミスる…」
- 桁の多い計算を自力でがんばって間違える
- 少し前のニュースをもっともらしく創作する
- 社内 API があるのに、うまく使うタイミングを選んでくれない
みたいな経験、かなり多いと思います。
Meta の Timo Schick らの論文
“Toolformer: Language Models Can Teach Themselves to Use Tools” は、このギャップに対して
「LLM 自身に、ツールの使い方データを自動で作らせて学習させる」
というアプローチを提案しています。
- 外部ツール(電卓・検索・翻訳など)への API 呼び出しを
- どこで、どんな引数で挟めば、次トークン予測が賢くなるか
- その「有益な API 呼び出し」だけを残して、再学習
することで、ツールをいい感じに使う LLM を作る、というアイデアです。
本記事では、この Toolformer 論文をベースに、
- 何をやっているのか(ざっくり数式レベル)
- 実務でどう役立つのか
- OpenAI API + Python で試せる
「簡易 Toolformer っぽいツール利用ラッパー」
をコンパクトにまとめます。
この記事で学べること
- Toolformer 論文が解決しようとしている 「ツール利用の学習」問題 の整理
- 「LLM 自身に API 呼び出しデータを作らせる」自己教師ありフロー
- 実務での落としどころ(フル再現ではなく設計の指針としての活かし方)
- OpenAI API + Python による、タグベースの ミニ Toolformer 風ツール利用実装
1. Toolformer が解決したい問題
論文冒頭では、LLM の限界としてざっくり次のような点が挙げられています。
- 最新情報にアクセスできない
- ファクトチェックが甘く、ハルシネーションしやすい
- 数値計算が苦手
- 時間の経過を理解していない
これらは、検索エンジン・電卓・カレンダー API などのツールを使えば、比較的簡単に解決できる種類の問題です。
では、なぜ「ツールを使える LLM」は簡単には作れないのか?
- 人手で「どの場面でどの API を呼ぶか」ラベルを付けるのはつらい
- 特定タスク用の「ツール付きモデル」は作れても、
- 横断的にいろんなツールを
- うまく使い分けられるようにするのは難しい
このギャップに対して Toolformer は、
LLM 自身の文脈理解能力を使って、
「ツール呼び出し付きコーパス」を自己生成 → 再学習
というフローを提案しています。
2. Toolformer のフローをざっくり

論文でやっていることを、実務でイメージしやすい形にざっくり落とすと、次の 3 ステップです。
- API 呼び出し位置と内容の候補をサンプリング
- 「本当に役に立つ呼び出し」だけをフィルタ
- 有益な API 呼び出しを埋め込んだデータで再学習
順番に見ていきます。
2.1 ステップ1:API 呼び出し候補をサンプリング
まず、普通のテキストコーパス(ニュース・会話など)に対して、
「ここで API を呼んだら役に立ちそう」という位置と内容
を、LLM にサンプリングさせます。
- 事前に少数のデモを用意しておき、
- 例:
「Pittsburgh is also known as <API>What other name is Pittsburgh known by?</API>」
- 例:
- コーパスの各位置に対して、
-
<API>トークンを出す確率を見て、 - 閾値以上の位置を「API 候補位置」として採用
-
- その位置ごとに、API の引数(クエリや式)候補を何個かサンプリング
ここでいう API は、例えば:
- 質問応答(QA)システム
- Wikipedia 検索
- 電卓
- 翻訳
- カレンダー
などを想定しています。
2.2 ステップ2:「役に立つ」呼び出しだけ残す
すべての API 呼び出し候補が役に立つとは限らないので、
「その API 呼び出しを挟んだときに、
未来のトークン予測がどれだけ楽になるか」
を指標にフィルタします。
もう少しだけ数式っぽく書くと、
- あるコーパス断片 (x) の位置 (i) に API 呼び出し (c_i) と結果 (r_i) を挿入したとき、
- その後のトークン列 (x_{i+1:i+L}) に対するクロスエントロピー損失を比較し、
[
\text{loss}(\text{without API}) - \text{loss}(\text{with } c_i, r_i) > \tau
]
となるような呼び出しだけを残す、
という形で「有益さ」を判定します。
- ざっくり言えば、「API を使った方が次の単語を当てやすくなるなら採用」
- そうでないもの(ノイズ)は捨てる
というイメージです。
2.3 ステップ3:API 呼び出し付きデータで再学習
最後に、
- フィルタ後のコーパス(テキスト + API 呼び出しとその結果)を教師データにして
- 元の言語モデルを再学習
することで、
「どの API を」「いつ」「どんな引数で」「どう結果を組み込むか」
を学ばせます。
実験では、6.7B パラメータの GPT-J をベースに、
- 電卓・QA・検索・翻訳・カレンダーなど複数ツールを組み合わせ、
- さまざまなベンチマークで、
- ツールなしの同サイズモデルよりもかなり良い性能
- ときには より大きなモデルにも対抗しうる性能
を達成したと報告されています。
3. 実務的にどう活かすか
Toolformer をそのまま再実装しようとすると、
- 大量のコーパス
- 自前の LM(微調整可能)
- トークンレベルの損失計算
などが必要になり、インフラ負荷がそれなりに重いです。
一方で、実務的には次のような観点が参考になります。
-
ツール呼び出しは、「プロンプト工学」だけでは限界がある
- いつ・どのツールを使うかを、人間が全部 if 文で書くのはスケールしにくい
-
API 呼び出しの“ログ”そのものが学習データとして重要
- どんな問い合わせに対して、どのツールが役立ったか
- どの引数が「次のトークン予測」を楽にしたか
-
ツール利用は「別モデル」ではなく「同じ LM の拡張」として学習できる
- モデル本体の言語能力を保ったまま、ツール利用だけ上乗せする
このあたりを踏まえると、たとえば:
- 初期フェーズでは
- 「プロンプトでツール用タグを出させる」
- 「タグ付きのログをためて分析する」
- その後、余裕が出たら
- 自前のオープンソース LM(LLaMA 系など)に
- よく使われるツールパターンを学習させる
といった段階的アプローチを取りやすくなります。
以下では、その「初期フェーズ」に相当する
タグベースの “簡易 Toolformer” っぽい実装を見ていきます。
4. Python でミニ Toolformer っぽいツール利用ラッパー
ここでは、OpenAI API と Python を使って、
「LLM が文中に
<calc>…</calc>や<faq>…</faq>などのタグを挟むと、
Python 側で実際にツールを呼び出して結果に置き換える」
という簡単なラッパーを実装してみます。
Toolformer のような自己教師あり学習はしていませんが、
- 「いつツールを使うか」は LLM 側の判断
- それを Python が実行&置換
という意味で、発想の雰囲気はかなり近いものになります。
4.1 前提
pip install --upgrade openai
export OPENAI_API_KEY="sk-..." # 自分のキーに置き換え
4.2 コード全体
mini_toolformer_wrapper.py のような名前を想定しています。
"""
mini_toolformer_wrapper.py
タグベースで「LLM がツール呼び出しを提案し、Python 側で実行する」ミニ実装。
- <calc>3 * (25 + 7)</calc> → Python の簡易電卓で評価
- <faq>請求書の再発行</faq> → 事前に用意したFAQから疑似検索
Toolformer の「いつ・何を呼ぶかはモデルが決める」発想を、
プロンプト+軽いパーサで再現するイメージです。
"""
import os
import re
from dataclasses import dataclass
from typing import Dict, Callable
from openai import OpenAI
MODEL = "gpt-4.1-mini" # 適宜変更可能
# ========= 1. ツール定義 =========
def safe_calc(expr: str) -> str:
"""
極めて簡易な「電卓」ツール。
eval は危険なので、本番では math パーサなどに置き換えること。
ここではデモのために簡略化。
"""
try:
# 数字と演算子以外を落とす(雑だけど最低限の防御)
if not re.fullmatch(r"[0-9+\-*/().\s]+", expr):
return f"[calc-error: unsafe expression]"
value = eval(expr, {"__builtins__": {}})
return str(value)
except Exception as e:
return f"[calc-error: {e}]"
FAQ_DB: Dict[str, str] = {
"請求書の再発行": "請求書の再発行は、管理画面の「請求」→「請求書一覧」から対象の請求書を選び、「再発行」をクリックしてください。",
"解約手続き": "解約は、サポート窓口へのご連絡が必要です。契約更新日の7営業日前までにお問い合わせください。",
}
def faq_search(query: str) -> str:
"""
疑似FAQ検索ツール。
実務ではベクタ検索やBM25に置き換えるイメージ。
"""
# ここでは雑に「完全一致 or 部分一致」を見るだけ
for key, answer in FAQ_DB.items():
if key in query or query in key:
return answer
return "[faq-not-found: 該当するFAQが見つかりませんでした]"
TOOLS: Dict[str, Callable[[str], str]] = {
"calc": safe_calc,
"faq": faq_search,
}
# ========= 2. タグ検出&ツール実行 =========
TAG_PATTERN = re.compile(r"<(?P<name>\w+)>(?P<body>.*?)</\1>", re.DOTALL)
def apply_tools(text: str) -> str:
"""
テキスト中の <tool>...</tool> タグを検出し、
対応する Python ツールで置き換える。
"""
def replacer(match: re.Match) -> str:
name = match.group("name")
body = match.group("body").strip()
if name not in TOOLS:
return f"[tool-error: unknown tool '{name}']"
fn = TOOLS[name]
result = fn(body)
return result
return TAG_PATTERN.sub(replacer, text)
# ========= 3. LLM 呼び出し =========
def create_client() -> OpenAI:
api_key = os.environ.get("OPENAI_API_KEY")
if not api_key:
raise RuntimeError("OPENAI_API_KEY が設定されていません。")
return OpenAI(api_key=api_key)
SYSTEM_PROMPT = """
あなたは外部ツールを使えるアシスタントです。
次の2種類のツールタグを使えます。
1. 電卓ツール:
- 形式: <calc>式</calc>
- 例: 3 * (25 + 7)
- 数学的な計算が必要なときだけ使ってください。
2. FAQ検索ツール:
- 形式: <faq>検索したいキーワードや質問</faq>
- 例: 請求書の再発行
- 社内FAQから該当しそうな回答を探したいときに使ってください。
ルール:
- 本当に必要なときだけツールタグを使い、不要なときは使わないでください。
- ツールの実行結果は、あなたの回答の一部として自然な文章に組み込まれます。
- ツールを直接説明するのではなく、「普通の回答」の中にタグを挿入してください。
"""
def ask_with_tools(question: str) -> str:
"""
LLM に質問を投げ、ツールタグを含む回答を生成させる。
その後 Python 側でツールを実行して、最終回答を返す。
"""
client = create_client()
resp = client.responses.create(
model=MODEL,
input=[
{"role": "system", "content": SYSTEM_PROMPT},
{"role": "user", "content": question},
],
temperature=0.4,
max_output_tokens=512,
)
raw = resp.output_text.strip()
final = apply_tools(raw)
print("=== [LLMの生の出力] ===")
print(raw)
print("\n=== [ツール適用後の最終回答] ===")
print(final)
return final
def main():
print("質問例1: 計算が必要なケース\n")
ask_with_tools("3年分のサブスクリプション料金として、月額 2,980 円を払うと合計はいくらになりますか? 日本語で丁寧に説明してください。")
print("\n" + "=" * 80 + "\n")
print("質問例2: FAQ検索が必要なケース\n")
ask_with_tools("請求書をなくしてしまったのですが、再発行の手順を教えてください。")
if __name__ == "__main__":
main()
4.3 実行結果イメージ
実際の出力はランダム性やプロンプトによって変わりますが、
ざっくりこんなイメージの動きになります。
例1:計算ツール
-
質問:
「3年分のサブスクリプション料金として、月額 2,980 円を払うと合計はいくらになりますか?」 -
LLM の生の出力(例):
3年間は36か月なので、合計金額は <calc>2980 * 36</calc> 円となります。したがって、3年間のサブスクリプション料金の総額は 107,280 円です。 -
ツール適用後の最終回答:
3年間は36か月なので、合計金額は 107280 円となります。したがって、3年間のサブスクリプション料金の総額は 107,280 円です。
ここで Toolformer 的なのは、
- 「計算が必要だ」と判断した LLM が、自分で
<calc>2980 * 36</calc>を挿入している - Python 側はそれを見て、電卓ツールを実行するだけ
という分業になっている点です。
例2:FAQ 検索ツール
-
質問:
「請求書をなくしてしまったのですが、再発行の手順を教えてください。」 -
LLM の生の出力(例):
請求書の再発行については、社内FAQを参照します。 <faq>請求書の再発行</faq> 上記の手順に従って操作していただければ、再発行が可能です。 -
ツール適用後の最終回答(FAQ_DB を使った場合):
請求書の再発行については、社内FAQを参照します。 請求書の再発行は、管理画面の「請求」→「請求書一覧」から対象の請求書を選び、「再発行」をクリックしてください。 上記の手順に従って操作していただければ、再発行が可能です。
ここでも、
- LLM 側が「ここで FAQ 検索を挟むとよさそう」と判断
- Python 側は、そのタグを見て FAQ 検索ツールを呼ぶ
という、Toolformer の構造とよく似た「役割分担」になっています。
5. どこまでやるかの見極め
最後に、実務で「ツール利用 LLM」を設計するときの視点をいくつか。
-
まずは「タグ+ラッパー」から始める
- 本記事のように、プロンプトでタグを出させて
- Python 側でツールを実行するだけでも、かなりのことができます
-
有用なタグ付きログをためる
- どの問いに対して、どのタグが出たか
- 実際にツールを使った結果、回答品質がどう変わるか
-
余裕があれば、自前 LM に「ツール付きコーパス」を食わせる
- LLaMA 系などオープンモデルを使う場合は、
- Toolformer や GPT4Tools, ToolAlpaca などの手法を参考に
- 自前ツールに合わせたデータセットを作る方向に進める
- LLaMA 系などオープンモデルを使う場合は、
Toolformer 論文のポイントは、
「ツールの使い方は、人間がルールベースで全部決めなくても、
LLM 自身に“どこでどう呼ぶと得か”を学ばせられる」
というところにあります。
本記事で紹介したミニ実装は、そのごく入口ですが、
- 「LLM 側はタグを出すだけ」
- 「Python 側はタグを見てツールを実行するだけ」
という構造を一度体験しておくと、
自社の API 群やワークフローに「ツール利用 LLM」を組み込むときの設計イメージが掴みやすくなるはずです。
【現在採用強化中です!】
- AIエンジニア
- PM/PdM
- 戦略投資コンサルタント
▼代表とのカジュアル面談URL
参考文献
-
Timo Schick, Jane Dwivedi-Yu, Roberto Dessì, Roberta Raileanu, Maria Lomeli, Luke Zettlemoyer, Nicola Cancedda, Thomas Scialom.
Toolformer: Language Models Can Teach Themselves to Use Tools.
arXiv:2302.04761, 2023.
https://arxiv.org/abs/2302.04761 -
Meta AI Blog:
Language Models Can Teach Themselves to Use Tools.
https://ai.meta.com/research/publications/toolformer-language-models-can-teach-themselves-to-use-tools/ -
Rui Yang et al.
GPT4Tools: Teaching Large Language Model to Use Tools via Self-instruction. arXiv:2305.18752, 2023.
https://arxiv.org/abs/2305.18752 -
Qiaoyu Tang et al.
ToolAlpaca: Generalized Tool Learning for Language Models with 3000 Simulated Cases. arXiv:2306.05301, 2023.
https://arxiv.org/abs/2306.05301
Discussion