惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

B
Blog
量子位
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
Last Week in AI
Last Week in AI
酷 壳 – CoolShell
酷 壳 – CoolShell
人人都是产品经理
人人都是产品经理
Jina AI
Jina AI
雷峰网
雷峰网
博客园_首页
WordPress大学
WordPress大学
博客园 - 司徒正美
爱范儿
爱范儿
博客园 - 聂微东
IT之家
IT之家
美团技术团队
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
博客园 - 三生石上(FineUI控件)
有赞技术团队
有赞技术团队
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
T
Tailwind CSS Blog
博客园 - Franky
V
V2EX
GbyAI
GbyAI
阮一峰的网络日志
阮一峰的网络日志

博客园_首页

Linux实操--组管理、权限管理和定时任务 Java + EasyExcel 实现单个接口导出多个Excel Mem0 源码解析系列(二):提示词工程的深度剖析 Openclaw TaskFlow究竟是什么?和普通Skill技能有什么区别 博文阅读密码验证 - 博客园 嘉立创开源:应该是全网MicroPython教程最多的开发板 Hermes Agent 集成实践:从协议到生产 2026年AI编程工具横评:Cursor、Codex、Claude Code、Zed、Windsurf Java程序员必看的RAG入门教程 2026 AI效率神器:Superpowers + Claude Code 保姆级教程 本地大模型部署全攻略:从 0 到 1 玩转 Ollama 【从0到1构建一个ClaudeAgent】内存管理-上下文压缩 .NET 高级开发 | 设计、实现一个事件总线框架 电子小白入门之NE555 3. WorkBuddy:隐藏玩法,一键召唤专家,让 AI 以"专家身份"给你干活 和AI一起搞事情#3:Claude Teammate 游戏开发翻车实录 【OpenClaw】通过 Nanobot 源码学习架构---(7)Memory C# .NET 周刊|2026年3月3期 我在 Debian 11 上把 K8s 单机搭起来了,过程没你想的那么顺(/opt 目录版) 深度学习进阶(七)Data-efficient Image Transformer CLI+Skill搭建浏览器AI自动化框架,告别一切重复枯燥任务 告别Token账单无底洞:OpenClaw本地部署,重塑企业数据主权的唯一解 FastAPI+Vue:文件分片上传+秒传+断点续传,这坑我帮你踩平了! SBTI 爆火后,我做了个程序员版的 CBTI。。已开源 + 附开发过程 多模态检索开始进入工程期:用 Sentence Transformers 搭建可落地的 Multimodal RAG 100多行代码实现一个最简单的Agent(用ReAct) Claude Code 通关手册(八):推荐 5 个 Hooks,代码质量提升 3 倍 老板:“有人截图了!”。安全部门:“收到,马上查暗水印!” - why技术 技术之外,皆是人间 C#/.NET/.NET Core技术前沿周刊 | 第 69 期(2026年4.01-4.12)
深入 SwiftWork(第 0 篇):用 SwiftUI 构建一个 Agent 可视...
四眼蒙面侠 · 2026-05-02 · via 博客园_首页

本文是「深入 SwiftWork」系列第 0 篇。

前面七篇文章加上番外篇,我们把 Open Agent SDK 的内部机制翻了个底朝天——Agent Loop、工具系统、MCP 集成、多 Agent 协作、会话持久化、多 LLM 支持。番外篇还把 SDK 塞进了一个 macOS 原生应用 Motive 里跑了跑。

但 Motive 只是一个替换后端的实验。真正的问题是:拿到 SDK 之后,怎么从零开始构建一个完整的 Agent 应用? SDK 给了你 Agent 的"大脑"——但用户看到的不是 Agent Loop,而是一个界面。Agent 在调工具、读文件、执行命令的时候,用户需要知道它在干什么、进展如何、结果是什么。

这就是 SwiftWork 要解决的问题——一个 macOS 原生的 Agent 可视化工作台。

SwiftWork 是什么

SwiftWork 是一个 macOS 原生的 AI Agent 桌面应用。它做的事情用一句话概括:让用户看到 Agent 正在做什么。

具体来说:

  • 用户在输入框输入 prompt
  • Agent 在后台跑 Agent Loop(调工具、读文件、执行命令)
  • 每一步执行都实时显示在时间线上——文本输出、工具调用、执行结果、错误信息,全部可视化

这不是一个终端里的 CLI 工具,也不是一个网页应用。它是用 SwiftUI 写的原生 macOS 应用,用 @Observable 做状态管理,用 SwiftData 做数据持久化,用 Apple 原生的渲染管线做 Markdown 和代码高亮。

为什么做 SwiftWork

做 SwiftWork 有两个动机。

第一,SDK 需要一个"展示应用"。 SDK 的 31 个示例项目覆盖了各种用法——流式输出、自定义工具、MCP 集成——但都是命令行工具。SDK 的能力需要一个 GUI 来完整展示,尤其是工具调用的可视化、事件流的实时渲染这些 CLI 做不好的事情。

第二,Agent 应用的可视化是一个被低估的问题。 当前的 Agent 应用(包括 Claude Code 自己)在终端里跑,用户看到的是滚动的文字流。但 Agent 在执行一个复杂任务时,可能调用十几次工具、读写多个文件、执行多条命令。终端里的线性输出很难让用户理解全局进展。SwiftWork 试图用事件时间线和工具卡片来改善这个问题。

架构总览

SwiftWork 采用事件驱动架构。整条数据流是一条单向管线:

SDK Agent Loop
  │
  │  AsyncStream<SDKMessage>
  ▼
AgentBridge (@Observable)
  │
  │  EventMapper.map() → AgentEvent
  ▼
AgentBridge.events: [AgentEvent]
  │
  │  SwiftUI 自动响应 @Observable 变化
  ▼
TimelineView → 各 EventView

四个角色:

组件 职责
Agent Loop SDK 提供,跑 Agent 的推理循环,产出 SDKMessage
AgentBridge 消费 AsyncStream<SDKMessage>,映射成 AgentEvent,管理生命周期
EventMapper 纯函数,SDKMessage → AgentEvent 的类型映射
TimelineView SwiftUI 视图,消费 AgentEvent 数组,渲染时间线

核心设计决策:视图不直接接触 SDK 类型。 AgentEvent 是 SwiftWork 自己定义的 UI 模型,跟 SDK 的 SDKMessage 完全解耦。视图只认 AgentEvent,不知道也不关心事件来自哪个 SDK 版本。

核心数据流

用户发一条消息,整条管线的运转过程:

1. 用户输入 → Agent 启动

// AgentBridge.swift
func sendMessage(_ text: String) {
    // 用户消息直接追加到事件列表
    let userEvent = AgentEvent(type: .userMessage, content: text, timestamp: .now)
    appendAndPersist(userEvent)

    isRunning = true

    // 在后台 Task 中消费 stream
    currentTask = Task { [weak self] in
        let stream = agent.stream(text)
        for await message in stream {
            let event = EventMapper.map(message)
            self.appendAndPersist(event)
        }
        self.isRunning = false
    }
}

用户消息先追加到事件列表(即时显示),然后启动一个 Task 来消费 SDK 的 AsyncStream

2. SDKMessage → AgentEvent

EventMapper 是一个纯函数,把 SDK 的 18 种 SDKMessage 映射成 SwiftWork 的 AgentEventType

// EventMapper.swift
static func map(_ message: SDKMessage) -> AgentEvent {
    switch message {
    case .assistant(let data):
        return AgentEvent(type: .assistant, content: data.text,
            metadata: ["model": data.model, "stopReason": data.stopReason], timestamp: .now)
    case .toolUse(let data):
        return AgentEvent(type: .toolUse, content: data.toolName,
            metadata: ["toolName": data.toolName, "toolUseId": data.toolUseId, "input": data.input],
            timestamp: .now)
    case .toolResult(let data):
        return AgentEvent(type: .toolResult, content: data.content,
            metadata: ["toolUseId": data.toolUseId, "isError": data.isError], timestamp: .now)
    // ... 18 种消息类型
    }
}

为什么要有这一层映射?因为 SDK 的类型是给 Agent 运行时用的,它包含很多 UI 不需要的细节。AgentEvent 只保留 UI 渲染需要的字段:类型、内容、元数据、时间戳。视图不需要知道 SDKMessage 的枚举定义,只需要处理 AgentEventType

3. 事件追加 + 持久化

每条事件都经过 appendAndPersist,同时更新内存数组和 SwiftData 数据库:

private func appendAndPersist(_ event: AgentEvent) {
    events.append(event)
    processToolContentMap(for: event)

    guard event.type != .partialMessage,
          let eventStore, let currentSession else { return }

    try eventStore.persist(event, session: currentSession, order: eventOrder)
    eventOrder += 1

    trimOldEvents()
}

注意 partialMessage 不持久化——它是流式文本的中间片段,累积完成后会生成一条完整的 .assistant 事件。

4. SwiftUI 自动渲染

AgentBridge 标记了 @Observable。当 events 数组变化时,TimelineView 自动重新渲染:

// TimelineView.swift
ForEach(virtualizedEvents) { event in
    eventView(for: event)
}

eventView 根据 event.type 分派到不同的视图组件——UserMessageViewAssistantMessageViewToolCardViewSystemEventView 等。

项目结构

SwiftWork/
├── App/
│   ├── SwiftWorkApp.swift            # @main 入口,注册 SwiftData 模型
│   └── ContentView.swift             # NavigationSplitView 根视图
├── Models/
│   ├── UI/                           # UI 模型层
│   │   ├── AgentEvent.swift          # 事件模型(SwiftUI 渲染用)
│   │   ├── AgentEventType.swift      # 18 种事件类型枚举
│   │   ├── ToolContent.swift         # 工具内容(配对 toolUse + toolResult)
│   │   ├── PermissionDecision.swift  # 权限决策
│   │   └── AppError.swift            # 错误模型
│   └── SwiftData/                    # 持久化模型层
│       ├── Session.swift             # 会话
│       ├── Event.swift               # 持久化事件
│       ├── AppConfiguration.swift    # 应用配置
│       └── PermissionRule.swift      # 权限规则
├── ViewModels/
│   ├── SessionViewModel.swift        # 会话 CRUD
│   └── SettingsViewModel.swift       # 设置管理
├── Views/
│   ├── Sidebar/                      # 会话列表
│   ├── Workspace/
│   │   ├── Timeline/
│   │   │   ├── TimelineView.swift    # 时间线主视图 + 虚拟化
│   │   │   ├── EventViews/           # 各事件类型视图 + ToolCardView
│   │   │   │   ├── ToolRenderers/    # 5 个内置工具渲染器
│   │   │   │   ├── StreamingTextView.swift
│   │   │   │   ├── MarkdownContentView.swift
│   │   │   │   └── ...
│   │   │   └── Inspector/            # 事件详情面板
│   │   └── InputBar/                 # 消息输入框
│   ├── Settings/                     # 设置界面
│   ├── Onboarding/                   # 首次启动引导
│   └── Permission/                   # 权限审批弹窗
├── SDKIntegration/
│   ├── AgentBridge.swift             # SDK ↔ ViewModel 桥接
│   ├── AgentBridge+ToolContentMap.swift  # 工具内容配对逻辑
│   ├── EventMapper.swift             # SDKMessage → AgentEvent
│   ├── ToolRenderable.swift          # 工具渲染协议
│   └── ToolRendererRegistry.swift    # 工具渲染注册表
├── Services/
│   ├── CodeHighlighter.swift         # Splash 代码高亮
│   ├── MarkdownRenderer.swift        # swift-markdown 渲染
│   ├── KeychainManager.swift         # API Key 安全存储
│   ├── EventStore.swift              # 事件持久化接口
│   ├── AppStateManager.swift         # 应用状态保存/恢复
│   └── TitleGenerator.swift          # 自动生成会话标题
└── Utils/
    └── Extensions/                   # 颜色、日期格式化等

结构上的核心分层:

  • Models/UI/Models/SwiftData/ 是两套独立的模型层。UI 模型(AgentEvent)是给 SwiftUI 渲染用的,SwiftData 模型(Event)是给持久化用的。两者之间有转换逻辑。
  • SDKIntegration/ 是 SDK 和 UI 之间的桥梁层。视图和 ViewModel 不直接 import OpenAgentSDK
  • Views/ 按功能分区,每个事件类型一个视图文件,工具渲染器有独立的子目录。

技术选型

组件 选择 原因
语言 Swift 6.1 严格并发 Agent SDK 要求,Sendable 保证线程安全
UI SwiftUI + @Observable macOS 14+ 支持,跟 Swift 并发配合好
持久化 SwiftData 跟 SwiftUI 深度集成,比 Core Data 简洁
Markdown swift-markdown (Apple) 原生 Apple 库,CommonMark 兼容
代码高亮 Splash (John Sundell) 轻量、支持 Swift/Python/JS/Bash
自动更新 Sparkle 2.x macOS 应用更新的标准方案
Agent SDK Open Agent SDK 自己写的 SDK,当然用自己的

系列预告

这篇文章给了一个全景图。接下来的文章会逐层拆开,看每个子系统怎么实现:

  • 第 1 篇:SDK 集成层——AgentBridge 如何消费 AsyncStream<SDKMessage>、映射事件、管理生命周期
  • 第 2 篇:事件时间线——18 种事件的可视化、流式文本、虚拟化
  • 第 3 篇:Tool Card 系统——ToolRenderable 协议和可扩展的工具渲染器
  • 第 4 篇:数据层与服务——SwiftData 持久化、状态恢复、Markdown 渲染、代码高亮

相关链接