魅力程序猿

  • 首页
  • Java
  • Android
  • APP
    • 扑克计分器
    • Video Wallpaper
  • 联系我
  • 关于我
  • 资助
道子
向阳而生
  1. 首页
  2. AI技术
  3. 正文

深入 Open Agent SDK(番外篇):实战验证——把 SDK 塞进一个 macOS 原生 Agent 应用

2026年5月1日 15点热度 0人点赞 0条评论

📰 来源: 博客园


本文是「深入 Open Agent SDK (Swift)」系列番外篇。

前七篇文章从各个子系统分析了 Open Agent SDK 的设计。但 SDK 写得好不好,最终得放到真实项目里验证。这篇文章记录我把 SDK 集成到一个开源 macOS 原生 Agent 应用——Motive——的完整过程:从理解原有架构到实现替换,以及一路上踩过的坑。

Motive 是一个 macOS 原生的 AI Agent 桌面应用,用 SwiftUI 写的。它的核心交互是:用户输入 prompt → Agent 在后台跑 Agent Loop(调工具、读文件、执行命令)→ 流式输出结果到 UI。

在集成 SDK 之前,Motive 的 Agent 后端长这样:

Motive App (SwiftUI)
  └── OpenCodeBridge (actor)
        ├── OpenCodeServer  — 启动外部 opencode 二进制进程 (opencode serve)
        ├── SSEClient       — 通过 Server-Sent Events 接收流式事件
        └── OpenCodeAPIClient — 通过 REST API 发送 prompt、回复权限请求

每次用户发 prompt,Motive 要:

  • 启动一个外部 opencode serve 进程(如果没在跑的话)
  • 通过 REST API POST /sessions 创建会话
  • 通过 REST API POST /sessions/{id}/prompt 发送 prompt
  • 通过 SSE 连接接收事件流(文本片段、工具调用、完成信号等)
  • 这套架构能用,但有几个问题:

  • 依赖外部二进制:用户要自己安装 opencode CLI,Motive 还要处理二进制签名、路径查找
  • 进程间通信开销:REST API + SSE 意味着事件要经过 HTTP 序列化/反序列化
  • 启动延迟:外部进程冷启动需要时间
  • 调试困难:跨进程的问题很难定位
  • SDK 的出现正好给了另一种可能——把 Agent Loop 直接跑在应用进程内。

    目标:SDKBridge

    我想做的替换:不启动外部进程,不经过 HTTP,直接在 Motive 进程内用 SDK 的 Agent.stream() 跑 Agent Loop。

    Motive App (SwiftUI)
      └── BackendBridge (enum wrapper)
            ├── .opencode → OpenCodeBridge  (原有架构,保留)
            └── .sdk      → SDKBridge       (新增,用 OpenAgentSDK)
                  └── Agent.stream() → 直接在进程内跑 Agent Loop
    

    保留原有的 OpenCodeBridge 作为备选,让用户可以在设置中切换后端类型。这是一个务实的决定——万一 SDK 后端有问题,用户还能切回去。

    第一步:BackendBridge 抽象层

    原有的 OpenCodeBridge 是一个 actor,Motive 的 AppState 直接跟它交互。现在要加一个平行的 SDKBridge,需要一个分派层。

    我用了一个 enum 而不是 protocol:

    enum BackendBridge {
        case opencode(OpenCodeBridge)
        case sdk(SDKBridge)
    
        func submitIntent(text: String, cwd: String, ...) async { ... }
        func interrupt() async { ... }
        func stop() async { ... }
        // ...
    }
    

    为什么不用 protocol?因为 OpenCodeBridge 和 SDKBridge 的能力不完全一样。OpenCodeBridge 有权限请求(permission)、问题回复(question)等 SDK 后端不需要的概念。用 enum 可以在共享接口上做统一分派,同时保留各自特有的方法:

    // OpenCode-only 方法,SDK 后端直接 no-op
    func replyToQuestion(requestID: String, answers: [[String]], ...) async {
        guard case .opencode(let bridge) = self else { return }
        await bridge.replyToQuestion(requestID: requestID, answers: answers, ...)
    }
    

    对于 AppState 来说,大部分代码不需要改——它调 bridge.submitIntent(),至于底层是 HTTP 还是 SDK,它不关心。

    第二步:SDKBridge 核心——361 行的 Actor

    SDKBridge 是整个替换的核心。它是一个 actor,负责:

  • 接收 Configuration(API key、model、MCP servers 等)
  • 用 SDK 的 createAgent() 创建 Agent
  • 调用 Agent.stream() 获取流式响应
  • 把 SDK 的 SDKMessage 映射成 Motive 已有的 OpenCodeEvent
  • actor SDKBridge {
        struct Configuration: Sendable {
            let apiKey: String
            let model: String
            let provider: String        // "anthropic", "openai", etc.
            let baseURL: String?
            let debugMode: Bool
            let projectDirectory: String
            let mcpEntries: [String: MCPEntry]?
            let env: [String: String]?
            let skillDirectories: [String]?
        }
    
        struct MCPEntry: Sendable {
            let command: String
            let args: [String]?
            let env: [String: String]?
        }
    }
    

    MCPEntry 是中间类型——Motive 的配置系统有自己的 MCP 描述格式,在传入 SDK 之前转成


    🔗 原文链接: 点击阅读原文

    标签: AI 人工智能 技术博客
    最后更新:2026年5月1日

    daozi

    这个人很懒,什么都没留下

    点赞
    < 上一篇

    文章评论

    razz evil exclaim smile redface biggrin eek confused idea lol mad twisted rolleyes wink cool arrow neutral cry mrgreen drooling persevering
    取消回复
    搜索
    联系方式

    QQ群:179730949
    QQ群:114559024
    欢迎您加入Android大家庭
    本人QQ:136049925

    赐我一丝安慰
    给我一点鼓励

    COPYRIGHT © 2023 魅力程序猿. ALL RIGHTS RESERVED.

    Theme Kratos Made By Seaton Jiang

    豫ICP备15000477号