OpenAIのChat Completions APIを使用する際、ストリーミングモードでファンクションコールを処理することは、リアルタイムな応答と高度な機能を組み合わせる上で重要です。本記事では、この処理を効率的に行うための2つのアプローチを紹介します。
アプローチ1: シンプルで直接的な方法
このアプローチは、単一のファンクションコールを期待する場合に適しています。
let funcCall = {
name: null,
arguments: ""
};
for (let res of response) {
let delta = res.choices[0].delta;
if ('function_call' in delta) {
if ('name' in delta.function_call) {
funcCall.name = delta.function_call.name;
}
if ('arguments' in delta.function_call) {
funcCall.arguments += delta.function_call.arguments;
}
}
if (res.choices[0].finish_reason === "function_call") {
// ここでfuncCallを使用してファンクションを呼び出す
return;
}
if (delta.content) {
process.stdout.write(delta.content);
}
}
コツ
- シンプルな構造で理解しやすい
- 明確な終了条件がある
- メモリ効率が良い
アプローチ2: 柔軟で即時処理可能な方法
このアプローチは、複数のファンクションコールや部分的な結果の即時表示が必要な場合に適しています。
let toolCallAccumulator = '';
let toolCallId = null;
for await (const part of stream) {
const content = part.choices[0]?.delta?.content || '';
const toolCalls = part.choices[0]?.delta?.tool_calls;
if (toolCalls) {
for (const tc of toolCalls) {
if (tc.id) toolCallId = tc.id;
toolCallAccumulator += tc.function?.arguments || '';
try {
const funcArgs = JSON.parse(toolCallAccumulator);
const functionName = tc.function?.name || 'defaultFunction';
const funcResponse = await callFunction(functionName, funcArgs);
result += \n関数結果: ${funcResponse}\n;
toolCallAccumulator = '';
} catch (error) {
// JSONが不完全な場合は続けて蓄積
}
}
} else {
result += content;
}
updateUI(result);
}
コツ
- 複数のファンクションコールに対応
- JSON解析を即時試行
- 結果を即座に統合してUIに反映
選択の基準
- 単一のファンクションコール: アプローチ1
- 複数のファンクションコールや即時表示: アプローチ2
- パフォーマンス重視: アプローチ1
- 柔軟性重視: アプローチ2
まとめ
ストリーミングAPIでのファンクションコール処理は、アプリケーションの要件に応じて適切なアプローチを選択することが重要です。シンプルさと効率性を重視するか、柔軟性と即時性を重視するかによって、最適な方法が変わってきます。両方のアプローチのコツを理解し、状況に応じて適切に使い分けることで、より効果的なアプリケーション開発が可能になります。