> ## Documentation Index
> Fetch the complete documentation index at: https://docs.fim.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# LLM 提供商兼容性

> FIM One 如何路由 LLM 调用、tool_choice 架构以及特定提供商的陷阱——特别是 Anthropic thinking + AWS Bedrock。

## 提供商检测

FIM One 使用 LiteLLM 作为通用适配器。`core/model/openai_compatible.py` 中的 `_resolve_litellm_model()` 函数将用户的 `LLM_BASE_URL` + `LLM_MODEL` 映射到带有提供商前缀的 LiteLLM 模型标识符。该前缀决定了 LiteLLM 如何路由请求 — 原生 API 协议（Anthropic Messages API、Gemini 等）或通用 OpenAI 兼容的 `/v1/chat/completions`。

解析顺序：

1. **显式提供商**（来自数据库 `ModelConfig.provider` 字段）— 最高优先级。如果提供商与 URL 中的已知域匹配，则不返回 `api_base`（LiteLLM 原生路由）。否则，`api_base` 设置为中继 URL。
2. **域名匹配** `KNOWN_DOMAINS` — 通过主机名识别官方 API 端点。
3. **URL 路径提示** `PATH_PROVIDER_HINTS` — 常见于 UniAPI 等中继平台，其中路径中的 `/claude` 或 `/anthropic` 表示上游协议。
4. **回退** — `openai/` 前缀（通用 OpenAI 兼容）。

| 域名 / 路径                             | 提供商前缀        | 协议                           |
| ----------------------------------- | ------------ | ---------------------------- |
| `api.openai.com`                    | `openai/`    | OpenAI Chat Completions      |
| `anthropic.com`                     | `anthropic/` | Anthropic Messages API       |
| `generativelanguage.googleapis.com` | `gemini/`    | Google Gemini                |
| `api.deepseek.com`                  | `deepseek/`  | DeepSeek（OpenAI 兼容）          |
| `api.mistral.ai`                    | `mistral/`   | Mistral                      |
| 路径包含 `/claude` 或 `/anthropic`       | `anthropic/` | Anthropic Messages API（通过中继） |
| 路径包含 `/gemini`                      | `gemini/`    | Google Gemini（通过中继）          |
| 其他任何情况                              | `openai/`    | 通用 OpenAI 兼容                 |

当提供商前缀是原生协议（anthropic、gemini 等）且 URL 不是官方端点时，LiteLLM 使用原生协议但将请求发送到中继的 `api_base`。这意味着提供商特定的行为 — 包括下面描述的 Bedrock 预填充问题 — 无论请求是发送到官方 API 还是通过中继，都会适用。

<Warning>
  如果你的中继 URL 路径中包含 `/claude`，FIM One 会自动通过 Anthropic 的原生协议路由。这通常是正确的（更好的流式传输、思考支持），但意味着提供商特定的行为会适用 — 包括下面描述的 Bedrock 预填充问题。
</Warning>

## tool\_choice — 四种模式

`tool_choice` 参数通过 OpenAI 格式标准化。LiteLLM 在发送请求前将其转换为每个提供商的原生协议。

| 模式                                            | 含义               | 提供商支持                                 |
| --------------------------------------------- | ---------------- | ------------------------------------- |
| `"auto"`                                      | 模型决定是否调用工具或以文本响应 | 所有提供商                                 |
| `"required"`                                  | 必须调用工具，但由模型选择哪个  | 大多数提供商                                |
| `{"type":"function","function":{"name":"X"}}` | 必须特别调用函数 X       | 大多数提供商 — **与 Anthropic thinking 不兼容** |
| `"none"`                                      | 无法使用工具，仅文本       | 所有提供商                                 |

`"auto"` 和强制模式（`{"type":"function",...}`）之间的区别是 FIM One 中每个兼容性问题的关键。这两种模式由具有不同要求的完全不同的子系统使用。

## tool\_choice 的使用位置

两个子系统使用 `tool_choice`，它们以根本不同的方式使用它。

### ReAct 引擎 — tool\_choice="auto"

ReAct 循环需要模型在每次迭代中做出决定：调用工具或给出最终答案。只有 `"auto"` 才有意义——模型可以自由选择生成 `tool_calls` 或文本内容。这与所有提供商、所有模型和所有模式（包括扩展思考）兼容。

```mermaid theme={null}
flowchart TD
    A["ReAct iteration"] --> B{"Model decides<br/>(tool_choice=auto)"}
    B -->|"tool_calls present"| C["Execute tool → next iteration"]
    B -->|"text content only"| D["Final answer"]
```

ReAct 引擎在 `abilities["tool_call"] = True` 时使用原生函数调用（`_run_native`），否则回退到 JSON-in-content 模式（`_run_json`）。两种模式都使用 `"auto"`——区别在于工具是通过 `tools` 参数传递还是在系统提示中描述。详见 [ReAct 引擎——双模式执行](/architecture/react-engine#dual-mode-execution)。

### structured\_llm\_call — tool\_choice=forced

一次性结构化提取（模式注解、DAG 规划、计划分析）。强制模型调用特定的虚拟函数，保证结构化 JSON 输出。这是触发提供商特定错误的调用点。

`structured_llm_call` 实现了一个 3 级降级链：

```mermaid theme={null}
flowchart TD
    START["structured_llm_call()"] --> L1{"abilities.tool_call<br/>AND tool_choice?"}
    L1 -->|Both true| FC["Level 1: native_fc<br/>forced tool_choice"]
    L1 -->|Either false| L2
    FC -->|"Success"| DONE["Return StructuredCallResult"]
    FC -->|"Fail (catch Exception)"| L2{"abilities.json_mode?"}
    L2 -->|Yes| JM["Level 2: json_mode<br/>response_format=json_object"]
    L2 -->|No| PT["Level 3: plain_text<br/>extract JSON from free text"]
    JM -->|"Success"| DONE
    JM -->|"Fail → retry once"| PT
    PT -->|"Success"| DONE
    PT -->|"Fail → retry once"| ERR["StructuredOutputError<br/>or default_value"]
```

关键设计差异：`structured_llm_call` 的回退是**运行时**——它动态尝试每个级别并捕获异常以进行回退。ReAct 引擎的模式选择是**构建时**——它在开始时检查一次 `_native_mode_active` 并为整个循环提交到一种模式。这意味着 `structured_llm_call` 可以透明地从提供商特定的 400 错误中恢复，而 ReAct 依赖于模式在前期被正确选择。

## Bedrock预填陷阱

当为使用`anthropic/`前缀解析的模型传递`response_format={"type":"json_object"}`时，LiteLLM会在内部注入一条助手预填消息来模拟JSON模式。Anthropic Messages API没有原生的`response_format`参数，所以LiteLLM通过在助手内容前加一个开括号来近似实现：

```json theme={null}
{"role": "assistant", "content": "{"}
```

这在Anthropic的直接API上可以正常工作。但是，较新的AWS Bedrock模型版本会拒绝任何最后一条消息具有`role: "assistant"`的对话——他们称之为"助手消息预填"并抛出错误：

```
ValidationException: This model does not support assistant message prefill.
The conversation must end with a user message.
```

仅当**同时满足以下三个条件**时才会出现此错误：

1. 模型使用`anthropic/`前缀解析（通过域名匹配或URL路径提示）。
2. 传递了`response_format={"type":"json_object"}`（`structured_llm_call`中的json\_mode代码路径）。
3. 实际后端是AWS Bedrock（拒绝预填）。

<Tip>
  **通过OpenAI兼容端点的Bedrock？** 如果你的Bedrock中继暴露了OpenAI兼容的`/v1/chat/completions`端点（AWS自己的OpenAI兼容网关或第三方代理），且URL路径**不**包含`/claude`或`/anthropic`，FIM One会使用`openai/`前缀解析。LiteLLM随后将后端视为标准OpenAI兼容服务器，直接传递`response_format`而不注入任何预填，服务器原生处理JSON约束。**预填陷阱不适用**——你无需设置`json_mode_enabled=false`。
</Tip>

<Warning>
  这**不**影响原生工具调用（`tool_choice="auto"`配合`tools=`参数）。预填注入仅在`response_format`时发生。ReAct智能体执行完全不受影响。
</Warning>

如果第1级（native\_fc）和第2级（json\_mode）在Bedrock上都失败，系统会在第3级（plain\_text）恢复。下面描述的`json_mode_enabled`标志消除了浪费的第2级调用。

### 修复方案：json\_mode\_enabled

一个按模型的 `json_mode_enabled` 标志控制是否尝试 Level 2（json\_mode）：

* **数据库配置的模型**：在 Admin → Models → Advanced settings 中切换。该标志存储在 `ModelProviderModel.json_mode_enabled` 上（默认值 `TRUE`）。
* **环境变量配置的模型**：在环境中设置 `LLM_JSON_MODE_ENABLED=false`。
* **效果**：禁用时，`abilities["json_mode"]` 返回 `False` → `response_format` 永远不会被传递 → 无预填充 → Bedrock 正常工作。降级链变为 `native_fc → plain_text`，完全跳过注定失败的 json\_mode 调用。
* **无质量损失**：模型仍然返回有效的 JSON，因为系统提示指示它这样做。plain\_text 级别使用 `extract_json()` 从自由格式内容中解析 JSON，这在现代模型中工作可靠。

## 思维模型 + 强制 tool\_choice

某些模型已永久启用扩展思维（思维链）。它们的 API 拒绝强制 `tool_choice`，因为强制特定函数调用与模型优先推理的自由度相矛盾：

```
tool_choice 'specified' is incompatible with thinking enabled
```

Anthropic 在协议级别强制执行此约束，某些其他提供商（例如 Moonshot AI / Kimi K2.5）遵循相同模式。

对于 Anthropic 模型，`structured_llm_call` 通过在调用 native\_fc 时传递 `reasoning_effort=None` 来自动处理此问题，禁用该特定调用的扩展思维。结构化输出调用需要**架构合规性**，而不是深度推理——在这里禁用思维既正确又有益（更低延迟、更低成本）。

但是，某些模型（例如 Kimi K2.5）的思维永久启用，无法从外部禁用。对于这些模型，native\_fc 总是以 400 错误失败，在结构化调用前浪费约 10 秒延迟，然后降级链才会降级到 json\_mode。

### 修复：tool\_choice\_enabled

一个针对每个模型的 `tool_choice_enabled` 标志控制是否尝试 Level 1（native\_fc）：

* **数据库配置的模型**：在 Admin → Models → Advanced → "Native Function Calling" 中切换。该标志存储在 `ModelProviderModel.tool_choice_enabled` 上（默认为 `TRUE`）。
* **ENV 配置的模型**：在环境中设置 `LLM_TOOL_CHOICE_ENABLED=false`。
* **效果**：禁用时，`abilities["tool_choice"]` 返回 `False` → 降级链从 Level 2（json\_mode）或 Level 3（plain\_text）开始，完全跳过 native\_fc。这消除了不兼容模型每次结构化调用约 10 秒的性能损失。
* **ReAct 智能体不受影响**：`tool_choice_enabled` 仅控制 `structured_llm_call` 中的强制工具选择。ReAct 引擎使用 `tool_choice="auto"`（模型自由决定），无论此设置如何都适用于所有模型。

<Note>
  `tool_choice_enabled` 和 `tool_call` 是独立的能力标志。`tool_call`（对于 `OpenAICompatibleLLM` 始终为 `True`）控制工具是否被传递给模型 — 禁用它会破坏 ReAct 智能体。`tool_choice` 仅控制是否尝试**强制**工具选择以进行结构化输出提取。
</Note>

`tool_choice="auto"` 不受思考模式影响。ReAct 引擎专门使用 `"auto"`，因此启用思考时智能体执行工作正常。

<Warning>
  不要设置 `abilities["tool_call"] = False` 来避免此约束。这会禁用 ReAct 的 `_run_native` 模式（使用 `tool_choice="auto"` 且与思考配合良好），强制其进入不太可靠的 `_run_json` 模式。
</Warning>

<Note>
  \*\*提供商迁移说明：\*\*某些第三方中继会静默丢弃不支持的参数，如 `reasoning_effort`（`drop_params=True`），因此即使配置了思考也永远不会激活。迁移到正确支持思考的提供商（Bedrock、直接 Anthropic API）时，native\_fc 中的 `reasoning_effort=None` 确保一致的行为。无需用户操作 — 结构化输出在所有提供商中的工作方式相同。
</Note>

## 快速参考：什么在哪里有效

| 场景                       | ReAct 模式      | structured\_llm\_call 路径                   | 备注                   |
| ------------------------ | ------------- | ------------------------------------------ | -------------------- |
| OpenAI（任何模型）             | `_run_native` | native\_fc                                 | 完全支持                 |
| Anthropic（无思考）           | `_run_native` | native\_fc                                 | 完全支持                 |
| Anthropic + 思考           | `_run_native` | native\_fc（思考自动禁用）                         | 仅对结构化输出禁用思考          |
| Bedrock 中继（无思考）          | `_run_native` | native\_fc                                 | 完全支持                 |
| Bedrock 中继 + 思考          | `_run_native` | native\_fc（思考自动禁用）                         | 仅对结构化输出禁用思考          |
| Bedrock 通过 OpenAI 兼容端点   | `_run_native` | native\_fc                                 | 完全支持——无预填充问题；见上面的提示  |
| Gemini                   | `_run_native` | native\_fc                                 | 完全支持                 |
| DeepSeek（非思考）            | `_run_native` | native\_fc                                 | 完全支持                 |
| DeepSeek R1（思考）          | `_run_native` | json\_mode（设置 `tool_choice_enabled=false`） | 思考始终开启；跳过 native\_fc |
| Kimi K2（非思考）             | `_run_native` | native\_fc                                 | 完全支持                 |
| Kimi K2.5（思考）            | `_run_native` | json\_mode（设置 `tool_choice_enabled=false`） | 思考始终开启；跳过 native\_fc |
| 通用 OpenAI 兼容             | `_run_native` | native\_fc                                 | 完全支持                 |
| 任何 `tool_call=false` 的模型 | `_run_json`   | json\_mode 或 plain\_text                   | 不支持工具调用的模型的备选方案      |

## 推荐的每个模型配置

`tool_choice_enabled` 和 `json_mode_enabled` 都可以在管理员 → 模型 → 高级设置中按模型切换。默认值（两者都为 `TRUE`）适用于大多数提供商。仅在遇到错误或不必要的延迟时进行调整。

| 模型类型                | 原生 FC  | JSON 模式 | 原因                            |
| ------------------- | ------ | ------- | ----------------------------- |
| OpenAI GPT 系列       | 开启     | 开启      | 完全支持 — 默认值正确                  |
| Anthropic Claude    | 开启     | 开启      | 原生 FC 自动禁用思考                  |
| Google Gemini       | 开启     | 开启      | 完全支持                          |
| DeepSeek V3 / Coder | 开启     | 开启      | 完全支持                          |
| **DeepSeek R1（思考）** | **关闭** | 开启      | 思考始终开启；原生 FC 被拒绝              |
| **Kimi K2.5（思考）**   | **关闭** | 开启      | 思考始终开启；原生 FC 被拒绝              |
| Kimi K2（非思考）        | 开启     | 开启      | 完全支持                          |
| **AWS Bedrock 中继**  | 开启     | **关闭**  | Bedrock 在 json\_mode 中拒绝助手预填充 |
| 弱小模型                | 关闭     | 关闭      | 直接进行纯文本提取                     |

<Tip>
  **何时更改：** 如果在日志中看到 `structured_llm_call: native_fc call raised` 警告，随后成功进行 json\_mode 提取，则该模型不受益于原生 FC。为该模型禁用"原生函数调用"以消除浪费的 API 调用（每个结构化输出请求约 10 秒）。
</Tip>

**环境变量级别的覆盖** 适用于通过环境变量配置的所有模型（不是管理员 UI）：

```bash theme={null}
# Disable native_fc globally (for thinking-model-only deployments)
LLM_TOOL_CHOICE_ENABLED=false

# Disable json_mode globally (for Bedrock relay deployments)
LLM_JSON_MODE_ENABLED=false
```

## 推理工作量和思考配置

FIM One 公开了两个环境变量用于控制扩展思考/推理：

| 变量                            | 值                     | 效果                                                                                                          |
| ----------------------------- | --------------------- | ----------------------------------------------------------------------------------------------------------- |
| `LLM_REASONING_EFFORT`        | `low`、`medium`、`high` | 作为 `reasoning_effort` 传递给 LiteLLM。Anthropic：映射到 `thinking` 参数。OpenAI o 系列：直接传递。其他：静默丢弃（`drop_params=True`）。 |
| `LLM_REASONING_BUDGET_TOKENS` | 整数（例如 `10000`）        | 仅 Anthropic：设置显式的 `thinking.budget_tokens` 上限，绕过 LiteLLM 的自动映射。用于控制 Claude 模型的成本。                           |

当设置了 `reasoning_effort` 且模型被解析为 `anthropic/` 时，应用以下两个额外行为：

1. **温度被强制设置为 1.0。** Bedrock 在启用思考时拒绝 `temperature != 1.0`。FIM One 会自动处理此问题 — 无需用户操作。
2. **GPT-5.x 与工具**：当存在 `tools` 时，`reasoning_effort` 被静默丢弃，因为 GPT-5 `/v1/chat/completions` 端点拒绝此组合。这仅影响 ReAct 工具循环；缺少 `tools` 参数的 `structured_llm_call` 调用不受影响。

## 结构化输出的防御性解析

即使 native\_fc 正常工作，结构化输出管道也包含一个防御性解析层，用于处理来自任何提供商或兼容性层的边界情况。

DAG 规划器的 `_dict_to_steps` 解析器处理三个常见的边界情况：

1. **单个对象而非数组。** 某些模型返回 `{"steps": {"id": "1", "task": "..."}}` （单个步骤对象）而不是 `{"steps": [{"id": "1", "task": "..."}]}` （数组）。解析器通过检查 `id` 或 `task` 键来检测这种情况，并将对象包装在列表中。

2. **双重编码的 JSON 字符串。** 当结构化输出降级到 json\_mode（缺乏模式强制）时，某些提供商将 `steps` 值作为 JSON 字符串而非原生数组返回 — 例如 `{"steps": "[{\"id\": \"1\", ...}]"}`。这个字符串可能还包含字面换行符（来自模型的格式化），会破坏标准的 `json.loads`。解析器使用 `extract_json_value()` （包含 `_repair_json_strings`）来处理：
   * JSON 字符串值内的字面换行符
   * 无效的转义序列（常见于 LaTeX 或代码内容）
   * 来自兼容性层的其他序列化问题

3. **缺少 `steps` 包装器。** 模型可能返回单个步骤作为顶级对象，而没有 `steps` 包装键。解析器在根级别检测 `id` 和 `task`，并相应地进行包装。

<Note>
  在正常操作下，native\_fc 返回正确结构化的工具调用参数，这些边界情况不会出现。防御性解析器作为安全网存在，用于自定义 `BaseLLM` 子类、异常的提供商行为，或结构化输出降级到 json\_mode 或 plain\_text 的回退场景。
</Note>

## 提示词缓存（跨提供商）

FIM One 通过 `cache_control` 断点实现 Anthropic 的显式提示词缓存，同时通过**提示词部分注册表**从其他所有提供商的自动前缀缓存中受益。目标是实现一条单一的提示词组装路径，在所有提供商中工作，而无需按调用的提示词形状差异。

### 架构

`fim_one.core.prompt` 模块公开三个基础元素：

* **`PromptSection`** — 一个命名片段，包含静态 `content: str` 或动态 `content: Callable`
* **`PromptRegistry`** — 一个记忆化存储（静态片段渲染一次，动态片段每次调用重新渲染）
* **`DYNAMIC_BOUNDARY`** — 一个哨兵标记，注册表在最后一个静态片段和第一个动态片段之间插入，以便调用者可以在缓存断点处分割渲染的提示词

ReAct 的系统提示词（JSON 模式、原生函数调用模式、综合）分为：

* **静态前缀**（\~95% 的提示词）— 身份、核心指南、工具描述
* **动态后缀** — 当前日期时间、每个请求的语言指令、交接上下文

### 能力检测

`fim_one.core.prompt.caching.is_cache_capable(model_id)` 当模型 id 包含以下任何内容时返回 `True`：`claude`、`anthropic`、`bedrock/anthropic`、`vertex_ai/claude`。这些提供商接收**两个** `role="system"` 消息，第一个（静态）消息带有 `cache_control: {"type": "ephemeral"}`。

所有其他提供商接收一个**单一的**连接系统消息，没有 `cache_control` 字段——这是必要的，因为非 Anthropic 端点要么拒绝该字段，要么静默丢弃它，通过某些中继发送它会导致 `400 unknown parameter` 错误。

### 跨提供商覆盖

| 提供商                              | 机制                            | 读取折扣        | 我们的处理                                            |
| -------------------------------- | ----------------------------- | ----------- | ------------------------------------------------ |
| **Anthropic Claude** (3, 3.5, 4) | 显式 `cache_control`            | 0.10×       | 两个系统消息，带有临时断点                                    |
| **AWS Bedrock Anthropic**        | 通过 Anthropic 缓存传递             | 0.10×       | 与 Anthropic 相同                                   |
| **GCP Vertex AI Claude**         | 通过 Anthropic 缓存传递             | 0.10×       | 与 Anthropic 相同                                   |
| **OpenAI GPT / o-series**        | 自动前缀哈希（≥1024 tokens）          | 0.50×       | 通过 Section Registry 的字节稳定前缀 → 自动命中               |
| **DeepSeek (v3 / R1)**           | 自动磁盘支持的前缀缓存                   | 0.10×       | 与 OpenAI 相同                                      |
| **Moonshot Kimi (K1/K2)**        | 自动前缀缓存                        | 0.10×/0.50× | 相同                                               |
| **ZhipuAI GLM-4.5+**             | 自动长上下文缓存                      | 0.20×       | 相同                                               |
| **Grok (xAI)**                   | 自动前缀缓存                        | 0.25×       | 相同                                               |
| **Google Gemini**                | 独立的 `createCachedContent` API | 0.25×       | **尚未实现** — 在 v0.9 路线图上作为 `GeminiCacheAdapter` 跟踪 |
| **Mistral / Cohere**             | 无原生缓存                         | N/A         | N/A                                              |

`PromptRegistry` 通过自动前缀缓存为每个提供商"免费"提供优势 — 通过在调用中保持静态部分字节相同（当前日期时间位于动态后缀而非前缀），每个自动缓存提供商的哈希匹配并命中其缓存。这就是为什么即使在考虑 Anthropic 特定的 `cache_control` 之前，Registry 也是一个基础的无模型优势。

### 可观测性

每个 `chat/*` 响应的 `done_payload` 现在包括：

```json theme={null}
"cache": {
  "read_tokens": 1067,
  "creation_tokens": 0
}
```

`TurnProfiler` 每轮发出一条结构化日志行：`turn_cache summary | model=claude-sonnet-4-6 | read_tokens=1067 | create_tokens=0 | saved_input_tokens=961 (~90%)`。这也充当**中继诚实探针** — 如果你通过 API 中继路由，比较实际计费的输入 token 与 `read_tokens` 来检测中继是否剥离 `cache_control` 或保留 0.10× 折扣。

LLM 层不返回美元估计 — 定价和中继加价在上层应用，因此 LLM 层仅返回客观的 token 计数。

### 多轮缓存 ROI

在 Claude 4 ReAct 轮次上测量，使用默认智能体提示词：

| 模式               | 静态前缀 token | 动态后缀 token | 缓存比率  |
| ---------------- | ---------- | ---------- | ----- |
| JSON 模式，无工具      | \~753      | \~46       | 94.2% |
| JSON 模式，约 10 个工具 | \~1067     | \~46       | 95.9% |
| 原生函数调用           | \~523      | \~46       | 91.9% |

一个包含 10 个工具的 10 次迭代 ReAct 运行，在第一次之后的每一轮可节省约 8,640 个输入 token（9 次缓存命中 × 1067 token × 90%）。Anthropic 对第一次调用的缓存写入收费 1.25 倍，因此损益平衡点在**第二次**调用——单次查询不会受益。

## 推理重放策略（无模型正确性）

扩展思考/推理块在不同提供商之间的行为不同。统一的序列化策略会破坏协议契约和自动前缀缓存。`fim_one.core.prompt.reasoning.reasoning_replay_policy(model_id)` 返回三个值之一，并在 `OpenAICompatibleLLM._build_request_kwargs()` 中控制 `ChatMessage.to_openai_dict(replay_policy=...)` 的行为。

### 三种策略

* **`anthropic_thinking`** — Claude 系列（包括 `anthropic/`、`bedrock/anthropic`、`vertex_ai/claude`）。思考块必须附加 `signature` 进行重放；如果缺少或更改签名，Anthropic 会拒绝后续轮次。
* **`informational_only`** — 发出 CoT 但不期望重放的模型：DeepSeek R1 / R1-Distill、Qwen QwQ、Gemini 2.x thinking、OpenAI o1 / o3 / o4。它们的文档明确说明"不要在消息历史中发送 `reasoning_content`"。即使发送：
  * 违反提供商合约（未来版本可能开始拒绝）
  * **静默失效其自动前缀缓存** — 消息字节在每个轮次变化，破坏哈希
* **`unsupported`** — 不具备推理能力的模型（GPT-4o、GPT-4 Turbo、Gemini 1.5、Mistral、Llama）。没有 CoT 可重放；字段不应出现。此策略也是未知模型 ID 的安全默认值。

### 执行

所有策略评估都在一个地方进行（`_build_request_kwargs`）。`ChatMessage.to_openai_dict(replay_policy=None)` 保留了 A3 宽松的默认设置，以便不协调的调用者不会回退。跨提供商测试矩阵位于 `tests/test_reasoning_replay_policy.py` 中，**反向断言**证明非 Anthropic 请求不会泄露 `reasoning_content`。

### 对于用户

特性和错误行为都是自动的 — 您无需配置任何内容。工作流影响：

* **如果您在同一对话中在 Claude 和 DeepSeek 智能体之间切换**，历史记录会完整保存思考块；在下一轮中，传出消息形状会根据当前模型进行调整。
* **如果您使用代理 / 自定义 `BaseLLM` 子类**，请确保其模型 id 是可识别的（包含其中一个片段），否则默认的 `unsupported` 策略将适用 — 这是安全的，但意味着异常代理后的 Claude 可能会丢失思考重放。将模型 id 片段添加到 `_CACHE_CAPABLE_MODEL_FRAGMENTS`（在 `core/prompt/caching.py` 中）和/或推理策略查找。

## 故障排除

**"This model does not support assistant message prefill"**
Bedrock + json\_mode。两种修复方法：（1）设置 `LLM_JSON_MODE_ENABLED=false` 或在管理员模型设置中禁用 JSON Mode；或（2）如果你的 Bedrock 提供商提供 OpenAI 兼容的 `/v1/chat/completions` 端点，切换到该端点——FIM One 将其解析为 `openai/`，预填充注入永远不会发生。

**"Thinking may not be enabled when tool\_choice forces tool use"** / **"tool\_choice 'specified' is incompatible with thinking enabled"**
对于 Anthropic 模型，`structured_llm_call` 会自动为 native\_fc 调用禁用思考。对于其他始终启用思考的提供商（例如 Kimi K2.5），在模型的高级设置中禁用"Native Function Calling"，或全局设置 `LLM_TOOL_CHOICE_ENABLED=false`。降级链将跳过 native\_fc，改为通过 json\_mode 或 plain\_text 提取结构化输出。

**"DAG pipeline failed: LLM 'steps' is not an array"**
LLM 将 `steps` 字段返回为字符串或单个对象，而不是数组。这通常意味着结构化输出降级到了 json\_mode（缺乏模式强制）。检查日志中的 `structured_llm_call: level=xxx`——如果显示 `json_mode` 而不是 `native_fc`，则 native\_fc 正在无声地失败。如果使用自定义 `BaseLLM` 子类，请验证它接受 `reasoning_effort` 关键字参数。

**ReAct 意外降级到 JSON mode**
检查模型的 `abilities["tool_call"]` 是否为 `True`。对于 `OpenAICompatibleLLM`，这始终为 `True`，但自定义 `BaseLLM` 子类可能会覆盖它。使用管理员 API 中的模型详情端点进行验证。

**structured\_llm\_call 耗尽所有级别并抛出 StructuredOutputError**
模型在任何级别都无法生成可解析的 JSON。这在现代模型中很少见。检查：（1）模式是有效的 JSON Schema，（2）模型有足够的 `max_tokens` 来生成完整响应，（3）系统提示没有与模式指令相冲突。DAG 规划器和分析器都提供 `default_value` 回退，因此此错误仅从显式省略默认值的调用站点传播。
