Skip to content

feat(anthropic): 补齐停止原因与 Fable 5.1 / Opus 5.5 兼容性 - #58

Merged
zhanghanduo merged 3 commits into
mainfrom
feat/surface-anthropic-stop-details
Oct 5, 2026
Merged

zhanghanduo merged 3 commits into
mainfrom
feat/surface-anthropic-stop-details

Conversation

@zhanghanduo

@zhanghanduo zhanghanduo commented Oct 5, 2026 •

Copy link
Copy Markdown
Collaborator

Anthropic 的 classifier refusal 是 HTTP 200,可能没有内容,也可能留下部分文本。此前 trace 无法可靠区分拒答、正常结束和输出截断。本次将停止原因完整传递到响应与两种 trajectory 格式,并补齐当前 Claude 的签名历史兼容性。

  • 普通和流式响应均在 response_metadata 保留原始 stop_reason 与结构化 stop_details;finish_reason 继续将 max_tokens 归一为 length。未知 category 和 SDK 扩展字段透传。
  • TurnContext 向观察者提供 finish_reason、stop_details,JSON 与 JSONL 均记录;新增字段有默认值。
  • 覆盖 claude-fable-5-1 和 claude-opus-5-5:未配置 thinking 时仍转发 effort;空 thinking 文本的签名与 interleaved tool-use 顺序完整保留,canonical tool calls 在过滤/修复后仍为权威来源。
  • 对明确的 conversation-bound thinking signature 400,仅去除历史 thinking 重试一次。成功响应通知 loop 清除旧签名后再保存本轮的新签名,避免每轮重复失败。调用方初始历史不被修改;其他 400 或重试失败继续抛出。
  • Anthropic SDK 锁定更新至 1.11.0,CI 新增最低支持版本 0.69.0 的隔离兼容测试。补 changes/58.feature.md,修复 version-bump 检查;项目版本保持 0.12.3。

验证:

  • Python 3.12 / SDK 1.11.0:全量 1817 passed、1 skipped;隔离 tokenizer contract 另行通过。
  • Python 3.12 / SDK 0.69.0:144 passed。
  • Ruff、Pyright、unconsumed-fields、diff whitespace、sdist/wheel 构建通过。
  • 新增测试使用真实 SDK + MockTransport 验证 JSON/SSE 解析、部分文本拒答、工具/签名 round-trip、窄范围重试、多轮恢复及 JSON/JSONL trace;未进行付费模型在线调用。

模型兼容性依据:Fable 5.1 迁移指南、Opus 5.5 迁移指南、preserved thinking。

本次保持现有 loop 终止策略;拒答后的 fallback 模型选择仍由 host 决定。直接使用 client 的调用方应消费 thinking_history_reset,同步清除自己的旧 thinking 历史。

zhanghanduo and others added 2 commits October 5, 2026 15:00
安全分类器拒答是 HTTP 200 + 空 content + stop_reason="refusal",引擎看到的
形状与"模型选择不说话"完全一致:无文本、无 tool call → 走 no_tool 退出 →
整跑结束且 trace 上看起来干净。Anthropic 的 stop_details(type / category /
explanation)此前从未被读取,response_metadata 只有 {"id": ...}。

2026-10-05 gdpval-220 取证正是卡在这里:20 个 run 以 llm_error 收场、另有 25 个
以 no_tool + 空交付物收场,而 trace / job.log / trial.log / breaker last_error
四处都没有记录哪些是拒答,只能逐个 case 手工解剖。

本次补齐三段:

1. providers/anthropic.py —— 新增 _anthropic_stop_details(),同时支持 SDK
   model 对象与 dict;_to_llm_response 把 RAW stop_reason 与 stop_details 放进
   response_metadata。raw 值与归一化后的 finish_reason 并存:max_tokens 会被
   归一成 length,消费者不该为了判断"provider 是否拒答"而去记住哪些标记被重写。
   category 是开放集合(cyber / bio / reasoning_extraction / frontier_llm /
   general_harms / …,新模型持续新增),原样透传不做白名单。

2. 流式路径 —— StreamDelta 新增 stop_details 字段(沿用 provider 字段"终端
   元数据折进 response_metadata"的既有模式),anthropic 的 message_delta 捕获
   它,_streaming 的组装器折进 response_metadata。否则一次流式拒答仍然隐形。

3. TurnContext 新增 finish_reason / stop_details 字段,agent_loop 填充,
   TrajectoryFileObserver 写进 JSONL。finish_reason 此前只在 metadata 里,
   观察者拿不到一等字段。end_turn 不写(无信息量且几乎每行都有),其余
   (length / tool_use / refusal / …)记录。

两个字段都有默认值,未迁移的 TurnContext 构造点保持有效 —— 不设置的生产者
与改动前一样看不见,但不会出错。

测试:新增 test_trajectory_stop_reason.py(5 条)+ test_provider_native_clients.py
的 refusal 组(7 条,含一条走真实 SDK 解析的 MockTransport 端到端)+
test_llm_runtime_stream_metadata.py 的流式组(2 条)。全量 1779 passed。
ruff 通过。

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@zhanghanduo zhanghanduo changed the title feat(observability): 把 refusal / finish_reason 暴露到 LLMResponse 与 trace feat(anthropic): 补齐停止原因与 Fable 5.1 / Opus 5.5 兼容性 Oct 5, 2026
Follow-up to 20b288f (whose body was lost to truncation). Summary of that
commit: forward effort without thinking; keep tool_use blocks in provider
order among signed thinking (canonical tool_calls stay authoritative); retry
once without historical thinking when a thinking signature is rejected, and
report thinking_history_reset so the loop drops stale signatures before
storing the new turn; record finish_reason/stop_details in JSON trajectories.

This commit:
- Signature retry now matches "signature" + "thinking" instead of the exact
  "bound to a different conversation" phrase. The wording is not a documented
  contract, and omitting historical thinking is always a valid request, so a
  narrower match would silently disable recovery on any rewording.
- Trajectories also omit finish_reason "stop" (OpenAI-compatible normal end),
  matching the existing end_turn omission.
- Docs/changelog: note the per-request double cost for direct client callers
  that ignore thinking_history_reset, and the effort-without-thinking change.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
@zhanghanduo
zhanghanduo merged commit 78ef26b into main Oct 5, 2026
5 checks passed
@zhanghanduo
zhanghanduo deleted the feat/surface-anthropic-stop-details branch October 5, 2026 08:18
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant