goose/source-notesupstream ↗
goose · version 1.45.0 · Rust 1.94.1

第 1 章:从 Cargo workspace 到 goose Runtime

1.1 是什么:一个多宿主的 Agent 产品

workspace 当前以 crates/* 为主,产品表面另放在 ui/,文档和发布工程放在 documentation/。核心 crate goose 暴露的模块已经说明了它的职责面:agentsproviderssessionrecipeskillspermissionsecurityacpgatewayschedulerconfig 等都在同一个 runtime 内。

graph TB
  subgraph Runtime[goose crate:运行时核心]
    Agent[Agent Loop]
    Conv[Conversation]
    Session[SessionManager]
    Ext[ExtensionManager]
    Sec[Permission + Security]
    Recipe[Recipe + Skill + Scheduler]
  end
  subgraph Libraries[可复用库]
    Types[goose-provider-types]
    Providers[goose-providers]
    MCP[goose-mcp]
    SDK[goose-sdk / sdk-types]
  end
  subgraph Surfaces[产品表面]
    CLI[goose-cli]
    Desktop[ui/desktop]
    ACP[ACP server / external clients]
  end
  Types --> Providers --> Runtime
  MCP --> Runtime
  SDK --> Surfaces
  Runtime --> CLI
  Runtime --> Desktop
  Runtime --> ACP

1.2 源码怎么做:依赖方向是“向内收敛”

goose-provider-types 放最稳定的跨 provider 类型:消息、模型配置、错误、权限、重试和格式化协议。goose-providers 实现 OpenAI、Anthropic、Google、Ollama、OpenAI-compatible 等协议,以及声明式 provider 定义。goose 在此之上组装 Agent、session、工具、安全和产品能力。goose-cli 只负责命令、终端交互和把 runtime 事件画出来。

这种分层有两个重要效果:

核心的编译开关也体现了“能力按 feature 注入”:portable-default 集合了 TLS、AWS provider、telemetry 和 OTEL;local-inferencecode-modecudavulkanmlx 则把重量级能力延迟到需要它的构建中。

1.3 为什么这样做:把变化最大的部分放在边界

goose 同时面对三个高变化面:模型供应商协议、外部 MCP 生态、不同宿主 UI。把它们都塞进 CLI 会造成不可维护的条件分支;把 Agent Loop 放在 CLI 里又会让桌面端和 SDK 复用困难。当前架构选择了一个更稳的中心:

Agent 只处理“下一步应该发生什么”,Provider/Extension/Host 分别处理“模型怎么说、工具怎么做、结果怎么展示”。

这也是为什么分析 goose 时不能只看 crates/goose-cli/src/session:CLI 是一个 consumer,不是系统的内核。

1.4 一次请求的边界图

输入 Message
  ↓
Agent::reply(事件身份边界)
  ↓
命令解析 / session 读取 / 自动压缩
  ↓
prepare_reply_context(system + history + tools + model config)
  ↓
Provider::stream
  ↓
Message(文本 / thinking / tool request)
  ↓
ToolInspection → Permission → ExtensionManager::dispatch_tool_call
  ↓
ToolResponse / ActionRequired / ServerNotification
  ↓
写回 SQLite,决定是否进入下一 turn

后面的章节会逐段拆开这条链。

源码定位