跳转至

goose:Recipe 与 Extension 的分阶段装配

为什么值得单独看

goose 把可复用的工作流输入和外部能力接在一条链上,沿着源码就能逐段核对。Recipe(配方)写明标题、指令、启动提示、参数、扩展和设置,Agent 与 ExtensionManager 再把 Extension 接入具体 Session,汇总其中的 Tool。这样确实方便复用任务,但也容易让人误以为「配置里写了扩展,模型就一定能调用」。

这里把扩展接入过程拆成五段来看:系统有没有发现并解析配置,扩展能不能连上,Model 看不看得到工具,调用有没有获准,工具最后是否真的执行。把每一段都记下来,你才能看出问题出在配方、依赖、Protocol(协议)、模型选择还是 Permission(权限)。Recipe 和工具清单都不能充当评测结果,更不能证明任务已经完成。两者都不够。

goose Recipe 与 Extension 装配链

Recipe 如何变成会话中的真实工具

Recipe 数据结构要求填写版本、标题和描述,也允许加入模型 instructions、启动 prompt、extensions、settings 与活动提示。源码注释规定 instructions 和 prompt 至少要有一个,因此配方既可以持续约束模型,也可以直接发起一次会话。

Recipe 把扩展配置和模型输入装进同一个可移植对象,但不会预先写死扩展会产出什么,因为参数替换、嵌套配方、工作目录和用户配置都会改变最终值。这一点要记住。因此,实验记录要保存解析完成的有效 Recipe,不能只留下原始 YAML,而只供界面展示的活动提示也不能证明执行到了哪一步。

会话构建器先把待加载的扩展整理成「名称、配置」对,再调用加载函数。Agent 只有在内部装载成功后,才由 add_extension 保存扩展状态。普通扩展会和会话工作目录一起交给 ExtensionManager,前端扩展则从另一条分支插入。随后,list_tools 向 ExtensionManager 取得带前缀的工具,再与前端工具合并。

这个顺序不能打乱。发现配置只说明候选项存在,装载成功只说明 Client 或前端完成了注册。即使工具已经列出来,也只表示当前会话有这批候选能力,模型最终能看到哪些工具,还要看系统怎样构造并过滤 Context。至于模型会不会调用、调用能否执行,则取决于模型选择、权限判断和执行结果。系统可以保存状态,却保存不了可用性,因为下次启动进程时,外部命令、网络或凭证都可能已经失效。可用性要重算。

从哪里开始读源码

你可以先读 Recipe 数据结构,把每个字段由谁填写、交给谁用列出来。再沿 builder 和验证代码往下读,核对参数要求、模板渲染,以及子配方怎样拼出最终指令。别从示例 Recipe 反推默认配置,因为示例只能证明这种写法允许存在。

扩展怎样装进会话,最好从 Agent 的扩展装载路径 看起。成功装载后怎样按会话保存状态,前端扩展和普通扩展怎样分流,工作目录怎样传递,工具怎样汇总,都集中在这段代码里。若还想看协议细节,再进入 extension_manager.rs 与 MCP client,核对它怎样连接、命名工具并分派调用。

把配置、连接和工具投影连起来

Recipe 解析完成后会得到一个有效对象,会话就从这个对象读取配置。等参数替换、嵌套配方和用户设置全部合并后,系统应当计算哈希,并分别保存原始来源与最终值。模型指令、启动 Prompt 和 ExtensionConfig 都要引用这份哈希。这样一来,即使同名 Recipe 因参数或工作目录不同而表现不同,复核者也不会只看到两个相同的文件名。

ExtensionConfig 能不能进入工具表,要看装载是否成功。普通扩展连上之后才进入 ExtensionManager,前端扩展则由 Host(宿主)登记。只有当前会话装载成功的实例,才能提供带前缀的工具。系统还应保存 Tool Schema、服务版本和连接时间的 Snapshot(快照),再由上下文构造器筛出模型本轮可见的集合。已经保存的扩展配置只是下次 Resume 时的候选,不能跳过重新连接和枚举能力。

Tool Call(工具调用)还要过第三道关。系统先根据模型可见的名称找到具体扩展实例,再检查权限、发送协议请求,最后把带原调用 ID 的 Result 送回来。若服务在枚举工具后重启,或者改了 Schema,本次调用就应该明确失败或重新协商,不能继续拿旧缓存参数请求未知版本。旧参数不能照发。做教学实验时,可以在枚举结束后关掉服务,看系统会不会把「列表还在」误报成执行成功。

沿一次 Recipe 启动走完整链

  1. CLI、桌面或调度入口取得 Recipe,并解析参数、指令、启动提示与扩展列表。
  2. 会话建立 Provider、工作目录和有效提示,整理需要加载的 ExtensionConfig。
  3. 普通扩展由 ExtensionManager 建立客户端,前端扩展由宿主登记;失败保持显式错误。
  4. 成功装载后记录会话扩展状态,再汇总前缀化工具与前端工具。
  5. 工具表进入模型可见能力,模型产生调用后再经过权限、检查与真实执行。
  6. 轨迹、产物与冻结 Recipe 进入独立评分,判断这次任务结果。

最好给每个扩展分配一个稳定名称,并记录配置哈希、连接方式、服务版本、发现的工具、过滤后的工具、调用 ID、权限决定和返回结果。若 MCP 服务会动态变化,还要记下连接时间和能力变化。否则,你很可能拿启动后才出现的新工具,去解释启动前已经发生的运行。

它补充了六条主课程的什么

goose 不会成为第七条一级主线。它让我们看到 Recipe 怎样驱动编排,也让我们看到会话怎样装入扩展。拿这两套机制回看六条一级主线时,你要逐项追问系统只是发现了配置还是扩展真的连上了,工具有没有进入模型请求,权限判断有没有在调用时生效,以及失败是否完整留在 Trace 里。

Recipe 很适合充当可复现实验的输入。不过,实验仍要固定来源 Commit、Provider、模型、扩展服务和工作目录,Evaluator 也必须读取真实 Artifact。同一份 Recipe 能成功启动多个会话,只能说明装配过程可以重复,不能推出每次任务都会得到相同结果。结果仍会变化。

什么时候值得采用

如果你想把重复任务、参数、指令和依赖扩展打包成可审核的输入,或者让团队共享受控工作流,Recipe 很合适。若任务高度依赖交互,扩展又使用短期凭据,或者 Environment 经常变化,Recipe 便只能固定意图,固定不了在线能力。运行清单仍须写清实际使用的 Provider、服务、工具和工作目录。

会话按需装入 Extension,很适合动态 MCP 和前端工具,不过你也要承担供应链、命名冲突、连接恢复和权限治理的成本。小型离线任务可能更适合静态内建工具。这里讲的只有锁定源码中的 Recipe 和装载次序,无法证明任意 Recipe 都安全,也无法证明扩展默认受到 Sandbox 隔离,或恢复后仍然可达。迁移时要保留五个阶段各自的状态,也要留下失败诊断。

最容易误判的地方

第一类风险,是看到目录里有 Recipe 或 Extension 配置,就认定扩展已经装载。第二类风险,是 MCP 握手成功、工具也列出来了,便认定模型已经收到、选择并获准使用某个工具。第三类风险,是扩展状态写入会话后,就认定它会一直可用。到了恢复时,外部二进制、端口、凭证和网络仍可能失效。

Recipe 还可能带入不受信任的指令和文件参数。解析成功并不表示内容安全,扩展返回的文本也可能影响模型后续判断。实验要同时固定输入来源、信任级别、工作目录和权限策略,还要把高风险工具放进外部隔离环境。

怎样亲手核对

你可以先建一个只提供只读工具的测试扩展,再建一个故意连不上的扩展,然后分别用同一份 Recipe 运行。失败的扩展不应进入工具表,成功的扩展则应加上前缀,并绑定当前会话。随后禁止调用这个工具,检查系统能否分清「模型可见」和「允许执行」,同时保存解析后的 Recipe 和每个阶段产生的 Event。

再做一次恢复实验:扩展成功装载后,关掉外部服务,再恢复会话,看看系统会不会把已保存的状态误判为当前可达。Eval 仍要单独检查目标文件或结构化输出,因为 Recipe 已加载,或者工具调用返回 Success,都不足以提前判定任务通过。证据仍看产物。

读完后做四个判断

问题 1

Recipe 中声明 Extension 是否等于启用?

答案: 不等于,还要解析、连接并进入当前会话工具表。

问题 2

工具出现在列表中是否一定被模型调用?

答案: 不一定,还受上下文、模型选择、权限和执行错误影响。

问题 3

扩展状态持久化是否保证恢复成功?

答案: 不保证,外部依赖可能已经变化或不可达。

问题 4

Recipe 能否直接作为发布评测?

答案: 它是可复现输入的一部分,仍需独立任务、评分器和发布门槛。