Sidekick代码定义分析:核心类与函数架构解析
核心管理类架构
Sidekick作为一款本地LLM对话应用,其架构围绕三大核心管理类构建:对话管理、专家系统和模型控制。这些类通过Swift的ObservableObject协议实现响应式状态管理,确保UI与数据实时同步。
ConversationManager:对话生命周期控制器
该类位于Sidekick/Logic/Data Models/ConversationManager.swift,负责所有对话数据的持久化与状态维护。核心属性包括:
@Published var conversations: [Conversation]:存储所有对话记录,变更时自动触发UI更新var recentConversation: Conversation?:获取最近更新的对话,无对话时自动创建
关键功能实现:
public func newConversation() {
let newConversation: Conversation = Conversation(
title: Date.now.formatted(),
createdAt: .now,
messages: []
)
self.conversations = [newConversation] + self.conversations
NotificationCenter.default.post(name: Notifications.newConversation.name, object: nil)
}
数据持久化通过JSON编解码实现,存储路径由datastoreUrl计算属性指定:
public var datastoreUrl: URL {
return Settings.containerUrl.appendingPathComponent("Conversations/conversations.json")
}
ExpertManager:专家角色系统
位于Sidekick/Logic/Data Models/ExpertManager.swift的专家管理系统支持多角色定制,核心特性包括:
- 专家资源管理:通过
resources属性关联本地文件、网页等知识源 - 系统提示定制:
systemPrompt属性支持角色行为定义 - 资源持久化控制:
persistResources开关控制跨会话数据保留
创建专家的核心代码:
public func newExpert(
name: String,
symbolName: String,
color: Color,
resources: [Resource]
) async {
var expert: Expert = Expert(name: name, symbolName: symbolName, color: color)
await expert.resources.setup()
await expert.resources.addResources(resources, expertName: name)
self.experts.append(expert)
}
数据模型定义
Conversation:对话数据结构
Sidekick/Types/Conversation/Conversation.swift定义了对话的核心数据结构,主要属性包括:
id: UUID:唯一标识符title: String:对话标题(默认使用创建时间)messages: [Message]:消息列表expertId: UUID?:关联专家ID
消息管理核心方法:
public mutating func addMessage(_ message: Message) -> Bool {
let lastSender: Sender? = self.messages.last?.getSender()
if lastSender != nil && lastSender == message.getSender() {
return false // 防止连续发送相同角色消息
}
self.messages.append(message)
return true
}
Expert:专家配置模型
Sidekick/Types/Expert/Expert.swift定义了专家角色的核心属性:
public struct Expert: Identifiable, Codable {
public var id: UUID = UUID()
public var name: String
public var symbolName: String // SF Symbol图标名称
public var color: Color
public var useWebSearch: Bool = true
public var resources: Resources = Resources()
public var systemPrompt: String? = nil
}
系统默认专家通过静态属性实现:
public static let `default`: Expert = Expert(
name: String(localized: "Default"),
symbolName: "person.fill",
color: Color.blue,
useWebSearch: false,
persistResources: false
)
关键功能控制器
CanvasController:可视化交互核心
Sidekick/Logic/View Controllers/Conversation/CanvasController.swift实现了对话内容的可视化提取功能。核心方法extractSnapshot通过以下流程工作:
- 定位最新助手消息
- 使用worker模型提取纯内容
- 创建快照并关联到消息
核心实现代码:
public func extractSnapshot(selectedConversation: Conversation?) async throws {
guard var assistantMessage = selectedConversation?.messages
.filter({ $0.getSender() == .assistant }).last else {
throw Snapshot.ExtractionError.noAssistantMessages
}
let snapshot: Snapshot = Snapshot(
text: response.text.reasoningRemoved.trimmingWhitespaceAndNewlines()
)
assistantMessage.snapshot = snapshot
selectedConversation?.updateMessage(assistantMessage)
}
ModelManager:本地模型控制器
Sidekick/Logic/Data Models/ModelManager.swift负责本地LLM模型的管理,支持:
- 模型文件选择与验证
- 模型元数据持久化
- 多模型切换
模型添加流程:
public func addModel() -> Bool {
if let modelUrls = try? FileManager.selectFile(
dialogTitle: String(localized: "Select a Model"),
allowedContentTypes: [UTType("com.npc-pet.Chats.gguf") ?? .data]
) {
guard let modelUrl = modelUrls.first else { return false }
ModelManager.shared.add(modelUrl)
return true
}
return false
}
架构交互关系
三大核心模块通过以下方式协同工作:
- 数据流:用户输入 → ConversationManager创建消息 → ModelManager获取LLM响应 → 更新UI
- 状态同步:各管理类通过
@Published属性实现状态共享,确保多视图一致性 - 扩展点:Expert的resources系统支持接入新类型知识源,ModelManager可扩展支持更多模型格式
关键函数性能分析
对话序列化性能
ConversationManager的save()方法使用JSONEncoder对整个对话数组进行编码:
public func save() {
let rawData: Data = try JSONEncoder().encode(self.conversations)
try rawData.write(to: self.datastoreUrl, options: .atomic)
}
对于包含大量消息的对话,此实现可能导致性能瓶颈。建议考虑:
- 增量序列化仅变更对话
- 分页加载历史消息
专家资源索引优化
Expert.resources的addResources方法需要异步处理文件扫描:
public func addResources(_ resources: [Resource], expertName: String) async {
for resource in resources {
switch resource.type {
case .file, .folder:
await self.indexFile(resource: resource, expertName: expertName)
case .website:
await self.indexWebsite(resource: resource)
}
}
}
当前实现为顺序处理,可通过并发调度优化大资源集的索引速度。
总结与扩展建议
Sidekick的核心架构采用清晰的职责分离设计,通过响应式状态管理实现流畅的用户体验。主要改进方向包括:
-
性能优化:
- 实现对话数据的增量持久化
- 优化大型资源库的索引效率
-
功能扩展:
- 通过Sidekick/Types/Conversation/Functions/扩展函数调用能力
- 增强CanvasController的可视化编辑功能
-
架构演进:
- 引入中间件模式处理复杂业务逻辑
- 实现插件系统支持第三方扩展
完整代码结构可参考项目Markdown文档,核心业务逻辑集中在Sidekick/Logic/目录,UI组件则位于Sidekick/Views/目录。
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考



