deepseek-harness について
deepseek-harness は、OpenAI 互換 API 経由で DeepSeek V4-Pro と V4-Flash を呼び出すためのスキル兼実装ガイドです。10 項目のリクエスト・ストリーミング規則を示し、Python、CLI、MCP サーバー、依存関係を抑えた方法を用意しています。
主な機能
- 0110 項目の API 契約ルール
- 02Python ライブラリと CLI の選択肢
- 03安全な初期化スクリプト
- 04ストリーミングとツール呼び出しの処理
- 05問題再現用の実行可能なプローブ
活用シーン
- 01DeepSeek V4 API リクエストの設定
- 02複数ターンの呼び出しで推論データを保持
- 03並列ツール呼び出しのストリーム処理
- 04記載された API 動作の再現
MCP 対応
`npx -y @deepseek-harness/mcp` で導入でき、MCP 設定に組み込む MCP サーバーの選択肢を紹介しています。
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ henryz838978/ deepseek-harness --skill deepseek-harnessインストール後、Claude Code で/deepseek-harnessを入力して読み込まれたか確認します。
同梱ファイルreference/findings.mdscripts/safe_init.py
詳細
DeepSeek V4 ハーネス
OpenAI 互換 API 経由で DeepSeek V4-Pro または V4-Flash を呼び出す場合は、以下の 10 個の契約ルールに必ず従ってください。いずれか 1 つでも違反すると、本番環境で不具合が発生することが確認されています(各ルールには、上流の問題と再現プローブを記載しています)。
影響範囲の大きさ順に並べた 10 個の契約ルール
C1 · タスクで本当に推論が必要な場合を除き、thinking を無効にする
deepseek-v4-pro はデフォルトで thinking=enabled になっています。そのため、単純なプロンプトでも呼び出しごとに 30~300 個の reasoning_tokens が消費されます。
client.chat.completions.create(
model="deepseek-v4-pro",
messages=messages,
extra_body={"thinking": {"type": "disabled"}}, # ← saves money on every non-reasoning call
)
TypeScript / openai-node では、thinking を extra_body の中ではなく、リクエストのトップレベルに指定してください(JS SDK は不明なトップレベルキーをそのまま渡します)。
await openai.chat.completions.create({
model: "deepseek-v4-pro",
messages,
thinking: { type: "disabled" },
} as any);
C2 · 複数ターンのループでは、アシスタントメッセージの reasoning_content を保持する
thinking が有効で、tool_calls を含む過去のアシスタントメッセージを再送する場合は、元の reasoning_content フィールドを必ず含めてください。そうしないと、次のリクエストで以下が返されます。
HTTP 400: The reasoning_content in the thinking mode must be passed back to the API.
(reports/probes/probe_2_reasoning_lifecycle.py で再現。2026-05-09 に V4-Pro と V4-Flash で各 3 回中 3 回。)
msg = response.choices[0].message
history.append({
"role": "assistant",
"content": msg.content,
"tool_calls": _serialize_tool_calls(msg.tool_calls),
"reasoning_content": getattr(msg, "reasoning_content", None), # ← REQUIRED
})
新しいユーザーターンが始まった場合、過去のアシスタントメッセージから reasoning_content を削除してもかまいません。DeepSeek はユーザーターンをまたいでこれを必要とせず、保持するとプレフィックスキャッシュのキーが肥大化します。
C3 · max_tokens を必ず設定する(デフォルトは 4096)
出力上限がないと、reasoning_content が 8000 個以上のチャンクをストリーミングすることがあります(probes/probe_9_reasoning_runaway.py では、自信を失うプロンプトで 26 KB / 84 秒を計測)。その結果、下流の Electron クライアント(ChatWise、Cherry Studio)は、文字列バッファーが V8 の上限である 512 MB に達すると RangeError: Invalid string length でクラッシュします。
C4 · ストリーミングでは、並列の tool_calls をリストの順序ではなく tc.index で集約する
DeepSeek は並列のツール呼び出し間でチャンクをインターリーブします(probe_7 では、V4-Pro と V4-Flash の各 3 回の試行すべてでインターリーブを確認)。dict[int, slot] を使用してください。
tool_call_acc: dict[int, dict] = {}
for chunk in stream:
for tc in (chunk.choices[0].delta.tool_calls or []):
slot = tool_call_acc.setdefault(tc.index, {"id": None, "name": None, "arguments": ""})
if tc.id: slot["id"] = tc.id
if tc.function and tc.function.name: slot["name"] = tc.function.name
if tc.function and tc.function.arguments: slot["arguments"] += tc.function.arguments
C5 · ストリーミングでは、state += chunk ではなく、リストバッファーと "".join を使用する
DeepSeek は推論チャンクごとに 1~3 文字をストリーミングします。state.text += chunk は O(n²) の文字列割り当てになります。
buf = []
for chunk in stream:
if c := (chunk.choices[0].delta.content or ""):
buf.append(c)
final = "".join(buf)
C6 · 空のストリームチャンクを許容する
DeepSeek はレスポンスごとに choices == [] のチャンクを約 3 個出力します。インデックス参照の前に真偽値を確認してください。
for chunk in stream:
choices = chunk.choices or []
if not choices:
if chunk.usage is not None: usage = chunk.usage
continue
...
C7 · コンテキスト長を 1,048,576 トークン未満に制限する
V4-Pro / V4-Flash の厳密な上限は 2^20 = 1,048,576 トークンです(probe_6b で検証済み)。サーバーは len(messages_tokens) + max_tokens <= 1,048,576 を強制し、超過すると 400 を返します。
C8 · キャッシュを意識する — 変動する内容をシステムプロンプトに挿入しない
DeepSeek のプレフィックスキャッシュは 256 トークン単位でバケット化され、キャッシュヒット時には 50 倍の割引が適用されます。効果を最大化するには、以下の点に従ってください。
- 履歴を過度に削除または要約しないでください(削除するたびにキャッシュミスが発生します)
- 「current date: 2026-05-09」のような内容をキャッシュ対象のプレフィックスに挿入しないでください
prompt_cache_hit_tokens(DeepSeek 独自)とprompt_tokens_details.cached_tokens(OpenAI 形式)の両方を読み取ってください。どちらも返されます
C9 · V4 でツールを使う場合は /beta エンドポイントを避ける
/beta は deepseek-v4-pro を deepseek-reasoner(レガシー)に暗黙的にマッピングします。このモデルは、tool_choice={"type":"function","function":{"name":"..."}} の形式を拒否します。ツールを使用するフローでは https://api.deepseek.com を使用してください。
C10 · V4 では strict: true は実証上問題ありません(#1069 は未解決のままですが)
V4-Pro / V4-Flash では function.strict=true と additionalProperties=false を有効にしてもかまいません。過去に発生した JSON 破損のバグ(deepseek-ai/DeepSeek-V3#1069)は、2026-05-09 の 32 回の試行では再現しませんでした。それでも、事後に jsonschema で JSON を検証してください。
ドロップイン実装
ユーザーの環境でサードパーティ製ライブラリをインストールできる場合は、次のいずれかを使用してください。
| 形式 | インストール | コマンド |
|---|---|---|
| Python ライブラリ | pip install deepseek-harness | from deepseek_harness import DeepSeekHarness |
| Python CLI | pip install deepseek-harness-cli | dsh chat、dsh doctor、dsh validate |
| MCP サーバー | npx -y @deepseek-harness/mcp | Claude/Cursor/Cline/ChatWise の MCP 設定に接続します |
openai 以外の依存関係を一切追加したくない場合は、このスキルフォルダーにある scripts/safe_init.py をプロジェクトにコピーしてください。200 行で 10 個すべてのルールを実装しています。
再現コマンド
このスキルに記載されているすべての主張は、reports/probes/ にある実行可能なプローブで裏付けられています。
# Reproduce the reasoning_content 400:
python reports/probes/probe_2_reasoning_lifecycle.py --n 3
# Expected: 3/3 phase-B BadRequestError with the verbatim error message above.
# Confirm tool-call leakage rate at 0% on official endpoint:
python reports/probes/probe_3_tool_call_leakage.py --n 30
# Map cache prefix sensitivity:
python reports/probes/probe_5_cache_prefix_sensitivity.py --n 8
16 項目の調査結果をまとめたレポート全文は、reports/REPORT_2026-05-09.md をご覧ください。