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?

DeepSeek V4 Pro (0813)をAPIで使って分かった、AIエージェント実装の落とし穴

0
Posted at

DeepSeek V4 Pro (0813) は、1M トークンのコンテキストと長い出力上限を持つ、DeepSeek V4 系の上位モデルです。

一方、AIエージェントやツール呼び出しに組み込むと、単発チャットでは見えにくい注意点があります。特に厄介なのが、思考(thinking)の履歴も会話状態として保持しなければならないことです。

私は AIHubMix に所属しており、本稿では AIHubMix の API 経由で行った検証結果を扱います。サービス紹介ではなく、実装時に再利用できる知見に絞ってまとめます。検証日は 2026 年 8 月 13 日です。

先に結論

  • thinking はデフォルトで有効
  • thinking を有効にしたマルチターン、特にツール呼び出しでは、前ターンの思考内容をそのまま再送する
  • Chat Completions / Responses / Messages では、同じ機能でもフィールド名と構造が異なる
  • thinking 中は tool_choice: "required" が 400 になる。特定の関数名を指定するか、thinking を無効にする
  • 画像入力はエラーにならず、プレースホルダーに置き換えられる場合がある。「200 が返った = 画像を見た」ではない
  • コンテキストキャッシュは自動で有効になる

モデルの前提

項目 内容
AIHubMix 上のモデル名 deepseek-v4-pro-0813
コンテキスト 1M tokens
最大出力 384K(検証で確認できた上限は 393,216 tokens)
入力 テキストのみ
thinking デフォルト有効。無効化可能
対応 API Chat Completions / Responses / Claude 互換 Messages

1. thinking の無効化方法が API ごとに違う

同じ「思考をオフにする」操作でも、3 つの API では指定方法が異なります。

Chat Completions

from openai import OpenAI

client = OpenAI(
    base_url="https://aihubmix.com/v1",
    api_key="<AIHUBMIX_API_KEY>",
)

completion = client.chat.completions.create(
    model="deepseek-v4-pro-0813",
    messages=[{"role": "user", "content": "2 + 2 は?"}],
    extra_body={"thinking": {"type": "disabled"}},
)

無効化すると、レスポンスの reasoning_contentreasoning_tokens が両方なくなります。

Responses API

response = client.responses.create(
    model="deepseek-v4-pro-0813",
    input="2 + 2 は?",
    reasoning={"effort": "none"},
)

Responses API では独立した on/off スイッチではなく、reasoning.effort="none" を使います。無効化できていれば、思考トークンは 0 になり、type="reasoning" の出力項目も消えます。

Claude 互換 Messages API

from anthropic import Anthropic

client = Anthropic(
    api_key="<AIHUBMIX_API_KEY>",
    base_url="https://aihubmix.com",
)

response = client.messages.create(
    model="deepseek-v4-pro-0813",
    max_tokens=1024,
    messages=[{"role": "user", "content": "2 + 2 は?"}],
    extra_body={"thinking": {"type": "disabled"}},
)

こちらは Chat Completions と同じ thinking: {"type": "disabled"} です。無効化すると thinking block が消え、text block だけが返ります。

2. マルチターンで突然 400 になる理由

今回いちばん実装に影響したのはここです。

thinking モードでツール呼び出しを含む会話を続ける場合、前ターンの思考内容を履歴から落とすと 400 になります。最終回答のテキストだけを保存する実装では不十分です。

API 履歴として再送するもの
Chat Completions assistant message の reasoning_content
Responses type="reasoning" の output item
Messages assistant content 内の thinking block

Chat Completions では、assistant message を次のように保持します。

messages = [
    {"role": "user", "content": "1 + 1 を覚えて"},
    {
        "role": "assistant",
        "content": "2",
        "reasoning_content": previous_reasoning_content,
    },
    {"role": "user", "content": "その結果に 1 を足して"},
]

Responses API は、前回の response.output を加工せず履歴へ戻すのが安全です。

next_input = previous_input + response.output + [
    {"role": "user", "content": "その結果に 1 を足して"}
]

よくある失敗は、履歴を組み立てるときに type == "message" だけを残すことです。このフィルタで reasoning item が消え、次のリクエストが 400 になります。

私なら、会話履歴のデータモデルを「ユーザーに見せる本文」だけで設計せず、プロバイダーから返った assistant turn をプロトコル単位で保存するようにします。

3. ツール定義の形も共通ではない

3 API のツール定義は似ていますが、そのまま差し替えることはできません。

API 主な構造
Chat Completions {"type":"function","function":{...}}
Responses {"type":"function","name":"...", "parameters":{...}}
Messages {"name":"...", "input_schema":{...}}

特に注意したいのは tool_choice です。

Chat Completions と Responses では、thinking が有効なまま tool_choice: "required" を送ると 400 になります。thinking を残したまま必ず特定のツールを呼ばせたい場合は、関数名を明示します。

tool_choice={
    "type": "function",
    "function": {"name": "get_weather"},
}

また、Responses と Messages では並列ツール呼び出しを無効化する指定が無視され、常に並列呼び出しが有効です。直列実行が必要なら、クライアント側でキュー制御する必要があります。

4. 構造化出力は Responses API が扱いやすい

JSON Schema を厳密に適用したい場合は Responses API が分かりやすいです。

response = client.responses.create(
    model="deepseek-v4-pro-0813",
    input="キー a に整数 1 を入れて返して",
    text={
        "format": {
            "type": "json_schema",
            "name": "extract",
            "strict": True,
            "schema": {
                "type": "object",
                "properties": {"a": {"type": "integer"}},
                "required": ["a"],
            },
        }
    },
)

Chat Completions では response_format={"type": "json_object"} が使えます。Messages API には同等の専用フィールドがないため、厳密なスキーマが必要ならツールの input_schema に載せるか、Chat / Responses を選ぶ方が素直です。

5. 「エラーにならない」ことを能力確認に使わない

画像やファイルは未対応です。ただし Responses API の input_image は必ずしもエラーにならず、プレースホルダーテキストへ置き換えられます。

そのため、マルチモーダルルーターでは HTTP ステータスだけを見て対応可否を判定してはいけません。モデルごとの capability table を持ち、画像対応モデルへ明示的に振り分けるべきです。

同じ考え方は未対応パラメータにも当てはまります。200 で返ってきても、値が無視されている可能性があります。

6. コンテキストキャッシュは自動

キャッシュを有効にするパラメータはありません。同じ長い prefix を再利用すると自動でヒットし、API ごとに次の usage フィールドへ現れます。

API キャッシュヒットの確認先
Chat Completions prompt_tokens_details.cached_tokens
Responses input_tokens_details.cached_tokens
Messages cache_read_input_tokens

エージェント実装では、system prompt、共通知識、ツール定義など、変化しにくい内容を前方へ置くとキャッシュを活かしやすくなります。ツールを大量に宣言すると、その定義自体も入力トークンになるため、シナリオごとに必要なツールだけ渡す方がよいです。

実装チェックリスト

  • assistant turn は thinking を含めて保存する
  • API ごとに adapter を分け、共通オブジェクトを無理に使い回さない
  • thinking と tool_choice="required" を同時に使わない
  • 並列ツール呼び出しをクライアント側で制御できるようにする
  • 画像対応はレスポンスの成功可否ではなく capability table で判定する
  • キャッシュヒット数を usage から観測する
  • 4xx の判定は error.type 文字列だけでなく HTTP ステータスも使う

まとめ

DeepSeek V4 Pro (0813) は、長いコンテキスト、thinking、ツール呼び出し、構造化出力を組み合わせられるモデルです。一方で、実装上は「思考内容も会話履歴の一部」という前提が重要です。

単発の API コールが通るだけでは、エージェントとして安定稼働するかは分かりません。少なくとも、thinking を含むマルチターン、ツール結果の再送、構造化出力、未対応入力の扱いまでテストしてから組み込むのがおすすめです。

参考

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?