Files
agcore/docs/9f-edge-cases.md
T

104 lines
4.4 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
```
> **🔄 待深入推演:Thinking signature 的端到端传递**
> 当前设计中,Anthropic 的 thinking block signature 在 `ContentBlock::Thinking.signature` 中存储,
> 但从流式事件到最终 ContentBlock 的传递路径没有闭环:
> 1. `content_block_start`(针对 thinking block)下发的是 `{ type: "thinking" }` 没有 signature
> 2. `content_block_delta` 下发 `thinking` delta + **signature**(在 delta 中,不是独立的字段)
> 3. `StreamEvent::ThinkingDelta { text }` 当前只有 text,缺少 signature 字段
> 4. 最终需要将 signature 附着到 `ContentBlock::Thinking { text, signature: Some(...) }` 上
>
> **需要推演:**
> 1. `StreamEvent::ThinkingDelta` 是否需要增加 `signature: Option<String>` 字段?
> 2. 或者改为每个 block 结束时发一个独立的 `ContentBlockSealed { index, block: ContentBlock }` 事件?
> 3. 非流式响应中,Anthropic 的 thinking block 是完整的(含 text 和 signature 在同一 block 中),
> 映射没有问题。但流式响应中,signature 的附着时机需要在 AnthropicProvider 的状态机中准确定义。
> 4. `PartialMessageResponse`(§4.3 待推演)需要能按 index 追踪 thinking block 的 text 累积和 signature 暂存。
>
> 此问题与 [9d-provider-implementations.md](9d-provider-implementations.md) §5.2 的"Anthropic 流式状态机设计"直接相关,
> 建议在推演状态机时一并解决。
> 优先级:高(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。