Files
agcore/design/pdd/9f-edge-cases.md
T
徐涛 28ca43ccb2 chore(docs): 将设计文档从 docs 移至 design 目录
将 note、pdd、prd、roadmap 四类文档分别归入 `design/` 下对应子目录中,并新增 `.gitkeep` 占位文件
2026-07-23 05:45:53 +08:00

122 lines
4.7 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 边界情况
> 本文档从 `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。