OpenAI 兼容介面升級:對 Claude 的深度支持

2026年7月31日 · AIHubMix · 8 min read · 新聞

OpenAI 兼容介面升級:對 Claude 的深度支持

我們已經對 OpenAI 兼容介面進行了升級,特別針對 Claude 系列模型進行了更深層的優化。您現在可以更精確和方便地控制思考和緩存。在多輪對話中,交錯思考變得更加人性化,允許無需額外參數的無縫集成。它還支持啟用 Anthropic 提供的測試功能。

1. 模型思考(擴展思考)

1.1 交錯思考的優勢

當未啟用交錯思考時,模型僅在助手回合開始時進行一次思考;隨後的回應是在接收到工具結果後直接生成,而不會產生新的思考區塊:

User → [Thinking] → Tool Call → Tool Result → Response

當啟用交錯思考時,模型在每次接收到工具結果時插入一個新的思考區塊,形成推理鏈:

User → [Thinking] → Tool Call → Tool Result → [Thinking] → Response
                                                ↑ 交錯思考

這使得模型能夠:

  • 基於工具結果進行二次推理,而不僅僅是簡單地連接輸出。
  • 在多個工具調用之間鏈接推理,每個決策都基於對前一步的分析。
參考: Anthropic 交錯思考

1.2 啟用思考

您可以通過四種方式啟用思考,選擇其中任何一種:

方法 示例 描述
reasoning_effort "reasoning_effort": "low" OpenAI 標準參數,放置在請求主體的頂層
reasoning.effort "reasoning": {"effort": "low"} 等同於前一種方法,放置在推理對象內
reasoning.max_tokens "reasoning": {"max_tokens": 1024} 精確控制思考的最大標記數
模型名稱加上 -think "model": "claude-sonnet-4-5-think" 最簡單的方法,無需額外參數
優先級(當使用多種方法時): reasoning_effort > reasoning.max_tokens > reasoning.effort > -think 後綴

努力的可能值: minimal / low / medium / high / xhigh

1.3 思考返回

回應消息將包括兩個新字段:

  • reasoning_content:思考內容(字符串),便於顯示。
  • reasoning_details:有關思考的完整結構化信息,在多輪對話中需要按原樣返回;內部結構可能因提供者而異。

非流式示例(省略不相關字段):

{
  "choices": [{
    "message": {
      "role": "assistant",
      "content": "你好!今天我能幫助你什麼?",
      "reasoning_content": "用戶只是打招呼...",
      "reasoning_details": {
        "type": "thinking",
        "thinking": "用戶只是打招呼...",
        "signature": "Er8CCkYI..."
      }
    }
  }]
}

在流式回應中,思考內容將通過 delta.reasoning_contentdelta.reasoning_details 以塊的形式發送。完整的流式串接邏輯請參考下面的完整示例。

1.4 在多輪對話中保留思考 (交錯思考是內建的,無需額外參數)

要使模型在多輪對話中繼續其推理能力,只需將先前返回的 reasoning_details 按原樣 放入下一輪的助手消息中:

messages = [
    {"role": "user", "content": "波士頓的天氣怎麼樣?"},
    {
        "role": "assistant",
        "content": response.choices[0].message.content,
        "tool_calls": response.choices[0].message.tool_calls,
        "reasoning_details": response.choices[0].message.reasoning_details,
    },
    {
        "role": "tool",
        "tool_call_id": "toolu_xxx",
        "content": '{"temperature": 45, "condition": "rainy"}',
    }
]

AIHubMix 將在檢測到請求中的歷史思考信息時自動啟用交錯思考,使模型在接收到工具調用結果後能夠繼續深度推理,而無需額外參數。

1.5 完整示例

以下兩個示例演示了完整的多輪工具調用 + 交錯思考過程:用戶詢問 → 模型思考並調用工具 → 注入工具結果(保留 reasoning_details) → 模型交錯思考給出最終回應。

非流式 · 交錯思考

import os
import json
from openai import OpenAI

client = OpenAI(
    base_url="https://aihubmix.com/v1",
    api_key=os.environ.get("AIHUBMIX_API_KEY", "sk-***"),
)

# ── 工具定義 ───────────────────────────────────────────
tools = [{
    "type": "function",
    "function": {
        "name": "get_weather",
        "description": "獲取某地的當前天氣",
        "parameters": {
            "type": "object",
            "properties": {"location": {"type": "string", "description": "城市名稱"}},
            "required": ["location"]
        }
    }
}]

# ── 模擬工具執行 ───────────────────────────────────────
WEATHER_DB = {
    "boston": {"temperature": "45°F (7°C)", "condition": "rainy", "humidity": "85%", "wind": "15 mph NE"},
    "tokyo":  {"temperature": "72°F (22°C)", "condition": "sunny", "humidity": "45%", "wind": "5 mph S"},
}

def execute_tool(name: str, args: dict) -> str:
    if name == "get_weather":
        key = next((k for k in WEATHER_DB if k in args.get("location", "").lower()), None)
        return json.dumps(WEATHER_DB.get(key, {"temperature": "65°F", "condition": "clear"}))
    return "{}"

# ── 多輪對話循環 ─────────────────────────────
messages = [
    {"role": "user", "content": "波士頓的天氣怎麼樣?然後建議我穿什麼。"}
]

turn = 0
while True:
    turn += 1
    print(f"\n── 回合 {turn} ──")

    response = client.chat.completions.create(
        model="claude-sonnet-4-5",
        messages=messages,
        tools=tools,
        extra_body={"reasoning": {"max_tokens": 2000}},
    )
    msg = response.choices[0].message

    # 打印思考過程
    if msg.reasoning_content:
        label = "交錯思考" if turn > 1 else "思考"
        print(f"[{label}] {msg.reasoning_content}")

    # 打印回應內容
    if msg.content:
        print(f"[回應] {msg.content}")

    # 打印工具調用
    if msg.tool_calls:
        for tc in msg.tool_calls:
            print(f"[工具調用: {tc.function.name}] {tc.function.arguments}")

    # 構建助手消息,保留 reasoning_details(關鍵!)
    assistant_msg = {"role": "assistant", "content": msg.content}
    if msg.tool_calls:
        assistant_msg["tool_calls"] = msg.tool_calls
    if msg.reasoning_details:
        assistant_msg["reasoning_details"] = msg.reasoning_details  # 不修改地傳回
    messages.append(assistant_msg)

    # 無工具調用意味著對話結束
    if not msg.tool_calls:
        break

    # 執行工具並將結果附加到消息中
    for tc in msg.tool_calls:
        args = json.loads(tc.function.arguments)
        result = execute_tool(tc.function.name, args)
        print(f"[工具結果: {tc.function.name}] {result}")
        messages.append({"role": "tool", "tool_call_id": tc.id, "content": result})

流式 · 交錯思考

import os
import sys
import json
from openai import OpenAI

client = OpenAI(
    base_url="https://aihubmix.com/v1",
    api_key=os.environ.get("AIHUBMIX_API_KEY", "sk-***"),
)

# ── 工具定義與模擬執行 ─────────────────────────
tools = [{
    "type": "function",
    "function": {
        "name": "get_weather",
        "description": "獲取某地的當前天氣",
        "parameters": {
            "type": "object",
            "properties": {"location": {"type": "string", "description": "城市名稱"}},
            "required": ["location"]
        }
    }
}]

WEATHER_DB = {
    "boston": {"temperature": "45°F (7°C)", "condition": "rainy", "humidity": "85%", "wind": "15 mph NE"},
    "tokyo":  {"temperature": "72°F (22°C)", "condition": "sunny", "humidity": "45%", "wind": "5 mph S"},
}

def execute_tool(name: str, args: dict) -> str:
    if name == "get_weather":
        key = next((k for k in WEATHER_DB if k in args.get("location", "").lower()), None)
        return json.dumps(WEATHER_DB.get(key, {"temperature": "65°F", "condition": "clear"}))
    return "{}"

# ── 流式回應收集器 ────────────────────────────────
def stream_and_collect(turn: int, **kwargs):
    """流式回應,實時打印思考/內容,累積 reasoning_details/tool_calls。"""
    rd = {}            # 累積的 reasoning_details
    content = ""       # 累積的回應文本
    tc_map = {}        # 累積的 tool_calls(按索引)
    cur = "none"       # 當前輸出部分:none / thinking / content

    stream = client.chat.completions.create(stream=True, **kwargs)
    for chunk in stream:
        if not chunk.choices:
            continue
        delta = chunk.choices[0].delta

        # ── 處理思考 ──
        rd_delta = getattr(delta, "reasoning_details", None)
        if rd_delta and isinstance(rd_delta, dict):
            for k, v in rd_delta.items():
                if k == "type":
                    rd[k] = v
                elif isinstance(v, str):
                    rd[k] = rd.get(k, "") + v
                elif v is not None:
                    rd[k] = v
            # 實時打印思考塊
            thinking_chunk = rd_delta.get("thinking", "")
            if thinking_chunk:
                if cur != "thinking":
                    cur = "thinking"
                    label = "交錯思考" if turn > 1 else "思考"
                    sys.stdout.write(f"\n[{label}] ")
                sys.stdout.write(thinking_chunk)
                sys.stdout.flush()

        # ── 處理內容 ──
        if delta.content:
            if cur != "content":
                if cur == "thinking":
                    sys.stdout.write("\n")
                cur = "content"
                sys.stdout.write("\n[回應] ")
            sys.stdout.write(delta.content)
            sys.stdout.flush()
            content += delta.content

        # ── 處理工具調用 ──
        for tc in delta.tool_calls or []:
            i = tc.index
            if i not in tc_map:
                tc_map[i] = {"id": "", "type": "function",
                             "function": {"name": "", "arguments": ""}}
            if tc.id:
                tc_map[i]["id"] = tc.id
            if tc.function:
                tc_map[i]["function"]["name"] += tc.function.name or ""
                tc_map[i]["function"]["arguments"] += tc.function.arguments or ""

    # 結束當前輸出部分
    if cur in ("thinking", "content"):
        sys.stdout.write("\n")

    tool_calls = [tc_map[i] for i in sorted(tc_map)] if tc_map else None
    return {
        "content": content or None,
        "reasoning_details": rd or None,
        "tool_calls": tool_calls,
    }

# ── 多輪對話循環 ─────────────────────────────
messages = [
    {"role": "user", "content": "波士頓的天氣怎麼樣?然後建議我穿什麼。"}
]

turn = 0
while True:
    turn += 1
    print(f"\n── 回合 {turn} ──")

    result = stream_and_collect(
        turn,
        model="claude-sonnet-4-5",
        messages=messages,
        tools=tools,
        extra_body={"reasoning": {"max_tokens": 2000}},
    )

    # 打印工具調用
    if result["tool_calls"]:
        for tc in result["tool_calls"]:
            print(f"[工具調用: {tc['function']['name']}] {tc['function']['arguments']}")

    # 構建助手消息,保留 reasoning_details(關鍵!)
    assistant_msg = {"role": "assistant", "content": result["content"]}
    if result["tool_calls"]:
        assistant_msg["tool_calls"] = result["tool_calls"]
    if result["reasoning_details"]:
        assistant_msg["reasoning_details"] = result["reasoning_details"]  # 不修改地傳回
    messages.append(assistant_msg)

    # 無工具調用意味著對話結束
    if not result["tool_calls"]:
        break

    # 執行工具並將結果附加到消息中
    for tc in result["tool_calls"]:
        args = json.loads(tc["function"]["arguments"])
        tool_result = execute_tool(tc["function"]["name"], args)
        print(f"[工具結果: {tc['function']['name']}] {tool_result}")
        messages.append({"role": "tool", "tool_call_id": tc["id"], "content": tool_result})

1.6 思考強度映射規則

努力模式:

  • Opus 4.6 / Sonnet 4.6 及以上:映射到 Anthropic 的原生 自適應思考 努力級別。
  • 其他模型:使用 budget_tokens 的公式計算:
budget_tokens = max(min(max_tokens × effort_ratio, 128000), 1024)
努力 努力比例
xhigh 0.95
high 0.80
medium 0.50
low 0.20
minimal 0.10

自適應思考努力映射:

進來的努力 Opus 4.6 Sonnet 4.6
xhigh max high
high high high
medium medium medium
low low low
minimal low low

max_tokens 模式: 直接分配為 Anthropic 的 budget_tokens

-think 後綴: Opus/Sonnet 4.6+ 使用自適應思考(努力=中);其他模型設置 budget_tokens = min(10240, max_tokens - 1),默認 max_tokens 為 4096。


2. 提示緩存

您可以在通過聊天介面向 Claude 模型發送請求時使用提示緩存。通過在消息中設置 cache_control 斷點,可以緩存大塊文本(如角色卡、RAG 數據、書籍章節等)以供重用,允許後續請求直接命中緩存,顯著降低成本。

Claude 官方文檔: 提示緩存

2.1 緩存成本

操作 價格乘數(相對於原始輸入價格)
緩存寫入(5 分鐘 TTL) 1.25x
緩存寫入(1 小時 TTL) 2x
緩存讀取 0.1x

2.2 支持的模型和最小緩存長度

模型 最小緩存標記數
Claude Opus 4.8 1024
Claude Opus 4.7 2048
Claude Opus 4.6 / Opus 4.5 4096
Claude Sonnet 4.6 / Sonnet 4.5 / Opus 4.1 / Opus 4 / Sonnet 4 / Sonnet 3.7(已棄用) 1024
Claude Haiku 4.5 4096
Claude Haiku 3.5(已棄用)/ Haiku 3 2048
斷點數量限制: 每個請求最多 4cache_control 斷點。

2.3 緩存 TTL

TTL 語法 適用場景
5 分鐘(默認) "cache_control": {"type": "ephemeral"} 短會話,常規請求
1 小時 "cache_control": {"type": "ephemeral", "ttl": "1h"} 長會話,以避免重複緩存寫入

1 小時 TTL 的寫入成本較高,但通過減少長會話中的重複寫入可以節省總開支。所有來自 Claude 4.5 及以後版本的所有提供者(包括 Anthropic、Amazon Bedrock、Google Vertex AI)均支持 1 小時 TTL。

2.4 使用

您可以在 systemuser(包括圖像)和 tools 中使用 cache_control 字段設置緩存斷點。以下示例僅顯示關鍵結構,省略大塊文本。

系統消息緩存(默認 5 分鐘 TTL):

{
  "model": "claude-opus-4-5",
  "messages": [
    {
      "role": "system",
      "content": [
        {"type": "text", "text": "您是一個 AI 助手"},
        {
          "type": "text",
          "text": "(長上下文)",
          "cache_control": {"type": "ephemeral"}
        }
      ]
    },
    {
      "role": "user",
      "content": [{"type": "text", "text": "你好"}]
    }
  ]
}

用戶消息緩存(1 小時 TTL):

{
  "model": "claude-opus-4-5",
  "messages": [
    {
      "role": "system",
      "content": [{"type": "text", "text": "您是一個 AI 助手"}]
    },
    {
      "role": "user",
      "content": [
        {
          "type": "text",
          "text": "(長上下文)",
          "cache_control": {"type": "ephemeral", "ttl": "1h"}
        },
        {"type": "text", "text": "你好"}
      ]
    }
  ]
}

圖像消息緩存:

{
  "role": "user",
  "content": [
    {
      "type": "image_url",
      "image_url": {"detail": "auto", "url": "data:image/jpeg;base64,/9j/4AAQ..."},
      "cache_control": {"type": "ephemeral"}
    },
    {"type": "text", "text": "這是什麼?"}
  ]
}

工具定義緩存:

cache_control 放置在工具對象的頂層(與 typefunction 一起):

{
  "tools": [{
    "type": "function",
    "function": {
      "name": "get_weather",
      "description": "獲取某地的當前天氣",
      "parameters": {
        "type": "object",
        "properties": {"city": {"type": "string"}},
        "required": ["city"]
      }
    },
    "cache_control": {"type": "ephemeral", "ttl": "1h"}
  }]
}

2.5 查看緩存狀態

回應的 usage 將返回 claude_cache_tokens_details,記錄詳細的緩存信息:

第一次請求(創建緩存):

{
  "usage": {
    "prompt_tokens": 22,
    "completion_tokens": 890,
    "total_tokens": 912,
    "claude_cache_tokens_details": {
      "cache_creation_input_tokens": 6266,
      "cache_read_input_tokens": 0,
      "cache_write_5_minutes_input_tokens": 6266,
      "cache_write_1_hour_input_tokens": 0
    }
  }
}

後續請求(緩存命中):

{
  "usage": {
    "prompt_tokens": 22,
    "completion_tokens": 810,
    "total_tokens": 832,
    "prompt_tokens_details": {
      "cached_tokens": 6266
    },
    "claude_cache_tokens_details": {
      "cache_creation_input_tokens": 0,
      "cache_read_input_tokens": 6266,
      "cache_write_5_minutes_input_tokens": 0,
      "cache_write_1_hour_input_tokens": 0
    }
  }
}
字段 含義
cache_creation_input_tokens 在此請求中寫入緩存的標記數
cache_read_input_tokens 在此請求中從緩存中讀取的標記數
cache_write_5_minutes_input_tokens 寫入 5 分鐘 TTL 緩存的標記數
cache_write_1_hour_input_tokens 寫入 1 小時 TTL 緩存的標記數
prompt_tokens_details.cached_tokens 緩存命中時的標記數,與 OpenAI 格式兼容

3. 用於 anthropic-beta 的請求標頭

您可以通過 HTTP 標頭 anthropic-beta 啟用 Claude 模型的測試功能,AIHubMix 將通過該標頭傳遞給 Anthropic API。

用法

在請求標頭中添加 anthropic-beta,其值為相應的測試功能標識符:

curl "https://aihubmix.com/v1/chat/completions" \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer $AIHUBMIX_API_KEY" \
  -H "anthropic-beta: context-1m-2025-08-07" \
  -d '{
  "model": "claude-opus-4-5",
  "messages": [
    {
      "role": "system",
      "content": [
        {"type": "text", "text": "您是一個 AI 助手"},
        {
          "type": "text",
          "text": "(長上下文)",
          "cache_control": {"type": "ephemeral"}
        }
      ]
    },
    {"role": "user", "content": [{"type": "text", "text": "你好"}]}
  ]
}'
有關具體可用的測試標識符,請參考 Anthropic API 文檔

最後更新:2026-06-01

More from the blog