Goose 架构分析
核心问题与约束
Goose 要解决的问题是:构建一个本地运行的通用 AI Agent,能连接任意 LLM 提供商、通过标准协议挂载工具扩展,并以桌面应用 / CLI / API 三种形态交付。
这带来了几个硬约束:
- 多模型异构性:必须统一 Anthropic、OpenAI、Google、Ollama、Bedrock 等 15+ 提供商的 API 差异
- 扩展生态开放性:第三方工具(Extension)以独立进程运行,需要安全的隔离与标准化的通信协议
- 多形态交付:同一核心逻辑要支撑 CLI(终端交互)、Desktop App(Tauri GUI)、Server(HTTP/WebSocket API)三种前端
- 本地安全边界:Agent 持有用户权限执行 shell 命令、文件操作,必须有完善的权限审批机制
架构假设:这是一个插件式 + 分层的 Agent 系统,核心数据流是
用户指令 → Agent 调度 → Provider(LLM推理) → Tool(Extension执行) → 响应聚合 → 用户,最关键的设计决策在 Agent 循环、Provider 抽象和 Extension Manager 三处。
架构全景
graph TB
subgraph "前端层 (Frontends)"
CLI["goose-cli<br/>终端交互"]
Desktop["UI / Tauri<br/>桌面应用"]
Server["goose-server (goosed)<br/>HTTP/WS API"]
Gateway["Gateway<br/>Telegram 等外部平台"]
end
subgraph "Agent 核心 (goose crate)"
Agent["Agent<br/>调度循环 / 上下文管理"]
PM["PromptManager<br/>系统提示词组装"]
PM2["PermissionManager<br/>权限审批"]
Retry["RetryManager<br/>错误重试"]
Compact["ContextMgmt<br/>对话压缩"]
end
subgraph "Provider 抽象层 (goose-providers + goose/providers)"
ProvTrait["Provider trait<br/>统一 LLM 接口"]
Conv["Conversation<br/>消息格式标准化"]
Canonical["CanonicalModel<br/>模型名映射"]
end
subgraph "工具扩展层 (Extensions)"
EM["ExtensionManager<br/>MCP 客户端管理"]
MCP["MCP Protocol (rmcp)<br/>工具通信协议"]
BuiltIn["内置扩展<br/>memory / autovisualiser<br/>computercontroller"]
External["外部扩展<br/>子进程 MCP Server"]
SubAgent["SubAgent<br/>子 Agent 调度"]
end
subgraph "基础设施层"
Session["SessionManager<br/>会话持久化 (SQLite)"]
Config["Config<br/>全局配置管理"]
Security["SecurityInspector<br/>安全检查 / 对抗检测"]
Telemetry["OpenTelemetry<br/>可观测性"]
end
CLI --> Agent
Desktop --> Agent
Server --> Agent
Gateway --> Agent
Agent --> PM
Agent --> PM2
Agent --> Retry
Agent --> Compact
Agent --> ProvTrait
Agent --> EM
ProvTrait --> Conv
ProvTrait --> Canonical
EM --> MCP
MCP --> BuiltIn
MCP --> External
EM --> SubAgent
Agent --> Session
Agent --> Config
Agent --> Security
Agent --> Telemetry
架构师注释:关键依赖方向是单向的——前端 → Agent → Provider / Extension → 基础设施。没有反向依赖。Agent 是唯一的调度中心,Provider 和 Extension 都是被动响应者。
架构风格:插件式 Agent 循环
Goose 采用了经典的 Agent Loop + 插件系统 架构,这在 AI Agent 领域是主流范式(类似 Claude Code、OpenAI Codex CLI),但 Goose 的独特之处在于:
| 设计维度 | Goose 的选择 | 同类对比 |
|---|---|---|
| 语言 | Rust(性能 + 安全) | Claude Code 用 TypeScript,Codex CLI 用 Rust |
| 工具协议 | MCP(Model Context Protocol) | Claude Code 也用 MCP,OpenAI 用 Function Calling |
| Agent 通信协议 | ACP(Agent Client Protocol) | 自研协议,已提交 Linux Foundation |
| 多模型 | 15+ Provider,Canonical Model 映射 | Claude Code 仅支持 Claude 系列 |
| 扩展形式 | 子进程 MCP Server | Claude Code 同样是子进程 |
| 上下文管理 | 自动 Compaction + 子 Agent 分流 | Claude Code 手动 /compact |
为什么选 Rust:Agent 需要(1)在用户本地持续运行、(2)安全地执行子进程管理、(3)处理流式 LLM 响应的并发。Rust 的零成本异步(tokio)和内存安全使得一个进程可以同时管理数十个 MCP 子进程连接而不泄露资源。
代价:Rust 编译慢、Feature Flag 爆炸(goose 核心 crate 有 20+ feature flags),对社区贡献者门槛高。
核心模块解析
1. goose crate — Agent 核心引擎
职责:实现 Agent 调度循环、上下文管理、权限控制、会话持久化
关键模块:
| 模块 | 职责 |
|---|---|
agents/agent.rs | Agent struct — 主循环,管理 Provider 调用 → 工具执行 → 响应聚合 |
agents/extension_manager.rs | ExtensionManager — 管理所有 MCP Extension 的生命周期 |
agents/prompt_manager.rs | PromptManager — 组装系统提示词(含工具描述、模式指令) |
agents/retry.rs | RetryManager — 工具调用失败时的重试策略 |
agents/tool_execution.rs | 工具调用的实际执行与结果路由 |
agents/subagent_handler.rs | 子 Agent 拆解(将复杂任务分发给子 Agent) |
context_mgmt | 对话历史过长时自动 Compaction(压缩) |
session/ | SessionManager — 基于 SQLite 的会话持久化 |
config/ | 全局配置(Goose Mode、Extension 启用状态、Provider 配置) |
security/ | AdversaryInspector + EgressInspector — 对抗性检测与出站流量审查 |
permission/ | PermissionInspector + PermissionJudge — 工具调用前的权限审批 |
gateway/ | Gateway trait — 外部平台接入(如 Telegram) |
耦合点:
- Agent → Provider:通过
Providertrait 解耦,运行时动态选择 - Agent → Extension:通过
ExtensionManager+ MCP 协议解耦 - Agent → Session:通过
SessionManagertrait 解耦
Agent 循环核心流程(从 agent.rs 源码推导):
loop (max_turns = 1000) {
1. PromptManager 组装 system_prompt + tools + conversation
2. check_if_compaction_needed → 若对话过长则 compact_messages
3. Provider::stream() → 获取 LLM 流式响应
4. 解析响应中的 tool_calls
5. ToolConfirmationRouter → 权限检查
├── frontend tools → 路由到前端执行
├── platform tools → 内部直接执行
└── extension tools → ExtensionManager 分发
6. 执行工具,收集结果
7. RetryManager 判断是否重试
8. 检查 CancellationToken
9. 若无 tool_call 或触发 FinalOutputTool → 结束循环
}
2. goose-providers crate — LLM 提供商抽象
职责:定义 Provider trait,统一所有 LLM 提供商的调用接口
核心 trait:
// 每个 Provider 必须实现的核心方法
pub trait Provider: Send + Sync {
fn get_name(&self) -> &str;
async fn stream(
&self,
model_config: &ModelConfig,
session_id: &str,
system: &str,
messages: &[Message],
tools: &[Tool],
) -> Result<MessageStream, ProviderError>;
}
// Provider 工厂 trait — 从环境变量创建 Provider 实例
pub trait ProviderDef: Send + Sync {
type Provider: Provider + 'static;
fn metadata() -> ProviderMetadata;
fn from_env(model, extensions) -> BoxFuture<Result<Self::Provider>>;
}
设计亮点:
stream()是唯一必须实现的方法,complete()有默认实现(收集流)complete_fast()先尝试 fast model,失败回退到主模型 — 这是一种优雅的降级策略ProviderDeftrait 提供工厂模式,支持从环境变量自动配置
Canonical Model 机制:canonical/ 模块维护了一个模型名映射表,将用户输入的模型别名(如 gpt-4)映射到提供商的特定模型 ID。这使得跨 Provider 切换模型时无需记忆各家的命名规则。
3. goose-mcp crate — MCP 工具扩展
职责:实现 MCP(Model Context Protocol)的 Server 端能力,提供内置工具扩展
内置扩展(通过 BUILTIN_EXTENSIONS 宏注册):
| 扩展名 | 功能 |
|---|---|
autovisualiser | 自动将内容可视化 |
computercontroller | 计算机控制(GUI 自动化) |
memory | 持久化记忆存储 |
tutorial | 教程引导 |
扩展模型:
- 内置扩展:同一进程内通过
tokio::io::DuplexStream双向管道通信 - 外部扩展:通过子进程启动 MCP Server,使用
rmcp库的 stdio 或 HTTP 传输 - 平台扩展(
platform_tools):Agent 自身提供的工具(如manage_extensions、manage_schedule)
4. goose-acp — Agent Client Protocol
职责:实现 ACP 协议,让 Goose 作为一个可被外部程序调用的 Agent 服务
ACP 是 Goose 团队主导的开放协议(已提交 Linux Foundation 的 AAIF),定义了:
- Agent 如何通过 stdio / HTTP 接受请求
- 消息格式(
custom_requests/custom_notifications) - 权限路由(
PermissionDecision)
这使得 Goose 可以被嵌入到任何 IDE、应用或工作流中。
5. goose-server crate (goosed) — HTTP/WebSocket API
职责:将 Agent 能力暴露为 HTTP API 和 WebSocket 流式接口
关键设计:
- 使用
axumWeb 框架 + WebSocket 支持流式响应 - 支持 TLS(rustls 或 native-tls 二选一)
- 自动生成自签名证书(
rcgen) - 跨平台文件锁(
fs2)确保单实例运行 - OpenAPI 文档自动生成(
utoipa)
6. goose-cli crate — 命令行界面
职责:终端交互入口
默认启用的 features 暴露了产品定位:
default = [
"code-mode", # 代码模式(PCTX 集成)
"local-inference", # 本地推理(Whisper 语音转文字、llama.cpp)
"tui", # TUI 交互界面
"aws-providers", # AWS Bedrock/SageMaker
"telemetry", # 遥测
"nostr", # Nostr 协议集成
"otel", # OpenTelemetry
"rustls-tls", # 纯 Rust TLS
"system-keyring", # 系统密钥环
"update", # 自动更新(Sigstore 验签)
]
数据流分析
flowchart LR
subgraph "用户输入"
U1["文本指令"]
U2["语音输入"]
U3["外部平台消息"]
end
subgraph "预处理"
P1["PromptManager<br/>组装系统提示"]
P2["Dictation<br/>Whisper 语音转文字"]
P3["Gateway<br/>平台消息标准化"]
end
subgraph "Agent 循环"
A1["Provider.stream()<br/>LLM 推理"]
A2["ThinkFilter<br/>分离 reasoning/content"]
A3["Tool 解析<br/>提取 tool_calls"]
A4["PermissionCheck<br/>权限审批"]
A5["ToolExecution<br/>工具执行"]
A6["RetryManager<br/>失败重试"]
A7["Compaction<br/>上下文压缩"]
end
subgraph "输出"
O1["流式文本响应"]
O2["工具执行结果"]
O3["子 Agent 报告"]
end
U1 --> P1 --> A1
U2 --> P2 --> P1
U3 --> P3 --> P1
A1 --> A2 --> A3
A3 -->|有工具调用| A4 --> A5 --> A6 --> A1
A3 -->|无工具调用| O1
A5 --> O2
A5 --> O3
A1 -.->|上下文过长| A7
A7 -.-> A1
架构师注释:数据流的核心特征是流式优先——从 LLM Provider 到最终输出,全程使用
BoxStream<Message>流式传递。这意味着用户可以在 LLM 生成过程中实时看到响应,同时工具调用在流结束后解析执行。Compaction 是唯一的异步中断点。
请求链路
以「用户在 CLI 中输入指令,Agent 调用外部工具并返回结果」为例:
sequenceDiagram
participant User as 用户 (CLI)
participant Agent as Agent
participant PM as PromptManager
participant Provider as Provider (LLM)
participant Perm as PermissionInspector
participant EM as ExtensionManager
participant MCP as MCP Server (子进程)
User->>Agent: 输入 "帮我创建一个 React 项目"
Agent->>PM: 组装 prompt (system + history + tools)
PM-->>Agent: 完整 prompt
Agent->>Agent: check_if_compaction_needed()
Agent->>Provider: stream(prompt, tools)
Provider-->>Agent: 流式响应 (text + tool_calls)
Note over Agent: 解析出 shell 工具调用
Agent->>Perm: 检查 shell 权限
Perm-->>Agent: PermissionCheckResult
alt 需要用户确认
Agent->>User: 请求权限确认
User-->>Agent: 批准
end
Agent->>EM: dispatch_tool_call("shell", args)
EM->>MCP: CallToolRequest (stdio)
MCP-->>EM: CallToolResult
EM-->>Agent: ToolCallResult
Agent->>Provider: stream(带工具结果的 prompt)
Provider-->>Agent: 流式响应 (最终文本)
Agent-->>User: 渲染输出
关键架构决策
ADR-1: 使用 MCP 作为工具扩展协议
- 背景:Agent 需要调用外部工具(文件操作、Shell、数据库等),这些工具由不同开发者提供
- 决策:采用 Model Context Protocol(MCP)作为工具通信协议,使用
rmcpRust 库实现 - 原因:MCP 是 Anthropic 主导的开放标准,已有丰富生态(70+ 扩展);它天然支持子进程隔离、Capability 声明、资源发现
- 代价:每个扩展是一个独立进程,有启动开销和内存占用;MCP 的 stdio 传输不支持双向流式通知
- 风险:MCP 协议仍在快速迭代,breaking changes 可能影响扩展兼容性
ADR-2: 自研 ACP(Agent Client Protocol)
- 背景:需要一种标准化的方式让外部程序(IDE、应用)调用 Goose Agent
- 决策:设计 ACP 协议,而非复用 MCP 的 client 模式
- 原因:MCP 是「工具协议」(tool use),ACP 是「Agent 协议」(agent interaction)—— 两者抽象层级不同。ACP 支持 session 管理、权限路由、自定义通知等 Agent 语义
- 代价:自研协议需要独立维护,社区接受度未知
- 风险:如果 OpenAI/Claude 推出类似的 Agent 交互标准,ACP 可能被边缘化
ADR-3: Feature Flag 矩阵控制编译变体
- 背景:Goose 要在不同平台(macOS/Linux/Windows)、不同交付形态(CLI/Server/Desktop)下运行,且支持可选功能(CUDA 加速、本地推理、AWS、遥测等)
- 决策:使用 Cargo Feature Flags 构建编译矩阵
- 原因:Rust 的零成本抽象使 feature gate 成为天然的条件编译手段
- 代价:Feature 组合爆炸——10 个 crate × 20+ features = 难以穷举测试所有组合;
portable-defaultfeature 的存在说明组合管理已是痛点 - 风险:某个 feature 组合可能有编译错误但未被 CI 覆盖
ADR-4: 子 Agent 分流处理复杂任务
- 背景:单个 Agent 的上下文窗口有限,复杂任务需要拆解
- 决策:实现
SubAgentHandler,允许主 Agent 将子任务分发给独立的子 Agent 执行 - 原因:子 Agent 有独立的上下文窗口和工具集,可以并行处理不相关的子任务
- 代价:增加了系统复杂度(子 Agent 的生命周期管理、结果聚合)
- 风险:子 Agent 的错误传播和超时管理需要精心设计
架构质量评估
| 质量属性 | 设计手段 | 评估 | 潜在风险 |
|---|---|---|---|
| 可扩展性 | MCP 协议 + ExtensionManager + 内置/外部扩展双轨制 | ★★★★★ | 扩展生态极度开放,新增扩展只需实现 MCP Server |
| 可维护性 | 10 个 crate 分层解耦,Provider/Extension trait 隔离 | ★★★★☆ | 核心 goose crate 仍然过重(40+ 模块) |
| 性能 | Rust + tokio 异步 + 流式处理 + tree-sitter 代码解析 | ★★★★☆ | 每个 MCP 扩展是独立进程,N 个扩展 = N+1 进程 |
| 安全性 | PermissionInspector + AdversaryInspector + EgressInspector + Sigstore 签名验证 | ★★★★☆ | Agent 可执行任意 shell 命令,权限粒度是「批准/拒绝」而非细粒度 ACL |
| 可移植性 | rustls 纯 Rust TLS + 跨平台编译 + Feature Flag | ★★★★☆ | macOS 独占扩展(peekaboo)、平台特定密钥环实现 |
| 可观测性 | OpenTelemetry 全链路追踪 + tracing 结构化日志 | ★★★☆☆ | 遥测和 OTel 都是可选 feature,默认未启用 |
| 开发体验 | Feature Flag 矩阵 + workspace 依赖统一管理 | ★★★☆☆ | 编译时间长、Feature 组合测试覆盖不全 |
架构风险与改进建议
风险 1: 核心 Crate 膨胀
goose crate 承担了过多职责(Agent 循环、上下文管理、权限、安全、会话、配置……),源码中有 40+ 模块。随着功能增长,这会成为维护瓶颈。
建议:将 security、permission、context_mgmt 抽取为独立 crate,goose 只保留 Agent 循环和协调逻辑。
风险 2: Feature Flag 爆炸
当前 Feature Flag 之间有隐式依赖(如 cuda 依赖 local-inference,portable-default 是多个 feature 的组合),但没有编译时强制校验。
建议:引入 feature 依赖声明(cuda = ["local-inference"] 已有,但缺少互斥检查)和 CI 矩阵测试。
风险 3: MCP 子进程管理
每个外部扩展启动一个子进程,长时间运行可能积累僵尸进程或内存泄漏。
建议:实现扩展进程健康检查 + 自动重启 + 资源限制(cgroup / job object)。
风险 4: Session 持久化的 SQLite 依赖
会话持久化依赖 SQLite(通过 sqlx),在大量历史会话场景下可能成为性能瓶颈。
建议:实现会话归档/过期清理机制,或提供可选的远程存储后端。
可复用架构经验
✅ 值得借鉴的模式
-
Canonical Model 映射(适用条件:多 LLM 提供商接入场景)
- 用统一的模型别名映射到各提供商的具体模型 ID,消除 API 差异
- 让用户用
gpt-4一个名字就能在 OpenAI / Azure / Bedrock 间切换
-
Agent 循环的 Tool Confirmation Router(适用条件:需要权限控制的 Agent 系统)
- 工具调用先经过权限检查,区分 frontend / platform / extension 三类工具分别路由
- 实现了「工具执行前必须获得授权」的安全模型
-
内置扩展的 DuplexStream 模式(适用条件:同一进程内的插件系统)
- 通过
tokio::io::DuplexStream在进程内模拟 MCP 通信,内置扩展享受与外部扩展相同的接口 - 避免了内置/外部扩展的双轨代码路径
- 通过
-
自动 Compaction(适用条件:长对话 Agent 场景)
- 当对话历史超过阈值时,自动调用 LLM 进行摘要压缩
- 相比 Claude Code 的手动
/compact,对用户更透明
⚠️ 值得警惕的反模式
-
Feature Flag 矩阵(出现场景:需要多种编译变体的 Rust 项目)
goosecrate 有portable-default、code-mode、local-inference、cuda、vulkan、aws-providers、rustls-tls、native-tls、system-keyring、nostr、telemetry、otel等 12+ 个 feature- 当
rustls-tls和native-tls同时启用时直接compile_error!,但其他冲突组合呢?
-
Agent 循环中的硬编码常量(出现场景:快速迭代的 Agent 系统)
DEFAULT_MAX_TURNS = 1000、DEFAULT_COMPACTION_THRESHOLD等核心参数以常量形式硬编码- 虽然可通过环境变量覆盖部分,但缺乏统一的配置层
关联概念
- MCP — Model Context Protocol,Goose 的工具扩展通信协议
- AI Agent — Agent 系统的通用架构模式
- ACP — Agent Client Protocol,Goose 主导的 Agent 交互协议
- ReAct Pattern — Agent 循环的推理-行动模式
- Tool Use — LLM 调用外部工具的设计模式
- Compaction — 对话历史压缩策略
- Feature Flag — Rust 条件编译的 Feature Gate 机制
- Tauri — Goose Desktop App 使用的跨平台 GUI 框架
- Goose vs oh-my-pi — 与 oh-my-pi 的对比分析