← 技術実践に戻る

AIAgent

Kimi K3 Tool Calls循環対策:2026年止血手順

約 12 分で読めます

Kimi K3 Tool Calls循環対策:2026年止血手順

公式のトラブルシューティング資料では、同じツールと同じ引数の連続呼び出しに対して、3回、5回、8回の段階的な注意を追加する例が示されています。これは全Agent共通の上限ではなく、重複検知を実装する際の参考値です。(公式のトラブルシューティング資料)

症状:同じ関数が繰り返し実行され、Agentが終了しない。
最短の解決策:まずassistantメッセージ、tool_call_id、ツール結果、ストリーミング引数を保存し、メッセージ連鎖が正しいと確認してから重複検知と熔断を追加します。

この手順は、Kimi K3 APIでTool Callsを使う開発者、無人運転のワークフローを管理するプラットフォーム担当者、発信・注文登録・データ更新など書き込み副作用のあるツールを運用するチーム向けです。単なるプロンプト調整ではなく、再現、修正、保護、受け入れ確認まで進めたい場合に使ってください。

先に止血してください。 書き込み系ツールが循環中なら、本番の発信、決済、建て付け登録、データ更新を一時停止し、読み取り専用またはサンドボックスへ切り替えます。原因調査より先に副作用を止めることが重要です。

最終更新日:2026年8月3日。内容は、同日確認した公式のAPI概要、Tool Calls資料、トラブルシューティング資料を基にしています。Kimi側でツール呼び出しの形式、finish_reason、ストリーミング項目、重複呼び出しの推奨方法が変更された場合は、再確認が必要です。

0分:再現前の証拠保全

画面に同じ関数名が表示されたからといって、すぐにモデルが同じ処理を何度も実行したと判断しないでください。少なくとも次の3つを分ける必要があります。

  • クライアント画面だけが同じ結果を二重表示している
  • SDKやHTTP層が同じリクエストを再送している
  • Kimi K3が実際に同じTool Callsを返している

脱敏したリクエストと応答を、1回の呼び出し単位で保存します。APIキー、個人情報、注文番号、顧客IDは置き換え、次の項目は残してください。

  • messagesの送信順序
  • finish_reason
  • tool_calls内のid
  • function.name
  • function.arguments
  • 実際に実行したツール名と実行ID
  • ツール結果の要約
  • request_idusage
  • SDKの再試行記録、タイムアウト、接続切断

公式資料でも、クライアントに結果が表示されなくてもサーバー側で処理が完了し、利用記録が残る場合があると説明されています。表示ログだけで費用や実行回数を判断せず、API応答と利用記録を突き合わせてください。(APIトラブルシューティング)

15分:メッセージ連鎖の照合

最初に非ストリーミングの最小リクエストを実行します。これで循環が消えるなら、ツール定義そのものより、ストリーミング処理、SDKラッパー、画面側の状態管理が原因である可能性が高まります。

finish_reason=tool_callsが返った場合、assistantのメッセージを加工せず、そのまま次のmessagesへ追加します。その後、返されたすべてのtool_callに対して、対応するrole=toolメッセージを追加してください。

{
  "role": "tool",
  "tool_call_id": "<CALL_ID_FROM_ASSISTANT>",
  "content": "<REDACTED_TOOL_RESULT>"
}

確認すべき条件は次のとおりです。

  1. assistantメッセージを省略していないこと
  2. assistantのtool_callsと同じ順序で結果を返していること
  3. すべてのtool_callに結果があること
  4. tool_call_idが1文字単位で一致していること
  5. ツール結果を別のassistantメッセージとして誤って登録していないこと

公式のTool Calls例でも、assistantの応答をmessagesへ追加し、同じIDを持つrole=toolメッセージを返す構成が示されています。(Tool Callsの公式ガイド)

観測した症状 まず疑う層 先に行う確認 回避策
画面だけ二重表示される UIまたは状態管理 同一request_idか確認 表示側の重複排除
同じIDの結果が再送される SDKまたは再試行処理 HTTPログと再試行回数を確認 リクエスト単位の再試行制御
異なるIDで同じ関数が続く メッセージ連鎖またはモデル判断 引数、結果、assistant履歴を比較 重複検知と停止状態
ストリームだけ失敗する 分片結合 indexとargumentsの原文を確認 非ストリームで再現後に修正
書き込みが重複する ツール実装 冪等キーと実行記録を確認 冪等処理、確認、熔断

tool_call_idの不一致は、単なる表示上の問題ではありません。モデルから見れば、実行した呼び出しに対する結果が届いていない状態になるため、同じ要求を続ける要因になります。(Tool Callsの公式実装例)

30分:ストリーミング分片の復元

ストリーミングでは、関数名とIDが最初の分片に現れ、function.argumentsが後続の分片へ分割されます。複数のTool Callsが同時に返る場合は、indexを使って呼び出しごとにargumentsを結合する必要があります。(ストリーミングTool Callsの公式資料)

実装では、次のように呼び出しごとのバッファーを分けます。

calls = {}

for chunk in stream:
    for item in chunk.choices[0].delta.tool_calls or []:
        index = item.index

        if index not in calls:
            calls[index] = {
                "id": "",
                "name": "",
                "arguments": ""
            }

        if item.id:
            calls[index]["id"] = item.id

        if item.function and item.function.name:
            calls[index]["name"] = item.function.name

        if item.function and item.function.arguments:
            calls[index]["arguments"] += item.function.arguments

for call in calls.values():
    # 完全なJSONとして検証できるまで実行しない
    arguments = parse_complete_json(call["arguments"])
    execute_tool(call["name"], arguments, call["id"])

ここで避けたいのは、JSONパーサーが壊れた文字列を自動修正し、そのままツールを実行することです。引用符の欠落、エスケープの崩れ、分片の順序逆転を静かに補正すると、モデルが意図していない宛先や対象へ書き込む危険があります。

保存するログは、結合後のJSONだけでは不十分です。index、各分片の到着順、元のarguments文字列、結合後の文字列、解析結果を残してください。異常が再発したとき、最終JSONだけでは「モデルが誤ったのか、クライアントが壊したのか」を区別できません。

実行条件を狭くしてください。 argumentsが完全なJSONであり、必須項目がそろい、許可された値の範囲に入り、現在のタスク状態と矛盾しない場合だけ実行します。構文が通ることと、安全に実行できることは別条件です。

45分:重複と無進展の判定

メッセージ連鎖とストリーミング結合が正しいなら、次に本当の循環かを判定します。重複キーは、関数名だけにしないでください。最低限、次の組み合わせを正規化して比較します。

重複キー =
ツール名
+ 正規化した引数
+ 前回結果の要約または結果ハッシュ

同じ関数でも、対象ファイル、検索条件、ページ番号、状態値が変わっていれば、必ずしも循環ではありません。一方、同じツール、同じ引数、同じ結果が続き、新しい状態変化がない場合は、無進展の反復として扱います。

公式資料では、同じツールと同じ引数の連続呼び出しについて、3回で注意を追加し、5回でより強い注意を加え、8回でも繰り返す例が示されています。ただし、これは公式資料にある実装例であり、すべてのAI Agentに適用する固定上限ではありません。(重複呼び出しの公式推奨)

判断は次の条件分岐にすると、運用担当者が迷いにくくなります。

  • 同じ関数でも引数または対象状態が変わる場合:処理を継続し、状態変化をログへ記録します。
  • 同じ関数、同じ引数だがツール結果に新しい情報がある場合:結果の差分を確認してから継続します。
  • 同じ関数、同じ引数、同じ結果で進展がない場合:次のモデル要求へ進まず、停止状態を返します。
  • 書き込み副作用があり、重複キーが一致する場合:ツール側で既存実行の結果を返し、新しい書き込みを行いません。
  • 判定に必要な情報が欠けている場合:成功扱いにせず、要確認として人間へ渡します。

停止時に「完了しました」と偽の成功結果を返すのは避けてください。利用者には、どのツールが、どの条件で、なぜ停止したかを示し、最後に安全に確定したチェックポイントから再開できる状態を渡します。

初日:副作用と熔断の実装

プロンプトに「同じツールを繰り返さないでください」と書くことは補助策にすぎません。APIの再試行、ネットワーク切断、SDKの状態不整合、モデルの再判断を止める強制力はないためです。

本番へ出す前に、ツール単位で次の制限を持たせます。

  • タスク単位の最大Tool Callsラウンド
  • タスク全体の累積実行時間
  • API利用量または累積費用
  • 書き込み系ツールの実行回数
  • 同一キーの再実行ポリシー
  • 人間へ引き継ぐ条件
  • 最後に安全に確定したチェックポイント

発信、支払い、注文登録、データ更新では、冪等キーを業務側の一意キーに結び付けます。例えば、<TASK_ID>-<ACTION_TYPE>のようなキーを作り、同じキーの再実行時には新規処理ではなく、既存の実行結果を返す設計にします。乱数だけでキーを作ると、再試行時に別処理として扱われるため、循環対策になりません。

API応答のusagerequest_idも、ツール実行IDと一緒に保存します。公式のAPI概要では、HTTP経由でChat Completionsを呼び出す場合のエンドポイントやAPIの基本構成が案内されています。(API概要の公式資料)

1週間:本番受け入れ

受け入れでは、少なくとも次の4種類を同じログ形式で流します。

  1. 正常に1回で完了する呼び出し
  2. 引数が変化しながら継続する呼び出し
  3. 同一関数・同一引数・無進展の反復
  4. ネットワーク切断後に再試行される呼び出し

確認するのは「モデルが止まったか」だけではありません。熔断後にバックグラウンドの非同期処理が残っていないか、同じ副作用が別ワーカーで再実行されていないか、停止理由が監査ログに残っているかを見ます。

最低限、次の3つのIDを追跡できるようにしてください。

  • APIのrequest_id
  • ツール実行ID
  • 業務上の冪等キー

この3つとusageを結び付けると、画面上は1回に見える処理が、実際には再試行で複数回課金されていないか、書き込みが二重になっていないかを確認できます。APIのデバッグ用資料でも、完全なリクエストを取得して調査する方法が案内されています。(APIデバッグ手順)

よくある確認事項

Kimi K3が同じ関数を何度も呼び出すのはなぜですか?

assistantのTool Callsと対応するrole=toolメッセージが正しい順番で戻っているかを確認してください。同じ関数名、同じ引数、同じ結果が続くなら、モデルが新しい進展を受け取れていない可能性があります。メッセージ連鎖を直しても無進展が続く場合は、クライアント側で停止状態を作ります。

tool_call_idの返し間違いでツール循環は起きますか?

起きる可能性があります。assistantが返したtool_call.idと、結果側のtool_call_idが一致しないと、実行結果が対応付けられません。結果を受け取っていないと判断されれば、同じ要求が続くことがあります。API応答のIDを加工せず保存し、すべての呼び出しに対応する結果を返してください。

ストリーミングのTool Calls引数はどう結合しますか?

関数名やIDを最初の分片から取得し、argumentsは後続分片をindexごとに順番どおり結合します。完全なJSONとして解析できるまで実行しないでください。結合前の各分片も保存すれば、引数の破損がモデル由来か、クライアント処理由来かを切り分けられます。

副作用のあるツールをAgentが繰り返す場合、どう止めますか?

プロンプトだけでは止まりません。冪等キー、実行済み結果の再利用、確認段階、トランザクション境界、最大実行時間、書き込み回数の上限を組み合わせます。循環を検出したときは成功結果を偽造せず、停止理由と再開可能なチェックポイントを返してください。

Kimi K3のツール呼び出しは何ラウンドまで許可すべきですか?

固定値をそのまま採用せず、業務リスクから決めてください。読み取り中心なら通常系の完了に必要な回数を基準にし、書き込みを含む場合はより厳しく設定します。公式資料にある3回、5回、8回の注意例は、重複検知の段階的な介入例であり、すべてのAgent向けの共通上限ではありません。

Kimi K3のTool Calls循環を止めるには、モデルへ強く命令するより、証拠を残してメッセージ連鎖を直し、完全な引数だけを実行し、無進展を検出した時点で副作用を遮断する方が確実です。長時間の回帰サンプルを無人で回す場合、手元のMacだけに任せると、スリープ、回線切断、ログ保存先の不足、開発作業とのリソース競合が障害になります。必要な期間だけ常時接続とログ保存を確保したいなら、日本向けのMacレンタル環境でサンドボックスを動かし、まず1回の循環を安全に再現してから本番へ戻す方法が現実的です。詳しいサービス情報はKvmkitの日本語案内で確認できます。

よくある質問

Kimi K3が同じ関数を何度も呼び出すのはなぜですか?

まず、assistantのtool_callsと対応するrole=toolメッセージが正しい順番でmessagesへ戻っているか確認します。同じ関数名、同じ引数、同じ結果が続く場合は、モデルの判断だけでなく、結果が新しい進展として伝わっていない可能性があります。クライアント側で重複を検知し、処理を停止できるようにします。

tool_call_idの返し間違いでツール循環は起きますか?

起きる可能性があります。assistantが返したtool_callのidと、ツール結果を入れるrole=toolメッセージのtool_call_idが一致しないと、モデルは実行結果を対応付けられません。結果を受け取っていない状態と判断し、同じ要求を再び出すことがあります。IDは表示名ではなく、API応答の値をそのまま保存してください。

ストリーミングのTool Calls引数はどう結合しますか?

ストリーミングでは、関数名やIDが最初の分片に入り、argumentsが後続分片へ分かれる場合があります。tool_callsが複数あるときはindexごとに別バッファーを持ち、順番を保って文字列を連結します。完全なJSONとして解析できるまで実行せず、元の分片と結合後の値を両方記録してください。

副作用のあるツールをAgentが繰り返す場合、どう止めますか?

発信、決済、注文登録、データ更新などは、重複検知だけに頼らず、冪等キー、確認段階、トランザクション境界を組み合わせます。実行前に安全な状態を保存し、同じキーの再実行では既存結果を返す設計にします。異常時は成功したように見せず、停止理由と再開地点をオペレーターへ渡します。

Kimi K3のツール呼び出しは何ラウンドまで許可すべきですか?

一律の正解はありません。読み取り中心の処理と、決済や更新を含む処理では許容範囲が異なります。まず業務単位で最大ラウンド、累積時間、累積費用、書き込み回数を決め、通常系と異常系のサンプルで調整してください。公式の3回、5回、8回という注意例は、普遍的な運用上限ではありません。

M4 Mac mini で CI/CD を回すのが一番ラク

Xcode, Fastlane, CocoaPods, and SPM are first-class on macOS. Mac mini M4 unified memory keeps signing and archiving smooth; ~4W standby power suits 24/7 build nodes.

View Kvmkit plans

技術サポートや選定のアドバイスが必要ですか?

Mac インスタンスや CI/CD パイプラインで問題があれば、まずヘルプセンターをご覧ください。