bug:Chat Completions 桥接把超大 exec 工具说明塞进每轮请求,导致上下文立即耗尽
- Dominant language
- TypeScript
- Stars
- 2.7k
- Forks
- 401
- Avg merge
- 21h 48m
- Merged PRs (30d)
- 776
Description
## 一句话说明
在 Cindy 里使用 **Codex + OpenCode Go 的 DeepSeek V4 Flash** 时,哪怕新建任务后只发一句 `hi`,也会马上占满约 256k 上下文并开始反复“自动压缩”。
这不是模型把 token 算错了,也不是用户消息太长:Cindy 的 Chat Completions 桥接层把 `exec` 工具里将近 **900 KB** 的完整工具说明,原封不动地塞进了每次上游请求。
## 使用环境
- Cindy 0.1.50
- macOS arm64
- Agent:Codex
- 供应商:OpenCode Go
- 模型:`deepseek-v4-flash`
- 协议:`openai-chat`(Cindy 将 Responses 请求转换为 Chat Completions)
## 如何复现
1. 在 Cindy 中选择 Codex。
2. 供应商选 OpenCode Go,模型选 `deepseek-v4-flash`。
3. 新建任务,只发送:`hi`。
4. 可以看到首轮输入接近 256k,随后出现自动压缩;有时会连续压缩、几乎无法正常开始任务。
## 实际表现
本地只记录了体积和用量统计,没有保存 API Key、完整提示词或对话内容。
| 上游 Chat Completions 请求的组成 | 体积 |
| --- | ---: |
| 整个请求 | 952,588 B |
| 普通消息内容 | 52,445 B |
| `tools` 字段 | 899,146 B |
| 其中 `exec` 一个工具 | 895,738 B |
同一轮 OpenCode Go 返回的用量:
```json
{
"prompt_tokens": 256174,
"completion_tokens": 66,
"total_tokens": 256240,
"cached_tokens": 247040
}
```
该路径下 Codex 实际可用窗口约为 258,400 token,所以一次简单问候就几乎没有剩余空间,触发自动压缩是必然结果。
对照:同一台 Cindy、同一工作目录,改用 GPT-5.6 时首轮约为 20k 输入 token,属于正常量级。
## 为什么会这样
这里的 `exec` 不是普通 shell 命令,而是 Code Mode 的一个“总入口”工具:它的说明里带了大量可调用工具及文档。
在 `packages/responses-chat-bridge/src/tool-context.ts` 的 `ChatBridgeToolContext.addFunction()` 中,桥接层目前会直接把 `source.description` 复制到 Chat Completions 的函数工具定义里:
```ts
function: {
name: chatName,
...(source.description ? { description: source.description } : {}),
parameters: normalizeFunctionParameters(source.parameters),
}
```
因此,这份接近 900 KB 的 `exec` 说明会随每一轮请求一起发给上游。上游实际收到了约 953 KB 的请求,也实际计为了约 256k 输入 token。
## 期望行为
新任务的简单问候不应占满整个上下文,更不应陷入自动压缩循环。`exec` 应保持可用,但桥接层不应把它的完整内部工具目录重复发送给上游。
## 建议修复方案
针对 Responses → Chat Completions 桥接路径:
1. 对 Code Mode 的 `exec` 这类“工具总入口”,发送一段简短、稳定的调用说明,而不是完整嵌套工具目录。
2. 保留工具名和参数 schema,避免影响工具调用及结果回传。
3. 为函数工具描述增加大小预算;超过预算时进行专门的压缩,并记录可诊断的警告。
4. 增加回归测试:构造一个描述特别大的 `exec`,验证转换后的 `tools` 请求体保持在合理大小。
不建议直接截断任意工具的参数 schema,因为那可能破坏工具调用语义;应优先压缩 `exec` 这类分发工具携带的长说明。
## 已验证的临时绕过
我在本机使用一个仅修改出站 `exec` 描述的兼容层测试过:没有改变模型、用户消息、凭据、工具名或参数 schema。
| 指标 | 原始请求 | 仅压缩 `exec` 描述后 |
| --- | ---: | ---: |
| 整个上游请求 | 952,588 B | 57,458 B |
| `exec` 定义 | 895,738 B | 608 B |
| 上游输入 token | 256,174 | 13,412 |
| 总 token | 256,240 | 13,505 |
| 首轮自动压缩 | 会触发 | 不再触发 |
这说明问题可以在 Cindy 的 Chat bridge 层解决。
## 相关 issue
- #1432:也是工具 schema 导致首轮 token 过高,但它关注的是非 Code Mode 情况下暴露大量顶层工具;本问题是 Code Mode 下单个 `exec` 描述被桥接层放大。
- #1466:关注 DeepSeek 实际窗口为何回退为约 256k;本问题解释了为什么这个 256k 窗口会被一个空任务立刻占满。
Contributor guide
Research direction
Start in packages/responses-chat-bridge/src/tool-context.ts at ChatBridgeToolContext.addFunction(), where source.description is copied into the Chat Completions tool definition. Add a regression test using an unusually large exec description and verify that the converted tools request stays within a reasonable size while preserving the tool name and parameters schema. Done means the bridge no longer sends the full nested exec directory on every request and emits a diagnostic warning when descriptions exceed the budget.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- typescript
- Domain
- api, backend-api-design
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Quiet
- Clarity
- Mostly clear
- Newbie friendliness
- 58/100