fix: 抗截断模式适配 agent 工具调用场景,恢复流式输出并保留 finish_reason 收尾块 - #415
Open
Theater-ahyeon wants to merge 2 commits into
Open
Theater-ahyeon wants to merge 2 commits into
Theater-ahyeon wants to merge 2 commits into
Conversation
修复抗截断(抗截断/ 前缀模型)在与编码智能体类客户端(如 pi)配合时的三个问题: 1. 真实工具调用回合被误续传:处理器只认 emit_answer 为完整回答,模型 调用客户端真实工具时会被当作"回答被截断"而重发请求,导致同一工具 调用在一条回复中被重复下发 max_attempts 次。现在只要本轮出现真实 工具调用,即视为完整助手回合并立即收尾(流式与非流式路径均已处理)。 2. 正文被整体缓冲导致无流式输出:普通文本原本先攒在 side buffer、等流 结束才一次性发出,且只要未等到 emit_answer 就续传重试,客户端要静默 等待 1~3 倍生成时间。现在正文/思考/工具调用全部实时透传(思考分块由 路由层转换为 reasoning_content),续传仅保留给"整个流零产出"(如上游 只回思考就结束)的回合;零产出续传时从预填充模式退回提示词模式 (空预填充等于原样重发)。 3. 上游收尾块被丢弃导致客户端误判流异常:实测上游在 emit_answer 回合后 发送 finishReason=STOP 的收尾块,但该块带有一个空的 text:"" part, 被判定为正文而丢弃。缺少 finish_reason 会让 pi 等严格校验的客户端抛出 "Stream ended without finish_reason" 并按错误自动重试,造成同一条回答 重复输出多次。现在只把非空正文文本视为产出,收尾块得以正常透传。 本地实测(Antigravity 凭证 / Gemini 3.8 Flash): - 纯聊天:思考 → 正文 → finish_reason:"stop" + usage → [DONE] - 工具调用:tool_calls → finish_reason:"tool_calls" + usage → [DONE], 不再重复下发 - 多轮回路(工具结果回传 → 生成答案)正常收尾
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
修复抗截断模式与 agent 类客户端配合时的三个问题
背景
在本地用 pi coding agent 通过
抗截断/gemini-3.8-flash-high(Antigravity 端点)做编码任务时,发现抗截断流式处理器对"模型调用客户端真实工具"和"流式输出"这两种场景处理有问题。逐层排查后定位到AntiTruncationStreamProcessor.process_stream()的三个缺陷,本 PR 一并修复。问题与修复
1. 真实工具调用回合被误续传 → 同一工具调用重复下发 N 次
处理器只把
emit_answer视为完整回答;当模型调用的是客户端自己的工具(agent 场景的常态)时,会被判定为"回答被截断"而重发请求,直到max_attempts用尽。结果是客户端在一条回复里收到max_attempts个一模一样的工具调用,会话直接卡死。修复:本轮只要出现了真实工具调用(
has_real_tool_calls),即视为完整助手回合,立即收尾(流式与非流式路径均已处理)。2. 正文被整体缓冲 → 长时间零输出 + 无意义续传
普通文本原本先攒在 side buffer、等整个流结束才一次性发出(防拼接设计),且只要没等到
emit_answer就续传重试。客户端的体验是:生成期间零输出,一次回答要静默等待 1~3 倍生成时间。修复:
reasoning_content),恢复流式体验;3. 上游收尾块被丢弃 → 客户端误判流异常并重试 → 回答重复输出
实测上游在
emit_answer回合后发送finishReason=STOP的收尾块,但该块带有一个空的text: ""part,被_chunk_has_plain_text("text" in part)判定为正文而丢弃。结果整个流没有任何块携带非空finish_reason。严格校验的客户端(pi 实测)会直接抛出
Stream ended without finish_reason并按错误自动重试——表现为同一条回答被重复输出多次,且每条消息stopReason: "error"。修复:只把非空正文文本视为产出(
part.get("text")真值判断),空的text: ""part 不再触发丢弃,收尾块(finish_reason + usage)正常透传。验证(本地实测,Antigravity 凭证 / Gemini 3.8 Flash)
finish_reason:"stop"+ usage →[DONE]finish_reason:"tool_calls"+ usage →[DONE]"stop"+ usage →[DONE]抓取上游原始 SSE 确认:
emit_answer回合的上游流为fnCall(emit_answer)→finishReason=STOP, parts=[text[0]](空文本 part),修复后收尾块可正常到达客户端。行为变化说明(供审阅参考)
emit_answer" 的防拼接场景:由于文本已实时透传,无法再撤回,现在会打出警告日志并可能造成内容重复。实测中模型要么走emit_answer、要么直接输出正文,两者混现较为少见;权衡下优先保证流式体验。emit_answer)的行为不受影响。