第10章 Function calling で外部の処理を呼び出す
本章は初稿です。
10.1 この章で学ぶこと
これまでの Agent は、渡された言葉から応答していました。ここに、モデルの外で情報を取得する関数を追加します。最初の関数は、架空の社内手順を検索する search_knowledge です。
到達点は、ツールの定義、モデルからの要求、実際の関数実行、結果の返却を区別することです。実習では外部サービスへの接続を必須にせず、ローカル関数で往復の仕組みを学びます。ここでいう「外部」は、まずモデルの外側という意味です。
10.2 要求、実行、結果の往復を追う
Function calling は、モデルが関数名と引数を含む要求を返し、アプリケーションが実行して結果を返す仕組みです。関数の名前を定義しただけでは、検索処理は実行されません。[^function-calling]
図10-1 モデルの要求と、検索の実行を分ける。 時間は上から下へ進み、実線は入力・実行依頼、破線は出力です。図は検索 API に接続する概念例ですが、本章の実習では検索部分をローカル関数へ置き換えます。サービス側で実行されるツールもあり、すべての Foundry ツールがこの方式で動くわけではありません。
図中の「回答案と引用を確認」は、第12章で行う評価に相当します。第13章の学習コードに、回答内容の自動判定まで実装したという意味ではありません。
10.3 ツールの契約を決める
モデルへ渡す定義と、実行するプログラムの契約を対応付けます。今回は検索語だけをモデルの引数とし、利用者の権限は含めません。
| 項目 | 学習用の設計 | 意味 |
|---|---|---|
| 名前 | search_knowledge | 呼び出す関数を識別する |
| 説明 | 閲覧可能な架空の IT 手順を検索する | どんな要求で使うかを伝える |
| 引数 | 必須の文字列 query | アプリが検証する検索語 |
| 結果 | 文書 ID、タイトル、抜粋、参照先 | 回答に使う根拠 |
| 実行側の情報 | 認証済み利用者のグループ | アプリが保持し、モデルに決めさせない |
| 失敗 | 不正入力や検索障害として明示する | 文書がないことと混同しない |
次は SDK の FunctionTool を使う定義の断片です。第13章で Agent の定義と結び付けます。独自の検索契約であり、Foundry に標準で備わる同名の関数ではありません。
from azure.ai.projects.models import FunctionTool
search_tool = FunctionTool(
name="search_knowledge",
description=(
"学習用 IT 手順を検索します。query は VPN Windows のように、"
"少数のキーワードを空白で区切って指定してください。"
),
parameters={
"type": "object",
"properties": {
"query": {"type": "string", "description": "検索キーワード"}
},
"required": ["query"],
"additionalProperties": False,
},
strict=True,
)
この例は通常の Prompt エージェントの公式例に沿います。別の harness や API では strict などの対応条件が異なるため、そのまま流用しません。SDK の設定があっても、実行側の入力検証は残します。[^function-calling]
10.4 モデルを呼ばずに検索関数を確かめる
次を、学習用作業ディレクトリの knowledge.py として保存する想定です。Python 標準ライブラリだけを使います。文書と参照先は架空で、.invalid の URL は実在する手順書ではありません。
from dataclasses import dataclass
@dataclass(frozen=True)
class Document:
document_id: str
title: str
passage: str
source: str
allowed_groups: frozenset[str]
DOCUMENTS = (
Document(
"vpn-windows",
"Windows の VPN 確認手順",
"学習用手順: Windows では接続先とエラー表示を確認します。",
"https://kb.example.invalid/vpn-windows",
frozenset({"employees"}),
),
Document(
"vpn-macos",
"macOS の VPN 確認手順",
"学習用手順: macOS では接続状態とエラー表示を確認します。",
"https://kb.example.invalid/vpn-macos",
frozenset({"employees"}),
),
Document(
"vpn-admin",
"VPN 管理者向け手順",
"学習用の管理者専用資料です。一般社員向けには返しません。",
"https://kb.example.invalid/vpn-admin",
frozenset({"admins"}),
),
)
def search_knowledge(
query: str, *, user_groups: frozenset[str]
) -> list[dict[str, str]]:
if not isinstance(query, str) or not query.strip() or len(query) > 200:
raise ValueError("query must be a non-empty string of at most 200 characters")
if not user_groups:
raise PermissionError("Trusted user groups are required")
terms = query.casefold().split()
hits = []
for document in DOCUMENTS:
if not document.allowed_groups.intersection(user_groups):
continue
searchable = f"{document.title} {document.passage}".casefold()
if all(term in searchable for term in terms):
hits.append(
{
"document_id": document.document_id,
"title": document.title,
"passage": document.passage,
"source": document.source,
}
)
return hits
検索は、空白区切りの全キーワードが含まれるかを見るだけです。日本語の形態素解析やベクトル検索ではありません。200文字という上限も教材独自の例であり、Foundry の制限値ではありません。
user_groups は、学習中は自分で用意するテスト用の認証済み文脈です。本番では、利用者が入力したグループ名を信頼する仕組みにしてはいけません。例のグループとの交差判定は、実際の組織の認可要件をすべて満たす実装ではありません。
10.5 呼び出しと結果を対応付ける
SDK が返す関数要求には、関数名、JSON 文字列としての引数、call_id などがあります。結果は同じ call_id の function_call_output として返します。[^function-calling] 関数名と結果だけを会話文へ追加すると、要求との対応が失われます。
実行側の処理を疑似コードで表すと、次のようになります。
応答を受け取る
関数要求がなければ、最終的な出力を確認する
関数要求があれば、許可された関数名かを確認する
JSON を読み、必須項目・型・余計な項目を検証する
アプリが保持する利用者の文脈で関数を実行する
要求ごとの call_id と結果を、同じ会話へ返す
次の応答を受け取り、上限付きで繰り返す
想定外の関数を任意のコードとして実行してはいけません。関数名からの eval や、引数で任意の URL・認証主体を指定できる仕組みは必要ありません。
10.6 小さな実習と確認課題
まず関数だけを直接呼び、期待結果を確認します。この断片は knowledge.py と同じ作業ディレクトリから試します。
from knowledge import search_knowledge
groups = frozenset({"employees"})
hits = search_knowledge("VPN Windows", user_groups=groups)
assert [hit["document_id"] for hit in hits] == ["vpn-windows"]
assert search_knowledge("VPN 管理者", user_groups=groups) == []
assert search_knowledge("存在しない製品", user_groups=groups) == []
続いて、空の検索語、権限文脈なし、想定外の引数を与えた場合を考えます。正しい要求で該当がないことと、検索を実行できないことは別です。検索障害を空の結果へ置き換えないようにしてください。
最後に「モデルが検索を要求したが、アプリが関数を実行しなかった」場合を説明します。その場合、社内文書を取得した証拠はありません。第13章で SDK の往復を組み立てる前に、この区別が説明できれば本章の目標を満たします。
10.7 まとめ
ツールは、定義と実装、実行側の検証、呼び出しに対応した結果の返却で成り立ちます。次章では、検索結果を単に表示するのではなく、回答の根拠として使う RAG を学びます。
10.8 参考情報
- Use function calling with Microsoft Foundry agents — 関数定義、要求、結果返却を SDK で扱う手順
- Build with runtime components in Foundry Agent Service — 応答に含まれるツール要求と会話の関係
[^function-calling]: Use function calling with Microsoft Foundry agents — Microsoft