非流式工具参数半截 JSON 改为原样透传
Project CI / Repository checks (pull_request) Failing after 11s
Project CI / Backend tests (pull_request) Failing after 12s
Project CI / Frontend tests (pull_request) Failing after 23s
Project CI / Native shell tests (pull_request) Failing after 9m30s

b1ef45fbd 把“arguments 非空必须是完整 JSON”同时应用到流式和非流式,
越界了。两者的“参数不完整”语义不同:流式意味着流被截断,是传输层事实;
非流式的外层 body 已经完整,参数半截只说明模型输出有问题,属于内容层事实。

App 早有更好的处理——工具计划格式修复循环把畸形响应回灌给模型重写,比硬
报错再重跑整轮 Provider 有效得多。平台层拦下来会丢掉修复所需的 call id、
函数名和原始参数,导致 background_agent_runtime_repairs_malformed_native_
function_arguments 直接以 kind=deserialize 失败,第二次 repair 请求不再发出。

按流式与非流式区分该校验。b1ef45fbd 的其余部分不变:禁止静默丢弃、id 与
函数名必填、空参数归一为空对象、三协议共用一份策略。

原用例反转为断言原样透传,并补一条成对的流式用例锁住流式仍然报错。
This commit is contained in:
2026-07-27 07:00:21 +00:00
parent 214357b175
commit eb63f44271
2 changed files with 61 additions and 16 deletions
@@ -257,7 +257,9 @@ npm run check:server-rs-ddd
流式 `on_delta` 只发送文本增量、累计文本和完成原因,工具调用不进入回调。平台层按协议 slot 聚合并行工具片段:Chat 使用 `delta.tool_calls[].index`,Responses 使用 `output_index`,Anthropic 使用 content block `index`。Responses 的 `function_call_arguments.done` 和 `response.completed` 中的完整 arguments 覆盖此前分片;completed-only 恢复以 `response.output[]` 数组下标作为 slot。该聚合只负责解析,不表示工具执行并发。
工具调用归一只有一份策略,流式与非流式、三种协议共用:协议层只把各自 DTO 映射成统一中间形态,接受与否全部由归一层判定。**被识别为工具调用(Chat 的 `tool_calls[]` 成员、Responses 的 `type=function_call`、Anthropic 的 `type=tool_use`)后,字段不全一律返回 `Deserialize`,不得静默丢弃。** 缺少 id 或函数名报错;arguments 缺省或空白归一为 `{}`(零参函数合法);arguments 非空则必须是完整 JSON,否则报错。这只是 JSON 语法完整性检查,不是按工具 `parameters` 执行 JSON Schema 校验。
工具调用归一只有一份策略,流式与非流式、三种协议共用:协议层只把各自 DTO 映射成统一中间形态,接受与否全部由归一层判定。**被识别为工具调用(Chat 的 `tool_calls[]` 成员、Responses 的 `type=function_call`、Anthropic 的 `type=tool_use`)后,字段不全一律返回 `Deserialize`,不得静默丢弃。** 缺少 id 或函数名报错;arguments 缺省或空白归一为 `{}`(零参函数合法)。
arguments 是否必须是完整 JSON **按流式与非流式区分,两者的“参数不完整”语义不同**:流式意味着流被截断,是传输层事实,平台层必须返回 `Deserialize`;非流式的外层 body 已经完整,参数半截只说明模型输出有问题,属于内容层事实,必须**原样透传给调用方**。平台层不得在非流式路径拦截——调用方的工具计划格式修复循环要靠 call id、函数名和原始畸形参数把响应回灌给模型重写,这比硬报错再重跑整轮 Provider 有效得多;在平台层报错会把这三样信息一起丢掉。无论哪种,这都只是 JSON 语法完整性检查,不是按工具 `parameters` 执行 JSON Schema 校验。
静默丢弃是明确禁止的实现方式:它会把“上游给了工具调用但我们没解出来”伪装成“上游只回了正文”——响应同时带解说文本时更会被当作普通回复成功返回,而带 `tool_choice=required` 的请求随后退化为格式修复循环,审计里只能看到“模型没按协议调用工具”,看不出真正成因在解析层。非流式 DTO 为兼容流式分片把字段改成可选后尤其要注意:可选字段解除了 serde 的强制校验,缺失必须在归一层重新拦截。