补齐 Anthropic 原生工具与三协议流式工具调用 #112
Reference in New Issue
Block a user
Delete Branch "fix/anthropic_tool"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Anthropic 请求发送 tools 与对象形态 tool_choice 并解析 tool_use block,解除本地对 function tools 的拦截
Chat / Responses / Anthropic 三种协议的流式工具增量按槽位聚合,收尾校验参数为完整 JSON,截断流不返回半截参数
解除 App 侧 Anthropic 降级为文本 JSON 协议的两处守卫与对应提示词分支
新增依赖真实凭据的流式工具验收用例,默认 ignore
背景
此前只有 OpenAI Chat / Responses 支持原生 function tools,Anthropic 走提示词驱动的文本 JSON,且流式路径固定返回空
tool_calls——工具调用被直接丢弃。改动
能力补齐
tools/tool_use(input_schema、对象形式tool_choice)LlmToolCall归一统一
filter_map静默丢弃{};参数完整性校验只在流式生效,非流式原样透传给上层的格式修复循环四道门禁
另修正"尾部错误后只看正文非空"这条过时判据,纯工具响应不再被误丢。
验证
response.incomplete的存在与载荷为抓包确认)platform-llm单测 44 → 84附带
清理后台 planning 提示词中失效的 legacy schema;同步后端架构契约、Runtime 技术方案与决策日志;合并 4 次
</html>codex/ai-game-creator-app上游更新。[P1] 同一流式 slot 的工具身份会被静默覆盖
push_tool_fragment 遇到已有 slot 时直接覆盖 id/name,却继续保留或追加原参数。重复 index 携带不同工具身份时,可能丢掉前一个调用,甚至形成“后一个工具 + 混合参数”并交给 Runtime 执行。应在身份冲突时 Deserialize 失败关闭。Chat 和 Anthropic 官方流式协议都把 index 作为调用/内容块标识。OpenAI Chat、Anthropic streaming
[P2] Responses 仅发送最终事件时会丢正文
response.completed/incomplete 分支 只恢复工具调用,没有从 response.output[] 恢复完整文本。现有测试载荷本身包含“我来查询。”,却没断言 response.text;纯文本 completed-only 响应最终会变成 EmptyResponse。官方终态事件确实携带完整 Response/output。OpenAI Responses streaming、response.incomplete
[P2] 真实验收脚本会把拼错的协议名默认为 Responses
parse_api_kind 除 anthropic/openai_chat 外全部回落到 Responses。比如 anthopic 可能产生错误的“Anthropic 已验收”证据。应仅允许空值默认,未知非空值直接报错。
codex 侧只多一个 6bcd176bd,但它把整个 master 历史带了进来:本分支与 master 的 merge-base 还停在 eb608ac6f,本次一并跟上 1305 个文件。 三处冲突: - 契约文档:保留本分支的 platform-llm 三协议工具契约整节,图片生成条目 采纳上游新版(逻辑 SKU 与首选模型固定 gpt-image-2 的表述)。 - scripts/check-native-shells.mjs:双方独立重写了 assertAiGameCreatorShellUserDevBoundary。取本分支版本,它是上游版本的 严格超集——多断言 projectSupervisorOnly ? false : isDeveloperMode()、 <ProjectWorkspaceChatPane 挂载,以及预览 iframe 必须落在 dev-only pane 内。按块解而非整文件取一侧,上游同文件的小程序订阅消息退役改动保留。 - decision-log.md:双方各自追加。上游那三条退役 bullet 保持贴在 2026-07-26 节(与上游文件一致),本分支两节排在其后。 验证:platform-llm 91 passed;assertAiGameCreatorShellUserDevBoundary 单独 抽出跑通(完整 check:native-shells 本机跑不了,Windows 上 spawnSync npm.cmd EINVAL,WSL 里 node_modules 是 win32 装的);check-server-rs-ddd-boundaries 通过;新引入的 check:module-runtime-artifact 在 WSL 通过,Windows 上因 MSVC rlib 走长名表、脚本按 endsWith('.o') 找成员而无法运行,属上游门禁的平台 限制。 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>Responses 与 Anthropic 的流式工具参数字段走裸 serde_json::Value 加 as_str, 类型不对时 as_str 返回 None,参数被当成字段缺失;归一层再把空参数补成 {}, 于是一个身份完整、参数是合法 JSON 的调用直接交给下游执行。既有校验全都拦不住 ——零参函数的 {} 和「参数类型错了所以变成 {}」在归一层无法区分。 实测复现(探针已删):Responses 的 delta / done / 终态载荷 arguments,以及 Anthropic 的 partial_json,无论传对象还是数字,一律返回 Ok(tool_calls=[{id, name, arguments:"{}"}])。同样输入下 Chat 返回 Deserialize("invalid type: map, expected a string")——它走强类型 DTO,本来就 失败关闭。同一平台层三协议对同一种畸形输入行为不一致。 归属:不是先前几次修复的后继问题。两个构成要件都来自 7c61e9a53——那批 as_str 用法是它一次性引入 Responses / Anthropic 流式工具解析时写的, 「空参数归一为 {}」当时也已在 finish_tool_calls 里;b1ef45fbd 把它挪进 normalize_tool_calls 时逐字搬运、语义未动。逐条查过本会话的槽位失败关闭、 身份冲突、截断门禁与终态正文恢复,都走别的分支,没有扩大它的可达面。 改为 tool_argument_str:字段不存在或为 null 返回 None(合法缺省),存在但 不是字符串返回 Deserialize。extract_responses_completed_tool_fragments 随之 改签名返回 Result。只覆盖参数字段——id 与函数名即使类型不对也只会退化成缺失, 随后被归一层按缺 id / 缺函数名拒绝,本来就是失败关闭。 隔离验证:把类型检查退回 as_str,五条拒绝用例全红。 platform-llm 101 passed(原 95)。新增 Responses delta / done / 终态载荷、 Anthropic 对象与数字 partial_json 五条拒绝,外加一条作用域守卫——字段缺失与 显式 null 仍归一为 {},零参函数不能被这条新规则误杀。 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>[P2] Responses 终态快照没有同步修正流式回调
platform-llm/src/lib.rs:2033
已收到任意文本 delta 后,完整终态快照只覆盖 accumulation.text,不再触发 on_delta。例如先流出“杭州今天”,终态正文是“杭州今天多云。”,最终 LlmRunResponse.text 正确,但调用方最后收到的累计正文仍停在半截。建议当前缀一致时补发 suffix;不构成前缀时发送权威修正回调或失败关闭。现有测试只覆盖“没有 delta”和“delta 已经完整”,漏了“部分 delta + 完整终态”。