Skip to content

[Bug] swe-1-7 reasoning-only finish в agentic 循环 — 模型声明工具意图但不发出 tool call,thinking-only + end_turn 让客户端停滞或报错 #237

Description

@warelik

版本 / Version

v3.9.6 (aafac86) — reproduced on current upstream master.

部署方式 / Deployment

Self-hosted, DEVIN_CONNECT backend, model swe-1-7 (free tier — currently free, which makes it the go-to SWE model for many deployments, so the case is likely relevant well beyond one setup).

Client note: the hunt ran intentionally through kimi CLI (Anthropic /v1/messages). Where other clients (claude-code et al.) silently drop a thinking-only turn, kimi's strict client surfaces a full trace (APIEmptyResponseError + retry log) — that trace is what made this bug catchable at all. Any OpenAI-compatible agentic client hits the same upstream behavior, just without the diagnostics.

问题描述 / What happens

Mid agentic loop, swe-1-7 finishes the turn with reasoning-only output: thinking deltas arrive, zero content, zero tool calls, finish=stop. The proxy forwards this as a valid-looking turn (stop_reason=end_turn with only a thinking block), which breaks clients differently:

  • Strict clients (kimi CLI): APIEmptyResponseError: response containing only thinking content without any text or tool calls — 10/10 identical retries, the turn dies loudly.
  • Lenient clients silently drop the turn; the agentic loop stalls indefinitely (an unattended overnight run produces nothing).

Measured on live probes (agentic payload, 18 tools, multi-turn): thinking-only in 5/20 (25%) of turns; on plain prompts 5/5 (100%). In 60% of cases the reasoning explicitly declares tool intent ("I'll use the read_file tool…") but the call is never emitted.

根因 / Root cause

Three links of one chain — fixing only one has no observable effect:

  1. Upstream quirk: swe-1-7 (Kimi K2 fine-tune) intermittently spends the whole turn in the reasoning channel (proto field cursor 模型命名不一样好像用不了 #9), emitting nothing into content (Firebase 登入失敗: 信箱或密碼錯誤 #3), then stops at 21–174 completion tokens — NOT budget exhaustion.
  2. Proxy treats reasoning-only as valid: isEmptyCompletion in devin-connect-openai.js counts reasoning as content (sawContent), so the empty-completion retry never fires, and the thinking-only turn streams out as end_turn.
  3. Empty-assistant poisoning: client retries carry the zero-length assistant turn; buildGetChatMessageRequest forwards it verbatim upstream, which provokes the model into repeating empty turns — this is why all 10 client retries failed identically.

复现 / Steps to reproduce

  1. OpenAI endpoint, stream=true, model swe-1-7, ~18 tool definitions, agentic multi-turn history (system + user + assistant tool_call + tool result + user).
  2. Repeat ~20× (fresh conversation each time): ~25% of turns return reasoning_content deltas followed by finish_reason=stop with empty content.
  3. Plain prompt without tools ("calculate 15*17 step by step"): 100% reasoning-only.

日志 / Logs

DEVIN_CONNECT: empty completion (finish=stop, completion_tokens=33) — retry 1/2
msg[2] role=assistant len=0 hash=e3b0c44298fc1c14   ← poisoned retry history

Client side (kimi CLI debug session):

APIEmptyResponseError: The API returned a response containing only thinking
content without any text or tool calls. finishReason=completed,
rawFinishReason=end_turn. model: ..swe   (attempts 1..10, all identical)

修复方向 / Fix direction (measured)

  • Blind fresh-session retry: 20% recovery — useless.
  • Corrective user nudge appended on retry ("Stop reasoning. Emit the tool call markup now."): 24/24 (100%) recovery; first-try success 100% when empty assistant turns are dropped from the retried history (vs 75% with them).

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions