28ca43ccb2
将 note、pdd、prd、roadmap 四类文档分别归入 `design/` 下对应子目录中,并新增 `.gitkeep` 占位文件
122 lines
4.7 KiB
Markdown
122 lines
4.7 KiB
Markdown
# 边界情况
|
||
|
||
> 本文档从 `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<ToolDefinition>` → `build_request()` 包装为 `Vec<OpenaiTool>` → `ChatRequest.tools` |
|
||
| **方案 C** | `ToolRegistry.definitions()` → `Vec<ToolDefinition>` → `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<String>` 字段
|
||
> - `PartialMessageResponse` 增加 `thinking_signature: Option<String>` 暂存字段
|
||
> - `PartialMessageResponse::finalize()` 增加回填逻辑(约 3 行代码)
|
||
> - AnthropicProvider 流式状态机在 `message_delta` 处理中提取 thinking.signature
|
||
>
|
||
> **何时实现:** Phase 4 AnthropicProvider 流式状态机实现时一并完成
|
||
|
||
### 9.3 Multiple ContentBlock 的处理
|
||
|
||
消息的 `content` 为 `Vec<ContentBlock>`,可包含多种类型的混合:
|
||
|
||
```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。
|