0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Responses APIのツール結果:call_idとitem.idの取り違えをローカルで確かめる

0
Posted at

Responses APIで関数の実行結果を返すとき、function_call_output.call_idには、元のfunction_call.call_idを入れます。呼び出し項目のidをそのまま入れる実装を見分けるには、テスト用の二つの値を別にすると確認できます。

この記事では、通信せずに動く小さな確認コードを使います。APIキーも追加パッケージも不要です。

二つのIDを別の値にする

次は説明用に作った呼び出し項目です。実際のAPI応答ではありません。

{
  "type": "function_call",
  "id": "fc_item_synthetic_7",
  "call_id": "call_synthetic_42",
  "name": "say",
  "arguments": "{\"text\":\"\"}",
  "status": "completed"
}

idは項目の識別子、call_idはツール結果を対応付ける呼び出しの識別子です。OpenAI公式のFunction callingガイドでも、結果のcall_idには元の呼び出しのcall_idを使っています。文字列の接頭辞から推測せず、返されたフィールドを使います。

ここではアプリ側が空の発話を拒否したと仮定します。拒否した理由も、その呼び出しに対応する結果として返す形にします。

{
  "type": "function_call_output",
  "call_id": "call_synthetic_42",
  "output": "Empty text was refused. Supply nonempty text or finish."
}

この例のcall_idをfc_item_synthetic_7に置き換えると、元の呼び出しのcall_idとは一致しなくなります。二つを同じ値にしたテストデータでは、この取り違えが表に出ません。

そのまま実行できる最小例

次をcheck_call_id.pyとして保存し、python check_call_id.pyで実行できます。Python 3の標準ライブラリだけを使います。

import copy

call = {
    "type": "function_call",
    "id": "fc_item_synthetic_7",
    "call_id": "call_synthetic_42",
    "name": "say",
    "arguments": '{"text":""}',
    "status": "completed",
}


def make_result(item):
    return {
        "type": "function_call_output",
        "call_id": item["call_id"],
        "output": "Empty text was refused. Supply nonempty text or finish.",
    }


def check_link(item, result):
    if result.get("type") != "function_call_output":
        raise ValueError("wrong result type")
    if result.get("call_id") != item["call_id"]:
        raise ValueError("result must reference the call_id")


good = make_result(call)
wrong_item = copy.deepcopy(good)
wrong_item["call_id"] = call["id"]
wrong_other = copy.deepcopy(good)
wrong_other["call_id"] = "call_unrelated_99"

assert call["id"] != call["call_id"]
for label, result, expected in [
    ("call_id", good, True),
    ("item.id", wrong_item, False),
    ("unrelated", wrong_other, False),
]:
    try:
        check_link(call, result)
        accepted = True
    except ValueError:
        accepted = False
    assert accepted == expected, label
    print(label + (": PASS" if accepted else ": REJECT"))

出力は次のとおりです。

call_id: PASS
item.id: REJECT
unrelated: REJECT

アダプターに組み込むなら、make_resultを自分の変換処理に置き換え、返った結果に同じ対応付けの検査をかけます。この例のcheck_linkは単一の呼び出しを前提にした確認用で、APIの入力全体を検証するものではありません。

会話の引き継ぎは別に確認する

このスクリプトはIDの対応だけを検査します。APIへ次のリクエストを送るコードは含めていません。

履歴を手元で管理する場合は、元の呼び出しを含む必要な出力項目も次の入力に引き継ぎます。上の呼び出し項目と結果だけで、すべてのモデルの次リクエストが完成するわけではありません。公式ガイドは、ツール呼び出しと一緒に返ったreasoning項目も引き継ぐよう説明しています。function_callだけを抽出して他の項目を捨てる処理とは分けて確認してください。

2026-10-31に上記コードをローカルで実行し、正例1件と意図的な誤り2件の結果を確認しました。API呼び出し、特定サービスの互換性、ストリーミング、複数呼び出しの対応は検証していません。

筆者は第三者API中継サービスElysia APIの運営者です。本文・コードの作成と確認にAIを使用しました。コードと架空データは本記事用の例です。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?