🐯

MastraでFunctionCallingを試してみた

に公開

背景

前回の記事でFunctionCallingを実装しました。
今回はTypescript ✕ Mastraを用いて、FunctionCallingを試してみました。


環境

項目 バージョン
OS Windows 10
ランタイム Node.js:22.04 + TypeScript
LLM Azure OpenAI:gpt-4.1-mini / 2024-12-01-preview

事前準備

  1. TypeScriptのテンプレート生成。
    # package.jsonの作成
    npm init -y
    # tsconfig.jsonの作成
    tsc --init
    
  2. 必要なlibraryのインストール。
    npm install @ai-sdk/azure@^1.3.23 @mastra/core@^0.10.4 dotenv@^16.5.0 --save
    
  3. TypeScriptで top levelでawaitを書ける様に、調整。
    1. package.json"type": "module"を追加。
    package.json
    "version": "1.0.0",
    "type": "module",
    
    1. tsconfig.jsonのモジュール設定を変更。
    tsconfig.json
    "target": "es2017",
    "module": "nodenext",
    "moduleResolution": "nodenext",
    

手順

1. 必要なライブラリと環境変数の読込

index.ts
import * as dotenv from 'dotenv'
import { createAzure } from "@ai-sdk/azure"
import { Agent, ToolsInput } from "@mastra/core/agent";
import { Mastra } from "@mastra/core"
import { ConsoleLogger } from "@mastra/core/logger";
dotenv.config()

2. toolで使う関数を定義

  1. 呼び出される関数を定義する。
index.ts
// 足し算をする関数
function add(a: number, b: number): number {
    return a + b;
}
// 掛け算をする関数
function multiplie(a: number, b: number): number {
    return a * b ;
}
  1. LLMに渡すToolを定義する。
index.ts
// Toolの定義
const addTool = {
    name: 'add',
    description: 'Adds two numbers',
    parameters: {
        type: "object",
        properties: {
            a: { type: "number", description: "First number" },
            b: { type: "number", description: "Second number" }
        },
        required: ["a", "b"]
    },
    execute: async (args: { a: number, b: number }) => add(args.a, args.b)
};

const multiTool = {
    name: 'multiplie',
    description: 'Multiplies two numbers',
    parameters: {
        type: "object",
        properties: {
            a: { type: "number", description: "First number" },
            b: { type: "number", description: "Second number" }
        },
        required: ["a", "b"]
    },
    execute: async (args: { a: number, b: number }) => multiplie(args.a, args.b)
};

// tools オブジェクトに追加
const tools: ToolsInput = {
    add: addTool,
    multiplie: multiTool
};

3. Mastraのエージェントを構築

  • AgentでLLMとの対話を実装。 複数処理をする場合の途中経過をログに出力する為に、Mastra経由でAgentを再取得する。
index.ts
// Azure OpenAIへの接続設定
const azure = createAzure({
    apiKey: process.env.AZURE_OPENAI_API_KEY,
    resourceName: process.env.AZURE_OPENAI_RESOURCE_NAME // Azure AI Foundary-> ホーム -> リソース構成 -> 名前
})

// Agentの設定
const mastraAgent = new Agent({
    name: "関数実行エージェント",
    instructions: "", 
    model: azure(process.env.AZURE_OPENAI_DEPLOYMENT_NAME!), // Azure AI Foundary-> デプロイ -> 名前
    tools: tools
});

// Mastraのインスタンスを作成
const mastra = new Mastra({
    agents: { mastraAgent },
    logger: new ConsoleLogger({ name: "Mastra", level: "debug" })
})
const mainAgent = mastra.getAgent("mastraAgent");

4. エージェントを実行

  • Agentにユーザプロンプトを渡し、実行
index.ts
const userMessage = "1+3*6は? ";
const result = await mastraAgent.generate(userMessage)
console.log("Agent Response:", result.text);

結果

  • ここまでで、作成したコードを実行すると、以下の様に出力されます。
Agent Response: 1 + 3 * 6 の計算結果は19です。
  • 実行ログを確認すると、以下の様に想定通りの計算順で関数が呼び出されている事が確認できます。
    • 見やすいように編集している為、実際の出力とは異なります。 実行ログ全文は別途記載しています。
[LLM] - Step Change: {
  toolCalls: [ {
      toolCallId: 'call_qmJbrQaOkz02RBpXXYngBYD5',
      toolName: 'multiplie', args: { a: 3, b: 6 }
    }, {
      toolCallId: 'call_I3PztD9eSocMg1RTb8WC1da4',
      toolName: 'add', args: { a: 1, b: 0 }
    }],
  toolResults: [ {
      toolName: 'multiplie', result: 18
    }, {
      toolName: 'add', result: 1
    }],
  finishReason: 'tool-calls',
}
[LLM] - Step Change: {
  toolCalls: [ {
      toolCallId: 'call_9WYLbsUMbI1lpuJKPjtF0mzw',
      toolName: 'add', args: { a: 1, b: 18 }
    }],
  toolResults: [ {
      toolName: 'add', result: 19
    }],
  finishReason: 'tool-calls',
}
実行ログ全文
Logger updated [component=AGENT] [name=関数実行エージェント]
[Agents:関数実行エージェント] initialized. {
  model: OpenAIChatLanguageModel {
    specificationVersion: 'v1',
    modelId: 'gpt-4.1-mini-2',
    settings: {},
    config: {
      provider: 'azure-openai.chat',
      url: [Function: url],
      headers: [Function: getHeaders],
      compatibility: 'strict',
      fetch: undefined
    }
  },
  name: '関数実行エージェント'
}
Logger updated [component=AGENT] [name=関数実行エージェント]
[Agents:関数実行エージェント] - Starting generation { runId: '4e70826c-4f95-44bd-8a41-e30a9fc2119f' }
[Agent:関数実行エージェント] - Enhancing tools:  {
  runId: '4e70826c-4f95-44bd-8a41-e30a9fc2119f',
  toolsets: undefined,
  clientTools: undefined,
  hasMemory: false,
  hasResourceId: false
}
[Agents:関数実行エージェント] - Assembling assigned tools {
  runId: '4e70826c-4f95-44bd-8a41-e30a9fc2119f',
  threadId: undefined,
  resourceId: undefined
}
The instructions property is deprecated. Please use getInstructions() instead.
[LLM] - Generating text {
  runId: '4e70826c-4f95-44bd-8a41-e30a9fc2119f',
  messages: [
    { role: 'system', content: '.' },
    { role: 'user', content: [Array] }
  ],
  maxSteps: 5,
  threadId: undefined,
  resourceId: undefined,
  tools: [ 'add', 'multiplie' ]
}
[Agent:関数実行エージェント] - Executing tool multiplie {
  name: 'multiplie',
  runId: '4e70826c-4f95-44bd-8a41-e30a9fc2119f',
  threadId: undefined,
  resourceId: undefined,
  agentName: '関数実行エージェント',
  model: OpenAIChatLanguageModel {
    specificationVersion: 'v1',
    modelId: 'gpt-4.1-mini-2',
    settings: {},
    config: {
      provider: 'azure-openai.chat',
      url: [Function: url],
      headers: [Function: getHeaders],
      compatibility: 'strict',
      fetch: undefined
    }
  },
  description: 'Multiplies two numbers',
  args: { a: 3, b: 6 }
}
[Agent:関数実行エージェント] - Executing tool add {
  name: 'add',
  runId: '4e70826c-4f95-44bd-8a41-e30a9fc2119f',
  threadId: undefined,
  resourceId: undefined,
  agentName: '関数実行エージェント',
  model: OpenAIChatLanguageModel {
    specificationVersion: 'v1',
    modelId: 'gpt-4.1-mini-2',
    settings: {},
    config: {
      provider: 'azure-openai.chat',
      url: [Function: url],
      headers: [Function: getHeaders],
      compatibility: 'strict',
      fetch: undefined
    }
  },
  description: 'Adds two numbers',
  args: { a: 1, b: 0 }
}
[LLM] - Step Change: {
  text: '',
  toolCalls: [
    {
      type: 'tool-call',
      toolCallId: 'call_qmJbrQaOkz02RBpXXYngBYD5',
      toolName: 'multiplie',
      args: [Object]
    },
    {
      type: 'tool-call',
      toolCallId: 'call_I3PztD9eSocMg1RTb8WC1da4',
      toolName: 'add',
      args: [Object]
    }
  ],
  toolResults: [
    {
      type: 'tool-result',
      toolCallId: 'call_qmJbrQaOkz02RBpXXYngBYD5',
      toolName: 'multiplie',
      args: [Object],
      result: 18
    },
    {
      type: 'tool-result',
      toolCallId: 'call_I3PztD9eSocMg1RTb8WC1da4',
      toolName: 'add',
      args: [Object],
      result: 1
    }
  ],
  finishReason: 'tool-calls',
  usage: { promptTokens: 91, completionTokens: 53, totalTokens: 144 },
  runId: '4e70826c-4f95-44bd-8a41-e30a9fc2119f'
}
[Agent:関数実行エージェント] - Executing tool add {
  name: 'add',
  runId: '4e70826c-4f95-44bd-8a41-e30a9fc2119f',
  threadId: undefined,
  resourceId: undefined,
  agentName: '関数実行エージェント',
  model: OpenAIChatLanguageModel {
    specificationVersion: 'v1',
    modelId: 'gpt-4.1-mini-2',
    settings: {},
    config: {
      provider: 'azure-openai.chat',
      url: [Function: url],
      headers: [Function: getHeaders],
      compatibility: 'strict',
      fetch: undefined
    }
  },
  description: 'Adds two numbers',
  args: { a: 1, b: 18 }
}
[LLM] - Step Change: {
  text: '',
  toolCalls: [
    {
      type: 'tool-call',
      toolCallId: 'call_9WYLbsUMbI1lpuJKPjtF0mzw',
      toolName: 'add',
      args: [Object]
    }
  ],
  toolResults: [
    {
      type: 'tool-result',
      toolCallId: 'call_9WYLbsUMbI1lpuJKPjtF0mzw',
      toolName: 'add',
      args: [Object],
      result: 19
    }
  ],
  finishReason: 'tool-calls',
  usage: { promptTokens: 159, completionTokens: 18, totalTokens: 177 },
  runId: '4e70826c-4f95-44bd-8a41-e30a9fc2119f'
}
[LLM] - Step Change: {
  text: '1 + 3 * 6 の計算結果は19です。',
  toolCalls: [],
  toolResults: [],
  finishReason: 'stop',
  usage: { promptTokens: 184, completionTokens: 17, totalTokens: 201 },
  runId: '4e70826c-4f95-44bd-8a41-e30a9fc2119f'
}
[Agent:関数実行エージェント] - Post processing LLM response {
  runId: '4e70826c-4f95-44bd-8a41-e30a9fc2119f',
  result: {
    text: '1 + 3 * 6 の計算結果は19です。',
    object: undefined,
    toolResults: [],
    toolCalls: [],
    usage: { promptTokens: 434, completionTokens: 88, totalTokens: 522 },
    steps: [ [Object], [Object], [Object] ]
  },
  threadId: undefined
}

まとめ

  • 今回はMastraを用いて、FunctionCallingを試してみましたが、関数を呼び出す部分も自動で実行されて少ないコード量で簡単に実装出来る事が出来ました。
比較:Mastra無しのFunctionCalling実装
  • 前回の記事と比べると、引数のpurseや関数を呼び出す部分やLLMを複数回呼び出している部分が減っています。
// 関数の呼び出し判定
const response = await openai.chat.completions.create ({
    model: "",
    messages: [{ role: "user", content: userMessage }],
    tools: tools,
    tool_choice: "auto", // 必要に応じてツールを呼び出す
});

const message = response.choices[0].message;
const toolCall =message.tool_calls[0];
const args = JSON.parse(toolCall.function.arguments);
const result = await getWeather(args.latitude, args.longitude);
// AIにツールの結果を渡して応答を得る
const finalResponse = await openai.chat.completions.create({
model: "",
messages: [
  { role: "user", content: userMessage }, // ユーザーメッセージ
  message, // AIのツール呼び出しリクエスト
  { 
    role: "tool", 
    tool_call_id: toolCall.id,
    content: result.toString()
  }, // ツールの実行結果
],
});
console.log(finalResponse.choices[0].message.content);
  • 呼び出す部分のコードは複雑化しやすいので、書かなくて済むのはとっても便利です!

Appendix

コード全文
index.ts
import * as dotenv from 'dotenv'
import { createAzure } from "@ai-sdk/azure"
import { Agent, ToolsInput } from "@mastra/core/agent";
import { Mastra } from "@mastra/core"
import { ConsoleLogger } from "@mastra/core/logger";
dotenv.config()

// 足し算をする関数
function add(a: number, b: number): number {
    return a + b;
}
// 掛け算をする関数
function multiplie(a: number, b: number): number {
    return a * b ;
}

// Toolの定義
const addTool = {
    name: 'add',
    description: 'Adds two numbers',
    parameters: {
        type: "object",
        properties: {
            a: { type: "number", description: "First number" },
            b: { type: "number", description: "Second number" }
        },
        required: ["a", "b"]
    },
    execute: async (args: { a: number, b: number }) => add(args.a, args.b)
};

const multiTool = {
    name: 'multiplie',
    description: 'Multiplies two numbers',
    parameters: {
        type: "object",
        properties: {
            a: { type: "number", description: "First number" },
            b: { type: "number", description: "Second number" }
        },
        required: ["a", "b"]
    },
    execute: async (args: { a: number, b: number }) => multiplie(args.a, args.b)
};

// tools オブジェクトに addTool を追加
const tools: ToolsInput = {
    add: addTool,
    multiplie: multiTool
};

const azure = createAzure({
    apiKey: process.env.AZURE_OPENAI_API_KEY,
    resourceName: process.env.AZURE_OPENAI_RESOURCE_NAME
})

const mastraAgent = new Agent({
    name: "関数実行エージェント",
    instructions: "", 
    model: azure(process.env.AZURE_OPENAI_DEPLOYMENT_NAME!),
    tools: tools
});

// Mastraのインスタンスを作成
const mastra = new Mastra({
    agents: { mastraAgent },
    logger: new ConsoleLogger({ name: "Mastra", level: "debug" })
})
const mainAgent = mastra.getAgent("mastraAgent");
const userMessage = "1+3*6は? ";
const result = await mastraAgent.generate(userMessage)
console.log("Agent Response:", result.text);
セリオ株式会社 テックブログ

Discussion