环境
- Kagura 版本:
v0.2.1 (commit 0a7289c,源码构建)
- 部署方式: macOS,PM2 双实例 (codex-001 + cc-001)
- cc-001 provider:
claude-code
- cc-001 后端: third-party Anthropic-compatible API (base URL + auth token)
- cc-001 模型:
opus[1m](实际代为解析为 gpt-5.5[1m],具备 1M 上下文上限)
问题描述
在同一个 Slack thread 中连续使用 cc-001(Claude Code provider)多轮对话后(约 10+ 轮),后续请求稳定出现 HTTP 408,Claude SDK 自动重试 10 次全部失败,最终 execution 状态变为 failed。
错误日志关键片段:
[bootstrap:claude:session] ⚠ Claude API retry 1/10 after 556ms (http=408 error=server_error)
...
[bootstrap:claude:session] ⚠ Claude API retry 10/10 after 38624ms (http=408 error=server_error)
[bootstrap:claude:session] ERROR Claude Agent SDK execution failed:
Claude Code returned an error result: API Error: 408
{"error":{"message":"stream error: stream disconnected before completion:
stream closed before response.completed","type":"invalid_request_error"}}
而在新开 thread 中发送相同请求,一切正常。
根因分析(阅读源码后)
我阅读了以下关键代码后,确认问题来自于 Kagura 没有对 Claude Agent SDK 的 session 做任何上下文压缩或自动释放:
- Slack 端:
thread-context-loader.ts 硬编码 limit: 200,每轮拉取最多 200 条 Slack 消息并全部塞入 prompt(processors.ts:237-268)
- SDK 端: 每次在同一 thread 里的后续消息都带
resumeHandle(即 claude_session_id),传给 agent-sdk 的 query({ resume: sessionId })(adapter.ts:291)
- 无 compact 机制: SDK 内部 session 历史持续累积——包括每次的工具调用、读文件、对话历史——且 Kagura 自身也没有任何截断/压缩/compact 的逻辑
实际观测到的单次请求规模:
Thread context loaded for ... (103 messages)
Token usage [gpt-5.5[1m]]: input=247,909 output=37,688 cache_read=713,728
这个请求规模加上 streaming 连接,上游/api 代理非常容易在任意环节(idle timeout/proxy/NLB/模型节点)触发 408 "stream disconnected before completion"。
一旦失败,SDK 的重试用的是同一个巨大的请求,必定持续失败,形成死循环。 只有重启 Kagura + 标记 execution 为 stopped 才能打破。
成功场景对比
- 新开 thread: 一切正常(Slack 历史为空,无 SDK session resume)
/model reset: 能清掉 session handle,新请求不带 resume,从零开始
- 切换 workspace: 文档提到会 "start a fresh session"
现有缓解手段(及其不足)
| 手段 |
是否有效 |
副作用 |
| 新开 thread |
✅ |
丢弃 Slack 内上下文的连续性 |
/model reset |
✅ |
未文档化为"上下文重置"工具 |
| switch workspace |
✅ |
需要来回切,不自然 |
改小 CLAUDE_MAX_TURNS |
⚠️ 部分缓解 |
只限制工具调用轮数,不管 session history 累积 |
建议
- 文档化: 在 README 或 architecture docs 中说明这个限制,推荐用户在长任务中定期使用
/model reset 或 save_memory + 新 thread 的工作流。
- 考虑自动机制: 例如在 SDK 层面提供 session compaction(类似 Claude CLI 的
/compact),或者在 Kagura 层面检测到 thread 过长后自动 reset session handle。
- 让 Slack 线程历史量可配置:
thread-context-loader.ts 中的 limit: 200 写死了,建议做成环境变量(如 SLACK_THREAD_HISTORY_LIMIT)。
补充
这个问题用相同的 gpt-5.5 模型在 Codex CLI provider(codex-001) 上表现不同——Codex 走的是 CLI stdin 的 --resume 线程机制,出问题时 CLI 会报 "thread/resume failed" 并自动 fallback 到无 resume 的新 session。Claude agent SDK 没有这个兜底。
感谢佬的这个项目,它非常棒!但是目前尚不清楚是模型的问题,还是其他的问题,因为我没有换别的模型试过,也没有用codex试过在一个thread里长对话,目前对话轮数稍微多一些,就会408,不知道佬有没有什么好办法呢?另外,我也装了你的handoff那个skill,或许在slack这里面能用上?
环境
v0.2.1(commit0a7289c,源码构建)claude-codeopus[1m](实际代为解析为gpt-5.5[1m],具备 1M 上下文上限)问题描述
在同一个 Slack thread 中连续使用
cc-001(Claude Code provider)多轮对话后(约 10+ 轮),后续请求稳定出现 HTTP 408,Claude SDK 自动重试 10 次全部失败,最终 execution 状态变为failed。错误日志关键片段:
而在新开 thread 中发送相同请求,一切正常。
根因分析(阅读源码后)
我阅读了以下关键代码后,确认问题来自于 Kagura 没有对 Claude Agent SDK 的 session 做任何上下文压缩或自动释放:
thread-context-loader.ts硬编码limit: 200,每轮拉取最多 200 条 Slack 消息并全部塞入 prompt(processors.ts:237-268)resumeHandle(即claude_session_id),传给agent-sdk的query({ resume: sessionId })(adapter.ts:291)实际观测到的单次请求规模:
这个请求规模加上 streaming 连接,上游/api 代理非常容易在任意环节(idle timeout/proxy/NLB/模型节点)触发 408 "stream disconnected before completion"。
一旦失败,SDK 的重试用的是同一个巨大的请求,必定持续失败,形成死循环。 只有重启 Kagura + 标记 execution 为 stopped 才能打破。
成功场景对比
/model reset: 能清掉 session handle,新请求不带 resume,从零开始现有缓解手段(及其不足)
/model resetCLAUDE_MAX_TURNS建议
/model reset或save_memory+ 新 thread 的工作流。/compact),或者在 Kagura 层面检测到 thread 过长后自动 reset session handle。thread-context-loader.ts中的limit: 200写死了,建议做成环境变量(如SLACK_THREAD_HISTORY_LIMIT)。补充
这个问题用相同的
gpt-5.5模型在 Codex CLI provider(codex-001) 上表现不同——Codex 走的是 CLI stdin 的--resume线程机制,出问题时 CLI 会报 "thread/resume failed" 并自动 fallback 到无 resume 的新 session。Claude agent SDK 没有这个兜底。感谢佬的这个项目,它非常棒!但是目前尚不清楚是模型的问题,还是其他的问题,因为我没有换别的模型试过,也没有用codex试过在一个thread里长对话,目前对话轮数稍微多一些,就会408,不知道佬有没有什么好办法呢?另外,我也装了你的handoff那个skill,或许在slack这里面能用上?