Claude Code 的 /btw:不打断主任务的“顺口一问”,源码里是怎么做隔离的?
最近 Claude Code 传出新增了
/btw功能,由 Anthropic 工程师 Eric Schluntz 作为内部 side project 做出来的,3 月 11 日前后开始在社区广泛传播。官方文档也已经上线:Interactive mode - Claude Code Docs。
简单说,/btw 就是用来问”顺手的小问题”的,不会打乱正在进行的对话。我去扒了一下源码,看看它具体是怎么实现的。
先说它解决了什么问题
用 Claude Code 的时候,Claude 正在帮你重构一段代码,这时你脑子里突然冒出一个问题:“这个 API 的文档在哪?“或者”Python 里 sorted 和 list.sort() 有什么区别?”
如果你直接把问题打进去,Claude 会把它当成主对话的一部分——它可能打断当前任务去回答你,而这个问答过程会写进对话历史,占用 context,影响后续的代码工作。
/btw 的设计目的就是处理这类”随口一问”:问完就算,答案给你看,但什么都不留下。
怎么验证的
把包装好的 Claude Code 装上,去 node_modules 里翻:
npm install -g @anthropic-ai/claude-code
grep -n "btw\|marble_whisper" ~/.nvm/versions/node/v22.22.0/lib/node_modules/@anthropic-ai/claude-code/cli.js
版本是 v2.1.74,这个命令在这个版本里存在,但默认是关闭的。
Feature Flag
第一个关键点:命令受 Feature Flag 控制,默认不开。
function k96() {
return P8("tengu_marble_whisper2", false)
}
P8 是读 Feature Flag 的函数,"tengu_marble_whisper2" 是 flag 名,默认值 false。也就是说,除非 Anthropic 在服务端把这个 flag 开给你,这个命令在 UI 上是隐藏的——帮助菜单里不显示,快捷键面板里也不出现。
对应的 UI 渲染逻辑:
V6 = k96() && createElement(Text, { dimColor: K }, "/btw for side question")
只有 flag 开了,这行提示才会出现在界面上。
Spinner 里也有一段:如果你用了超过 30 秒没用过 /btw,它会显示一条 Tip:
let n = o && s > 30000 && k96() && !D1().btwUseCount
// ...
"Use /btw to ask a quick side question without interrupting Claude's current work"
核心实现:怎么做到不影响主对话的
这是最关键的部分。找到 IW4 函数(就是处理 /btw 问题的函数):
async function IW4({ question, cacheSafeParams }) {
const prompt = `<system-reminder>This is a side question from the user. You must answer this question directly in a single response.\n${question}`
const result = await GR({
promptMessages: [UserMessage({ content: prompt })],
cacheSafeParams,
canUseTool: async () => ({
behavior: "deny",
message: "Side questions cannot use tools",
decisionReason: { type: "other", reason: "side_question" }
}),
querySource: "side_question",
forkLabel: "side_question",
maxTurns: 1,
skipCacheWrite: true // ← 关键
})
return { response: parseResponse(result.messages), usage: result.totalUsage }
}
两个关键参数:
maxTurns: 1这次对话只允许一轮。Claude 回答你,结束,不来回。避免 /btw 演变成一个子任务.
skipCacheWrite: true这是核心。LLM API 有 Prompt Cache 机制——系统会缓存你的对话 context,下次调用时命中缓存可以节省 token 消耗、降低延迟。skipCacheWrite 设为 true 意味着:这次调用不往 cache 里写任何东西。
实际效果:
/btw的问题和 Claude 的回答不会出现在主对话的 cache 里- 下一次主对话的 API 调用,cache 状态和没发生过
/btw一样 - 主对话的 context 窗口没有被污染
还有一个细节:
canUseTool: async () => ({
behavior: "deny",
message: "Side questions cannot use tools",
})
/btw 的回答不能调用任何工具。你问的问题只能靠模型自己的知识回答,不能触发文件读写、命令执行等操作,进一步保证了隔离性。
System Prompt 的处理
注意 prompt 里有一段 <system-reminder> 标签:
<system-reminder>This is a side question from the user. You must answer this question directly in a single response.
这不是普通的 system prompt,而是注入到 user message 里的”提醒”。作用是告诉模型:这是个独立的小问题,不要试图调用工具,不要问追问,直接给答案。
对比一下 /btw 和普通消息的区别:
| 普通消息 | /btw | |
|---|---|---|
| 写入对话历史 | 是 | 否 |
| 写入 Prompt Cache | 是 | 否(skipCacheWrite) |
| 允许调用工具 | 是 | 否 |
| 最大轮次 | 无限制 | 1 |
| 影响后续 context | 是 | 否 |
正则匹配
命令的触发是通过正则匹配输入:
var F6Y = /^\/btw\b/gi
匹配以 /btw 开头、后面跟词边界的输入(大小写不敏感)。所以 /btw 这是什么 会触发,/btws 不会。
高亮处理函数:
function CW4(A) {
if (!k96()) return []
let matches = []
for (let Y of A.matchAll(F6Y))
if (Y.index !== undefined)
matches.push({ word: Y[0], start: Y.index, end: Y.index + Y[0].length })
return matches
}
flag 关闭时直接返回空数组,连高亮都不做。
总结
/btw 的实现思路本质上是一个受控的旁路请求:
- 触发条件受 Feature Flag 控制,默认隐藏
- 请求走独立的 API 调用路径(
forkLabel: "side_question") skipCacheWrite: true确保不污染主会话的 Prompt CachemaxTurns: 1+ 禁止工具调用,保证它是轻量的一次性问答- 问答结果展示给用户,但不写入主对话历史
这个思路对设计 AI 编程助手其实有参考价值——当用户需要”插嘴问一句”时,不应该让这个问题破坏当前工作流的 context。隔离不是难事,难的是把隔离做得足够彻底:cache 不写、history 不记、工具不调。
目前这个命令还在 flag 后面,大多数用户看不到。不过既然代码里已经有了,应该是快放出来了。