大規模言語モデル (LLM) は、リアルタイム性が求められるタスクや数学的な計算が苦手な場合があります。関数呼び出しは、外部ツールを統合することでこの制限に対処し、LLM が単独では処理できない質問に回答できるようにします。
仕組み
関数呼び出しにより、アプリケーションと LLM の間で複数ステップの対話が可能になります。これにより、LLM は応答を生成する際に外部ツールの情報を参照できます。
最初のモデル呼び出し
アプリケーションが LLM にリクエストを送信します。リクエストには、ユーザーの質問とモデルが呼び出せるツールのリストが含まれます。
モデルからのツール呼び出し命令 (ツール名と入力パラメーター) の受信
モデルが外部ツールが必要だと判断した場合、実行する関数と渡すパラメーターを指定した JSON 形式の命令を返します。
モデルがツールは不要だと判断した場合は、自然言語の応答を返します。
アプリケーションでのツールの実行
ツール命令を受信した後、アプリケーションはツールを実行し、その出力を取得します。
2 回目のモデル呼び出し
ツールの出力を取得した後、それをモデルのコンテキスト (メッセージ) に追加し、再度モデルを呼び出します。
モデルからの最終応答の受信
モデルはツールの出力とユーザーの質問を組み合わせて、自然言語の応答を生成します。
ワークフロー図を以下に示します。
サポート対象モデル
Qwen
DeepSeek
deepseek-v3.2
deepseek-v3.2-exp (ノンシンキングモード)
deepseek-v3.1 (ノンシンキングモード)
deepseek-r1
deepseek-r1-0528
deepseek-v3
GLM
glm-5
glm-4.7
glm-4.6
Kimi
kimi-k2.5
kimi-k2-thinking
Moonshot-Kimi-K2-Instruct
MiniMax
MiniMax-M2.5
クイックスタート
API キーを取得し、環境変数として設定している必要があります (この方法は非推奨となり、API キーの設定に統合されています)。OpenAI SDK または DashScope SDK を介して呼び出しを行う場合は、SDK をインストールする必要があります。
この例では、関数呼び出しを使用して天気を検索する方法を示します。
OpenAI 互換
from openai import OpenAI
from datetime import datetime
import json
import os
import random
client = OpenAI(
# API キーはリージョンによって異なります。API キーの取得方法:https://www.alibabacloud.com/help/model-studio/get-api-key
# 環境変数を設定していない場合は、次の行を Model Studio API キーに置き換えてください: api_key="sk-xxx"
api_key=os.getenv("DASHSCOPE_API_KEY"),
# 北京リージョンの場合は、https://dashscope.aliyuncs.com/compatible-mode/v1 を使用します
base_url="https://dashscope-intl.aliyuncs.com/compatible-mode/v1",
)
# ユーザーの質問をシミュレート
USER_QUESTION = "シンガポールの天気は?"
# ツールリストを定義
tools = [
{
"type": "function",
"function": {
"name": "get_current_weather",
"description": "特定の都市の天気を調べたいときに便利です。",
"parameters": {
"type": "object",
"properties": {
"location": {
"type": "string",
"description": "市や県。例:シンガポール、ニューヨーク。",
}
},
"required": ["location"],
},
},
},
]
# 天気検索ツールをシミュレート
def get_current_weather(arguments):
weather_conditions = ["sunny", "cloudy", "rainy"]
random_weather = random.choice(weather_conditions)
location = arguments["location"]
return f"今日の {location} は {random_weather} です。"
# モデル応答関数をラップ
def get_response(messages):
completion = client.chat.completions.create(
model="qwen-plus",
messages=messages,
tools=tools,
)
return completion
messages = [{"role": "user", "content": USER_QUESTION}]
response = get_response(messages)
assistant_output = response.choices[0].message
if assistant_output.content is None:
assistant_output.content = ""
messages.append(assistant_output)
# ツール呼び出しが不要な場合は、コンテンツを直接出力
if assistant_output.tool_calls is None:
print(f"天気ツールの呼び出しは不要です。直接の返信:{assistant_output.content}")
else:
# ツール呼び出しループに入る
while assistant_output.tool_calls is not None:
tool_call = assistant_output.tool_calls[0]
tool_call_id = tool_call.id
func_name = tool_call.function.name
arguments = json.loads(tool_call.function.arguments)
print(f"ツール [{func_name}] を引数:{arguments} で呼び出しています")
# ツールを実行
tool_result = get_current_weather(arguments)
# ツール応答を構築
tool_message = {
"role": "tool",
"tool_call_id": tool_call_id,
"content": tool_result, # 元のツール出力を保持
}
print(f"ツールからの返り値:{tool_message['content']}")
messages.append(tool_message)
# モデルを再度呼び出して、要約された自然言語の返信を取得
response = get_response(messages)
assistant_output = response.choices[0].message
if assistant_output.content is None:
assistant_output.content = ""
messages.append(assistant_output)
print(f"アシスタントからの最終返信:{assistant_output.content}")import OpenAI from 'openai';
// クライアントを初期化
const openai = new OpenAI({
apiKey: process.env.DASHSCOPE_API_KEY,
// 北京リージョンの場合は、https://dashscope.aliyuncs.com/compatible-mode/v1 を使用します
baseURL: "https://dashscope-intl.aliyuncs.com/compatible-mode/v1",
});
// ツールリストを定義
const tools = [
{
type: "function",
function: {
name: "get_current_weather",
description: "特定の都市の天気を調べたいときに便利です。",
parameters: {
type: "object",
properties: {
location: {
type: "string",
description: "市や県。例:シンガポール、ニューヨーク。",
},
},
required: ["location"],
},
},
},
];
// 天気検索ツールをシミュレート
const getCurrentWeather = (args) => {
const weatherConditions = ["sunny", "cloudy", "rainy"];
const randomWeather = weatherConditions[Math.floor(Math.random() * weatherConditions.length)];
const location = args.location;
return `今日の ${location} は ${randomWeather} です。`;
};
// モデル応答関数をラップ
const getResponse = async (messages) => {
const response = await openai.chat.completions.create({
model: "qwen-plus",
messages: messages,
tools: tools,
});
return response;
};
const main = async () => {
const input = "シンガポールの天気は?";
let messages = [
{
role: "user",
content: input,
}
];
let response = await getResponse(messages);
let assistantOutput = response.choices[0].message;
// content が null でないことを確認
if (!assistantOutput.content) assistantOutput.content = "";
messages.push(assistantOutput);
// ツール呼び出しが必要か確認
if (!assistantOutput.tool_calls) {
console.log(`天気ツールの呼び出しは不要です。直接の返信:${assistantOutput.content}`);
} else {
// ツール呼び出しループに入る
while (assistantOutput.tool_calls) {
const toolCall = assistantOutput.tool_calls[0];
const toolCallId = toolCall.id;
const funcName = toolCall.function.name;
const funcArgs = JSON.parse(toolCall.function.arguments);
console.log(`ツール [${funcName}] を引数:`, funcArgs, `で呼び出しています`);
// ツールを実行
const toolResult = getCurrentWeather(funcArgs);
// ツール応答を構築
const toolMessage = {
role: "tool",
tool_call_id: toolCallId,
content: toolResult,
};
console.log(`ツールからの返り値:${toolMessage.content}`);
messages.push(toolMessage);
// モデルを再度呼び出して、自然言語の要約を取得
response = await getResponse(messages);
assistantOutput = response.choices[0].message;
if (!assistantOutput.content) assistantOutput.content = "";
messages.push(assistantOutput);
}
console.log(`アシスタントからの最終返信:${assistantOutput.content}`);
}
};
// プログラムを実行
main().catch(console.error);DashScope
import os
from dashscope import Generation
import dashscope
import json
import random
# 北京リージョンの場合は、https://dashscope.aliyuncs.com/api/v1 を使用します
dashscope.base_http_api_url = 'https://dashscope-intl.aliyuncs.com/api/v1'
# 1. ツールリストを定義
tools = [
{
"type": "function",
"function": {
"name": "get_current_weather",
"description": "特定の都市の天気を調べたいときに便利です。",
"parameters": {
"type": "object",
"properties": {
"location": {
"type": "string",
"description": "市や県。例:シンガポール、ニューヨーク。",
}
},
"required": ["location"],
},
},
}
]
# 2. 天気検索ツールをシミュレート
def get_current_weather(arguments):
weather_conditions = ["sunny", "cloudy", "rainy"]
random_weather = random.choice(weather_conditions)
location = arguments["location"]
return f"今日の {location} は {random_weather} です。"
# 3. モデル応答関数をラップ
def get_response(messages):
response = Generation.call(
# API キーはリージョンによって異なります。API キーの取得方法:https://www.alibabacloud.com/help/model-studio/get-api-key
# 環境変数を設定していない場合は、次の行を api_key="sk-xxx" に置き換えてください
api_key=os.getenv("DASHSCOPE_API_KEY"),
model="qwen-plus",
messages=messages,
tools=tools,
result_format="message",
)
return response
# 4. 会話履歴を初期化
messages = [
{
"role": "user",
"content": "シンガポールの天気は?"
}
]
# 5. 最初のモデル呼び出し
response = get_response(messages)
assistant_output = response.output.choices[0].message
messages.append(assistant_output)
# 6. ツール呼び出しが必要か確認
if "tool_calls" not in assistant_output or not assistant_output["tool_calls"]:
print(f"ツール呼び出しは不要です。直接の返信:{assistant_output['content']}")
else:
# 7. ツール呼び出しループに入る
# ループ条件:最新のモデル応答にツール呼び出しリクエストが含まれている間、継続する
while "tool_calls" in assistant_output and assistant_output["tool_calls"]:
tool_call = assistant_output["tool_calls"][0]
# ツール呼び出し情報を解析
func_name = tool_call["function"]["name"]
arguments = json.loads(tool_call["function"]["arguments"])
tool_call_id = tool_call.get("id") # tool_call_id を取得
print(f"ツール [{func_name}] を引数:{arguments} で呼び出しています")
# 対応するツール関数を実行
tool_result = get_current_weather(arguments)
# ツール応答を構築
tool_message = {
"role": "tool",
"content": tool_result,
"tool_call_id": tool_call_id
}
print(f"ツールからの返り値:{tool_message['content']}")
messages.append(tool_message)
# ツール結果に基づいて返信するためにモデルを再度呼び出す
response = get_response(messages)
assistant_output = response.output.choices[0].message
messages.append(assistant_output)
# 8. 最終的な自然言語の返信を出力
print(f"アシスタントからの最終返信:{assistant_output['content']}")import com.alibaba.dashscope.aigc.generation.Generation;
import com.alibaba.dashscope.aigc.generation.GenerationParam;
import com.alibaba.dashscope.aigc.generation.GenerationResult;
import com.alibaba.dashscope.common.Message;
import com.alibaba.dashscope.common.Role;
import com.alibaba.dashscope.protocol.Protocol;
import com.alibaba.dashscope.exception.InputRequiredException;
import com.alibaba.dashscope.exception.NoApiKeyException;
import com.alibaba.dashscope.tools.FunctionDefinition;
import com.alibaba.dashscope.tools.ToolCallBase;
import com.alibaba.dashscope.tools.ToolCallFunction;
import com.alibaba.dashscope.tools.ToolFunction;
import com.alibaba.dashscope.utils.JsonUtils;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Random;
public class Main {
/**
* ツールのローカル実装。
* @param arguments モデルから渡されたツールパラメーターを含む JSON 文字列。
* @return ツール実行後の文字列結果。
*/
public static String getCurrentWeather(String arguments) {
try {
// モデルからのパラメーターは JSON 形式であり、手動で解析する必要があります。
ObjectMapper objectMapper = new ObjectMapper();
JsonNode argsNode = objectMapper.readTree(arguments);
String location = argsNode.get("location").asText();
// 実際の API 呼び出しやビジネスロジックをシミュレートするためにランダムな結果を使用します。
List<String> weatherConditions = Arrays.asList("sunny", "cloudy", "rainy");
String randomWeather = weatherConditions.get(new Random().nextInt(weatherConditions.size()));
return location + " is " + randomWeather + " today.";
} catch (Exception e) {
// 堅牢性を確保するための例外処理。
return "Failed to parse location parameter.";
}
}
public static void main(String[] args) {
try {
// ツールをモデルに登録します。
String weatherParamsSchema =
"{\"type\":\"object\",\"properties\":{\"location\":{\"type\":\"string\",\"description\":\"市や県。例:シンガポール、ニューヨーク。\"}},\"required\":[\"location\"]}";
FunctionDefinition weatherFunction = FunctionDefinition.builder()
.name("get_current_weather") // ツールのユニークな識別子。ローカル実装と一致する必要があります。
.description("特定の都市の天気を調べたいときに便利です。") // 明確な説明は、モデルがいつツールを使用するかを決定するのに役立ちます。
.parameters(JsonUtils.parseString(weatherParamsSchema).getAsJsonObject())
.build();
// 北京リージョンの場合は、https://dashscope.aliyuncs.com/api/v1 を使用します
Generation gen = new Generation(Protocol.HTTP.getValue(), "https://dashscope-intl.aliyuncs.com/api/v1");
String userInput = "シンガポールの天気は?";
List<Message> messages = new ArrayList<>();
messages.add(Message.builder().role(Role.USER.getValue()).content(userInput).build());
// 最初のモデル呼び出し。ユーザーリクエストとツールリストをモデルに送信します。
GenerationParam param = GenerationParam.builder()
.model("qwen-plus") // 呼び出すモデルを指定します。
.apiKey(System.getenv("DASHSCOPE_API_KEY")) // 環境変数から API キーを取得します。キーはリージョンによって異なります。API キーの取得方法:https://www.alibabacloud.com/help/model-studio/get-api-key
.messages(messages) // 現在の会話履歴を渡します。
.tools(Arrays.asList(ToolFunction.builder().function(weatherFunction).build())) // 利用可能なツールを渡します。
.resultFormat(GenerationParam.ResultFormat.MESSAGE)
.build();
GenerationResult result = gen.call(param);
Message assistantOutput = result.getOutput().getChoices().get(0).getMessage();
messages.add(assistantOutput); // 最初のモデル応答を会話履歴に追加します。
// モデル応答をチェックして、ツール呼び出しを要求しているかどうかを確認します。
if (assistantOutput.getToolCalls() == null || assistantOutput.getToolCalls().isEmpty()) {
// ケース A:モデルはツールを呼び出さずに直接応答します。
System.out.println("天気ツールの呼び出しは不要です。直接の返信:" + assistantOutput.getContent());
} else {
// ケース B:モデルはツールを呼び出すことを決定します。
// while ループを使用して複数のツール呼び出しを処理します。
while (assistantOutput.getToolCalls() != null && !assistantOutput.getToolCalls().isEmpty()) {
ToolCallBase toolCall = assistantOutput.getToolCalls().get(0);
// モデル応答からツール呼び出しの詳細 (関数名とパラメーター) を解析します。
ToolCallFunction functionCall = (ToolCallFunction) toolCall;
String funcName = functionCall.getFunction().getName();
String arguments = functionCall.getFunction().getArguments();
System.out.println("ツール [" + funcName + "] を引数:" + arguments + " で呼び出しています");
// 対応する Java メソッドをローカルで実行します。
String toolResult = getCurrentWeather(arguments);
// ツール実行結果を含むロール "tool" のメッセージを構築します。
Message toolMessage = Message.builder()
.role("tool")
.toolCallId(toolCall.getId())
.content(toolResult)
.build();
System.out.println("ツールからの返り値:" + toolMessage.getContent());
messages.add(toolMessage); // ツール結果を会話履歴に追加します。
// モデルを再度呼び出します。
param.setMessages(messages);
result = gen.call(param);
assistantOutput = result.getOutput().getChoices().get(0).getMessage();
messages.add(assistantOutput);
}
// モデルによって生成された最終応答を出力します。
System.out.println("アシスタントからの最終返信:" + assistantOutput.getContent());
}
} catch (NoApiKeyException | InputRequiredException e) {
System.err.println("エラー:" + e.getMessage());
} catch (Exception e) {
e.printStackTrace();
}
}
}実行後の出力:
ツール [get_current_weather] を引数:{'location': 'Singapore'} で呼び出しています
ツールからの返り値:今日のシンガポールは曇りです。
アシスタントからの最終返信:今日のシンガポールの天気は曇りです。使用方法
関数呼び出しは、ツール情報を渡す 2 つの方法をサポートしています。
方法 1:`tools` パラメーターを介して渡す (推奨)
「使用方法」をご参照ください。次の手順に従います:ツールの定義、メッセージ配列の作成、関数呼び出しの開始、ツール関数の実行、LLM によるツール出力の要約。
方法 2:システムメッセージを介して渡す
`tools` パラメーターを介してツール情報を渡すと、サーバーは自動的に最適なプロンプトテンプレートを選択して組み立てます。そのため、`tools` パラメーターの使用を推奨します。Qwen モデルで `tools` パラメーターを使用したくない場合は、「システムメッセージによるツール情報の受け渡し」をご参照ください。
以下の例では、OpenAI 互換のメソッドを使用しています。`tools` パラメーターを使用してツール情報を渡す方法を示しています。各ステップで関数呼び出しの使用方法を説明します。
アプリケーションが天気検索と時刻検索の 2 種類の質問を処理すると仮定します。
1. ツールの定義
ツールは LLM を外部世界に接続します。まず、それらを定義することから始めます。
1.1. ツール関数の作成
天気検索用と時刻検索用の 2 つのツール関数を作成します。
天気検索ツール
argumentsパラメーターを受け入れます。ここで、argumentsは{"location": "問い合わせる場所"}の形式です。ツールは"{場所} の今日の天気は {天気}"の形式の文字列を出力します。このデモを簡略化するため、天気ツールは実際の気象データを照会しません。晴れ、曇り、雨の中からランダムに選択します。本番環境では、Amap Weather API (高徳地図天気 API) などの実際のサービスに置き換えてください。
時刻検索ツール
入力パラメーターは不要です。
「現在の時刻:{時刻}」のような文字列を返します。Node.js を使用する場合は、
npm install date-fnsを実行して date-fns パッケージをインストールしてください。
## ステップ 1:ツール関数を定義
# random モジュールをインポート
import random
from datetime import datetime
# 天気検索ツールをシミュレート。戻り値の例:「今日の北京は雨です。」
def get_current_weather(arguments):
# 考えられる天気のリスト
weather_conditions = ["sunny", "cloudy", "rainy"]
# ランダムな天気を選択
random_weather = random.choice(weather_conditions)
# JSON から場所を抽出
location = arguments["location"]
# フォーマットされた天気情報を返す
return f"今日の {location} は {random_weather} です。"
# 現在時刻を検索。戻り値の例:「現在の時刻:2024-04-15 17:15:18」
def get_current_time():
# 現在の日時を取得
current_datetime = datetime.now()
# 現在の日時をフォーマット
formatted_time = current_datetime.strftime('%Y-%m-%d %H:%M:%S')
# フォーマットされた現在時刻を返す
return f"現在の時刻:{formatted_time}"
# ツール関数をテストし、結果を出力。後のステップを実行する前に、この 4 行を削除してください。
print("ツール出力のテスト:")
print(get_current_weather({"location": "上海"}))
print(get_current_time())
print("\n")// ステップ 1:ツール関数を定義
// 時刻検索ツールをインポート
import { format } from 'date-fns';
function getCurrentWeather(args) {
// 考えられる天気のリスト
const weatherConditions = ["sunny", "cloudy", "rainy"];
// ランダムな天気を選択
const randomWeather = weatherConditions[Math.floor(Math.random() * weatherConditions.length)];
// JSON から場所を抽出
const location = args.location;
// フォーマットされた天気情報を返す
return `今日の ${location} は ${randomWeather} です。`;
}
function getCurrentTime() {
// 現在の日時を取得
const currentDatetime = new Date();
// 現在の日時をフォーマット
const formattedTime = format(currentDatetime, 'yyyy-MM-dd HH:mm:ss');
// フォーマットされた現在時刻を返す
return `現在の時刻:${formattedTime}`;
}
// ツール関数をテストし、結果を出力。後のステップを実行する前に、この 4 行を削除してください。
console.log("ツール出力のテスト:")
console.log(getCurrentWeather({location:"上海"}));
console.log(getCurrentTime());
console.log("\n")ツールを実行すると、次の出力が得られます。
ツール出力のテスト:
今日の上海は曇りです。
現在の時刻:2025-01-08 20:21:451.2 ツール配列の作成
人間がツールを選ぶ前に、そのツールに関する完全な知識 (何をするか、いつ使うか、どんな入力が必要か) が必要です。LLM もツールを正確に選択するために同じ情報が必要です。以下の JSON 形式でツール情報を提供してください。
| 天気検索ツールの場合、情報は次のようになります。 |
関数呼び出しを開始する前に、コードでツール配列を定義します。各ツールの名前、説明、パラメーター定義を含めます。この配列を関数呼び出しリクエストのパラメーターとして渡します。
# ステップ 1 の後にこのコードを貼り付けます
## ステップ 2:ツール配列を作成
tools = [
{
"type": "function",
"function": {
"name": "get_current_time",
"description": "現在時刻を知りたいときに便利です。",
"parameters": {}
}
},
{
"type": "function",
"function": {
"name": "get_current_weather",
"description": "特定の都市の天気を調べたいときに便利です。",
"parameters": {
"type": "object",
"properties": {
"location": {
"type": "string",
"description": "市や県。例:北京、杭州、余杭区。",
}
},
"required": ["location"]
}
}
}
]
tool_name = [tool["function"]["name"] for tool in tools]
print(f"{len(tools)} 個のツールを作成しました:{tool_name}\n")// ステップ 1 の後にこのコードを貼り付けます
// ステップ 2:ツール配列を作成
const tools = [
{
type: "function",
function: {
name: "get_current_time",
description: "現在時刻を知りたいときに便利です。",
parameters: {}
}
},
{
type: "function",
function: {
name: "get_current_weather",
description: "特定の都市の天気を調べたいときに便利です。",
parameters: {
type: "object",
properties: {
location: {
type: "string",
description: "市や県。例:北京、杭州、余杭区。",
}
},
required: ["location"]
}
}
}
];
const toolNames = tools.map(tool => tool.function.name);
console.log(`${tools.length} 個のツールを作成しました:${toolNames.join(', ')}\n`);2. メッセージ配列の作成
関数呼び出しは、メッセージ配列を使用して LLM に指示とコンテキストを送信します。関数呼び出しを開始する前に、メッセージ配列にはシステムメッセージとユーザーメッセージが含まれている必要があります。
システムメッセージ
「ツール配列の作成」ステップでツールの目的と使用法を説明しましたが、システムメッセージに明確なガイダンスを追加すると、ツールの選択精度が向上することがよくあります。このシナリオでは、システムプロンプトを次のように設定します。
あなたは役立つアシスタントです。ユーザーが天気について尋ねたら、「get_current_weather」関数を呼び出してください。ユーザーが時刻について尋ねたら、「get_current_time」関数を呼び出してください。フレンドリーな口調で答えてください。ユーザーメッセージ
ユーザーメッセージはユーザーの質問を渡します。ユーザーが「上海の天気」と尋ねた場合、メッセージ配列は次のようになります。
# ステップ 3:メッセージ配列を作成
# ステップ 2 の後にこのコードを貼り付けます
# テキスト生成モデル用のユーザーメッセージ
messages = [
{
"role": "system",
"content": """あなたは役立つアシスタントです。ユーザーが天気について尋ねたら、「get_current_weather」関数を呼び出してください。ユーザーが時刻について尋ねたら、「get_current_time」関数を呼び出してください。フレンドリーな口調で答えてください。""",
},
{
"role": "user",
"content": "上海の天気"
}
]
# マルチモーダルモデル用のユーザーメッセージ
# messages=[
# {
# "role": "system",
# "content": """あなたは役立つアシスタントです。ユーザーが天気について尋ねたら、「get_current_weather」関数を呼び出してください。ユーザーが時刻について尋ねたら、「get_current_time」関数を呼び出してください。フレンドリーな口調で答えてください。""",
# },
# {"role": "user",
# "content": [{"type": "image_url","image_url": {"url": "https://img.alicdn.com/imgextra/i2/O1CN01FbTJon1ErXVGMRdsN_!!6000000000405-0-tps-1024-683.jpg"}},
# {"type": "text", "text": "画像内の場所の現在の天気を調べてください"}]},
# ]
print("メッセージ配列が作成されました\n") // ステップ 3:メッセージ配列を作成
// ステップ 2 の後にこのコードを貼り付けます
const messages = [
{
role: "system",
content: "あなたは役立つアシスタントです。ユーザーが天気について尋ねたら、「get_current_weather」関数を呼び出してください。ユーザーが時刻について尋ねたら、「get_current_time」関数を呼び出してください。フレンドリーな口調で答えてください。",
},
{
role: "user",
content: "上海の天気"
}
];
// マルチモーダルモデル用のユーザーメッセージ
// const messages: [{
// role: "user",
// content: [{type: "image_url", image_url: {"url": "https://help-static-aliyun-doc.aliyuncs.com/file-manage-files/zh-CN/20241022/emyrja/dog_and_girl.jpeg"}},
// {type: "text", text: "画像にはどのようなシーンが描かれていますか?"}]
// }];
console.log("メッセージ配列が作成されました\n");利用可能なツールは天気と時刻の検索をカバーしているため、現在時刻について尋ねることもできます。
3. 関数呼び出しの開始
tools と messages 配列を LLM に渡して、関数呼び出しを開始します。LLM はツールを呼び出すかどうかを決定します。呼び出す場合は、ツール名とパラメーターを返します。
サポートされているモデルについては、「サポート対象モデル」をご参照ください。
# ステップ 4:関数呼び出しを開始
# ステップ 3 の後にこのコードを貼り付けます
from openai import OpenAI
import os
client = OpenAI(
# API キーはリージョンによって異なります。API キーの取得方法:https://www.alibabacloud.com/help/model-studio/get-api-key
# 環境変数を設定していない場合は、次の行を Model Studio API キーに置き換えてください: api_key="sk-xxx"
api_key=os.getenv("DASHSCOPE_API_KEY"),
# 北京リージョンの場合は、https://dashscope.aliyuncs.com/compatible-mode/v1 を使用します
base_url="https://dashscope-intl.aliyuncs.com/compatible-mode/v1",
)
def function_calling():
completion = client.chat.completions.create(
# モデル例。必要に応じて置き換えてください。モデルリスト:https://www.alibabacloud.com/help/ja/model-studio/getting-started/models
model="qwen-plus",
messages=messages,
tools=tools
)
print("応答オブジェクト:")
print(completion.choices[0].message.model_dump_json())
print("\n")
return completion
print("関数呼び出しを開始しています...")
completion = function_calling()// ステップ 4:関数呼び出しを開始
// ステップ 3 の後にこのコードを貼り付けます
import OpenAI from "openai";
const openai = new OpenAI(
{
// API キーはリージョンによって異なります。API キーの取得方法:https://www.alibabacloud.com/help/model-studio/get-api-key
// 環境変数を設定していない場合は、次の行を Model Studio API キーに置き換えてください: apiKey: "sk-xxx"
apiKey: process.env.DASHSCOPE_API_KEY,
// 北京リージョンの場合は、https://dashscope.aliyuncs.com/compatible-mode/v1 を使用します
baseURL: "https://dashscope-intl.aliyuncs.com/compatible-mode/v1"
}
);
async function functionCalling() {
const completion = await openai.chat.completions.create({
model: "qwen-plus", // モデル例。必要に応じて置き換えてください。モデルリスト:https://www.alibabacloud.com/help/ja/model-studio/getting-started/models
messages: messages,
tools: tools
});
console.log("応答オブジェクト:");
console.log(JSON.stringify(completion.choices[0].message));
console.log("\n");
return completion;
}
const completion = await functionCalling();ユーザーが上海の天気について尋ねたため、LLM はツール関数名を "get_current_weather"、入力パラメーターを {"location": "Shanghai"} と指定します。
{
"content": "",
"refusal": null,
"role": "assistant",
"audio": null,
"function_call": null,
"tool_calls": [
{
"id": "call_6596dafa2a6a46f7a217da",
"function": {
"arguments": "{\"location\": \"Shanghai\"}",
"name": "get_current_weather"
},
"type": "function",
"index": 0
}
]
}LLM がツールは不要だと判断した場合、content パラメーターを介して直接返信することに注意してください。「こんにちは」と入力すると、tool_calls パラメーターは空になります。応答オブジェクトは次のようになります。
{
"content": "こんにちは!何かお手伝いできることはありますか?特に天気や時刻に関する質問が得意です。",
"refusal": null,
"role": "assistant",
"audio": null,
"function_call": null,
"tool_calls": null
}tool_callsパラメーターが空の場合、プログラムはcontentの値を直接返すことができます。以下の手順はスキップしてください。
LLM に毎回特定のツールを強制的に呼び出させる方法については、「ツール呼び出しの強制」をご参照ください。
4. ツール関数の実行
ツール関数を実行することで、LLM の決定が行動に移されます。
アプリケーション (LLM ではなく) がツール関数を実行します。
LLM は文字列のみを出力するため、ツールを実行する前にツール名と入力パラメーターを解析する必要があります。
ツール関数
ツール関数名からツール関数エンティティへのマッピング
function_mapperを作成します。これにより、返されたツール関数文字列がツール関数エンティティにマッピングされます。入力パラメーター
関数呼び出しは入力パラメーターを JSON 文字列として返します。これを JSON オブジェクトに解析して値を抽出します。
解析後、パラメーターをツール関数に渡して実行し、結果を取得します。
# ステップ 5:ツール関数を実行
# ステップ 4 の後にこのコードを貼り付けます
import json
print("ツール関数を実行しています...")
# 応答から関数名と入力パラメーターを取得
function_name = completion.choices[0].message.tool_calls[0].function.name
arguments_string = completion.choices[0].message.tool_calls[0].function.arguments
# json モジュールを使用してパラメーター文字列を解析
arguments = json.loads(arguments_string)
# 関数マッピングテーブルを作成
function_mapper = {
"get_current_weather": get_current_weather,
"get_current_time": get_current_time
}
# 関数オブジェクトを取得
function = function_mapper[function_name]
# パラメーターなしで関数を呼び出す
if arguments == {}:
function_output = function()
# それ以外の場合は、パラメーター付きで関数を呼び出す
else:
function_output = function(arguments)
# ツール出力を表示
print(f"ツール関数の出力:{function_output}\n")// ステップ 5:ツール関数を実行
// ステップ 4 の後にこのコードを貼り付けます
console.log("ツール関数を実行しています...");
const function_name = completion.choices[0].message.tool_calls[0].function.name;
const arguments_string = completion.choices[0].message.tool_calls[0].function.arguments;
// JSON モジュールを使用してパラメーター文字列を解析
const args = JSON.parse(arguments_string);
// 関数マッピングテーブルを作成
const functionMapper = {
"get_current_weather": getCurrentWeather,
"get_current_time": getCurrentTime
};
// 関数オブジェクトを取得
const func = functionMapper[function_name];
// パラメーターなしで関数を呼び出す
let functionOutput;
if (Object.keys(args).length === 0) {
functionOutput = func();
} else {
// それ以外の場合は、パラメーター付きで関数を呼び出す
functionOutput = func(args);
}
// ツール出力を表示
console.log(`ツール関数の出力:${functionOutput}\n`);実行後の出力:
今日の上海は曇りです。実際のアプリケーションでは、多くのツールはデータ検索だけでなく、アクション (メール送信やファイルアップロードなど) を実行します。これらのツールは文字列を返さない場合があります。LLM がそのステータスを理解できるように、「メール送信済み」や「操作失敗」などのステータスメッセージをツール設計に追加してください。
5. LLM によるツール出力の要約
ツールの出力は固定形式に従います。これを直接ユーザーに返すと、硬い印象や柔軟性に欠ける印象を与える可能性があります。ユーザー入力とツール出力を組み合わせた自然言語の返信を得るには、ツール出力をモデルコンテキストに送信し、モデルを再度呼び出します。
アシスタントメッセージの追加
「関数呼び出しの開始」の後、
completion.choices[0].messageを使用してアシスタントメッセージを取得し、まずメッセージ配列に追加します。ツールメッセージの追加
ツール出力を次の形式でメッセージ配列に追加します:
{"role": "tool", "content": "ツール出力", "tool_call_id": completion.choices[0].message.tool_calls[0].id}。説明ツール出力が文字列であることを確認してください。
tool_call_idは、システムが各ツール呼び出しに対して生成する一意の ID です。モデルは一度に複数のツールを要求する場合があります。tool_call_idを使用することで、各ツール出力がその呼び出し意図と一致することが保証されます。
# ステップ 6:ツール出力を LLM に送信
# ステップ 5 の後にこのコードを貼り付けます
messages.append(completion.choices[0].message)
print("アシスタントメッセージが追加されました")
messages.append({"role": "tool", "content": function_output, "tool_call_id": completion.choices[0].message.tool_calls[0].id})
print("ツールメッセージが追加されました\n")// ステップ 6:ツール出力を LLM に送信
// ステップ 5 の後にこのコードを貼り付けます
messages.push(completion.choices[0].message);
console.log("アシスタントメッセージが追加されました")
messages.push({
"role": "tool",
"content": functionOutput,
"tool_call_id": completion.choices[0].message.tool_calls[0].id
});
console.log("ツールメッセージが追加されました\n");メッセージ配列は次のようになります。
[
システムメッセージ -- ツール呼び出しをガイドする戦略
ユーザーメッセージ -- ユーザーの質問
アシスタントメッセージ -- モデルからのツール呼び出し情報
ツールメッセージ -- ツール出力 (並列ツール呼び出しを使用している場合、複数のツールメッセージが存在する可能性があります)
]メッセージ配列を更新した後、次のコードを実行します。
# ステップ 7:LLM がツール出力を要約
# ステップ 6 の後にこのコードを貼り付けます
print("ツール出力を要約しています...")
completion = function_calling()// ステップ 7:LLM がツール出力を要約
// ステップ 6 の後にこのコードを貼り付けます
console.log("ツール出力を要約しています...");
const completion_1 = await functionCalling();content フィールドで「今日の上海の天気は曇りです。他に質問はありますか?」という返信を取得します。
{
"content": "今日の上海の天気は曇りです。他に質問はありますか?",
"refusal": null,
"role": "assistant",
"audio": null,
"function_call": null,
"tool_calls": null
}これで、完全な関数呼び出しフローが完了しました。
高度な使用方法
ツール呼び出しメソッドの指定
並列ツール呼び出し
単一都市の天気検索には 1 回のツール呼び出しが必要です。しかし、「北京と上海の天気は?」や「杭州の天気と今の時刻は?」など、複数のツール呼び出しが必要な質問もあります。「関数呼び出しの開始」後、応答には 1 つのツール呼び出ししか表示されません。「北京と上海の天気は?」の場合、応答は次のようになります。
{
"content": "",
"refusal": null,
"role": "assistant",
"audio": null,
"function_call": null,
"tool_calls": [
{
"id": "call_61a2bbd82a8042289f1ff2",
"function": {
"arguments": "{\"location\": \"Beijing\"}",
"name": "get_current_weather"
},
"type": "function",
"index": 0
}
]
}応答には北京の入力パラメーターのみが含まれています。この問題を解決するには、「関数呼び出し」を開始する際に、リクエストパラメーター parallel_tool_calls を true に設定します。これにより、応答オブジェクトには呼び出す必要のあるすべてのツール関数の入力パラメーターが含まれます。
タスクに依存関係がない場合は、並列ツール呼び出しを使用してください。タスクが互いに依存している場合 (たとえば、ツール A の入力がツール B の出力に依存する場合) は、「クイックスタート」をご参照ください。while ループを使用して、直列ツール呼び出し (一度に 1 つのツール) を行います。
def function_calling():
completion = client.chat.completions.create(
model="qwen-plus", # モデル例。必要に応じて置き換えてください
messages=messages,
tools=tools,
# 新しいパラメーター
parallel_tool_calls=True
)
print("応答オブジェクト:")
print(completion.choices[0].message.model_dump_json())
print("\n")
return completion
print("関数呼び出しを開始しています...")
completion = function_calling()async function functionCalling() {
const completion = await openai.chat.completions.create({
model: "qwen-plus", // モデル例。必要に応じて置き換えてください
messages: messages,
tools: tools,
parallel_tool_calls: true
});
console.log("応答オブジェクト:");
console.log(JSON.stringify(completion.choices[0].message));
console.log("\n");
return completion;
}
const completion = await functionCalling();応答オブジェクトの tool_calls 配列には、北京と上海の入力パラメーター情報が含まれています。
{
"content": "",
"role": "assistant",
"tool_calls": [
{
"function": {
"name": "get_current_weather",
"arguments": "{\"location\": \"Beijing\"}"
},
"index": 0,
"id": "call_c2d8a3a24c4d4929b26ae2",
"type": "function"
},
{
"function": {
"name": "get_current_weather",
"arguments": "{\"location\": \"Shanghai\"}"
},
"index": 1,
"id": "call_dc7f2f678f1944da9194cd",
"type": "function"
}
]
}ツール呼び出しの強制
LLM が生成するコンテンツは不確実であり、LLM はツール呼び出しに対して誤ったツールを選択することがあります。特定の種類の質問に対して人間が定義した戦略に従わせたい場合 (特定のツールの使用を強制したり、ツールの使用を禁止したりするなど)、tool_choice パラメーターを変更できます。tool_choice パラメーターのデフォルト値は "auto" であり、LLM がツールを呼び出すかどうか、またどのように呼び出すかを自律的に決定することを意味します。
ツール出力を要約する際には、tool_choice パラメーターを削除してください。そうしないと、API は依然としてツール呼び出し情報を返します。特定のツールを強制する
特定の質問に対して関数呼び出しに特定のツールを使用させるには、
tool_choiceを{"type": "function", "function": {"name": "呼び出す関数"}}に設定します。LLM はツールの選択をスキップし、パラメーターのみを出力します。たとえば、アプリが天気の質問のみを処理する場合、`function_calling` コードを次のように更新します。
def function_calling(): completion = client.chat.completions.create( model="qwen-plus", messages=messages, tools=tools, tool_choice={"type": "function", "function": {"name": "get_current_weather"}} ) print(completion.model_dump_json()) function_calling()async function functionCalling() { const response = await openai.chat.completions.create({ model: "qwen-plus", messages: messages, tools: tools, tool_choice: {"type": "function", "function": {"name": "get_current_weather"}} }); console.log("応答オブジェクト:"); console.log(JSON.stringify(response.choices[0].message)); console.log("\n"); return response; } const response = await functionCalling();どのような質問をしても、応答は常に
get_current_weatherを使用します。この戦略を使用する前に、質問が選択したツールと一致していることを確認してください。そうしないと、予期しない結果になる可能性があります。
すべてのツールをブロックする
どの質問に対してもツール呼び出しをブロックする (つまり、応答に
contentは含まれるがtool_callsパラメーターは空になるようにする) には、tool_choiceを"none"に設定するか、toolsパラメーターを省略します。tool_callsパラメーターは常に空になります。たとえば、ツールを必要とする質問がない場合、`function_calling` コードを次のように更新します。
def function_calling(): completion = client.chat.completions.create( model="qwen-plus", messages=messages, tools=tools, tool_choice="none" ) print(completion.model_dump_json()) function_calling()async function functionCalling() { const completion = await openai.chat.completions.create({ model: "qwen-plus", messages: messages, tools: tools, tool_choice: "none" }); console.log("応答オブジェクト:"); console.log(JSON.stringify(completion.choices[0].message)); console.log("\n"); return completion; } const completion = await functionCalling();
マルチターン会話
ユーザーは、第 1 ラウンドで「北京の天気は?」と尋ね、第 2 ラウンドで「上海はどう?」と尋ねるかもしれません。第 1 ラウンドのコンテキストがなければ、LLM はどのツールを呼び出すべきかわかりません。マルチターン会話では、各ラウンドの後にメッセージ配列を保持します。その後、ユーザーメッセージを追加し、「関数呼び出しの開始」以降のステップを実行します。メッセージ構造は次のようになります。
[
システムメッセージ -- ツール呼び出しをガイドする戦略
ユーザーメッセージ -- ユーザーの質問
アシスタントメッセージ -- モデルからのツール呼び出し情報
ツールメッセージ -- ツール出力
アシスタントメッセージ -- ツール呼び出しのモデル要約
ユーザーメッセージ -- 第 2 ラウンドの質問
]ストリーミング出力
ユーザーエクスペリエンスを向上させ、待機時間を短縮するために、ストリーミング出力を使用してツール名とパラメーターをリアルタイムで取得します。方法は次のとおりです。
ツール呼び出しパラメーターはチャンクで到着します。
ツール関数名は最初のチャンクで到着します。
from openai import OpenAI
import os
client = OpenAI(
api_key=os.getenv("DASHSCOPE_API_KEY"),
# 北京リージョンの場合は、https://dashscope.aliyuncs.com/compatible-mode/v1 を使用します
base_url="https://dashscope-intl.aliyuncs.com/compatible-mode/v1",
)
tools = [
{
"type": "function",
"function": {
"name": "get_current_weather",
"description": "特定の都市の天気を調べたいときに便利です。",
"parameters": {
"type": "object",
"properties": {
"location": {
"type": "string",
"description": "市や県。例:北京、杭州、余杭区。",
}
},
"required": ["location"],
},
},
},
]
stream = client.chat.completions.create(
model="qwen-plus",
messages=[{"role": "user", "content": "杭州の天気は?"}],
tools=tools,
stream=True
)
for chunk in stream:
delta = chunk.choices[0].delta
print(delta.tool_calls)import { OpenAI } from "openai";
const openai = new OpenAI(
{
// API キーはリージョンによって異なります。API キーの取得方法: https://www.alibabacloud.com/help/zh/model-studio/get-api-key
// 環境変数を設定していない場合は、次の行を Model Studio API キーに置き換えます: apiKey: "sk-xxx"
apiKey: process.env.DASHSCOPE_API_KEY,
// 北京リージョンの場合は、こちらを使用します: https://dashscope.aliyuncs.com/compatible-mode/v1
baseURL: "https://dashscope.aliyuncs.com/compatible-mode/v1"
}
);
const tools = [
{
"type": "function",
"function": {
"name": "getCurrentWeather",
"description": "特定の都市の天気を確認したい場合に便利です。",
"parameters": {
"type": "object",
"properties": {
"location": {
"type": "string",
"description": "都市または県(例:北京、杭州、余杭区)"
}
},
"required": ["location"]
}
}
}
];
const stream = await openai.chat.completions.create({
model: "qwen-plus",
messages: [{ role: "user", content: "北京の天気はどうですか?" }],
tools: tools,
stream: true,
});
for await (const chunk of stream) {
const delta = chunk.choices[0].delta;
console.log(delta.tool_calls);
}実行後の出力:
[ChoiceDeltaToolCall(index=0, id='call_8f08d2b0fc0c4d8fab7123', function=ChoiceDeltaToolCallFunction(arguments='{"location":', name='get_current_weather'), type='function')]
[ChoiceDeltaToolCall(index=0, id='', function=ChoiceDeltaToolCallFunction(arguments=' "Hangzhou"}', name=None), type='function')]
Noneこのコードを実行して、パラメーターチャンク (arguments) を結合します。
tool_calls = {}
for response_chunk in stream:
delta_tool_calls = response_chunk.choices[0].delta.tool_calls
if delta_tool_calls:
for tool_call_chunk in delta_tool_calls:
call_index = tool_call_chunk.index
tool_call_chunk.function.arguments = tool_call_chunk.function.arguments or ""
if call_index not in tool_calls:
tool_calls[call_index] = tool_call_chunk
else:
tool_calls[call_index].function.arguments += tool_call_chunk.function.arguments
print(tool_calls[0].model_dump_json())const toolCalls = {};
for await (const responseChunk of stream) {
const deltaToolCalls = responseChunk.choices[0]?.delta?.tool_calls;
if (deltaToolCalls) {
for (const toolCallChunk of deltaToolCalls) {
const index = toolCallChunk.index;
toolCallChunk.function.arguments = toolCallChunk.function.arguments || "";
if (!toolCalls[index]) {
toolCalls[index] = { ...toolCallChunk };
if (!toolCalls[index].function) {
toolCalls[index].function = { name: '', arguments: '' };
}
}
else if (toolCallChunk.function?.arguments) {
toolCalls[index].function.arguments += toolCallChunk.function.arguments;
}
}
}
}
console.log(JSON.stringify(toolCalls[0]));出力:
{"index":0,"id":"call_16c72bef988a4c6c8cc662","function":{"arguments":"{\"location\": \"Hangzhou\"}","name":"get_current_weather"},"type":"function"}ツール出力を要約する場合、アシスタントメッセージは以下の形式と一致する必要があります。tool_calls 要素を上記の内容に置き換えるだけです。
{
"content": "",
"refusal": None,
"role": "assistant",
"audio": None,
"function_call": None,
"tool_calls": [
{
"id": "call_xxx",
"function": {
"arguments": '{"location": "xx"}',
"name": "get_current_weather",
},
"type": "function",
"index": 0,
}
],
}Responses API を使用したツール呼び出し
上記の例では、ツール呼び出しに OpenAI Chat Completions API と DashScope API を使用しています。OpenAI Responses API を使用する場合、全体的なフローは同じですが、インターフェイスの形式が異なります。
ディメンション | Chat Completions | Responses API |
base_url | https://dashscope-intl.aliyuncs.com/compatible-mode/v1 | https://dashscope-intl.aliyuncs.com/api/v2/apps/protocols/compatible-mode/v1 |
ツール定義形式 | | |
ツール呼び出し出力 | response.choices[0].message.tool_calls | response.output のうち、type が function_call の項目 |
ツール結果を返す | | |
最終返信 | response.choices[0].message.content | response.output_text |
from openai import OpenAI
import json
import os
import random
# クライアントを初期化
client = OpenAI(
# 環境変数を設定していない場合は、次の行を Model Studio API キーに置き換えてください: api_key="sk-xxx"
# API キーはリージョンによって異なります。API キーの取得方法:https://www.alibabacloud.com/help/model-studio/get-api-key
api_key=os.getenv("DASHSCOPE_API_KEY"),
base_url="https://dashscope-intl.aliyuncs.com/api/v2/apps/protocols/compatible-mode/v1",
)
# ユーザーの質問をシミュレート
USER_QUESTION = "シンガポールの天気は?"
# ツールを定義
tools = [
{
"type": "function",
"name": "get_current_weather",
"description": "指定された都市の天気を照会したいときに便利です。",
"parameters": {
"type": "object",
"properties": {
"location": {
"type": "string",
"description": "市や県。例:シンガポール、ロンドン。",
}
},
"required": ["location"],
},
}
]
# 天気照会ツールをシミュレート
def get_current_weather(arguments):
weather_conditions = ["sunny", "cloudy", "rainy"]
random_weather = random.choice(weather_conditions)
location = arguments["location"]
return f"今日の {location} は {random_weather} です。"
# モデル応答関数をラップ
def get_response(input_data):
response = client.responses.create(
model="qwen3.5-plus", # オプション:qwen3.5-flash, qwen3.5-flash-2026-02-23
input=input_data,
tools=tools,
)
return response
# 会話コンテキストを維持
conversation = [{"role": "user", "content": USER_QUESTION}]
response = get_response(conversation)
function_calls = [item for item in response.output if item.type == "function_call"]
# ツールを呼び出す必要がない場合は、コンテンツを直接出力
if not function_calls:
print(f"アシスタントからの最終応答:{response.output_text}")
else:
# ツール呼び出しループに入る
while function_calls:
for fc in function_calls:
func_name = fc.name
arguments = json.loads(fc.arguments)
print(f"ツール [{func_name}] を引数:{arguments} で呼び出しています")
# ツールを実行
tool_result = get_current_weather(arguments)
print(f"ツール結果:{tool_result}")
# ツール呼び出しと結果をペアでコンテキストに追加
conversation.append(
{
"type": "function_call",
"name": fc.name,
"arguments": fc.arguments,
"call_id": fc.call_id,
}
)
conversation.append(
{
"type": "function_call_output",
"call_id": fc.call_id,
"output": tool_result,
}
)
# 完全なコンテキストでモデルを再度呼び出す
response = get_response(conversation)
function_calls = [
item for item in response.output if item.type == "function_call"
]
print(f"アシスタントからの最終応答:{response.output_text}")
import OpenAI from "openai";
// クライアントを初期化
const openai = new OpenAI({
// API キーはリージョンによって異なります。API キーの取得方法:https://www.alibabacloud.com/help/model-studio/get-api-key
// 環境変数を設定していない場合は、次の行を Model Studio API キーに置き換えてください: apiKey: "sk-xxx"
apiKey: process.env.DASHSCOPE_API_KEY,
baseURL:
"https://dashscope-intl.aliyuncs.com/api/v2/apps/protocols/compatible-mode/v1",
});
// ツールリストを定義
const tools = [
{
type: "function",
name: "get_current_weather",
description: "特定の都市の天気を調べたいときに便利です。",
parameters: {
type: "object",
properties: {
location: {
type: "string",
description: "市や県。例:シンガポール、ロンドン。",
},
},
required: ["location"],
},
},
];
// 天気検索ツールをシミュレート
const getCurrentWeather = (args) => {
const weatherConditions = ["sunny", "cloudy", "rainy"];
const randomWeather =
weatherConditions[Math.floor(Math.random() * weatherConditions.length)];
const location = args.location;
return `今日の ${location} は ${randomWeather} です。`;
};
// モデル応答関数をラップ
const getResponse = async (inputData) => {
const response = await openai.responses.create({
model: "qwen3.5-plus", // オプション:qwen3.5-flash, qwen3.5-flash-2026-02-23
input: inputData,
tools: tools,
});
return response;
};
const main = async () => {
const userQuestion = "シンガポールの天気は?";
// 会話コンテキストを維持
const conversation = [{ role: "user", content: userQuestion }];
let response = await getResponse(conversation);
let functionCalls = response.output.filter(
(item) => item.type === "function_call"
);
// ツール呼び出しが不要な場合は、コンテンツを直接出力
if (functionCalls.length === 0) {
console.log(`アシスタントからの最終返信:${response.output_text}`);
} else {
// ツール呼び出しループに入る
while (functionCalls.length > 0) {
for (const fc of functionCalls) {
const funcName = fc.name;
const args = JSON.parse(fc.arguments);
console.log(`ツール [${funcName}] を引数:`, args, `で呼び出しています`);
// ツールを実行
const toolResult = getCurrentWeather(args);
console.log(`ツールからの返り値:${toolResult}`);
// ツール呼び出しと結果をコンテキストに追加
conversation.push({
type: "function_call",
name: fc.name,
arguments: fc.arguments,
call_id: fc.call_id,
});
conversation.push({
type: "function_call_output",
call_id: fc.call_id,
output: toolResult,
});
}
// 完全なコンテキストでモデルを再度呼び出す
response = await getResponse(conversation);
functionCalls = response.output.filter(
(item) => item.type === "function_call"
);
}
console.log(`アシスタントからの最終返信:${response.output_text}`);
}
};
// プログラムを実行
main().catch(console.error);
Qwen3-Omni-Flash を使用したツール呼び出し
ツール情報取得段階において、Qwen3-Omni-Flash は他のモデルと 2 つの点で異なります。
ストリーミング出力が必須:
Qwen3-Omni-Flashはストリーミング出力のみをサポートします。ツール情報を取得する際はstream=Trueを設定してください。テキストのみの出力が推奨: ツール情報 (関数名とパラメーター) を取得する際は、テキストのみが重要です。不要な音声を避けるため、
modalities=["text"]を設定してください。出力にテキストと音声の両方が含まれる場合は、ツール情報を取得する際に音声チャンクをスキップしてください。
Qwen3-Omni-Flash の詳細については、「非リアルタイム (Qwen-Omni)」をご参照ください。
from openai import OpenAI
import os
client = OpenAI(
# API キーはリージョンによって異なります。API キーの取得方法:https://www.alibabacloud.com/help/model-studio/get-api-key
api_key=os.getenv("DASHSCOPE_API_KEY"),
# 北京リージョンの場合は、https://dashscope.aliyuncs.com/compatible-mode/v1 を使用します
base_url="https://dashscope-intl.aliyuncs.com/compatible-mode/v1",
)
tools = [
{
"type": "function",
"function": {
"name": "get_current_weather",
"description": "特定の都市の天気を調べたいときに便利です。",
"parameters": {
"type": "object",
"properties": {
"location": {
"type": "string",
"description": "市や県。例:北京、杭州、余杭区。",
}
},
"required": ["location"],
},
},
},
]
completion = client.chat.completions.create(
model="qwen3-omni-flash",
messages=[{"role": "user", "content": "杭州の天気は?"}],
# 出力モダリティを設定。オプション:["text"], ["text","audio"]。推奨:["text"]
modalities=["text"],
# stream は True にする必要があります。そうしないと失敗します
stream=True,
tools=tools
)
for chunk in completion:
# 出力に音声が含まれる場合は、条件を次のように変更します:if chunk.choices and not hasattr(chunk.choices[0].delta, "audio"):
if chunk.choices:
delta = chunk.choices[0].delta
print(delta.tool_calls)import { OpenAI } from "openai";
const openai = new OpenAI(
{
// API キーはリージョンによって異なります。API キーの取得方法:https://www.alibabacloud.com/help/model-studio/get-api-key
// 環境変数を設定していない場合は、次の行を Model Studio API キーに置き換えてください: apiKey: "sk-xxx"
apiKey: process.env.DASHSCOPE_API_KEY,
// 北京リージョンの場合は、https://dashscope.aliyuncs.com/compatible-mode/v1 を使用します
baseURL: "https://dashscope-intl.aliyuncs.com/compatible-mode/v1"
}
);
const tools = [
{
"type": "function",
"function": {
"name": "getCurrentWeather",
"description": "特定の都市の天気を調べたいときに便利です。",
"parameters": {
"type": "object",
"properties": {
"location": {
"type": "string",
"description": "市や県。例:北京、杭州、余杭区。"
}
},
"required": ["location"]
}
}
}
];
const stream = await openai.chat.completions.create({
model: "qwen3-omni-flash",
messages: [
{
"role": "user",
"content": "杭州の天気は?"
}],
stream: true,
// 出力モダリティを設定。オプション:["text"], ["text","audio"]。推奨:["text"]
modalities: ["text"],
tools:tools
});
for await (const chunk of stream) {
// 出力に音声が含まれる場合は、条件を次のように変更します:if (chunk.choices?.length && chunk.choices[0].delta && !('audio' in chunk.choices[0].delta))
if (chunk.choices?.length){
const delta = chunk.choices[0].delta;
console.log(delta.tool_calls);
}}実行後の出力:
[ChoiceDeltaToolCall(index=0, id='call_391c8e5787bc4972a388aa', function=ChoiceDeltaToolCallFunction(arguments=None, name='get_current_weather'), type='function')]
[ChoiceDeltaToolCall(index=0, id='call_391c8e5787bc4972a388aa', function=ChoiceDeltaToolCallFunction(arguments=' {"location": "Hangzhou"}', name=None), type='function')]
Noneパラメーターチャンク (arguments) を結合するコードについては、「ストリーミング出力」をご参照ください。
ディープシンキングモデルを使用したツール呼び出し
ディープシンキングモデルは、ツール呼び出しを出力する前に推論を行います。これにより、決定の解釈可能性と信頼性が向上します。
推論プロセス
モデルはユーザーの意図を分析し、必要なツールを特定し、パラメーターの正当性を検証し、呼び出し戦略を計画します。
ツール呼び出し
モデルは 1 つ以上の関数呼び出しを構造化形式で出力します。
並列ツール呼び出しをサポートします。
以下は、ディープシンキングモデルのストリーミング例です。
テキスト生成ディープシンキングモデルについては、「ディープシンキング」をご参照ください。マルチモーダルディープシンキングモデルについては、「画像と動画の理解」および「非リアルタイム (Qwen-Omni)」をご参照ください。
tool_choiceパラメーターは"auto"(デフォルト、モデルがツールを選択) または"none"(ツール選択をブロック) のみをサポートします。
OpenAI 互換
Python
サンプルコード
import os
from openai import OpenAI
# Alibaba Cloud DashScope サービスで OpenAI クライアントを初期化
client = OpenAI(
# API キーはリージョンによって異なります。API キーの取得方法:https://www.alibabacloud.com/help/model-studio/get-api-key
# 環境変数を設定していない場合は、次の行を Model Studio API キーに置き換えてください: api_key="sk-xxx"
api_key=os.getenv("DASHSCOPE_API_KEY"), # 環境変数から API キーを読み取る
base_url="https://dashscope-intl.aliyuncs.com/compatible-mode/v1",
)
# 利用可能なツールを定義
tools = [
# ツール 1:現在時刻を取得
{
"type": "function",
"function": {
"name": "get_current_time",
"description": "現在時刻を知りたいときに便利です。",
"parameters": {} # パラメーターは不要
}
},
# ツール 2:特定の都市の天気を取得
{
"type": "function",
"function": {
"name": "get_current_weather",
"description": "特定の都市の天気を調べたいときに便利です。",
"parameters": {
"type": "object",
"properties": {
"location": {
"type": "string",
"description": "市や県。例:北京、杭州、余杭区。"
}
},
"required": ["location"] # 必須パラメーター
}
}
}
]
messages = [{"role": "user", "content": input("質問を入力してください:")}]
# マルチモーダルモデルのメッセージ例
# messages = [{
# "role": "user",
# "content": [
# {"type": "image_url","image_url": {"url": "https://img.alicdn.com/imgextra/i4/O1CN014CJhzi20NOzo7atOC_!!6000000006837-2-tps-2048-1365.png"}},
# {"type": "text", "text": "画像内の場所に基づいて、現在の天気はどうですか?"}]
# }]
completion = client.chat.completions.create(
# モデル例。他のディープシンキングモデルに置き換えてください。
model="qwen-plus",
messages=messages,
extra_body={
# ディープシンキングを有効化。これは qwen3-30b-a3b-thinking-2507、qwen3-235b-a22b-thinking-2507、または QwQ モデルには影響しません。
"enable_thinking": True
},
tools=tools,
parallel_tool_calls=True,
stream=True,
# トークン使用量情報を取得するにはコメントを解除
# stream_options={
# "include_usage": True
# }
)
reasoning_content = "" # 完全な推論プロセス
answer_content = "" # 完全な返信
tool_info = [] # ツール呼び出し情報を保存
is_answering = False # 推論が終了し、返信が開始されたかを追跡するフラグ
print("="*20+"推論プロセス"+"="*20)
for chunk in completion:
if not chunk.choices:
# 使用量情報を処理
print("\n"+"="*20+"使用量"+"="*20)
print(chunk.usage)
else:
delta = chunk.choices[0].delta
# AI の推論 (Chain-of-Thought) を処理
if hasattr(delta, 'reasoning_content') and delta.reasoning_content is not None:
reasoning_content += delta.reasoning_content
print(delta.reasoning_content,end="",flush=True) # 推論プロセスをストリーミング
# 最終的な返信コンテンツを処理
else:
if not is_answering: # 最初の返信エントリでのみタイトルを出力
is_answering = True
print("\n"+"="*20+"返信コンテンツ"+"="*20)
if delta.content is not None:
answer_content += delta.content
print(delta.content,end="",flush=True) # 返信コンテンツをストリーミング
# ツール呼び出し情報を処理 (並列呼び出しをサポート)
if delta.tool_calls is not None:
for tool_call in delta.tool_calls:
index = tool_call.index # 並列呼び出し用のツール呼び出しインデックス
# ツール情報リストを動的に拡張
while len(tool_info) <= index:
tool_info.append({})
# ツール呼び出し ID を収集 (後の関数呼び出し用)
if tool_call.id:
tool_info[index]['id'] = tool_info[index].get('id', '') + tool_call.id
# 関数名を収集 (正しい関数にルーティングするため)
if tool_call.function and tool_call.function.name:
tool_info[index]['name'] = tool_info[index].get('name', '') + tool_call.function.name
# 関数引数を収集 (JSON 文字列、後で解析が必要)
if tool_call.function and tool_call.function.arguments:
tool_info[index]['arguments'] = tool_info[index].get('arguments', '') + tool_call.function.arguments
print(f"\n"+"="*19+"ツール呼び出し情報"+"="*19)
if not tool_info:
print("ツール呼び出しなし")
else:
print(tool_info)応答
「四直轄市の天気」と入力した場合の出力:
====================推論プロセス====================
わかりました、ユーザーは「四直轄市の天気」を尋ねました。まず、どの都市が四直轄市であるかを特定する必要があります。中国の行政区画によれば、北京、上海、天津、重慶です。つまり、ユーザーはこれら 4 都市の天気を知りたいのです。
次に、利用可能なツールを確認します。提供されているツールは get_current_weather で、型が文字列の location パラメーターがあります。この関数は一度に 1 つの場所しか処理できないため、各都市を個別に照会する必要があります。したがって、各直轄市に対して 1 回ずつ呼び出しを行う必要があります。
次に、正しいツール呼び出しを生成する方法を考えます。各呼び出しには、パラメーターとして都市名を含める必要があります。たとえば、最初の呼び出しは北京、2 番目は上海、というようになります。パラメーター名が location で、値が正しい都市名であることを確認します。
また、ユーザーは各都市の天気を知りたいでしょうから、各関数呼び出しが正しいことを確認する必要があります。おそらく、各都市に対して 1 回ずつ、合計 4 回の個別の呼び出しを行う必要があります。しかし、例に基づくと、複数の呼び出しを段階的に行うことになるかもしれません。
最後に、パラメーターの正しさ、都市名の正確さ、都市が見つからない場合や API が利用できない場合のエラー処理など、考慮すべき他の要因があるかどうかを確認します。しかし、今のところ四直轄市は明確なので、問題ないはずです。
====================返信コンテンツ====================
===================ツール呼び出し情報===================
[{'id': 'call_767af2834c12488a8fe6e3', 'name': 'get_current_weather', 'arguments': '{"location": "Beijing"}'}, {'id': 'call_2cb05a349c89437a947ada', 'name': 'get_current_weather', 'arguments': '{"location": "Shanghai"}'}, {'id': 'call_988dd180b2ca4b0a864ea7', 'name': 'get_current_weather', 'arguments': '{"location": "Tianjin"}'}, {'id': 'call_4e98c57ea96a40dba26d12', 'name': 'get_current_weather', 'arguments': '{"location": "Chongqing"}'}]Node.js
サンプルコード
import OpenAI from "openai";
import readline from 'node:readline/promises';
import { stdin as input, stdout as output } from 'node:process';
const openai = new OpenAI({
// API キーはリージョンによって異なります。API キーの取得方法:https://www.alibabacloud.com/help/model-studio/get-api-key
apiKey: process.env.DASHSCOPE_API_KEY,
baseURL: "https://dashscope-intl.aliyuncs.com/compatible-mode/v1"
});
const tools = [
{
type: "function",
function: {
name: "get_current_time",
description: "現在時刻を知りたいときに便利です。",
parameters: {}
}
},
{
type: "function",
function: {
name: "get_current_weather",
description: "特定の都市の天気を調べたいときに便利です。",
parameters: {
type: "object",
properties: {
location: {
type: "string",
description: "市や県。例:北京、杭州、余杭区。"
}
},
required: ["location"]
}
}
}
];
async function main() {
const rl = readline.createInterface({ input, output });
const question = await rl.question("質問を入力してください:");
rl.close();
const messages = [{ role: "user", content: question }];
// マルチモーダルモデルのメッセージ例
// const messages= [{
// role: "user",
// content: [{type: "image_url", image_url: {url: "https://img.alicdn.com/imgextra/i2/O1CN01FbTJon1ErXVGMRdsN_!!6000000000405-0-tps-1024-683.jpg"}},
// {type: "text", text: "画像内の場所の天気はどうですか?"}]
// }];
let reasoningContent = "";
let answerContent = "";
const toolInfo = [];
let isAnswering = false;
console.log("=".repeat(20) + "推論プロセス" + "=".repeat(20));
try {
const stream = await openai.chat.completions.create({
// モデル例。他のディープシンキングモデルに置き換えてください。
model: "qwen-plus",
messages,
// ディープシンキングを有効化。これは qwen3-30b-a3b-thinking-2507、qwen3-235b-a22b-thinking-2507、または QwQ モデルには影響しません。
enable_thinking: true,
tools,
stream: true,
parallel_tool_calls: true
});
for await (const chunk of stream) {
if (!chunk.choices?.length) {
console.log("\n" + "=".repeat(20) + "使用量" + "=".repeat(20));
console.log(chunk.usage);
continue;
}
const delta = chunk.choices[0]?.delta;
if (!delta) continue;
// 推論を処理
if (delta.reasoning_content) {
reasoningContent += delta.reasoning_content;
process.stdout.write(delta.reasoning_content);
}
// 返信コンテンツを処理
else {
if (!isAnswering) {
isAnswering = true;
console.log("\n" + "=".repeat(20) + "返信コンテンツ" + "=".repeat(20));
}
if (delta.content) {
answerContent += delta.content;
process.stdout.write(delta.content);
}
// ツール呼び出しを処理
if (delta.tool_calls) {
for (const toolCall of delta.tool_calls) {
const index = toolCall.index;
// 配列の長さが十分であることを確認
while (toolInfo.length <= index) {
toolInfo.push({});
}
// ツール ID を更新
if (toolCall.id) {
toolInfo[index].id = (toolInfo[index].id || "") + toolCall.id;
}
// 関数名を更新
if (toolCall.function?.name) {
toolInfo[index].name = (toolInfo[index].name || "") + toolCall.function.name;
}
// パラメーターを更新
if (toolCall.function?.arguments) {
toolInfo[index].arguments = (toolInfo[index].arguments || "") + toolCall.function.arguments;
}
}
}
}
}
console.log("\n" + "=".repeat(19) + "ツール呼び出し情報" + "=".repeat(19));
console.log(toolInfo.length ? toolInfo : "ツール呼び出しなし");
} catch (error) {
console.error("エラー:", error);
}
}
main(); 応答
「四直轄市の天気」と入力した場合の出力:
質問を入力してください:四直轄市の天気
====================推論プロセス====================
わかりました、ユーザーは四直轄市の天気を尋ねました。まず、どの都市が四直轄市であるかを特定する必要があります。北京、上海、天津、重慶ですよね?次に、各都市の天気予報を照会するために、天気検索関数を呼び出す必要があります。
しかし、ユーザーの質問は「四直轄市」としか言っておらず、具体的な都市名を指定していません。各直轄市の名前を明確にし、それぞれの天気を照会する必要があります。たとえば、北京、上海、天津、重慶です。それぞれの都市に対して get_current_weather 関数を呼び出し、対応する都市名をパラメーターとして渡す必要があります。たとえば、最初の呼び出しの場所は「北京」、2 番目は「上海」、3 番目は「天津」、4 番目は「重慶」です。
次に、4 つのツール呼び出しを生成する必要があります。それぞれが 1 つの直轄市に対応します。各パラメーターが正しいことを確認し、順番に並べます。これにより、ユーザーは 4 つの直轄市すべての完全な天気情報を取得できます。
====================返信コンテンツ====================
===================ツール呼び出し情報===================
[
{
id: 'call_21dc802e717f491298d1b2',
name: 'get_current_weather',
arguments: '{"location": "Beijing"}'
},
{
id: 'call_2cd3be1d2f694c4eafd4e5',
name: 'get_current_weather',
arguments: '{"location": "Shanghai"}'
},
{
id: 'call_48cf3f78e02940bd9085e4',
name: 'get_current_weather',
arguments: '{"location": "Tianjin"}'
},
{
id: 'call_e230a2b4c64f4e658d223e',
name: 'get_current_weather',
arguments: '{"location": "Chongqing"}'
}
]HTTP
サンプルコード
curl
curl -X POST https://dashscope-intl.aliyuncs.com/compatible-mode/v1/chat/completions \
-H "Authorization: Bearer $DASHSCOPE_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "qwen-plus",
"messages": [
{
"role": "user",
"content": "杭州の天気はどうですか?"
}
],
"tools": [
{
"type": "function",
"function": {
"name": "get_current_time",
"description": "現在時刻を知りたいときに便利です。",
"parameters": {}
}
},
{
"type": "function",
"function": {
"name": "get_current_weather",
"description": "特定の都市の天気を調べたいときに便利です。",
"parameters": {
"type": "object",
"properties": {
"location":{
"type": "string",
"description": "市や県。例:北京、杭州、余杭区。"
}
},
"required": ["location"]
}
}
}
],
"enable_thinking": true,
"stream": true
}'DashScope
Python
サンプルコード
import dashscope
dashscope.base_http_api_url = "https://dashscope-intl.aliyuncs.com/api/v1/"
tools = [
# ツール 1:現在時刻を取得
{
"type": "function",
"function": {
"name": "get_current_time",
"description": "現在時刻を知りたいときに便利です。",
"parameters": {} # 入力パラメーターは不要なので空の辞書
}
},
# ツール 2:特定の都市の天気を取得
{
"type": "function",
"function": {
"name": "get_current_weather",
"description": "特定の都市の天気を調べたいときに便利です。",
"parameters": {
"type": "object",
"properties": {
# 天気検索には場所が必要なので、パラメーターは location
"location": {
"type": "string",
"description": "市や県。例:北京、杭州、余杭区。"
}
},
"required": ["location"]
}
}
}
]
# 質問を定義
messages = [{"role": "user", "content": input("質問を入力してください:")}]
# マルチモーダルモデルのメッセージ例
# messages = [
# {
# "role": "user",
# "content": [
# {"image": "https://img.alicdn.com/imgextra/i2/O1CN01FbTJon1ErXVGMRdsN_!!6000000000405-0-tps-1024-683.jpg"},
# {"text": "画像内の場所の天気はどうですか?"}]
# }]
# Generation の代わりにマルチモーダルモデルには MultiModalConversation インターフェイスを使用
completion = dashscope.Generation.call(
# モデル例。他のディープシンキングモデルに置き換えてください。
model="qwen-plus",
messages=messages,
enable_thinking=True,
tools=tools,
parallel_tool_calls=True,
stream=True,
incremental_output=True,
result_format="message"
)
reasoning_content = ""
answer_content = ""
tool_info = []
is_answering = False
print("="*20+"推論プロセス"+"="*20)
for chunk in completion:
if chunk.status_code == 200:
msg = chunk.output.choices[0].message
# 推論を処理
if 'reasoning_content' in msg and msg.reasoning_content:
reasoning_content += msg.reasoning_content
print(msg.reasoning_content, end="", flush=True)
# 返信コンテンツを処理
if 'content' in msg and msg.content:
if not is_answering:
is_answering = True
print("\n"+"="*20+"返信コンテンツ"+"="*20)
answer_content += msg.content
print(msg.content, end="", flush=True)
# ツール呼び出しを処理
if 'tool_calls' in msg and msg.tool_calls:
for tool_call in msg.tool_calls:
index = tool_call['index']
while len(tool_info) <= index:
tool_info.append({'id': '', 'name': '', 'arguments': ''}) # すべてのフィールドを初期化
# ツール ID を増分更新
if 'id' in tool_call:
tool_info[index]['id'] += tool_call.get('id', '')
# 関数情報を増分更新
if 'function' in tool_call:
func = tool_call['function']
# 関数名を増分更新
if 'name' in func:
tool_info[index]['name'] += func.get('name', '')
# パラメーターを増分更新
if 'arguments' in func:
tool_info[index]['arguments'] += func.get('arguments', '')
print(f"\n"+"="*19+"ツール呼び出し情報"+"="*19)
if not tool_info:
print("ツール呼び出しなし")
else:
print(tool_info)応答
「四直轄市の天気」と入力した場合の出力:
質問を入力してください:四直轄市の天気
====================推論プロセス====================
わかりました、ユーザーは四直轄市の天気を尋ねました。まず、どの都市が四直轄市であるかを確認する必要があります。北京、上海、天津、重慶ですよね?次に、ユーザーは各都市の天気を必要としているので、天気検索関数を呼び出す必要があります。
しかし、質問では「四直轄市」としか言っておらず、具体的な都市名は指定されていません。各直轄市の名前を明確にし、それぞれの天気を照会する必要があります。たとえば、北京、上海、天津、重慶です。それぞれの都市に対して get_current_weather 関数を呼び出し、対応する都市名をパラメーターとして渡す必要があります。たとえば、最初の呼び出しの場所は「北京」、2 番目は「上海」、3 番目は「天津」、4 番目は「重慶」です。
しかし、各呼び出しのパラメーターが正しく、省略がないことを確認する必要があります。これにより、ユーザーは 4 つの直轄市すべての完全な天気情報を取得できます。
===================ツール呼び出し情報===================
[{'id': 'call_2f774ed97b0e4b24ab10ec', 'name': 'get_current_weather', 'arguments': '{"location": "Beijing"}'}, {'id': 'call_dc3b05b88baa48c58bc33a', 'name': 'get_current_weather', 'arguments': '{"location": "Shanghai"}}'}, {'id': 'call_249b2de2f73340cdb46cbc', 'name': 'get_current_weather', 'arguments': '{"location": "Tianjin"}'}, {'id': 'call_833333634fda49d1b39e87', 'name': 'get_current_weather', 'arguments': '{"location": "Chongqing"}}'}]Java
サンプルコード
// DashScope SDK バージョン >= 2.19.4
import java.util.Arrays;
import com.alibaba.dashscope.exception.UploadFileException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.alibaba.dashscope.aigc.multimodalconversation.MultiModalConversation;
import com.alibaba.dashscope.aigc.multimodalconversation.MultiModalConversationParam;
import com.alibaba.dashscope.aigc.multimodalconversation.MultiModalConversationResult;
import com.alibaba.dashscope.common.MultiModalMessage;
import com.alibaba.dashscope.aigc.generation.Generation;
import com.alibaba.dashscope.aigc.generation.GenerationParam;
import com.alibaba.dashscope.aigc.generation.GenerationResult;
import com.alibaba.dashscope.common.Message;
import com.alibaba.dashscope.common.Role;
import com.alibaba.dashscope.exception.ApiException;
import com.alibaba.dashscope.exception.InputRequiredException;
import com.alibaba.dashscope.exception.NoApiKeyException;
import com.alibaba.dashscope.utils.Constants;
import com.alibaba.dashscope.utils.JsonUtils;
import com.alibaba.dashscope.tools.ToolFunction;
import com.alibaba.dashscope.tools.FunctionDefinition;
import io.reactivex.Flowable;
import com.fasterxml.jackson.databind.node.ObjectNode;
import java.lang.System;
import com.github.victools.jsonschema.generator.Option;
import com.github.victools.jsonschema.generator.OptionPreset;
import com.github.victools.jsonschema.generator.SchemaGenerator;
import com.github.victools.jsonschema.generator.SchemaGeneratorConfig;
import com.github.victools.jsonschema.generator.SchemaGeneratorConfigBuilder;
import com.github.victools.jsonschema.generator.SchemaVersion;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.Collections;
public class Main {
private static final Logger logger = LoggerFactory.getLogger(Main.class);
private static ObjectNode jsonSchemaWeather;
private static ObjectNode jsonSchemaTime;
static {Constants.baseHttpApiUrl="https://dashscope-intl.aliyuncs.com/api/v1";}
static class TimeTool {
public String call() {
LocalDateTime now = LocalDateTime.now();
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
return "現在の時刻: " + now.format(formatter) + ".";
}
}
static class WeatherTool {
private String location;
public WeatherTool(String location) {
this.location = location;
}
public String call() {
return location + " は今日晴れです";
}
}
static {
SchemaGeneratorConfigBuilder configBuilder = new SchemaGeneratorConfigBuilder(
SchemaVersion.DRAFT_2020_12, OptionPreset.PLAIN_JSON);
SchemaGeneratorConfig config = configBuilder
.with(Option.EXTRA_OPEN_API_FORMAT_VALUES)
.without(Option.FLATTENED_ENUMS_FROM_TOSTRING)
.build();
SchemaGenerator generator = new SchemaGenerator(config);
jsonSchemaWeather = generator.generateSchema(WeatherTool.class);
jsonSchemaTime = generator.generateSchema(TimeTool.class);
}
private static void handleGenerationResult(GenerationResult message) {
System.out.println(JsonUtils.toJson(message));
}
// テキスト生成モデル用のツール呼び出しメソッドを作成
public static void streamCallWithMessage(Generation gen, Message userMsg)
throws NoApiKeyException, ApiException, InputRequiredException {
GenerationParam param = buildGenerationParam(userMsg);
Flowable<GenerationResult> result = gen.streamCall(param);
result.blockingForEach(message -> handleGenerationResult(message));
}
// ツール呼び出しをサポートするテキスト生成モデルのパラメーターを構築
private static GenerationParam buildGenerationParam(Message userMsg) {
FunctionDefinition fdWeather = buildFunctionDefinition(
"get_current_weather", "指定された場所の天気を取得", jsonSchemaWeather);
FunctionDefinition fdTime = buildFunctionDefinition(
"get_current_time", "現在時刻を取得", jsonSchemaTime);
return GenerationParam.builder()
.apiKey(System.getenv("DASHSCOPE_API_KEY"))
.model("qwen-plus")
.enableThinking(true)
.messages(Arrays.asList(userMsg))
.resultFormat(GenerationParam.ResultFormat.MESSAGE)
.incrementalOutput(true)
.tools(Arrays.asList(
ToolFunction.builder().function(fdWeather).build(),
ToolFunction.builder().function(fdTime).build()))
.build();
}
// マルチモーダルモデル用のツール呼び出しメソッドを作成
public static void streamCallWithMultiModalMessage(MultiModalConversation conv, MultiModalMessage userMsg)
throws NoApiKeyException, ApiException, UploadFileException {
MultiModalConversationParam param = buildMultiModalConversationParam(userMsg);
Flowable<MultiModalConversationResult> result = conv.streamCall(param);
result.blockingForEach(message -> System.out.println(JsonUtils.toJson(message)));
}
// ツール呼び出しをサポートするマルチモーダルモデルのパラメーターを構築
private static MultiModalConversationParam buildMultiModalConversationParam(MultiModalMessage userMsg) {
FunctionDefinition fdWeather = buildFunctionDefinition(
"get_current_weather", "指定された場所の天気を取得", jsonSchemaWeather);
FunctionDefinition fdTime = buildFunctionDefinition(
"get_current_time", "現在時刻を取得", jsonSchemaTime);
return MultiModalConversationParam.builder()
.apiKey(System.getenv("DASHSCOPE_API_KEY"))
.model("qwen3-vl-plus") // マルチモーダルモデル Qwen3-VL を使用
.enableThinking(true)
.messages(Arrays.asList(userMsg))
.tools(Arrays.asList( // ツールリストを設定
ToolFunction.builder().function(fdWeather).build(),
ToolFunction.builder().function(fdTime).build()))
.build();
}
private static FunctionDefinition buildFunctionDefinition(
String name, String description, ObjectNode schema) {
return FunctionDefinition.builder()
.name(name)
.description(description)
.parameters(JsonUtils.parseString(schema.toString()).getAsJsonObject())
.build();
}
public static void main(String[] args) {
try {
Generation gen = new Generation();
Message userMsg = Message.builder()
.role(Role.USER.getValue())
.content("杭州の天気を教えてください")
.build();
try {
streamCallWithMessage(gen, userMsg);
} catch (InputRequiredException e) {
throw new RuntimeException(e);
}
// ツール呼び出しにマルチモーダルモデルを使用するには、コメントを解除してください
// MultiModalConversation conv = new MultiModalConversation();
// MultiModalMessage userMessage = MultiModalMessage.builder().role(Role.USER.getValue())
// .content(Arrays.asList(Collections.singletonMap("image", "https://img.alicdn.com/imgextra/i2/O1CN01FbTJon1ErXVGMRdsN_!!6000000000405-0-tps-1024-683.jpg"),
// Collections.singletonMap("text", "画像内の場所の天気はどうですか?"))).build();
// try {
// streamCallWithMultiModalMessage(conv,userMessage);
// } catch (UploadFileException e) {
// throw new RuntimeException(e);
// }
} catch (ApiException | NoApiKeyException e) {
logger.error("例外が発生しました:{}", e.getMessage());
}
System.exit(0);
}
}
応答
{"requestId":"4edb81cd-4647-9d5d-88f9-a4f30bc6d8dd","usage":{"input_tokens":238,"output_tokens":6,"total_tokens":244},"output":{"choices":[{"finish_reason":"null","message":{"role":"assistant","content":"","reasoning_content":"わかりました、ユーザーは"}}]}}
{"requestId":"4edb81cd-4647-9d5d-88f9-a4f30bc6d8dd","usage":{"input_tokens":238,"output_tokens":12,"total_tokens":250},"output":{"choices":[{"finish_reason":"null","message":{"role":"assistant","content":"","reasoning_content":"杭州の天気を教えてほしいとのことです。私"}}]}}
{"requestId":"4edb81cd-4647-9d5d-88f9-a4f30bc6d8dd","usage":{"input_tokens":238,"output_tokens":16,"total_tokens":254},"output":{"choices":[{"finish_reason":"null","message":{"role":"assistant","content":"","reasoning_content":"は、関連するツールがあるかどうかを判断する"}}]}}
{"requestId":"4edb81cd-4647-9d5d-88f9-a4f30bc6d8dd","usage":{"input_tokens":238,"output_tokens":22,"total_tokens":260},"output":{"choices":[{"finish_reason":"null","message":{"role":"assistant","content":"","reasoning_content":"必要があります。提供されたツールを見ると、get_current"}}]}}
{"requestId":"4edb81cd-4647-9d5d-88f9-a4f30bc6d8dd","usage":{"input_tokens":238,"output_tokens":28,"total_tokens":266},"output":{"choices":[{"finish_reason":"null","message":{"role":"assistant","content":"","reasoning_content":"_weather 関数があり、パラメーターは location"}}]}}
{"requestId":"4edb81cd-4647-9d5d-88f9-a4f30bc6d8dd","usage":{"input_tokens":238,"output_tokens":34,"total_tokens":272},"output":{"choices":[{"finish_reason":"null","message":{"role":"assistant","content":"","reasoning_content":"です。したがって、この関数をパラメーター"}}]}}
{"requestId":"4edb81cd-4647-9d5d-88f9-a4f30bc6d8dd","usage":{"input_tokens":238,"output_tokens":38,"total_tokens":276},"output":{"choices":[{"finish_reason":"null","message":{"role":"assistant","content":"","reasoning_content":"を杭州に設定して呼び出すべきです。"}}]}}
{"requestId":"4edb81cd-4647-9d5d-88f9-a4f30bc6d8dd","usage":{"input_tokens":238,"output_tokens":43,"total_tokens":281},"output":{"choices":[{"finish_reason":"null","message":{"role":"assistant","content":"","reasoning_content":"ユーザーは天気についてのみ尋ねているため、"}}]}}
{"requestId":"4edb81cd-4647-9d5d-88f9-a4f30bc6d8dd","usage":{"input_tokens":238,"output_tokens":48,"total_tokens":286},"output":{"choices":[{"finish_reason":"null","message":{"role":"assistant","content":"","reasoning_content":"他のツールは不要です。次に、tool_call を"}}]}}
{"requestId":"4edb81cd-4647-9d5d-88f9-a4f30bc6d8dd","usage":{"input_tokens":238,"output_tokens":52,"total_tokens":290},"output":{"choices":[{"finish_reason":"null","message":{"role":"assistant","content":"","reasoning_content":"構築し、名前とパラメーターを"}}]}}
{"requestId":"4edb81cd-4647-9d5d-88f9-a4f30bc6d8dd","usage":{"input_tokens":238,"output_tokens":56,"total_tokens":294},"output":{"choices":[{"finish_reason":"null","message":{"role":"assistant","content":"","reasoning_content":"入力します。パラメーターが"}}]}}
{"requestId":"4edb81cd-4647-9d5d-88f9-a4f30bc6d8dd","usage":{"input_tokens":238,"output_tokens":60,"total_tokens":298},"output":{"choices":[{"finish_reason":"null","message":{"role":"assistant","content":"","reasoning_content":"JSON 形式で、location が文字列であることを確認します。"}}]}}
{"requestId":"4edb81cd-4647-9d5d-88f9-a4f30bc6d8dd","usage":{"input_tokens":238,"output_tokens":64,"total_tokens":302},"output":{"choices":[{"finish_reason":"null","message":{"role":"assistant","content":"","reasoning_content":"エラーを確認して進みます。"}}]}}
{"requestId":"4edb81cd-4647-9d5d-88f9-a4f30bc6d8dd","usage":{"input_tokens":238,"output_tokens":68,"total_tokens":306},"output":{"choices":[{"finish_reason":"null","message":{"role":"assistant","content":"","reasoning_content":""}}]}}
{"requestId":"4edb81cd-4647-9d5d-88f9-a4f30bc6d8dd","usage":{"input_tokens":238,"output_tokens":73,"total_tokens":311},"output":{"choices":[{"finish_reason":"null","message":{"role":"assistant","content":"","reasoning_content":"","tool_calls":[{"type":"function","id":"call_ecc41296dccc47baa01567","function":{"name":"get_current_weather","arguments":"{\"location\": \"Hangzhou\"}}"}]}}]}}
{"requestId":"4edb81cd-4647-9d5d-88f9-a4f30bc6d8dd","usage":{"input_tokens":238,"output_tokens":78,"total_tokens":316},"output":{"choices":[{"finish_reason":"tool_calls","message":{"role":"assistant","content":"","reasoning_content":"","tool_calls":[{"type":"function","id":"","function":{"arguments":"\"}"}}]}}]}}HTTP
サンプルコード
curl
# ======= 重要事項 =======
# マルチモーダルモデルの場合、message パラメーターを変更し、URL を https://dashscope-intl.aliyuncs.com/api/v1/services/aigc/multimodal-generation/generation に変更してください
# API キーはリージョンによって異なります。API キーの取得方法:https://www.alibabacloud.com/help/model-studio/get-api-key
# 北京リージョンの場合は、URL を https://dashscope.aliyuncs.com/api/v1/services/aigc/text-generation/generation に変更します
# === 実行前にこのコメントを削除してください ===
curl -X POST "https://dashscope-intl.aliyuncs.com/api/v1/services/aigc/text-generation/generation" \
-H "Authorization: Bearer $DASHSCOPE_API_KEY" \
-H "Content-Type: application/json" \
-H "X-DashScope-SSE: enable" \
-d '{
"model": "qwen-plus",
"input":{
"messages":[
{
"role": "user",
"content": "杭州の天気はどうですか?"
}
]
},
"parameters": {
"enable_thinking": true,
"incremental_output": true,
"result_format": "message",
"tools": [{
"type": "function",
"function": {
"name": "get_current_time",
"description": "現在時刻を知りたいときに便利です。",
"parameters": {}
}
},{
"type": "function",
"function": {
"name": "get_current_weather",
"description": "特定の都市の天気を調べたいときに便利です。",
"parameters": {
"type": "object",
"properties": {
"location": {
"type": "string",
"description": "市や県。例:北京、杭州、余杭区。"
}
},
"required": ["location"]
}
}
}]
}
}'本番環境への適用
ツール呼び出し精度のテスト
評価システムの構築:
実際のビジネスシナリオを反映したテストデータセットを作成します。ツール選択の精度、パラメーター抽出の精度、エンドツーエンドの成功率など、明確なメトリックを定義します。
プロンプトの最適化
テストで見つかった問題 (誤ったツールの選択、不正確なパラメーター) に基づいて、システムプロンプト、ツールの説明、パラメーターの説明を改良します。これが中心的なチューニング方法です。
モデルのアップグレード
プロンプトエンジニアリングでパフォーマンスが向上しない場合、より高性能なモデル (例:
qwen3-max-preview) にアップグレードすることが、メトリックを向上させる最も直接的で効果的な方法です。
ツール数の動的制御
アプリケーションが数十または数百のツールを統合する場合、完全なツールライブラリをモデルに提供すると問題が発生します。
パフォーマンスの低下:モデルは大規模なライブラリから適切なツールを選択するのに苦労します。
コストとレイテンシー:大規模なツールの説明は多くの入力トークンを消費し、コストを増加させ、応答を遅くします。
解決策:モデルを呼び出す前にツールルーティングまたは取得レイヤーを追加します。ユーザーのクエリに基づいて、完全なライブラリから関連性の高いツールの小さなサブセットを迅速かつ正確にフィルタリングし、そのサブセットをモデルに提供します。
ツールルーティングの一般的な方法:
セマンティック検索
埋め込みモデルを使用してすべてのツールの説明 (
description) を前処理してベクトルを作成します。ベクトルをベクトルデータベースに保存します。クエリ時に、ユーザーのクエリをベクトルに変換し、ベクトル類似度検索を使用して、最も関連性の高い上位 K 件のツールを取得します。ハイブリッド検索
セマンティック検索の「あいまい一致」と、従来のキーワードまたはメタデータタグの「完全一致」を組み合わせます。ツールに
tagsまたはkeywordsを追加します。検索中に、ベクトル検索とキーワードフィルタリングの両方を実行して、高頻度または特定のシナリオでの検索精度を向上させます。軽量 LLM ルーター
複雑なルーティングロジックには、より小さく、高速で、安価なモデル (Qwen-Flash など) を「ルーターモデル」として使用します。その役割は、ユーザーの質問に基づいて関連するツール名のリストを出力することです。
ベストプラクティス
候補セットを小さく保つ:どの方法を使用する場合でも、メインモデルに渡すツールの数を20 以下に制限します。これにより、モデルの認知負荷、コスト、レイテンシー、精度のバランスが取れます。
階層型フィルタリング戦略:ファネル形式のルーティング戦略を構築します。たとえば、まず低コストのキーワードまたはルールマッチングを使用して、明らかに無関係なツールを除外し、次に残りのツールに対してセマンティック検索を実行して、効率と品質を向上させます。
ツールのセキュリティ原則
LLM にツール実行能力を付与する場合、セキュリティを最優先する必要があります。中心的な原則は「最小権限」と「人間による確認」です。
最小権限の原則:モデルに提供されるツールセットは、最小権限の原則に厳密に従う必要があります。デフォルトでは、ツールは読み取り専用 (天気検索やドキュメント検索など) であるべきです。状態やリソースを変更する「書き込み」権限への直接アクセスは避けてください。
危険なツールの隔離:任意のコード実行 (
code interpreter)、ファイルシステム操作 (fs.delete)、データベースの削除または更新操作 (db.drop_table)、または金融取引ツール (payment.transfer) などの危険なツールを LLM に直接与えないでください。人間の介在:すべての高権限または不可逆的なアクションには、人間によるレビューと確認を追加します。モデルはアクションリクエストを生成できますが、最終的な「実行」ボタンは人間のユーザーがクリックする必要があります。たとえば、モデルはメールの下書きを作成できますが、送信にはユーザーの確認が必要です。
ユーザーエクスペリエンスの最適化
関数呼び出しには多くのステップが含まれます。どのステップで失敗しても、ユーザーエクスペリエンスが低下します。
ツール実行失敗の処理
ツールの実行失敗はよくあることです。次の戦略を使用してください。
最大リトライ回数:繰り返しの失敗による長い待機時間やシステムリソースの浪費を避けるため、妥当なリトライ回数 (3 回など) を設定します。
フォールバックメッセージ:リトライが尽きた場合や解決不可能なエラーが発生した場合は、明確でフレンドリーなメッセージをユーザーに返します。例:「申し訳ありませんが、現在その情報を取得できません。サービスが混み合っている可能性があります。後でもう一度お試しください。」
処理遅延の処理
高いレイテンシーはユーザー満足度を低下させます。フロントエンドのインタラクションとバックエンドの最適化を通じて改善します。
タイムアウト値の設定:各関数呼び出しステップに、独立した妥当なタイムアウトを割り当てます。タイムアウト時には、直ちに停止してフィードバックを提供します。
即時フィードバックの提供:関数呼び出しを開始する際に、UI に「天気を検索しています…」や「関連情報を検索しています…」などのプロンプトを表示します。ユーザーにリアルタイムの進捗状況を更新します。
課金詳細
メッセージ配列のトークンに加えて、ツールの説明も課金対象のプロンプトの入力トークンとしてカウントされます。
システムメッセージによるツール情報の受け渡し
エラーコード
モデル呼び出しが失敗してエラーメッセージが返された場合は、「エラーメッセージ」をご参照いただき、解決方法を確認してください。