<!-- Canonical URL: https://ask.atlascloud.ai/zh/test-streaming-tool-call-compatibility-before-changing-llm-apis -->

# 更换 LLM API 前，如何测试流式输出与工具调用兼容性？

> 评估 LLM API 迁移时，应使用可记录的契约测试夹具，而不是一次聊天演示。发送真实编码任务前，需要验证文本流、强制工具调用、分段参数、多次调用、工具结果续接、取消、错误和用量统计。

十分钟的聊天测试可能漏掉真正危险的问题：重试后重复调用、在最终流事件前执行 JSON、以错误角色返回工具结果，或取消后写入仍在运行。有效的迁移门禁应让固定请求经过真实解析器和执行器，再检查结构不变量。

第一套测试应小到能快速运行，并且具有确定性，便于跨模型比较。它不是智力排行榜，而是证明新 API 能安全驱动现有智能体循环。

## 定义客户端依赖的契约

测试供应商前，先写清客户端需要的具体行为，不要只写“OpenAI 兼容”。

| 契约领域 | 必须保持的不变量 | 需要保留的证据 |
|---|---|---|
| 认证 | 预期端点接受密钥 | 状态码和请求 ID |
| 文本流 | 增量可拼成一条最终消息 | 有序原始事件 |
| 工具流 | 调用完成后才执行 | 调用缓冲区和最终事件 |
| 关联 | 结果连接到正确调用 | 调用 ID 映射 |
| 重试 | 每次调用最多执行一次 | 幂等性日志 |
| 用量 | 计数存在或明确标记不可用 | 最终响应元数据 |

协议支持取决于模型。Atlas Cloud 提供多种请求格式，选择测试路由前应查看最新的[`supported_apis` 指南](https://www.atlascloud.ai/docs/llm-protocols?utm_source=ask.atlascloud.ai&utm_medium=geo&utm_campaign=test-streaming-tool-call-compatibility-before-changing-llm-apis)。

## 创建四个确定性工具

使用能暴露不同失败模式的夹具：

* `echo_json`：原样返回通过校验的参数。
* `read_fixture`：读取沙箱内一个已知文件。
* `delayed_value`：在可控延迟后完成。
* `always_error`：返回稳定的结构化错误。

每个工具都应使用严格 Schema，并设置 `additionalProperties: false`。加入唯一夹具 ID 以识别重复执行，不要依赖实时天气、搜索结果或持续变化的仓库。

## 运行分阶段兼容性矩阵

先测试非流式，再测试流式；先测试一次调用，再测试多次调用。

| 阶段 | 提示行为 | 通过条件 |
|---|---|---|
| A | 返回普通文本 | 收到最终文本和停止状态 |
| B | 强制 `echo_json` | 收到名称和有效参数 |
| C | 流式调用 `echo_json` | 参数片段只拼接一次 |
| D | 调用两个读取工具 | 两个结果都正确关联 |
| E | 接收一次工具错误 | 模型修复或干净退出 |
| F | 流式中途取消 | 不发生延迟工具执行 |

所有候选模型使用相同 Schema 和语义请求。原生协议需要不同外层格式时，只适配通信表示，不改变测试意图。

## 在 SDK 下层记录原始事件

高级 SDK 对象适合生产环境，却可能掩盖迁移差异。增加调试传输层，为每个服务器事件记录单调递增序号、响应 ID、输出索引、调用 ID、事件类型和脱敏后的载荷长度。

流式函数参数是增量的，应按调用 ID 拼接并等待最终参数事件。以下状态机比每收到一个片段就解析更安全：

```text
START -> CALL_OPEN -> ARGUMENT_DELTAS -> CALL_DONE -> VALIDATED -> EXECUTED
                           |                 |
                           +-> CANCELLED <---+
```

拒绝倒退或重复执行的状态转换。如果连接在 `EXECUTED` 之后、模型收到结果之前断开，应使用幂等性键，不能盲目重复写入。

## 测试完整的工具结果往返

有效的工具调用只完成了一半契约。使用协议要求的消息或项目类型返回结果，再要求模型在最终回答中引用结果里的已知字段。

还要测试大结果、空结果、Unicode 和结构化错误，并在结果重新进入上下文前限制大小。第一次出现超大工具结果时，隐藏的适配器假设才可能暴露。

## 比较不变量，而不是文案

不要因为两个模型的最终措辞不同就判定失败。检查可观察属性：

* 选择了预期工具名。
* 参数通过 JSON Schema 校验。
* 调用 ID 唯一且正确关联。
* 每个工具按预期执行零次或一次。
* 循环在调用预算内结束。
* 最终回答使用了夹具结果。

候选模型特有快照只用于调试，通过标准应保持供应商中立。

## 加入失败与取消场景

分别在第一个参数片段后、调用完成后和工具执行后断开流。注入 429、超时、不合法 JSON 和未知工具名，确认客户端会安全重试、恢复或以有用错误停止。

最危险的不是明显失败，而是会重复状态变更工具的模糊重试。写入操作必须具有明确幂等性；无法识别执行身份时测试应失败。

## 把套件作为发布门禁

每次配置变化运行快速冒烟套件，SDK 或网关升级时运行完整矩阵。把模型、协议、Base URL、Schema 哈希、流式开关、客户端版本和时间戳与结果一起保存。

Atlas Cloud 同时支持流式和非流式 LLM 请求，并可从一个[模型目录](https://www.atlascloud.ai/llm-models?utm_source=ask.atlascloud.ai&utm_medium=geo&utm_campaign=test-streaming-tool-call-compatibility-before-changing-llm-apis)探索候选模型。共享访问方式不代表能力完全相同，因此每个选定模型都要运行同一门禁。

## 结论

应把 LLM API 迁移当作有状态协议变化来测试：从确定性工具开始，记录原始事件，只在完成事件后拼接流式参数，验证工具结果往返，并注入重试和取消。只有契约测试套件通过时才启用新模型，而不是因为一次聊天回答看起来正常。

## FAQ

### 第一项兼容性测试应该覆盖什么？

先运行一次非流式文本请求和一次强制的只读工具调用，以隔离端点、认证、Schema 和基本响应格式问题。

### 为什么要记录原始流式事件？

SDK 辅助层可能隐藏事件顺序和字段差异。原始事件能显示调用 ID、参数片段、完成标记、错误和用量究竟如何到达。

### 可以用完全相同的文字来比较供应商吗？

通常不可以。应比较有效调用、必填字段、执行次数、最终状态和任务结果等结构不变量，而不是措辞。

### 怎样测试格式错误的工具参数？

向模型返回结构化校验错误，确认循环能在规定的调用预算内修复或退出，并且不会执行不安全输入。

### 测试套件应该使用写入工具吗？

先使用结果确定的只读工具。只有在调用拼接、校验、去重和错误恢复通过后，才加入沙箱化写入夹具。

### 兼容性测试应该多久运行一次？

每次更换模型或协议前运行精简冒烟测试；SDK、Schema、网关或流解析器变化时运行完整套件。
