【iOS27】FoundationModels入門②(Tool)

【iOS27】FoundationModels入門②(Tool)

Foundation ModelsのToolで自作ツールを作る方法について、実装例を交えて紹介します。
2026.09.28

はじめに

こんにちは。リテールアプリ共創部のYahiroです。

9月も下旬に入り、今年は梅雨が9月になったかのように雨が降りますね。
夏が終わったようで少し寂しい気がしますが、そろそろ衣替えをしていこうかと思います。

さて、Foundation ModelsにはiOS26からToolという仕組みが用意されています。

iOS27ではシステムツールとしてAppleがOCRTool・バーコードリーダ・端末内検索を用意してきましたが、今回は自作のツールを作って呼び出すところをやってみたいと思います。

この記事でわかること

  • Tool プロトコルで必須なのは description だけ
  • description はコメントではなく仕様書
  • @Generable で引数の形をモデルに伝える
  • 戻り値は「値」ではなく「意味」を返す

検証環境

この記事では、以下の環境で検証しています。

  • Mac mini(2024)
  • Apple M4
  • メモリ 16GB
  • macOS GoldenGate 27.0
  • Xcode 27.0

先に概要

自作のツールに書くのはざっくり以下の三つになります。

書くもの 役割
description モデルが読む説明
Arguments モデルが埋める引数
call(arguments:) 実際に動く処理

Tool プロトコルにはツール名を表す name もありますが、デフォルト実装があるので省略できます。省略すると型名がそのまま使われます。description の方はデフォルトがないので必須です。

ただし、instructionsやプロンプトの中で「SearchInventory ツールを使って」のようにツールを名前で指名する場合は、name を明示しておくと良いです。型名をそのまま使っていると、リファクタで型名を変えた瞬間にプロンプト側の指名が宙に浮いてしまいます。型名が長い場合に短い名前を見せたいときも同様です。

struct SearchInventoryTool: Tool {
    let name = "SearchInventory"
    let description = "商品名から、商品の在庫数を調べる"
    // ...
}

在庫をチェックするツールを作ってみる

ここでは、アパレルを例に商品の在庫をチェックするツールを作ってみます。


import Playgrounds
import FoundationModels

#Playground {
    let session = LanguageModelSession(
        tools: [SearchInventoryTool()],
        instructions: "店舗スタッフの質問にこたえてください"
    )

    let answer = try await session.respond(to: "トートバッグの在庫ある?")
    print(answer.content)
}

struct SearchInventoryTool: Tool {
    let description = "商品名から、商品の在庫数を調べる"

    @Generable
    struct Arguments {
        @Guide(description: "検索する商品名")
        let productName: String
    }

    func call(arguments: Arguments) async throws -> some PromptRepresentable {
        let stock = inventoryMock(name: arguments.productName)
        return "\(arguments.productName) の在庫は \(stock) 個です。"
    }
}

// なにを渡されても2しか返さないモック関数
func inventoryMock(name: String) -> Int {
    return 2
}

動かしてみると以下のような出力になりました。狙い通りですね。

スクリーンショット 2026-09-28 16.08.48

モデルはdescriptionを見て判断する

モデルはToolの中身を確認しません。そのため、descriptionに読んで使い方がわかるように説明を書く必要があります。
今回で言うと、「商品名から、商品の在庫数を調べる」まで書いているので、モデルはToolの意図を解釈し、使用していました。

しかし、例えば「在庫チェック」のようにシンプルに書いてしまうと、モデルはToolの意図を理解できず、渡されたプロンプトに対して該当のToolを使用しないことがあります。

なお、iOS27では GenerationOptions の toolCallingMode で呼び出しを制御できるようになりました。.required で必ずツールを呼ばせる、.disallowed で使わせない、という指定ができます。既定は .allowed で、この場合はモデルの判断に任されます。

// 必ずツールを呼ばせる
let answer = try await session.respond(
    to: "トートバッグの在庫ある?",
    options: GenerationOptions(toolCallingMode: .required)
)

@Generableで引数の形をモデルに教える

Argumentsに@Generableをつけると、モデルに引数の形を教えることができます。

今回で言うと以下の部分です。

@Generable
struct Arguments {
    @Guide(description: "検索する商品名")
    let productName: String
}

@Guideでは、そのプロパティが何なのかをモデルに説明することができます。
省略してもコンパイルは通りますが、モデルが何を入れればいいか分かるようになるので、設定しておくと引数の精度が上がります。

また、@Generableをつけると逆方向の変換も自動で行われます。モデルが埋めた値は Arguments 型になった状態で call(arguments:) に届くので、受け取ったものを自分でパースする必要はありません。

戻り値は「値」ではなく「意味」を返す

callの戻り値は、ユーザが受け取るものではなく、モデルが受け取るものです。

そのため、今回のコードで言うと単に2と返してしまうと、モデルは何の数字かを理解できません。

そこで、以下のように意味が通る文章で返すようにしましょう。

func call(arguments: Arguments) async throws -> some PromptRepresentable {
    let stock = inventoryMock(name: arguments.productName)
    // トートバッグの在庫は2個です。と返るようにする
    return "\(arguments.productName) の在庫は \(stock) 個です。"
}

まとめ

  • 自作のツールは description / Arguments / call の三つを書く
  • name は省略できる。description は必須
  • description はモデルがツールを使うかどうかを判断する唯一の材料
  • @Generable で引数の形が伝わり、モデルの出力から Arguments への変換も自動で行われる
  • 戻り値はユーザ向けではなくモデル向け。意味が分かる形で返す

最後に

今回は、アパレルを例に書きましたが、様々な業界でのドメイン特有の動きをToolとして組み込むことができそうです。

次回はダイナミックプロファイルについて触れていきます。値を返さずにアプリの状態を書き換えるToolを使うと、モデル自身が次の状態を選べるようになるので、そのあたりも合わせて見ていく予定です。

ではまた。


AI白書2026 配布中

クラスメソッドが独自に行なったAI診断調査をもとに、企業のAI活用の現在地を調査レポートとしてまとめました。企業規模別の活用度傾向に加え、規模を超えてAI活用を進める企業に共通する取り組みまで、自社の現在地を捉えるためのヒントにぜひ。

AI白書2026

無料でダウンロードする

この記事をシェアする

DevelopersIO 2026

関連記事