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

推荐订阅源

J
Java Code Geeks
F
Fortinet All Blogs
云风的 BLOG
云风的 BLOG
MyScale Blog
MyScale Blog
D
DataBreaches.Net
Stack Overflow Blog
Stack Overflow Blog
A
About on SuperTechFans
Google DeepMind News
Google DeepMind News
Microsoft Security Blog
Microsoft Security Blog
腾讯CDC
The GitHub Blog
The GitHub Blog
Jina AI
Jina AI
B
Blog RSS Feed
I
InfoQ
N
Netflix TechBlog - Medium
T
The Blog of Author Tim Ferriss
Microsoft Azure Blog
Microsoft Azure Blog
Recent Announcements
Recent Announcements
GbyAI
GbyAI
H
Help Net Security
L
LangChain Blog
M
MIT News - Artificial intelligence
Y
Y Combinator Blog
aimingoo的专栏
aimingoo的专栏

博客园 - 四眼蒙面侠

# 基于 Claude Code 的 Moltbook 心跳脚本:让你的 AI Agent 全自动参与社区 Moltbook 要把事情高大: AI 机器人的"身份证"来了 电视黑屏了,还在听吗?聊透 LG 智能电视隐私争议 Chrome 今年第六个零日漏洞:V8 认错了对象,而且真的有人在打 GLM-5.3 开放权重:该买机器把 AI 搬回家吗? 苹果没料到:Mac mini 怎么成了企业 AI 香饽饽 同一个模型,两道安全闸门:克劳德寓言 5.1 真正变了什么 AI 推荐的软件,到底是谁推荐的? GPT-6 Astra:破纪录之后,AGI 真的来了吗? OpenAI 的智能体,在德国老 wiki 上开了一块作弊黑板 一串会清空手机的密码,为什么可能换来五年牢狱? 作业高分,考试却跌两成:AI到底帮你学了什么? GitHub 上的第二张假面:一个大学生如何拦住会骗人的 AI 从十一到九十九,AI 创业公司为什么都爱叫 Labs? 卖十块板子,先交一千欧?欧洲包装新规为何难倒小卖家 玄戒 O3 跑分追上苹果,是真突破还是数字游戏? 当 AI 开始拆你的摄像头:桌面外设的安全边界正在消失 你用 AI 裁我,我就造个 AI CEO? 英伟达为什么想花 130 亿美元买下 Hugging Face? AI 已经会设计分子,为什么新药还没变快? 模型越强,为什么我们反而越不敢放手? DeepSeek Harness:为什么要把智能体的所有部件都做成插件 AI 让代码变便宜以后,工程师真正昂贵的是什么 加密的思考,也会被偷走吗?这篇论文真正发现了什么 一群模型,各干各的活:Nemotron 3.5 Lightning 和 Switchyard 到底解决什么 它真的“懂”你吗?用一杯咖啡理解大语言模型 买书、扫描、然后销毁:AI 训练的旧书去哪儿了? 七十GB与八十TB:当个人和科技巨头站上不同的法律天平 只读到小学五年级的 AI,能自己悟出高等知识吗? 低价 Token:省下的钱,够不够买回风险?
深入 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 渲染、代码高亮

相关链接