Skip to content

[Bug] DeepSeek官方的key调用失败 #718

Closed

Description

@ki10Moc

What happened? / 问题描述

报错信息:
400 {"error":{"message":"messages.135.content.3: tool_use ids must be unique","type":"invalid_request_error","param":null,"code":"invalid_request_error"}}

Steps to reproduce / 复现步骤

配置deepseek官方的key

Expected behavior / 预期行为

能够正常调用

Actual behavior / 实际行为

一直返回:
400 {"error":{"message":"messages.135.content.3: tool_use ids must be unique","type":"invalid_request_error","param":null,"code":"invalid_request_error"}}

App version / 应用版本

0.15.1

Operating system / 操作系统

macOS

Extra environment / 其他环境信息

No response

Logs / 日志

Screenshots / 截图

Image

Activity

  1. vastsa commented on Sep 20, 2026

    @vastsa
    Owner

    可以让 ai 诊断一下日志,看看是怎么回事

  2. muzimu217 commented on Sep 20, 2026

    @muzimu217
    Contributor

    按 @vastsa 的建议做了一轮诊断。没有 DeepSeek 官方 key 做端到端复现,但在本机 140 个真实会话 transcript(约 2.2 万条工具调用记录)上做了数据侧核查,可以把范围收窄很多:

    数据侧核查(本机真实数据)

    • assistant 工具调用的 callId 在磁盘上零重复:140 个会话文件里 5,786 个非空 callId,同会话内无一重复。
    • 但有一个重要的结构事实:transcript JSONL 是 append-only 快照流——同一个 tool_call 会以多条记录出现(pending 态无 id、complete 态带 id),本机 22,190 条 tool_call 块快照实际只对应 5,786 个真实调用,约 74% 是中间态快照。

    推论

    tool_use ids must be unique 不太可能来自存储层(磁盘上没有重复),大概率产生在发送前的消息组装/重放层:把同一调用的多个快照、或压缩边界两侧的重叠段一起放进了 messages。结合报错位置 messages.135.content.3(很靠后的长序列),三个最值得查的候选:

    1. 快照归并:重放时是否严格按消息 id 取“最后一条快照”?若按 block 收集而非按消息归并,同一 callId 会进两次。
    2. 压缩边界:compaction 保留段与续写段是否可能各含一份同一 tool_use(保留摘要 + 原文未截断)。
    3. 重试重放:retry 路径是否会把最后一条 assistant(含其 tool_use)重复追加。

    建议的定位手段(低成本)

    在 agent-runtime 组装完请求、发送前加一个断言:扫一遍 messages 里 assistant 的 tool_use.id,发现重复即 warn 日志打出(会话 id + 重复 id + 前后各 2 条消息摘要)。这样下一个复现案例会直接给出重复产生的位置,而不是只拿到服务商的 400。

    也请报告者补充两点,能进一步收窄:出错会话是否很长/经历过上下文压缩;logs/app/session.log 里 400 前后的记录(有没有紧邻的 retry 或 compaction 事件)。

  3. vastsa commented on Sep 21, 2026

    @vastsa
    Owner

    复核结论:报告的 400 错误真实,但根因仍未闭环。当前 main 的持久化会话数据中 callId 可保持唯一,已有修复覆盖跨会话碰撞;现有证据更指向发送前的快照归并、compaction 或 retry 重放。请补充复现会话是否经历压缩/重试,以及 400 前后的 session/app 日志,issue 保持开放。

  4. muzimu217 commented on Sep 21, 2026

    @muzimu217
    Contributor

    收到复核结论,方向一致——快照归并/压缩边界/重试重放正是此前数据侧排查收敛的三个候选(磁盘 callId 零重复、74% 为中间态快照,问题必然在组装层)。坐等报告者补充压缩/重试线索。另外之前提过的定位手段依然有效:在 agent-runtime 组装完 messages、发送前扫一遍 tool_use id,发现重复即打 warn(会话 id + 重复 id + 邻近消息摘要)——等方向确认后愿意直接做成 PR。

  5. vastsa commented on Sep 22, 2026

    @vastsa
    Owner

    已在 main 修复并随 v0.15.2 发布,本 issue 可以关闭。

    根因:transcript 是 append-only 流,且容忍重试追加,因此同一个 tool call 可能以新的 id 再次进入下一次请求;宿主原有的 keep-last 去重按行 id 折叠,覆盖不到"新 id 的重复形态"。Anthropic 系端点(含 DeepSeek 官方)于是以 tool_use ids must be unique 拒绝整个回合,且该会话此后再也无法继续。

    修复(PR #780 / commit 184e7c489):在最后一层视图、convertToLlm 之前对每个出站请求执行 dropDuplicateToolCalls —— 首次出现者保留,同一 id 的后续 call 与 result 一并丢弃,保证"一个 call 对应一个 result",同时把这次重建日志连同被丢弃的 id 打出来(最多 8 个),下次再出现时能直接指向写入方,而不是只看到 provider 的报错文案。子代理侧的重复 id 由 PR #782(3de2a6561)覆盖。代码注释中直接引用了本 issue 编号。

    包含版本:v0.15.2 / v0.15.3(已用 git tag --contains 184e7c489 确认)。

    请用 ≥ v0.15.2 复跑一次原来的长会话场景。若仍出现同样的 400,日志里应当能找到那一行 duplicate tool-call 记录(含 id),贴回来即可重新打开 —— 那时问题会明确指向写入方,而不是 provider。

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions