gbrain claude-cli 配方:用本地 `claude` CLI 子进程承载聊天与工具循环的订阅直连方案

发布时间:2026/9/19 23:54:46
gbrain claude-cli 配方:用本地 `claude` CLI 子进程承载聊天与工具循环的订阅直连方案 gbrain claude-cli 配方用本地claudeCLI 子进程承载聊天与工具循环的订阅直连方案【免费下载链接】gbrainGarrys Opinionated OpenClaw/Hermes Agent Brain项目地址: https://gitcode.com/gh_mirrors/gb/gbrain本文讲解 gbrain 的claude-cli配方recipe——一条把gateway.chat()与gateway.toolLoop()路由到本机claudeCLI 二进制以claude --print子进程方式运行而非 Anthropic SDK 的完整实现路径。核心应用场景是Claude Max / Claude Code 订阅用户希望子代理Minion/subagent调度复用现有订阅的 OAuth 登录态而不是按 token 支付 API 费用源码注释中标注的 #334 动机。读完本文你将掌握claude-cli:模型前缀的配置方法、子进程调用时的参数与隔离细节、工具调用协议、环境变量清洗规则、预算与用量上报口径以及gbrain models doctor探针与排障手段。一、claude-cli是什么与anthropic配方并行的第二条 Claude 通路gbrain 的 AI 网关gateway通过Recipe抽象描述一个模型家族如何被实例化。claude-cli配方与原生anthropic配方共享相同的触达面touchpoint形状区别只在实现层anthropic:claude-sonnet-5→ 解析到native-anthropic实现Anthropic SDK ANTHROPIC_API_KEY按 token 计费claude-cli:claude-sonnet-5→ 解析到ClaudeCliLanguageModel子进程方式认证完全交给 CLI 自身即 Claude Code / Claude Max 订阅的 OAuth 会话。也就是说走哪条通路是**每次调用per-call**的选择由模型字符串的前缀决定而不是全局开关。两个配方的实现在仓库中分别位于 claude-cli 配方声明 与 ClaudeCliLanguageModel 适配器二者共同构成本文所介绍配方的全部代码面。Chat 查询扩展无嵌入embedding。查询扩展query expansion走与聊天相同的claude --print子进程跑在无 schema 的纯文本路径上CLI 无法携带 JSON schema因此它回答的 fenced JSON 会被解析回来。这意味着只要 utility 层tier指向claude-cli:多查询召回multi-query recall这一臂就仍然可用。而gateway.embed()对claude-cli模型会立即抛错claude-cli has no embedding model. Use openai or google for embeddings.见 gateway.ts 实现。原因很实在Claude 无论走什么传输层都没有第一方 embedding 模型。若需要 Anthropic 系聊天 embedding 的完整能力应像anthropic配方的文档所建议的那样把claude-cli与openai、google或voyage搭配使用。值得注意的一个细节claude-cli配方声明了expansion触达面这一点在源码注释里被专门强调为一次修复。在修复之前expansion未声明导致gateway.ts中的isAvailable(expansion)对每个 claude-cli 模型都返回 falseexpandQuery()在发起任何模型调用前就返回[query]——查询扩展会静默消失既无报错也无日志而gbrain models doctor因为探针用显式model:覆盖调用chat()、从不查阅配方仍然显示该触达面为绿色。这正是配方声明必须与 gateway 消费逻辑对齐的典型教训配方源码注释 完整记录了这一过程。二、Setup两条命令完成接入安装 Claude CodeclaudeCLI并登录安装后运行一次claude完成登录Claude Code 的 onboarding 会完成这一步。如果二进制不在PATH上用环境变量显式指给它export GBRAIN_CLAUDE_CLI_BIN/path/to/claude从源码看二进制路径的解析逻辑是process.env.GBRAIN_CLAUDE_CLI_BIN ?? claudeclaude-cli-language-model.ts也就是说该变量可选缺省即从PATH找claude。把模型层或任意每次调用的模型字符串指向claude-cli:gbrain config set models.tier.subagent claude-cli:claude-sonnet-5配方声明的所有模型都可以同样方式使用claude-cli:claude-opus-5、claude-cli:claude-haiku-4-5-20251001等。短别名claude-cli:sonnet、claude-cli:haiku、claude-cli:opus与anthropic配方的别名解析方式一致。以 配方源码 为准别名映射为别名解析到的规范模型 IDsonnetclaude-sonnet-4-6haikuclaude-haiku-4-5-20251001opusclaude-opus-4-7claude-haiku-4-5claude-haiku-4-5-20251001claude-sonnet-4-6-20250929claude-sonnet-4-6反向别名把遗留 ID 改写回规范 ID前两个短别名在 test/claude-cli-recipe.test.ts 中有测试锁定。认证与配置约定配方声明auth_env: { required: [] }——不要求任何 API key 形态的配置。配方与适配器代码都不会读取或向子进程传递任何 key 形态的配置值claude二进制自己的认证行为见下节由它自己的登录状态决定gbrain 的配置层完全不参与。同时配方没有provider_base_urls条目——它没有 base URL只有一个子进程二进制路径GBRAIN_CLAUDE_CLI_BIN。三、一次调用实际发生了什么每次doGenerate调用都会以子进程方式拉起claude --print --output-format json --model id --disable-slash-commands --tools --strict-mcp-config并满足以下约束见 runClaude 的 argv 组装工作目录子进程的cwd被设置到 OS 临时目录下按进程 PID 命名的独立目录join(tmpdir(), gbrain-claude-cli-cwd- process.pid)由mkdirSync(..., { recursive: true })创建代码不触碰也不检查其内容。这样做的目的是上下文隔离claude的 CLAUDE.md 自动发现机制在该目录下找不到任何本地文件避免把调用方目录里的项目指令意外带入。同时 gbrain 把渲染好的 prompt 通过 stdin 管道喂给子进程。--tools 禁用所有内置工具Bash/Read/WebSearch 等——子进程必须表现得像一个纯 LLM而不是完整 Claude Code agent。--strict-mcp-config跳过加载用户的 MCP 服务器。如果不带这个 flag每次调用都会启动用户配置的所有 MCP 服务器——包括 gbrain 自己的 MCP那会导致递归并争抢 PGLite 单写锁。--system-prompt当存在系统消息时用渲染出的系统文本替换默认系统提示renderPrompt 会把 ai-sdk 消息数组渲染成单一文本 prompt系统消息提取到--system-prompt工具调用与工具结果渲染为[tool_use ...]/[tool_result ...]占位符。为什么没有--bare--bare会完全跳过加载用户级~/.claude/CLAUDE.md但它同时会强制ANTHROPIC_API_KEY认证见配方源码注释这恰恰违背本配方的初衷。不传它的副作用是用户级~/.claude/CLAUDE.md每次调用都会加载并产生缓存 token——这是订阅路径上一个成本可忽略的取舍用户级指令约 42k 缓存 token源码 #4119 注释有记录。环境变量清洗subscription-only 契约子进程 env 是 gbrain 自身进程 env 的一份拷贝但 spawn 前会清洗掉云端路由相关变量env 清洗实现三个直连 API 键ANTHROPIC_API_KEY、ANTHROPIC_AUTH_TOKEN、ANTHROPIC_BASE_URL所有CLAUDE_CODE_USE_*后端切换 flag——注意这是前缀整体抹除prefix wipe不是白名单Bedrock、Vertex AI、Mantle、Microsoft Foundry、Claude Platform on AWS 各自由其中一个 flag 门控设置时优先于订阅 OAuth 并把计费路由到云账户清除开关就够了因为 provider 专用凭据AWS_*、ANTHROPIC_VERTEX_*等在开关未置位时是惰性的。gbrain 环境里其余变量原样继承。这条规则的动机在源码注释里写得很直白如果不清洗gbrain 环境里恰好存在的ANTHROPIC_API_KEY本配方正是要替代的配置会静默把计费翻转成按 token 的 API 计费。订阅专属是配方的契约子代理永远用 CLI 自己的登录状态认证。如果你有意把负载路由到云后端应该改用anthropic配方并配置云凭据。而安装在机器上的claude二进制在自己配置文件而非 env里携带的任何认证/计费配置仍然归它自己的登录状态管gbrain 不检查也不转发。认证解析到此为止除上述 env 清洗外认证完全由已安装的claude二进制决定——本机claude用什么登录这里就用什么认证CLI 如何存储和解析这些由claude自身文档说明。测试 test/claude-cli-recipe.test.ts 用 shell 桩子进程逐项断言了这 8 个变量全部以UNSET到达子进程。进程内一次性清扫由于工作目录按 PID 命名崩溃/被杀死的 gbrain 进程会遗留永久的 scratch 目录。适配器在 provider 初始化时调用一次sweepDeadClaudeCliScratchDirs()claude-cli-scratch.ts枚举 tmpdir 下gbrain-claude-cli-cwd-*与gbrain-claude-cli-config-*前缀目录剔除存活 PID 后删除每一步都是 best-effort清扫失败绝不影响聊天调用。该模块刻意做得很轻以便 transcript 发现逻辑src/core/transcripts/discover.ts无需导入整个 provider 就能识别这些会话的指纹——因为 Claude Code 会为每个 cwd 在~/.claude/projects/slugified-cwd/下记录会话 transcript若把 gbrain 自己的内部 LLM 调用再导入大脑就构成了自我投喂self-ingestion反馈环。四、工具调用协议跑在文本上的use_tools机制适配器不使用claude自有的 agentic 工具调用——它在系统提示中注入一段 fenced 指令块教模型用use_tools[{id,name,input}, ...]/use_tools的 JSON 发射格式buildToolUseInstructions见 claude-cli-language-model.ts然后把纯文本响应中的该块解析回 ai-sdk 的 tool-call partsextractToolCalls。注入的协议模板要求调用工具时本轮恰好发射一个该形式的块块外不带其他文本多个工具调用放进数组工具结果在下一轮以[tool_result text]条目返回准备最终作答时只输出散文、不输出use_tools块。当本轮没有注册任何工具时指令块为空字符串模型拿到的是不含协议噪音的普通补全 prompt。工具调用 ID 一律本地铸造绝不信模型。每个 ID 形如toolu_claude_cli_randomUUIDv7()无条件铸造。原因源码 #4155 注释每一次--print都是一个全新子进程对之前的 ID 毫无记忆若信任模型自选的 ID模型会在每轮重复诸如toolu_01、toolu_02的短 ID撞上每个任务工具 ID 唯一性约束——这在历史上真实地让 dream 类任务死信dead-letter过。同时prompt 模板刻意不再要求模型发明 ID一个遗留的id字段会被容忍但忽略什么都回传不到子进程替换对工具结果配对是透明的。测试对跨轮重复模型 ID 仍铸造不同 ID单块内重复 ID 仍各自铸造模板不再含 unique tool call id 字样均有源码级 pintest/claude-cli-recipe.test.ts。解析器的健壮性是这套协议的重头戏extractToolCalls的枚举式锚定策略不再以第一个开标签或最后一个开标签锚定——散文里提前提到use_tools、参数 JSON 字符串里字面包含use_tools//use_tools都会让旧锚定失效。现在的做法是枚举所有 (open, close) 配对最外层优先最早的开标签配每个后续闭标签接受第一个能解析为 JSON 数组的切片。标签出现在参数字符串内部时要么落在已接受的切片里要么产生被拒绝的切片永远不会成为锚点。容忍 fenced 写法use_tools内包json ... 。扁平参数兜底模型有时把参数平铺在条目上{name: brain_search, query: ...}即去掉input包装的 Anthropic tool_use 形状。只读e.input会把每次调用变成{}工具因缺必填参数而失败。现在input缺席时条目的非保留键就是输入input在场时原样胜出杂散兄弟键忽略。保留键的判定只豁免name、input、type: tool_use和toolu_前缀的id——任何其它type/id值是真正的工具参数例如list_pages的type过滤器在扁平模式下必须保留。畸形块不得静默块存在但 JSON.parse 失败时工具调用作废、整段按散文处理同时向 stderr 写一行[claude-cli] use_tools block failed to parse — tool call discarded: 原因诊断缺失闭标签、只有孤儿闭标签、或载荷不是数组则静默恢复为散文。这套协议跑在文本上的方案正是supports_subagent_loop: true能在上述--print 无内置工具的子进程形态下成立的原因配方声明。五、约束速查表维度行为Embedding不支持——gateway.embed()对claude-cli模型抛错gateway.ts。嵌入请搭配其他 provider。Streaming未实现。doStream()抛错claude-cli-language-model.ts。gateway.toolLoop()主要调用方本身非流式因此对子代理调度不构成实际限制但任何期望流式聊天面的调用方都不能使用claude-cli。工具调用通过系统提示注入协议走 JSON 发射而非 CLI 原生工具调用机制。单轮并行工具调用可正确往返。多模态子进程路径上不支持。文件/图片消息部分被渲染成[file mediaType]文本桩不作为真实内容发送。Prompt 缓存配方声明supports_prompt_cache: trueClaude Code 每次--print运行都会自行缓存 prompt 前缀按 Anthropic Claude Code 文档-p运行落在主会话 TTL 桶——订阅一小时、API key 五分钟。gbrain 无法在此路径放置cache_control断点——适配器把消息渲染为 stdin 文本并忽略providerOptions——因此缓存是自动的而非网关驱动doctor的subagent_capability不再把 claude-cli 子代理层评分为degraded:no_caching。cache_read_input_tokens以usage.cachedInputTokens呈现见下条。用量/token 统计上报的usage.input_tokens/usage.output_tokens直接读取 CLI--output-format json信封result.usage?.input_tokens/output_tokensgbrain 不为此路径独立数 token。信封的cache_read_input_tokens映射为usage.cachedInputTokens因此缓存读取计入 gbrain 用量账cache_creation_input_tokens不呈现AI SDK 的 usage 形状没有对应字段。测试对0 与缺失两种情况区分对待有专门 pintest/claude-cli-recipe.test.ts。成本数字配方声明cost_per_1m_input_usd: 3.0/cost_per_1m_output_usd: 15.0——与anthropic配方声明的 Sonnet 档数字相同price_last_verified: 2026-06-17——纯粹为了让 gbrain 的预算账本能给每次调用挂一个数。配方和适配器代码都不检查你实际被收取多少请把这些数字当作账本的名义每次调用数字而非已核实的收费。--max-usd/--max-cost预算门也不读它们它按该模型的 Anthropic 名义费率给claude-cli:model定价短别名claude-cli:haiku与其解析到的带日期 ID 定价一致pricing.overrides可以强制把任意claude-cli:*字符串设为 $0 或真实费率。用户级 CLAUDE.md每次调用仍加载见上文——只有工作目录变化该目录是什么、不是什么见第三节。六、Doctor 探针超时claude-cli 专属的 30 秒gbrain models doctor的聊天可达性探针probeModel位于 src/commands/models.ts按模型解析超时配方触达面声明了default_timeout_ms就用它否则用平坦的 5000ms 默认值这个默认对纯 HTTP 往返是对的。claude-cli配方声明default_timeout_ms: 30_000chat 与 expansion 两个触达面都声明了见 配方源码 与 L116因为每次调用都要拉起claude -p子进程CLI 冷启动 用户级 CLAUDE.md 加载即使 CLI 与订阅完全健康也常常要 56 秒平坦 5 秒中止会让探针每次都假失败报status: unknownclaude-cli adapter aborted而chat()在正常调用点却成功。30 秒既给子进程留足启动空间又不会把真正死掉或未认证的 CLI 掩藏太久。仍然跑超 30 秒的探针会杀掉子进程child.kill(SIGTERM)并报status: unknown——适配器的 abort 消息不匹配classifyError的网络错误模式落入 catch-all 分支探针超时解析逻辑见 src/commands/models.ts。持续的unknown值得直接排查用gbrain models doctor --json跑同一模型或手动调用claude而不是默认归因于冷启动。七、Troubleshooting症状对照表症状来源尝试claude-cli spawn failed: .../ stdin 写失败spawn()的error事件或stdin.write失败——通常意味着PATH上找不到claude二进制安装 Claude Code或把GBRAIN_CLAUDE_CLI_BIN设为该二进制的路径claude-cli API error status: messageCLI 在结果信封里报告 API 失败is_error: true带api_error_status例如 429 限额/限速——无论子进程以非零还是零码退出都会呈现。错误是携带apiErrorStatusexitCode的ClaudeCliProcessError便于程序化处理读消息本身——那是 CLI 自己的人类可读解释例如带修复 URL 的限额提示claude-cli reported error: message同一信封报告失败is_error: true但没有api_error_status字段同上——消息是 CLI 自己的解释claude-cli exited code--- raw ---块claude子进程非零退出且 stdout 无可解析的结果信封CLI 的 stderr/stdout 跟在--- raw ---标记之后放在标记后是为了让错误分类器永远不会对模型派生的文本做短语匹配用同一模型交互式运行claude直接看底层 CLI 错误例如未登录、模型不可用claude-cli output not JSON: ...JSON.parse(stdout)抛错stdout 根本不是合法 JSON确认所装claudeCLI 版本仍支持--print --output-format json本适配器的 JSON 处理是针对 CLI 2.1.145 验证的claude-cli JSON event array had no result eventstdout 解析为 JSON 数组~/.claude/settings.json中verbose: true的事件流形状但事件中没有type: result检查~/.claude/settings.json是否有verbose: true适配器容忍数组形状但仍需其中存在result事件gbrain models doctor对claude-cli:模型报chat为status: unknown探针给子进程 30 秒见上节持续unknown意味着调用真失败或连 30 秒窗口都跑超——classifyError对适配器的 abort 消息落入unknown直接排查用gbrain models doctor --json跑同一模型或手动调用claude例如未登录、模型不可用调用走 Anthropic API 或云后端计费而非本地会话适配器从子进程 env 删除ANTHROPIC_API_KEY/ANTHROPIC_AUTH_TOKEN/ANTHROPIC_BASE_URL及每个CLAUDE_CODE_USE_*后端切换 flag——这覆盖了 gbrain 自身 env 泄漏进调用的情况。它不检查已安装claudeCLI 在自己配置文件里携带的计费开关若计费看起来不对检查本机claudeCLI 自身的认证/计费配置而不只是 gbrain 的 env。如需有意走云路由改用anthropic配方两条排障要点补充其一非零退出时若 stdout 上是报错信封is_error: true即使退出码非零也会把它提升为带状态的类型化错误但成功信封 非零退出是进程故障例如 API 调用成功后 CLI 崩溃必须回落到带 stderr 的 raw blob——这两条路径的区分在 runClaude 的 close 处理 与测试 test/claude-cli-recipe.test.ts 中均有覆盖。其二abort 语义有专门测试AbortSignal触发时对子进程SIGTERM并拒绝为claude-cli adapter abortedtest/claude-cli-recipe.test.ts。八、源码结构与测试佐证怎么继续深入想亲自验证本配方行为可以从三处入手配方声明src/core/ai/recipes/claude-cli.ts完整的模型清单chat 触达面含claude-fable-5、claude-fable-5-1、claude-opus-5、claude-opus-4-8、claude-opus-4-7、claude-sonnet-5、claude-sonnet-4-6、claude-haiku-4-5-20251001、supports_tools/supports_subagent_loop/supports_prompt_cache能力位、成本数字与setup_hint。适配器实现src/core/ai/providers/claude-cli-language-model.tsdoGenerate→renderPrompt→runClaude→extractToolCalls的完整调用链以及ClaudeCliProcessError类型化错误。测试锁定test/claude-cli-recipe.test.ts用一个 POSIX shell 桩子进程GBRAIN_CLAUDE_CLI_BIN指向桩发射脚本化的--output-format json信封无需安装 claude-cli 或消耗 API 额度即可覆盖文本往返、单/多并行工具提取、abort 语义、上下文隔离 flag、env 清洗、错误信封与 raw blob 回退等全部行为——是理解适配器契约的最佳入口。另外若你的 SkillOpt 实验需要密封hermetic测量不让用户级 CLAUDE.md / settings.json / hooks 泄漏进 rollout可以设置GBRAIN_CLAUDE_CLI_HERMETIC_CONFIG#4119取值为1/true时子进程的CLAUDE_CONFIG_DIR指向一个按进程隔离的空 tmpdir任何其它非空值被原样用作配置目录路径。注意该开关默认关闭且是opt-in配置目录同时存放 CLI 的会话凭据空目录形态会让 CLI 在从配置目录读取会话的地方登出因此需要保留认证的密封场景应使用预先播种了凭据的显式路径形态详见 resolveHermeticConfigDir 与docs/guides/skillopt.md中 Hermetic claude-cli rollouts 一节。【免费下载链接】gbrainGarrys Opinionated OpenClaw/Hermes Agent Brain项目地址: https://gitcode.com/gh_mirrors/gb/gbrain创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考