# 边界情况 > 本文档从 `9-llm-provider-unified-interface.md` 拆分而来,包含 §9 边界情况。 > > **相关文件:** > - [9b-ir-type-system.md](9b-ir-type-system.md) — IR 类型定义(ContentBlock、ThinkingConfig、MessageRequest/Response 等) > - [9c-llm-provider-trait.md](9c-llm-provider-trait.md) — StreamEvent 类型定义(ThinkingDelta 等) > - [9d-provider-implementations.md](9d-provider-implementations.md) — Anthropic 流式状态机(与 Thinking signature 强相关) > - [9e-llm-cycle-and-upstream.md](9e-llm-cycle-and-upstream.md) — LlmCycle 的 tool 循环与 compact 逻辑 ## 9. 边界情况 ### 9.1 工具定义的传递路径变化 | 阶段 | 路径 | |------|------| | **当前** | `ToolRegistry.definitions()` → `Vec` → `build_request()` 包装为 `Vec` → `ChatRequest.tools` | | **方案 C** | `ToolRegistry.definitions()` → `Vec` → `build_request()` 直接放入 → `MessageRequest.tools` → **Provider 内部**包装为原生格式 | 工具定义的抽象层次从 LlmCycle 下移到 Provider 内部,更合理。 ### 9.2 Thinking 的端到端流程 ``` Agent 启用 thinking: config.thinking = Some(ThinkingConfig { budget_tokens: 16000 }) LlmCycle.build_request(): → MessageRequest { thinking: config.thinking, ... } OpenaiProvider: → capabilities().features.thinking == false → 忽略 thinking 字段(或转为 extra.reasoning_tokens) AnthropicProvider: → capabilities().features.thinking == true → 将 thinking 写入 Anthropic 请求参数 → 流式响应中返回 StreamEvent::ThinkingDelta → 最终消息的 content 中包含 ContentBlock::Thinking ``` > **✅ 推演结论(2026-06-17):方案 C —— MessageComplete 兜底 + finalize 统一回填** > > **决策:** Thinking signature 不在 event 级传递,而是通过 `MessageComplete.thinking_signature` 携带, > 由 `PartialMessageResponse::finalize()` 统一回填到最后一个 Thinking block。 > > **具体路径:** > ``` > Anthropic message_delta > → AnthropicProvider 提取 thinking.signature > → 发出 MessageComplete { stop_reason, thinking_signature: Some("0x...") } > > PartialMessageResponse: > ContentBlockEnd(thinking_idx) ← signature 还没到,builder 中 signature = None > ... > MessageComplete { thinking_signature: Some("0x...") } > → state.thinking_signature = Some("0x...") > > finalize(): > 遍历 blocks → 找到 Thinking builder > → builder.signature 为 None,用 state.thinking_signature 回填 > → ContentBlock::Thinking { text, signature: Some("0x...") } > ``` > > **理由:** > 1. 不新增独立事件类型(保持 StreamEvent 简洁) > 2. signature 在 message_delta 中下发(晚于 content_block_stop),MessageComplete 是该时刻的天然载体 > 3. Anthropic 同一响应中最多一个 thinking block,不存在"多 thinking block 归属"的歧义 > 4. 非流式响应中,thinking block 的 signature 直接通过 IR→原生映射填充,路径不变 > > **影响范围:** > - `StreamEvent::MessageComplete` 增加 `thinking_signature: Option` 字段 > - `PartialMessageResponse` 增加 `thinking_signature: Option` 暂存字段 > - `PartialMessageResponse::finalize()` 增加回填逻辑(约 3 行代码) > - AnthropicProvider 流式状态机在 `message_delta` 处理中提取 thinking.signature > > **何时实现:** Phase 4 AnthropicProvider 流式状态机实现时一并完成 ### 9.3 Multiple ContentBlock 的处理 消息的 `content` 为 `Vec`,可包含多种类型的混合: ```rust Message::Assistant { content: vec![ ContentBlock::Text { text: "让我思考一下..." }, ContentBlock::Thinking { text: "先用加法工具...", signature: None }, ContentBlock::Text { text: "答案是 3" }, ContentBlock::ToolUse { id: "call_1", name: "add", input: json!({"a":1,"b":2}) }, ], } ``` LlmCycle 处理 tool 循环时只关心 `ToolUse` block,其他 block 按原样传递给消息历史。 ### 9.4 多 Choice 场景 OpenAI 的 `n > 1` 参数在 IR 中通过 `extra` 传递: ```rust // 请求 request.set_extra("n", json!(3)); // 响应 response.extra["all_choices"] = json!([...choices 2..n]); ``` `MessageResponse` 只承载 `choices[0]`(主消息),其他 choices 放 `extra`。 ### 9.5 OpenAI Response API 的内置工具 通过 `extra` + 可选能力 trait 支持: ```rust // 配置内置工具 request.set_extra("built_in_tools", json!(["web_search", "file_search"])); // Response API 返回的搜索结果 // → ContentBlock::Extension { kind: "web_search_result", data: {...} } ``` 如果未来内置工具成为跨 Provider 通用特性,将 `BuiltInToolsCapable` 升级为核心 trait。