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

4.4 KiB
Raw Blame History

边界情况

本文档从 9-llm-provider-unified-interface.md 拆分而来,包含 §9 边界情况。

相关文件:

9. 边界情况

9.1 工具定义的传递路径变化

阶段 路径
当前 ToolRegistry.definitions()Vec<ToolDefinition>build_request() 包装为 Vec<OpenaiTool>ChatRequest.tools
方案 C ToolRegistry.definitions()Vec<ToolDefinition>build_request() 直接放入 → MessageRequest.toolsProvider 内部包装为原生格式

工具定义的抽象层次从 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 §5.2 的"Anthropic 流式状态机设计"直接相关, 建议在推演状态机时一并解决。 优先级:高(Phase 4 AnthropicProvider 实现的前提条件)

9.3 Multiple ContentBlock 的处理

消息的 contentVec<ContentBlock>,可包含多种类型的混合:

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 传递:

// 请求
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 支持:

// 配置内置工具
request.set_extra("built_in_tools", json!(["web_search", "file_search"]));

// Response API 返回的搜索结果
// → ContentBlock::Extension { kind: "web_search_result", data: {...} }

如果未来内置工具成为跨 Provider 通用特性,将 BuiltInToolsCapable 升级为核心 trait。