ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

Claude Code 源码笔记:queryLoop 流式调用与上下文压缩的配置骨架

Claude Code 源码笔记:queryLoop 流式调用与上下文压缩的配置骨架 1. 从一次流式请求超时说起queryLoop 到底在管什么如果你在用 Claude Code 做本地 AI 编码大概率遇到过这种情况聊到二三十轮突然报prompt_too_long或者流式输出到一半卡住不动再或者工具调用结果越堆越大token 消耗像坐火箭。这些现象背后其实都指向同一个核心循环——queryLoop。queryLoop是 Claude Code 里负责和模型对话一轮的状态机。它不是一个简单的fetch封装而是一个while(true)的外层循环每一轮都要做四件事压缩上下文、发流式请求、边流边执行工具、判断要不要进入下一轮。理解它的骨架你才能知道为什么有些配置项改了没用、有些报错该往哪个方向排查。这篇笔记面向的是已经在本地跑 Claude Code、并且希望通过统一 API 通道接入模型的开发者。我会把源码里读到的压缩链路和流式调用链路落成两份可以直接复制的配置骨架settings.json和config.toml再用一次真实的流式请求验证整条链路是通的。目标很明确读完你能自己搭出一个上下文不炸、流式不断、工具能跑的最小可用配置。需要提前说明的是queryLoop的压缩不是单一机制而是一条流水线applyToolResultBudget卸载超大工具结果、snipCompactIfNeeded剪切旧历史、microcompact清理旧工具结果、contextCollapse按跨度折叠、autoCompactIfNeeded全量摘要兜底。它们按顺序执行前面的生效了后面的可能直接跳过。搞不清顺序就会出现我明明开了自动压缩怎么还是爆了的困惑。2. 接入前的准备统一 Key 与 API 通道在动手写配置之前先把通道打通。Claude Code 本身支持自定义 API 端点这意味着你可以让它走一个统一的 API 通道而不是把 Key 散落在各个环境变量里。我这边用的是 TaoToken 的通道它的好处是模型对话、编码计划、Key 管理都在一个后台配置时只需要一个 base URL 和一个 Key。具体操作路径是这样的先到控制台创建一个 API Key然后确认你要用的模型名。如果你打算长期跑编码任务或者 Agent 类工作流建议直接看 Coding Plan它针对长会话和高频工具调用做了额度上的优化比按量计费更适合queryLoop这种一轮接一轮的场景。拿到 Key 之后记住两个地址API 端点是https://taotoken.net/api注意这里不带任何查询参数直接作为 base URL 用。控制台和 Key 管理在官网https://taotoken.net/?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content里能找到入口。有一点要提醒不要把 Key 硬编码进settings.json然后提交到 Git。Claude Code 支持从环境变量读取配置里只写变量名Key 放在 shell 的 profile 里或者用系统的密钥管理。这是很多人第一次接入时踩的坑Key 泄露了才发现。3. 可复制的配置骨架settings.json 与 config.tomlClaude Code 的配置分两层settings.json管行为开关config.toml管模型和通道。两份都要写对压缩链路和流式调用才会按预期工作。3.1 settings.json压缩与流式的开关先看settings.json。这份配置的核心是把压缩流水线的各个开关显式打开同时给流式调用留出足够的超时和重试空间。{ env: { ANTHROPIC_BASE_URL: https://taotoken.net/api, ANTHROPIC_AUTH_TOKEN: ${TAOTOKEN_API_KEY}, ANTHROPIC_MODEL: claude-sonnet-4-20250514 }, compact: { autoCompact: true, autoCompactThreshold: 0.85, microcompact: { enabled: true, idleMinutes: 30, keepRecentResults: 5 }, toolResultBudget: { enabled: true, perMessageLimit: 20000, skipTools: [Read] }, contextCollapse: { enabled: true, stageThreshold: 0.90, forceCommitThreshold: 0.95 } }, streaming: { enabled: true, timeoutMs: 120000, maxRetries: 3, fallbackModel: claude-haiku-4-20250514 }, tools: { streamingExecutor: true, maxConcurrent: 4 } }几个参数值得单独解释。autoCompactThreshold设成 0.85意思是 token 用量到上下文窗口的 85% 时触发全量摘要压缩。这个值不要设太高设到 0.95 的话摘要请求本身可能就超长了源码里compactConversation遇到prompt_too_long会截掉最老的消息重试但重试次数有限不如提前压。toolResultBudget.perMessageLimit是单条消息里工具结果的 token 预算。超过这个值的工具返回会被持久化到磁盘消息里只留一个带文件路径的摘要预览。skipTools里放Read是因为读文件工具本身有自己的大小限制不需要再被预算机制处理一遍。contextCollapse的两个阈值对应源码里的 staged 和 committed 两个阶段到 90% 开始暂存折叠决策到 95% 强制提交。它和autoCompact是替代关系collapse 生效后 autocompact 会跳过。3.2 config.toml模型与通道config.toml管的是模型映射和通道细节。如果你要在不同任务间切换模型这份配置比环境变量更灵活。[api] base_url https://taotoken.net/api auth_token_env TAOTOKEN_API_KEY timeout_seconds 120 [models] default claude-sonnet-4-20250514 fallback claude-haiku-4-20250514 summarizer claude-haiku-4-20250514 [models.context_window] claude-sonnet-4-20250514 200000 claude-haiku-4-20250514 200000 [streaming] enabled true backfill_observable_input true withheld_recovery true [streaming.tool_executor] mode streaming concurrency_safe_only truesummarizer单独指定成 Haiku是因为autoCompactIfNeeded里的全量压缩要用 AI 生成摘要用便宜快的模型做这件事更划算。backfill_observable_input对应源码里的backfillObservableInput它会在工具参数被外部观察者看到之前把~和相对路径展开成绝对路径但发回 API 的原始消息字节不变这样 prompt cache 不会被打断。withheld_recovery打开后流式过程中遇到的prompt_too_long、媒体过大、输出超限这几类错误会先被扣押等流结束后尝试恢复恢复成功就消化掉失败才抛给上层。这个机制对 SDK 调用者特别重要因为有些客户端一看到 error 消息就终止会话。4. 验证一次流式请求从发起到拿到结果配置写完得验证整条链路真的通了。最直接的办法是发一次带工具调用的流式请求观察三件事流式输出是否逐块到达、工具是否边流边执行、压缩状态是否被正确记录。4.1 用 curl 验证通道先确认通道本身没问题。这一步不涉及 Claude Code纯粹验证 Key 和端点。export TAOTOKEN_API_KEY你的Key curl -N https://taotoken.net/api/v1/messages \ -H x-api-key: ${TAOTOKEN_API_KEY} \ -H anthropic-version: 2023-06-01 \ -H content-type: application/json \ -d { model: claude-sonnet-4-20250514, max_tokens: 1024, stream: true, messages: [ {role: user, content: 用一句话说明什么是流式调用} ] }-N关掉 curl 的缓冲这样你能看到event: content_block_delta一块一块地打出来。如果这里就卡住或者返回 401那问题在 Key 或通道不用往下查 Claude Code 的配置。4.2 在 Claude Code 里跑一次带工具的流式请求通道通了之后启动 Claude Code让它做一个会触发工具调用的任务比如读一个文件再改一行。claude 读取 ./src/index.ts 的前 20 行然后把第一行的注释改掉观察输出。正常情况下你会看到模型先流式输出一段说明文字然后Read工具的调用块出现工具结果返回后模型继续流式输出修改内容Edit工具执行最后给出完成说明。整个过程里工具是边流边执行的不是等模型全部说完才开始。4.3 确认压缩状态验证压缩是否生效可以看会话文件里的替换记录。applyToolResultBudget在替换超大工具结果时会把替换记录写入 transcript用于 resume 时重建相同决策、保持缓存稳定。ls -la ~/.claude/projects/*/transcript*.jsonl grep -c replacement ~/.claude/projects/*/transcript*.jsonl如果replacement计数大于 0说明工具结果预算机制在工作。如果一直是 0要么是工具结果都没超过perMessageLimit要么是配置没被读到——检查settings.json的路径对不对Claude Code 只读项目根目录和用户目录下的配置。5. 常见报错排查对照 queryLoop 的执行顺序配置跑起来之后报错基本集中在几个点上。下面按queryLoop的执行顺序把高频问题和对应位置列出来。5.1 prompt_too_long 反复出现这个报错来自 API 请求阶段但根因在压缩链路。按顺序排查先看applyToolResultBudget有没有生效如果工具结果没被卸载单条消息就可能撑爆预算再看microcompact的idleMinutes设得太长的话旧工具结果一直不清最后看autoCompactThreshold设太高会导致摘要请求本身超长。源码里compactConversation遇到摘要请求也prompt_too_long时会截掉最老的消息重试最多MAX_PTL_RETRIES次。如果你看到重试日志刷屏说明阈值设得太晚了往下调到 0.8 试试。5.2 流式输出中途断掉流断掉有两种可能。一种是streamingFallbackOccured主模型流式传输失败切到了备用模型。这种情况下源码会对已经 yield 的 assistant 消息发 tombstone通知 UI 和 transcript 删除它们然后重建StreamingToolExecutor防止旧tool_use_id泄漏到新请求。你会在日志里看到 fallback 相关记录属于正常恢复。另一种是网络层超时。检查streaming.timeoutMs默认 120 秒对长输出可能不够尤其是模型在生成大段代码时。调到 180 秒或更长。5.3 工具调用结果对不上如果出现tool_use没有对应的tool_result多半是中断处理的问题。源码里中断检查会调getRemainingResults()让 executor 为未完成的工具生成合成结果保证配对完整。如果这个机制没生效检查tools.streamingExecutor是不是开着以及maxConcurrent是不是设得太小导致工具排队超时。5.4 输出 token 耗尽max_output_tokens报错会被 withheld 机制扣押然后走恢复链先把输出上限从 8k 升到 64k 重试同一请求不行再注入提示消息让模型继续最多 3 次。如果你频繁看到这个错误说明单轮任务太重考虑把任务拆小或者把max_tokens在请求层调高。报错触发位置优先检查的配置prompt_too_longAPI 请求前autoCompactThreshold、toolResultBudget流中断流式接收中streaming.timeoutMs、fallbackModeltool_result 缺失中断检查tools.streamingExecutormax_output_tokens流结束后max_tokens、任务粒度6. 把配置落到你的工作流里配置骨架和排查表都有了最后说几个实际用下来的经验。第一压缩链路是有优先级的前面的机制生效了后面的会跳过。所以调参时不要一次改好几个改一个观察一轮否则你分不清是哪个参数起了作用。我一般先调toolResultBudget因为工具结果是 token 增长的大头把它管住后面的压缩压力小很多。第二contextCollapse和autoCompact二选一。collapse 按跨度折叠保留近期原文信息损失比全量摘要小但需要模型支持 cache_edits。如果你的模型不支持就老老实实用 autocompact别硬开 collapse。第三流式工具执行对编码任务体验提升明显但它要求工具实现isConcurrencySafe。你自己写的自定义工具如果没实现这个方法默认会排队执行不会并发。想让自定义工具也并发得在工具定义里显式声明。如果你还没配好通道先去 API Keys 页面创建一个 Key接入文档里有各语言的调用示例。想先验证模型对话是否正常可以直接在模型对话里发一条消息试试。长期跑编码和 Agent 任务的话Coding Plan 的额度模型更适合queryLoop这种一轮接一轮的消耗方式。配置这东西跑通一次比读十遍文档管用先把最小链路验证通过再往上加压缩和并发。
返回列表